~jose-exposito89/ubuntuone-ios-files/social-networking

« back to all changes in this revision

Viewing changes to Dependencies/ShareKit/Submodules/facebook-ios-sdk/src/JSON/SBJsonParser.m

  • Committer: José Expósito
  • Date: 2012-10-23 15:22:36 UTC
  • Revision ID: jose.exposito89@gmail.com-20121023152236-f2rfafq000ex0vgz
Added support to share links in social networks

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 Copyright (C) 2009 Stig Brautaset. All rights reserved.
 
3
 
 
4
 Redistribution and use in source and binary forms, with or without
 
5
 modification, are permitted provided that the following conditions are met:
 
6
 
 
7
 * Redistributions of source code must retain the above copyright notice, this
 
8
   list of conditions and the following disclaimer.
 
9
 
 
10
 * Redistributions in binary form must reproduce the above copyright notice,
 
11
   this list of conditions and the following disclaimer in the documentation
 
12
   and/or other materials provided with the distribution.
 
13
 
 
14
 * Neither the name of the author nor the names of its contributors may be used
 
15
   to endorse or promote products derived from this software without specific
 
16
   prior written permission.
 
17
 
 
18
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
19
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
20
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 
21
 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 
22
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 
23
 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 
24
 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
25
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 
26
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 
27
 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
28
 */
 
29
 
 
30
#import "SBJsonParser.h"
 
31
 
 
32
@interface SBJsonParser ()
 
33
 
 
34
- (BOOL)scanValue:(NSObject **)o;
 
35
 
 
36
- (BOOL)scanRestOfArray:(NSMutableArray **)o;
 
37
- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o;
 
38
- (BOOL)scanRestOfNull:(NSNull **)o;
 
39
- (BOOL)scanRestOfFalse:(NSNumber **)o;
 
40
- (BOOL)scanRestOfTrue:(NSNumber **)o;
 
41
- (BOOL)scanRestOfString:(NSMutableString **)o;
 
42
 
 
43
// Cannot manage without looking at the first digit
 
44
- (BOOL)scanNumber:(NSNumber **)o;
 
45
 
 
46
- (BOOL)scanHexQuad:(unichar *)x;
 
47
- (BOOL)scanUnicodeChar:(unichar *)x;
 
48
 
 
49
- (BOOL)scanIsAtEnd;
 
50
 
 
51
@end
 
52
 
 
53
#define skipWhitespace(c) while (isspace(*c)) c++
 
54
#define skipDigits(c) while (isdigit(*c)) c++
 
55
 
 
56
 
 
57
@implementation SBJsonParser
 
58
 
 
59
static char ctrl[0x22];
 
60
 
 
61
 
 
62
+ (void)initialize {
 
63
    ctrl[0] = '\"';
 
64
    ctrl[1] = '\\';
 
65
    for (int i = 1; i < 0x20; i++)
 
66
        ctrl[i+1] = i;
 
67
    ctrl[0x21] = 0;    
 
68
}
 
69
 
 
70
/**
 
71
 @deprecated This exists in order to provide fragment support in older APIs in one more version.
 
72
 It should be removed in the next major version.
 
73
 */
 
74
- (id)fragmentWithString:(id)repr {
 
75
    [self clearErrorTrace];
 
76
    
 
77
    if (!repr) {
 
78
        [self addErrorWithCode:EINPUT description:@"Input was 'nil'"];
 
79
        return nil;
 
80
    }
 
81
    
 
82
    depth = 0;
 
83
    c = [repr UTF8String];
 
84
    
 
85
    id o;
 
86
    if (![self scanValue:&o]) {
 
87
        return nil;
 
88
    }
 
89
    
 
90
    // We found some valid JSON. But did it also contain something else?
 
91
    if (![self scanIsAtEnd]) {
 
92
        [self addErrorWithCode:ETRAILGARBAGE description:@"Garbage after JSON"];
 
93
        return nil;
 
94
    }
 
95
        
 
96
    NSAssert1(o, @"Should have a valid object from %@", repr);
 
97
    return o;    
 
98
}
 
99
 
 
100
- (id)objectWithString:(NSString *)repr {
 
101
 
 
102
    id o = [self fragmentWithString:repr];
 
103
    if (!o)
 
104
        return nil;
 
105
    
 
106
    // Check that the object we've found is a valid JSON container.
 
107
    if (![o isKindOfClass:[NSDictionary class]] && ![o isKindOfClass:[NSArray class]]) {
 
108
        [self addErrorWithCode:EFRAGMENT description:@"Valid fragment, but not JSON"];
 
109
        return nil;
 
110
    }
 
111
 
 
112
    return o;
 
113
}
 
114
 
 
115
/*
 
116
 In contrast to the public methods, it is an error to omit the error parameter here.
 
117
 */
 
118
- (BOOL)scanValue:(NSObject **)o
 
119
{
 
120
    skipWhitespace(c);
 
121
    
 
122
    switch (*c++) {
 
123
        case '{':
 
124
            return [self scanRestOfDictionary:(NSMutableDictionary **)o];
 
125
            break;
 
126
        case '[':
 
127
            return [self scanRestOfArray:(NSMutableArray **)o];
 
128
            break;
 
129
        case '"':
 
130
            return [self scanRestOfString:(NSMutableString **)o];
 
131
            break;
 
132
        case 'f':
 
133
            return [self scanRestOfFalse:(NSNumber **)o];
 
134
            break;
 
135
        case 't':
 
136
            return [self scanRestOfTrue:(NSNumber **)o];
 
137
            break;
 
138
        case 'n':
 
139
            return [self scanRestOfNull:(NSNull **)o];
 
140
            break;
 
141
        case '-':
 
142
        case '0'...'9':
 
143
            c--; // cannot verify number correctly without the first character
 
144
            return [self scanNumber:(NSNumber **)o];
 
145
            break;
 
146
        case '+':
 
147
            [self addErrorWithCode:EPARSENUM description: @"Leading + disallowed in number"];
 
148
            return NO;
 
149
            break;
 
150
        case 0x0:
 
151
            [self addErrorWithCode:EEOF description:@"Unexpected end of string"];
 
152
            return NO;
 
153
            break;
 
154
        default:
 
155
            [self addErrorWithCode:EPARSE description: @"Unrecognised leading character"];
 
156
            return NO;
 
157
            break;
 
158
    }
 
159
    
 
160
    NSAssert(0, @"Should never get here");
 
161
    return NO;
 
162
}
 
163
 
 
164
- (BOOL)scanRestOfTrue:(NSNumber **)o
 
165
{
 
166
    if (!strncmp(c, "rue", 3)) {
 
167
        c += 3;
 
168
        *o = [NSNumber numberWithBool:YES];
 
169
        return YES;
 
170
    }
 
171
    [self addErrorWithCode:EPARSE description:@"Expected 'true'"];
 
172
    return NO;
 
173
}
 
174
 
 
175
- (BOOL)scanRestOfFalse:(NSNumber **)o
 
176
{
 
177
    if (!strncmp(c, "alse", 4)) {
 
178
        c += 4;
 
179
        *o = [NSNumber numberWithBool:NO];
 
180
        return YES;
 
181
    }
 
182
    [self addErrorWithCode:EPARSE description: @"Expected 'false'"];
 
183
    return NO;
 
184
}
 
185
 
 
186
- (BOOL)scanRestOfNull:(NSNull **)o {
 
187
    if (!strncmp(c, "ull", 3)) {
 
188
        c += 3;
 
189
        *o = [NSNull null];
 
190
        return YES;
 
191
    }
 
192
    [self addErrorWithCode:EPARSE description: @"Expected 'null'"];
 
193
    return NO;
 
194
}
 
195
 
 
196
- (BOOL)scanRestOfArray:(NSMutableArray **)o {
 
197
    if (maxDepth && ++depth > maxDepth) {
 
198
        [self addErrorWithCode:EDEPTH description: @"Nested too deep"];
 
199
        return NO;
 
200
    }
 
201
    
 
202
    *o = [NSMutableArray arrayWithCapacity:8];
 
203
    
 
204
    for (; *c ;) {
 
205
        id v;
 
206
        
 
207
        skipWhitespace(c);
 
208
        if (*c == ']' && c++) {
 
209
            depth--;
 
210
            return YES;
 
211
        }
 
212
        
 
213
        if (![self scanValue:&v]) {
 
214
            [self addErrorWithCode:EPARSE description:@"Expected value while parsing array"];
 
215
            return NO;
 
216
        }
 
217
        
 
218
        [*o addObject:v];
 
219
        
 
220
        skipWhitespace(c);
 
221
        if (*c == ',' && c++) {
 
222
            skipWhitespace(c);
 
223
            if (*c == ']') {
 
224
                [self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in array"];
 
225
                return NO;
 
226
            }
 
227
        }        
 
228
    }
 
229
    
 
230
    [self addErrorWithCode:EEOF description: @"End of input while parsing array"];
 
231
    return NO;
 
232
}
 
233
 
 
234
- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o 
 
235
{
 
236
    if (maxDepth && ++depth > maxDepth) {
 
237
        [self addErrorWithCode:EDEPTH description: @"Nested too deep"];
 
238
        return NO;
 
239
    }
 
240
    
 
241
    *o = [NSMutableDictionary dictionaryWithCapacity:7];
 
242
    
 
243
    for (; *c ;) {
 
244
        id k, v;
 
245
        
 
246
        skipWhitespace(c);
 
247
        if (*c == '}' && c++) {
 
248
            depth--;
 
249
            return YES;
 
250
        }    
 
251
        
 
252
        if (!(*c == '\"' && c++ && [self scanRestOfString:&k])) {
 
253
            [self addErrorWithCode:EPARSE description: @"Object key string expected"];
 
254
            return NO;
 
255
        }
 
256
        
 
257
        skipWhitespace(c);
 
258
        if (*c != ':') {
 
259
            [self addErrorWithCode:EPARSE description: @"Expected ':' separating key and value"];
 
260
            return NO;
 
261
        }
 
262
        
 
263
        c++;
 
264
        if (![self scanValue:&v]) {
 
265
            NSString *string = [NSString stringWithFormat:@"Object value expected for key: %@", k];
 
266
            [self addErrorWithCode:EPARSE description: string];
 
267
            return NO;
 
268
        }
 
269
        
 
270
        [*o setObject:v forKey:k];
 
271
        
 
272
        skipWhitespace(c);
 
273
        if (*c == ',' && c++) {
 
274
            skipWhitespace(c);
 
275
            if (*c == '}') {
 
276
                [self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in object"];
 
277
                return NO;
 
278
            }
 
279
        }        
 
280
    }
 
281
    
 
282
    [self addErrorWithCode:EEOF description: @"End of input while parsing object"];
 
283
    return NO;
 
284
}
 
285
 
 
286
- (BOOL)scanRestOfString:(NSMutableString **)o 
 
287
{
 
288
    *o = [NSMutableString stringWithCapacity:16];
 
289
    do {
 
290
        // First see if there's a portion we can grab in one go. 
 
291
        // Doing this caused a massive speedup on the long string.
 
292
        size_t len = strcspn(c, ctrl);
 
293
        if (len) {
 
294
            // check for 
 
295
            id t = [[NSString alloc] initWithBytesNoCopy:(char*)c
 
296
                                                  length:len
 
297
                                                encoding:NSUTF8StringEncoding
 
298
                                            freeWhenDone:NO];
 
299
            if (t) {
 
300
                [*o appendString:t];
 
301
                [t release];
 
302
                c += len;
 
303
            }
 
304
        }
 
305
        
 
306
        if (*c == '"') {
 
307
            c++;
 
308
            return YES;
 
309
            
 
310
        } else if (*c == '\\') {
 
311
            unichar uc = *++c;
 
312
            switch (uc) {
 
313
                case '\\':
 
314
                case '/':
 
315
                case '"':
 
316
                    break;
 
317
                    
 
318
                case 'b':   uc = '\b';  break;
 
319
                case 'n':   uc = '\n';  break;
 
320
                case 'r':   uc = '\r';  break;
 
321
                case 't':   uc = '\t';  break;
 
322
                case 'f':   uc = '\f';  break;                    
 
323
                    
 
324
                case 'u':
 
325
                    c++;
 
326
                    if (![self scanUnicodeChar:&uc]) {
 
327
                        [self addErrorWithCode:EUNICODE description: @"Broken unicode character"];
 
328
                        return NO;
 
329
                    }
 
330
                    c--; // hack.
 
331
                    break;
 
332
                default:
 
333
                    [self addErrorWithCode:EESCAPE description: [NSString stringWithFormat:@"Illegal escape sequence '0x%x'", uc]];
 
334
                    return NO;
 
335
                    break;
 
336
            }
 
337
            CFStringAppendCharacters((CFMutableStringRef)*o, &uc, 1);
 
338
            c++;
 
339
            
 
340
        } else if (*c < 0x20) {
 
341
            [self addErrorWithCode:ECTRL description: [NSString stringWithFormat:@"Unescaped control character '0x%x'", *c]];
 
342
            return NO;
 
343
            
 
344
        } else {
 
345
            NSLog(@"should not be able to get here");
 
346
        }
 
347
    } while (*c);
 
348
    
 
349
    [self addErrorWithCode:EEOF description:@"Unexpected EOF while parsing string"];
 
350
    return NO;
 
351
}
 
352
 
 
353
- (BOOL)scanUnicodeChar:(unichar *)x
 
354
{
 
355
    unichar hi, lo;
 
356
    
 
357
    if (![self scanHexQuad:&hi]) {
 
358
        [self addErrorWithCode:EUNICODE description: @"Missing hex quad"];
 
359
        return NO;        
 
360
    }
 
361
    
 
362
    if (hi >= 0xd800) {     // high surrogate char?
 
363
        if (hi < 0xdc00) {  // yes - expect a low char
 
364
            
 
365
            if (!(*c == '\\' && ++c && *c == 'u' && ++c && [self scanHexQuad:&lo])) {
 
366
                [self addErrorWithCode:EUNICODE description: @"Missing low character in surrogate pair"];
 
367
                return NO;
 
368
            }
 
369
            
 
370
            if (lo < 0xdc00 || lo >= 0xdfff) {
 
371
                [self addErrorWithCode:EUNICODE description:@"Invalid low surrogate char"];
 
372
                return NO;
 
373
            }
 
374
            
 
375
            hi = (hi - 0xd800) * 0x400 + (lo - 0xdc00) + 0x10000;
 
376
            
 
377
        } else if (hi < 0xe000) {
 
378
            [self addErrorWithCode:EUNICODE description:@"Invalid high character in surrogate pair"];
 
379
            return NO;
 
380
        }
 
381
    }
 
382
    
 
383
    *x = hi;
 
384
    return YES;
 
385
}
 
386
 
 
387
- (BOOL)scanHexQuad:(unichar *)x
 
388
{
 
389
    *x = 0;
 
390
    for (int i = 0; i < 4; i++) {
 
391
        unichar uc = *c;
 
392
        c++;
 
393
        int d = (uc >= '0' && uc <= '9')
 
394
        ? uc - '0' : (uc >= 'a' && uc <= 'f')
 
395
        ? (uc - 'a' + 10) : (uc >= 'A' && uc <= 'F')
 
396
        ? (uc - 'A' + 10) : -1;
 
397
        if (d == -1) {
 
398
            [self addErrorWithCode:EUNICODE description:@"Missing hex digit in quad"];
 
399
            return NO;
 
400
        }
 
401
        *x *= 16;
 
402
        *x += d;
 
403
    }
 
404
    return YES;
 
405
}
 
406
 
 
407
- (BOOL)scanNumber:(NSNumber **)o
 
408
{
 
409
    const char *ns = c;
 
410
    
 
411
    // The logic to test for validity of the number formatting is relicensed
 
412
    // from JSON::XS with permission from its author Marc Lehmann.
 
413
    // (Available at the CPAN: http://search.cpan.org/dist/JSON-XS/ .)
 
414
    
 
415
    if ('-' == *c)
 
416
        c++;
 
417
    
 
418
    if ('0' == *c && c++) {        
 
419
        if (isdigit(*c)) {
 
420
            [self addErrorWithCode:EPARSENUM description: @"Leading 0 disallowed in number"];
 
421
            return NO;
 
422
        }
 
423
        
 
424
    } else if (!isdigit(*c) && c != ns) {
 
425
        [self addErrorWithCode:EPARSENUM description: @"No digits after initial minus"];
 
426
        return NO;
 
427
        
 
428
    } else {
 
429
        skipDigits(c);
 
430
    }
 
431
    
 
432
    // Fractional part
 
433
    if ('.' == *c && c++) {
 
434
        
 
435
        if (!isdigit(*c)) {
 
436
            [self addErrorWithCode:EPARSENUM description: @"No digits after decimal point"];
 
437
            return NO;
 
438
        }        
 
439
        skipDigits(c);
 
440
    }
 
441
    
 
442
    // Exponential part
 
443
    if ('e' == *c || 'E' == *c) {
 
444
        c++;
 
445
        
 
446
        if ('-' == *c || '+' == *c)
 
447
            c++;
 
448
        
 
449
        if (!isdigit(*c)) {
 
450
            [self addErrorWithCode:EPARSENUM description: @"No digits after exponent"];
 
451
            return NO;
 
452
        }
 
453
        skipDigits(c);
 
454
    }
 
455
    
 
456
    id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns
 
457
                                            length:c - ns
 
458
                                          encoding:NSUTF8StringEncoding
 
459
                                      freeWhenDone:NO];
 
460
    [str autorelease];
 
461
    if (str && (*o = [NSDecimalNumber decimalNumberWithString:str]))
 
462
        return YES;
 
463
    
 
464
    [self addErrorWithCode:EPARSENUM description: @"Failed creating decimal instance"];
 
465
    return NO;
 
466
}
 
467
 
 
468
- (BOOL)scanIsAtEnd
 
469
{
 
470
    skipWhitespace(c);
 
471
    return !*c;
 
472
}
 
473
 
 
474
 
 
475
@end