Playlist Generator  1.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Properties Defines
SBJsonStreamWriter.m
Go to the documentation of this file.
00001 /*
00002  Copyright (c) 2010, Stig Brautaset.
00003  All rights reserved.
00004 
00005  Redistribution and use in source and binary forms, with or without
00006  modification, are permitted provided that the following conditions are
00007  met:
00008 
00009    Redistributions of source code must retain the above copyright
00010    notice, this list of conditions and the following disclaimer.
00011 
00012    Redistributions in binary form must reproduce the above copyright
00013    notice, this list of conditions and the following disclaimer in the
00014    documentation and/or other materials provided with the distribution.
00015 
00016    Neither the name of the the author nor the names of its contributors
00017    may be used to endorse or promote products derived from this software
00018    without specific prior written permission.
00019 
00020  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
00021  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00022  TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
00023  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
00024  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00025  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00026  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00027  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00028  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00029  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
00030  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00031  */
00032 
00033 #import "SBJsonStreamWriter.h"
00034 #import "SBJsonStreamWriterState.h"
00035 
00036 static NSNumber *kNotANumber;
00037 static NSNumber *kTrue;
00038 static NSNumber *kFalse;
00039 static NSNumber *kPositiveInfinity;
00040 static NSNumber *kNegativeInfinity;
00041 
00042 
00043 @implementation SBJsonStreamWriter
00044 
00045 @synthesize error;
00046 @synthesize maxDepth;
00047 @synthesize state;
00048 @synthesize stateStack;
00049 @synthesize humanReadable;
00050 @synthesize sortKeys;
00051 
00052 + (void)initialize {
00053         kNotANumber = [NSDecimalNumber notANumber];
00054     kPositiveInfinity = [NSNumber numberWithDouble:+INFINITY];
00055     kNegativeInfinity = [NSNumber numberWithDouble:-INFINITY];
00056     kTrue = [NSNumber numberWithBool:YES];
00057     kFalse = [NSNumber numberWithBool:NO];
00058 }
00059 
00060 #pragma mark Housekeeping
00061 
00062 @synthesize delegate;
00063 
00064 - (id)init {
00065         self = [super init];
00066         if (self) {
00067                 maxDepth = 32u;
00068         stateStack = [[NSMutableArray alloc] initWithCapacity:maxDepth];
00069         state = [SBJsonStreamWriterStateStart sharedInstance];
00070         cache = [[NSMutableDictionary alloc] initWithCapacity:32];
00071     }
00072         return self;
00073 }
00074 
00075 - (void)dealloc {
00076     self.state = nil;
00077 }
00078 
00079 #pragma mark Methods
00080 
00081 - (void)appendBytes:(const void *)bytes length:(NSUInteger)length {
00082     [delegate writer:self appendBytes:bytes length:length];
00083 }
00084 
00085 - (BOOL)writeObject:(NSDictionary *)dict {
00086         if (![self writeObjectOpen])
00087                 return NO;
00088 
00089         NSArray *keys = [dict allKeys];
00090         if (sortKeys)
00091                 keys = [keys sortedArrayUsingSelector:@selector(compare:)];
00092 
00093         for (id k in keys) {
00094                 if (![k isKindOfClass:[NSString class]]) {
00095                         self.error = [NSString stringWithFormat:@"JSON object key must be string: %@", k];
00096                         return NO;
00097                 }
00098 
00099                 if (![self writeString:k])
00100                         return NO;
00101                 if (![self writeValue:[dict objectForKey:k]])
00102                         return NO;
00103         }
00104 
00105         return [self writeObjectClose];
00106 }
00107 
00108 - (BOOL)writeArray:(NSArray*)array {
00109         if (![self writeArrayOpen])
00110                 return NO;
00111         for (id v in array)
00112                 if (![self writeValue:v])
00113                         return NO;
00114         return [self writeArrayClose];
00115 }
00116 
00117 
00118 - (BOOL)writeObjectOpen {
00119         if ([state isInvalidState:self]) return NO;
00120         if ([state expectingKey:self]) return NO;
00121         [state appendSeparator:self];
00122         if (humanReadable && stateStack.count) [state appendWhitespace:self];
00123 
00124     [stateStack addObject:state];
00125     self.state = [SBJsonStreamWriterStateObjectStart sharedInstance];
00126 
00127         if (maxDepth && stateStack.count > maxDepth) {
00128                 self.error = @"Nested too deep";
00129                 return NO;
00130         }
00131 
00132         [delegate writer:self appendBytes:"{" length:1];
00133         return YES;
00134 }
00135 
00136 - (BOOL)writeObjectClose {
00137         if ([state isInvalidState:self]) return NO;
00138 
00139     SBJsonStreamWriterState *prev = state;
00140 
00141     self.state = [stateStack lastObject];
00142     [stateStack removeLastObject];
00143 
00144         if (humanReadable) [prev appendWhitespace:self];
00145         [delegate writer:self appendBytes:"}" length:1];
00146 
00147         [state transitionState:self];
00148         return YES;
00149 }
00150 
00151 - (BOOL)writeArrayOpen {
00152         if ([state isInvalidState:self]) return NO;
00153         if ([state expectingKey:self]) return NO;
00154         [state appendSeparator:self];
00155         if (humanReadable && stateStack.count) [state appendWhitespace:self];
00156 
00157     [stateStack addObject:state];
00158         self.state = [SBJsonStreamWriterStateArrayStart sharedInstance];
00159 
00160         if (maxDepth && stateStack.count > maxDepth) {
00161                 self.error = @"Nested too deep";
00162                 return NO;
00163         }
00164 
00165         [delegate writer:self appendBytes:"[" length:1];
00166         return YES;
00167 }
00168 
00169 - (BOOL)writeArrayClose {
00170         if ([state isInvalidState:self]) return NO;
00171         if ([state expectingKey:self]) return NO;
00172 
00173     SBJsonStreamWriterState *prev = state;
00174 
00175     self.state = [stateStack lastObject];
00176     [stateStack removeLastObject];
00177 
00178         if (humanReadable) [prev appendWhitespace:self];
00179         [delegate writer:self appendBytes:"]" length:1];
00180 
00181         [state transitionState:self];
00182         return YES;
00183 }
00184 
00185 - (BOOL)writeNull {
00186         if ([state isInvalidState:self]) return NO;
00187         if ([state expectingKey:self]) return NO;
00188         [state appendSeparator:self];
00189         if (humanReadable) [state appendWhitespace:self];
00190 
00191         [delegate writer:self appendBytes:"null" length:4];
00192         [state transitionState:self];
00193         return YES;
00194 }
00195 
00196 - (BOOL)writeBool:(BOOL)x {
00197         if ([state isInvalidState:self]) return NO;
00198         if ([state expectingKey:self]) return NO;
00199         [state appendSeparator:self];
00200         if (humanReadable) [state appendWhitespace:self];
00201 
00202         if (x)
00203                 [delegate writer:self appendBytes:"true" length:4];
00204         else
00205                 [delegate writer:self appendBytes:"false" length:5];
00206         [state transitionState:self];
00207         return YES;
00208 }
00209 
00210 
00211 - (BOOL)writeValue:(id)o {
00212         if ([o isKindOfClass:[NSDictionary class]]) {
00213                 return [self writeObject:o];
00214 
00215         } else if ([o isKindOfClass:[NSArray class]]) {
00216                 return [self writeArray:o];
00217 
00218         } else if ([o isKindOfClass:[NSString class]]) {
00219                 [self writeString:o];
00220                 return YES;
00221 
00222         } else if ([o isKindOfClass:[NSNumber class]]) {
00223                 return [self writeNumber:o];
00224 
00225         } else if ([o isKindOfClass:[NSNull class]]) {
00226                 return [self writeNull];
00227 
00228         } else if ([o respondsToSelector:@selector(proxyForJson)]) {
00229                 return [self writeValue:[o proxyForJson]];
00230 
00231         }
00232 
00233         self.error = [NSString stringWithFormat:@"JSON serialisation not supported for %@", [o class]];
00234         return NO;
00235 }
00236 
00237 static const char *strForChar(int c) {
00238         switch (c) {
00239                 case 0: return "\\u0000"; break;
00240                 case 1: return "\\u0001"; break;
00241                 case 2: return "\\u0002"; break;
00242                 case 3: return "\\u0003"; break;
00243                 case 4: return "\\u0004"; break;
00244                 case 5: return "\\u0005"; break;
00245                 case 6: return "\\u0006"; break;
00246                 case 7: return "\\u0007"; break;
00247                 case 8: return "\\b"; break;
00248                 case 9: return "\\t"; break;
00249                 case 10: return "\\n"; break;
00250                 case 11: return "\\u000b"; break;
00251                 case 12: return "\\f"; break;
00252                 case 13: return "\\r"; break;
00253                 case 14: return "\\u000e"; break;
00254                 case 15: return "\\u000f"; break;
00255                 case 16: return "\\u0010"; break;
00256                 case 17: return "\\u0011"; break;
00257                 case 18: return "\\u0012"; break;
00258                 case 19: return "\\u0013"; break;
00259                 case 20: return "\\u0014"; break;
00260                 case 21: return "\\u0015"; break;
00261                 case 22: return "\\u0016"; break;
00262                 case 23: return "\\u0017"; break;
00263                 case 24: return "\\u0018"; break;
00264                 case 25: return "\\u0019"; break;
00265                 case 26: return "\\u001a"; break;
00266                 case 27: return "\\u001b"; break;
00267                 case 28: return "\\u001c"; break;
00268                 case 29: return "\\u001d"; break;
00269                 case 30: return "\\u001e"; break;
00270                 case 31: return "\\u001f"; break;
00271                 case 34: return "\\\""; break;
00272                 case 92: return "\\\\"; break;
00273         }
00274         NSLog(@"FUTFUTFUT: -->'%c'<---", c);
00275         return "FUTFUTFUT";
00276 }
00277 
00278 - (BOOL)writeString:(NSString*)string {
00279         if ([state isInvalidState:self]) return NO;
00280         [state appendSeparator:self];
00281         if (humanReadable) [state appendWhitespace:self];
00282 
00283         NSMutableData *buf = [cache objectForKey:string];
00284         if (!buf) {
00285 
00286         NSUInteger len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
00287         const char *utf8 = [string UTF8String];
00288         NSUInteger written = 0, i = 0;
00289 
00290         buf = [NSMutableData dataWithCapacity:(NSUInteger)(len * 1.1f)];
00291         [buf appendBytes:"\"" length:1];
00292 
00293         for (i = 0; i < len; i++) {
00294             int c = utf8[i];
00295             BOOL isControlChar = c >= 0 && c < 32;
00296             if (isControlChar || c == '"' || c == '\\') {
00297                 if (i - written)
00298                     [buf appendBytes:utf8 + written length:i - written];
00299                 written = i + 1;
00300 
00301                 const char *t = strForChar(c);
00302                 [buf appendBytes:t length:strlen(t)];
00303             }
00304         }
00305 
00306         if (i - written)
00307             [buf appendBytes:utf8 + written length:i - written];
00308 
00309         [buf appendBytes:"\"" length:1];
00310         [cache setObject:buf forKey:string];
00311     }
00312 
00313         [delegate writer:self appendBytes:[buf bytes] length:[buf length]];
00314         [state transitionState:self];
00315         return YES;
00316 }
00317 
00318 - (BOOL)writeNumber:(NSNumber*)number {
00319         if (number == kTrue || number == kFalse)
00320                 return [self writeBool:[number boolValue]];
00321 
00322         if ([state isInvalidState:self]) return NO;
00323         if ([state expectingKey:self]) return NO;
00324         [state appendSeparator:self];
00325         if (humanReadable) [state appendWhitespace:self];
00326 
00327         if ([kPositiveInfinity isEqualToNumber:number]) {
00328                 self.error = @"+Infinity is not a valid number in JSON";
00329                 return NO;
00330 
00331         } else if ([kNegativeInfinity isEqualToNumber:number]) {
00332                 self.error = @"-Infinity is not a valid number in JSON";
00333                 return NO;
00334 
00335         } else if ([kNotANumber isEqualToNumber:number]) {
00336                 self.error = @"NaN is not a valid number in JSON";
00337                 return NO;
00338         }
00339 
00340         const char *objcType = [number objCType];
00341         char num[128];
00342         size_t len;
00343 
00344         switch (objcType[0]) {
00345                 case 'c': case 'i': case 's': case 'l': case 'q':
00346                         len = snprintf(num, sizeof num, "%lld", [number longLongValue]);
00347                         break;
00348                 case 'C': case 'I': case 'S': case 'L': case 'Q':
00349                         len = snprintf(num, sizeof num, "%llu", [number unsignedLongLongValue]);
00350                         break;
00351                 case 'f': case 'd': default:
00352                         if ([number isKindOfClass:[NSDecimalNumber class]]) {
00353                                 char const *utf8 = [[number stringValue] UTF8String];
00354                                 [delegate writer:self appendBytes:utf8 length: strlen(utf8)];
00355                                 [state transitionState:self];
00356                                 return YES;
00357                         }
00358                         len = snprintf(num, sizeof num, "%.17g", [number doubleValue]);
00359                         break;
00360         }
00361         [delegate writer:self appendBytes:num length: len];
00362         [state transitionState:self];
00363         return YES;
00364 }
00365 
00366 @end