![]() |
API 0.9.5
|
00001 /* 00002 * CPScanner.j 00003 * Foundation 00004 * 00005 * Created by Emanuele Vulcano. 00006 * Copyright 2008, Emanuele Vulcano. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 @implementation CPScanner : CPObject 00025 { 00026 CPString _string; 00027 CPDictionary _locale; 00028 int _scanLocation; 00029 BOOL _caseSensitive; 00030 CPCharacterSet _charactersToBeSkipped; 00031 } 00032 00033 + (id)scannerWithString:(CPString)aString 00034 { 00035 return [[self alloc] initWithString:aString]; 00036 } 00037 00038 - (id)initWithString:(CPString)aString 00039 { 00040 if (self = [super init]) 00041 { 00042 _string = [aString copy]; 00043 _scanLocation = 0; 00044 _charactersToBeSkipped = [CPCharacterSet whitespaceCharacterSet]; 00045 _caseSensitive = NO; 00046 } 00047 00048 return self; 00049 } 00050 00051 - (id)copy 00052 { 00053 var copy = [[CPScanner alloc] initWithString:[self string]]; 00054 00055 [copy setCharactersToBeSkipped:[self charactersToBeSkipped]]; 00056 [copy setCaseSensitive:[self caseSensitive]]; 00057 [copy setLocale:[self locale]]; 00058 [copy setScanLocation:[self scanLocation]]; 00059 00060 return copy; 00061 } 00062 00063 - (CPDictionary)locale 00064 { 00065 return _locale; 00066 } 00067 00068 - (void)setLocale:(CPDictionary)aLocale 00069 { 00070 _locale = aLocale; 00071 } 00072 00073 - (void)setCaseSensitive:(BOOL)flag 00074 { 00075 _caseSensitive = flag; 00076 } 00077 00078 - (BOOL)caseSensitive 00079 { 00080 return _caseSensitive; 00081 } 00082 00083 - (CPString)string 00084 { 00085 return _string; 00086 } 00087 00088 - (CPCharacterSet)charactersToBeSkipped 00089 { 00090 return _charactersToBeSkipped; 00091 } 00092 00093 - (void)setCharactersToBeSkipped:(CPCharacterSet)c 00094 { 00095 _charactersToBeSkipped = c; 00096 } 00097 00098 - (BOOL)isAtEnd 00099 { 00100 return _scanLocation == _string.length; 00101 } 00102 00103 - (int)scanLocation 00104 { 00105 return _scanLocation; 00106 } 00107 00108 - (void)setScanLocation:(int)aLocation 00109 { 00110 if (aLocation > _string.length) 00111 aLocation = _string.length; // clamp to just after the last character 00112 else if (aLocation < 0) 00113 aLocation = 0; // clamp to the first 00114 00115 _scanLocation = aLocation; 00116 } 00117 00118 // Method body for all methods that return their value by reference. 00119 - (BOOL)_performScanWithSelector:(SEL)s withObject:(id)arg into:(id)ref 00120 { 00121 var ret = [self performSelector:s withObject:arg]; 00122 00123 if (ret == nil) 00124 return NO; 00125 00126 if (ref != nil) 00127 ref(ret); 00128 00129 return YES; 00130 } 00131 00132 /* ================================ */ 00133 /* = Scanning with CPCharacterSet = */ 00134 /* ================================ */ 00135 00136 - (BOOL)scanCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref 00137 { 00138 return [self _performScanWithSelector:@selector(scanCharactersFromSet:) withObject:scanSet into:ref]; 00139 } 00140 00141 - (CPString)scanCharactersFromSet:(CPCharacterSet)scanSet 00142 { 00143 return [self _scanWithSet:scanSet breakFlag:NO]; 00144 } 00145 00146 - (BOOL)scanUpToCharactersFromSet:(CPCharacterSet)scanSet intoString:(id)ref 00147 { 00148 return [self _performScanWithSelector:@selector(scanUpToCharactersFromSet:) withObject:scanSet into:ref]; 00149 } 00150 00151 - (CPString)scanUpToCharactersFromSet:(CPCharacterSet)scanSet 00152 { 00153 return [self _scanWithSet:scanSet breakFlag:YES]; 00154 } 00155 00156 // If stop == YES, it will stop when it sees a character from 00157 // the set (scanUpToCharactersFromSet:); if stop == NO, it will 00158 // stop when it sees a character NOT from the set 00159 // (scanCharactersFromSet:). 00160 - (CPString)_scanWithSet:(CPCharacterSet)scanSet breakFlag:(BOOL)stop 00161 { 00162 if ([self isAtEnd]) 00163 return nil; 00164 00165 var current = [self scanLocation], 00166 str = nil; 00167 00168 while (current < _string.length) 00169 { 00170 var c = (_string.charAt(current)); 00171 00172 if ([scanSet characterIsMember:c] == stop) 00173 break; 00174 00175 if (![_charactersToBeSkipped characterIsMember:c]) 00176 { 00177 if (!str) 00178 str = ''; 00179 str += c; 00180 } 00181 00182 current++; 00183 } 00184 00185 if (str) 00186 [self setScanLocation:current]; 00187 00188 return str; 00189 } 00190 00191 /* ==================== */ 00192 /* = Scanning strings = */ 00193 /* ==================== */ 00194 00195 - (void)_movePastCharactersToBeSkipped 00196 { 00197 var current = [self scanLocation], 00198 string = [self string], 00199 toSkip = [self charactersToBeSkipped]; 00200 00201 while (current < string.length) 00202 { 00203 if (![toSkip characterIsMember:string.charAt(current)]) 00204 break; 00205 00206 current++; 00207 } 00208 00209 [self setScanLocation:current]; 00210 } 00211 00212 00213 - (BOOL)scanString:(CPString)aString intoString:(id)ref 00214 { 00215 return [self _performScanWithSelector:@selector(scanString:) withObject:aString into:ref]; 00216 } 00217 00218 - (CPString)scanString:(CPString)s 00219 { 00220 [self _movePastCharactersToBeSkipped]; 00221 if ([self isAtEnd]) 00222 return nil; 00223 00224 var currentStr = [self string].substr([self scanLocation], s.length); 00225 if ((_caseSensitive && currentStr != s) || (!_caseSensitive && (currentStr.toLowerCase() != s.toLowerCase()))) 00226 { 00227 return nil; 00228 } 00229 else 00230 { 00231 [self setScanLocation:[self scanLocation] + s.length]; 00232 return s; 00233 } 00234 } 00235 00236 - (BOOL)scanUpToString:(CPString)aString intoString:(id)ref 00237 { 00238 return [self _performScanWithSelector:@selector(scanUpToString:) withObject:aString into:ref]; 00239 } 00240 00241 - (CPString)scanUpToString:(CPString)s 00242 { 00243 var current = [self scanLocation], 00244 str = [self string], 00245 captured = nil; 00246 00247 while (current < str.length) 00248 { 00249 var currentStr = str.substr(current, s.length); 00250 if (currentStr == s || (!_caseSensitive && currentStr.toLowerCase() == s.toLowerCase())) 00251 break; 00252 00253 if (!captured) 00254 captured = ''; 00255 captured += str.charAt(current); 00256 current++; 00257 } 00258 00259 if (captured) 00260 [self setScanLocation:current]; 00261 00262 // evil private method use! 00263 // this method is defined in the category on CPString 00264 // in CPCharacterSet.j 00265 if ([self charactersToBeSkipped]) 00266 captured = [captured _stringByTrimmingCharactersInSet:[self charactersToBeSkipped] options:_CPCharacterSetTrimAtBeginning]; 00267 00268 return captured; 00269 } 00270 00271 /* ==================== */ 00272 /* = Scanning numbers = */ 00273 /* ==================== */ 00274 00275 - (float)scanWithParseFunction:(Function)parseFunction 00276 { 00277 [self _movePastCharactersToBeSkipped]; 00278 var str = [self string], 00279 loc = [self scanLocation]; 00280 00281 if ([self isAtEnd]) 00282 return 0; 00283 00284 var s = str.substring(loc, str.length), 00285 f = parseFunction(s); 00286 00287 if (isNaN(f)) 00288 return nil; 00289 00290 loc += (""+f).length; 00291 var i = 0; 00292 while (!isNaN(parseFloat(str.substring(loc+i, str.length)))) 00293 {i++;} 00294 00295 [self setScanLocation:loc + i]; 00296 return f; 00297 00298 } 00299 00300 - (float)scanFloat 00301 { 00302 return [self scanWithParseFunction:parseFloat]; 00303 } 00304 00305 - (int)scanInt 00306 { 00307 return [self scanWithParseFunction:parseInt]; 00308 } 00309 00310 - (BOOL)scanInt:(int)intoInt 00311 { 00312 return [self _performScanWithSelector:@selector(scanInt) withObject:nil into:intoInt]; 00313 } 00314 00315 - (BOOL)scanFloat:(float)intoFloat 00316 { 00317 return [self _performScanWithSelector:@selector(scanFloat) withObject:nil into:intoFloat]; 00318 } 00319 00320 - (BOOL)scanDouble:(float)intoDouble 00321 { 00322 return [self scanFloat:intoDouble]; 00323 } 00324 00325 /* ========= */ 00326 /* = Debug = */ 00327 /* ========= */ 00328 00329 - (void)description 00330 { 00331 return [super description] + " {" + CPStringFromClass([self class]) + ", state = '" + ([self string].substr(0, _scanLocation) + "{{ SCAN LOCATION ->}}" + [self string].substr(_scanLocation)) + "'; }"; 00332 } 00333 00334 @end