API  1.0.0
CPScanner.j
Go to the documentation of this file.
1 /*
2  * CPScanner.j
3  * Foundation
4  *
5  * Created by Emanuele Vulcano.
6  * Copyright 2008, Emanuele Vulcano.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 @implementation CPScanner : CPObject
25 {
26  CPString _string;
27  CPDictionary _locale;
28  int _scanLocation;
29  BOOL _caseSensitive;
30  CPCharacterSet _charactersToBeSkipped;
31 }
32 
33 + (id)scannerWithString:(CPString)aString
34 {
35  return [[self alloc] initWithString:aString];
36 }
37 
38 - (id)initWithString:(CPString)aString
39 {
40  if (self = [super init])
41  {
42  _string = [aString copy];
43  _scanLocation = 0;
44  _charactersToBeSkipped = [CPCharacterSet whitespaceCharacterSet];
45  _caseSensitive = NO;
46  }
47 
48  return self;
49 }
50 
51 - (id)copy
52 {
53  var copy = [[CPScanner alloc] initWithString:[self string]];
54 
55  [copy setCharactersToBeSkipped:[self charactersToBeSkipped]];
56  [copy setCaseSensitive:[self caseSensitive]];
57  [copy setLocale:[self locale]];
58  [copy setScanLocation:[self scanLocation]];
59 
60  return copy;
61 }
62 
63 - (CPDictionary)locale
64 {
65  return _locale;
66 }
67 
68 - (void)setLocale:(CPDictionary)aLocale
69 {
70  _locale = aLocale;
71 }
72 
73 - (void)setCaseSensitive:(BOOL)flag
74 {
75  _caseSensitive = flag;
76 }
77 
78 - (BOOL)caseSensitive
79 {
80  return _caseSensitive;
81 }
82 
83 - (CPString)string
84 {
85  return _string;
86 }
87 
88 - (CPCharacterSet)charactersToBeSkipped
89 {
90  return _charactersToBeSkipped;
91 }
92 
93 - (void)setCharactersToBeSkipped:(CPCharacterSet)c
94 {
95  _charactersToBeSkipped = c;
96 }
97 
98 - (BOOL)isAtEnd
99 {
100  return _scanLocation == _string.length;
101 }
102 
103 - (int)scanLocation
104 {
105  return _scanLocation;
106 }
107 
108 - (void)setScanLocation:(int)aLocation
109 {
110  if (aLocation > _string.length)
111  aLocation = _string.length; // clamp to just after the last character
112  else if (aLocation < 0)
113  aLocation = 0; // clamp to the first
114 
115  _scanLocation = aLocation;
116 }
117 
118 // Method body for all methods that return their value by reference.
119 - (BOOL)_performScanWithSelector:(SEL)s withObject:(id)arg into:(id)ref
120 {
121  var ret = [self performSelector:s withObject:arg];
122 
123  if (ret == nil)
124  return NO;
125 
126  if (ref != nil)
127  ref(ret);
128 
129  return YES;
130 }
131 
132 /* ================================ */
133 /* = Scanning with CPCharacterSet = */
134 /* ================================ */
135 
136 - (BOOL)scanCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref
137 {
138  return [self _performScanWithSelector:@selector(scanCharactersFromSet:) withObject:scanSet into:ref];
139 }
140 
141 - (CPString)scanCharactersFromSet:(CPCharacterSet)scanSet
142 {
143  return [self _scanWithSet:scanSet breakFlag:NO];
144 }
145 
146 - (BOOL)scanUpToCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref
147 {
148  return [self _performScanWithSelector:@selector(scanUpToCharactersFromSet:) withObject:scanSet into:ref];
149 }
150 
151 - (CPString)scanUpToCharactersFromSet:(CPCharacterSet)scanSet
152 {
153  return [self _scanWithSet:scanSet breakFlag:YES];
154 }
155 
156 // If stop == YES, it will stop when it sees a character from
157 // the set (scanUpToCharactersFromSet:); if stop == NO, it will
158 // stop when it sees a character NOT from the set
159 // (scanCharactersFromSet:).
160 - (CPString)_scanWithSet:(CPCharacterSet)scanSet breakFlag:(BOOL)stop
161 {
162  if ([self isAtEnd])
163  return nil;
164 
165  var current = [self scanLocation],
166  str = nil;
167 
168  while (current < _string.length)
169  {
170  var c = (_string.charAt(current));
171 
172  if ([scanSet characterIsMember:c] == stop)
173  break;
174 
175  if (![_charactersToBeSkipped characterIsMember:c])
176  {
177  if (!str)
178  str = '';
179  str += c;
180  }
181 
182  current++;
183  }
184 
185  if (str)
186  [self setScanLocation:current];
187 
188  return str;
189 }
190 
191 /* ==================== */
192 /* = Scanning strings = */
193 /* ==================== */
194 
195 - (void)_movePastCharactersToBeSkipped
196 {
197  var current = [self scanLocation],
198  string = [self string],
199  toSkip = [self charactersToBeSkipped];
200 
201  while (current < string.length)
202  {
203  if (![toSkip characterIsMember:string.charAt(current)])
204  break;
205 
206  current++;
207  }
208 
209  [self setScanLocation:current];
210 }
211 
212 
213 - (BOOL)scanString:(CPString)aString intoString:(id)ref
214 {
215  return [self _performScanWithSelector:@selector(scanString:) withObject:aString into:ref];
216 }
217 
218 - (CPString)scanString:(CPString)s
219 {
220  [self _movePastCharactersToBeSkipped];
221  if ([self isAtEnd])
222  return nil;
223 
224  var currentStr = [self string].substr([self scanLocation], s.length);
225  if ((_caseSensitive && currentStr != s) || (!_caseSensitive && (currentStr.toLowerCase() != s.toLowerCase())))
226  {
227  return nil;
228  }
229  else
230  {
231  [self setScanLocation:[self scanLocation] + s.length];
232  return s;
233  }
234 }
235 
236 - (BOOL)scanUpToString:(CPString)aString intoString:(id)ref
237 {
238  return [self _performScanWithSelector:@selector(scanUpToString:) withObject:aString into:ref];
239 }
240 
241 - (CPString)scanUpToString:(CPString)s
242 {
243  var current = [self scanLocation],
244  str = [self string],
245  captured = nil;
246 
247  while (current < str.length)
248  {
249  var currentStr = str.substr(current, s.length);
250  if (currentStr == s || (!_caseSensitive && currentStr.toLowerCase() == s.toLowerCase()))
251  break;
252 
253  if (!captured)
254  captured = '';
255  captured += str.charAt(current);
256  current++;
257  }
258 
259  if (captured)
260  [self setScanLocation:current];
261 
262  // evil private method use!
263  // this method is defined in the category on CPString
264  // in CPCharacterSet.j
265  if ([self charactersToBeSkipped])
266  captured = [captured _stringByTrimmingCharactersInSet:[self charactersToBeSkipped] options:_CPCharacterSetTrimAtBeginning];
267 
268  return captured;
269 }
270 
271 /* ==================== */
272 /* = Scanning numbers = */
273 /* ==================== */
274 
275 - (float)scanWithParseFunction:(Function)parseFunction
276 {
277  [self _movePastCharactersToBeSkipped];
278  var str = [self string],
279  loc = [self scanLocation];
280 
281  if ([self isAtEnd])
282  return 0;
283 
284  var s = str.substring(loc, str.length),
285  f = parseFunction(s);
286 
287  if (isNaN(f))
288  return nil;
289 
290  loc += (""+f).length;
291  var i = 0;
292  while (!isNaN(parseFloat(str.substring(loc + i, str.length))))
293  {i++;}
294 
295  [self setScanLocation:loc + i];
296  return f;
297 
298 }
299 
300 - (float)scanFloat
301 {
302  return [self scanWithParseFunction:parseFloat];
303 }
304 
305 - (int)scanInt
306 {
307  return [self scanWithParseFunction:parseInt];
308 }
309 
310 - (BOOL)scanInt:(int)intoInt
311 {
312  return [self _performScanWithSelector:@selector(scanInt) withObject:nil into:intoInt];
313 }
314 
315 - (BOOL)scanFloat:(float)intoFloat
316 {
317  return [self _performScanWithSelector:@selector(scanFloat) withObject:nil into:intoFloat];
318 }
319 
320 - (BOOL)scanDouble:(float)intoDouble
321 {
322  return [self scanFloat:intoDouble];
323 }
324 
325 /* ========= */
326 /* = Debug = */
327 /* ========= */
328 
330 {
331  return [super description] + " {" + CPStringFromClass([self class]) + ", state = '" + ([self string].substr(0, _scanLocation) + "{{ SCAN LOCATION ->}}" + [self string].substr(_scanLocation)) + "'; }";
332 }
333 
334 @end
void setScanLocation:(int aLocation)
Definition: CPScanner.j:108
id init()
Definition: CALayer.j:126
function CPStringFromClass(aClass)
Definition: CPObjJRuntime.j:38
int scanLocation()
Definition: CPScanner.j:103
id copy()
Definition: CPScanner.j:51
BOOL caseSensitive()
Definition: CPScanner.j:78
CPString description()
Definition: CPObject.j:358
A mutable key-value pair collection.
Definition: CPDictionary.h:2
FrameUpdater prototype stop
An immutable string (collection of characters).
Definition: CPString.h:2
BOOL scanFloat:(float intoFloat)
Definition: CPScanner.j:315
CPCharacterSet charactersToBeSkipped()
Definition: CPScanner.j:88
int length()
Definition: CPString.j:186
id copy()
Definition: CPObject.j:154
float scanWithParseFunction:(Function parseFunction)
Definition: CPScanner.j:275
CPString string()
Definition: CPScanner.j:83
CPDictionary locale()
Definition: CPScanner.j:63
id alloc()
Definition: CPObject.j:130
FrameUpdater prototype description