API 0.9.5
Foundation/CPScanner.j
Go to the documentation of this file.
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
 All Classes Files Functions Variables Defines