summaryrefslogtreecommitdiffstats
path: root/Classes/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Classes/Source')
-rw-r--r--Classes/Source/CDataScanner.h71
-rw-r--r--Classes/Source/CDataScanner.m340
-rw-r--r--Classes/Source/Experimental/CFilteringJSONSerializer.h25
-rw-r--r--Classes/Source/Experimental/CFilteringJSONSerializer.m87
-rw-r--r--Classes/Source/Experimental/CJSONDeserializer_BlocksExtensions.h16
-rw-r--r--Classes/Source/Experimental/CJSONDeserializer_BlocksExtensions.m63
-rw-r--r--Classes/Source/Experimental/CJSONSerialization.h34
-rw-r--r--Classes/Source/Experimental/CJSONSerialization.m59
-rw-r--r--Classes/Source/Experimental/CJSONSerializedData.h25
-rw-r--r--Classes/Source/Experimental/CJSONSerializedData.m42
-rw-r--r--Classes/Source/Extensions/CDataScanner_Extensions.h40
-rw-r--r--Classes/Source/Extensions/CDataScanner_Extensions.m135
-rw-r--r--Classes/Source/Extensions/NSDictionary_JSONExtensions.h37
-rw-r--r--Classes/Source/Extensions/NSDictionary_JSONExtensions.m47
-rw-r--r--Classes/Source/JSON/CJSONDeserializer.h63
-rw-r--r--Classes/Source/JSON/CJSONDeserializer.m161
-rw-r--r--Classes/Source/JSON/CJSONScanner.h95
-rw-r--r--Classes/Source/JSON/CJSONScanner.m676
-rw-r--r--Classes/Source/JSON/CJSONSerializer.h53
-rw-r--r--Classes/Source/JSON/CJSONSerializer.m342
-rw-r--r--Classes/Source/JSON/JSONRepresentation.h18
21 files changed, 2429 insertions, 0 deletions
diff --git a/Classes/Source/CDataScanner.h b/Classes/Source/CDataScanner.h
new file mode 100644
index 0000000..41f68e8
--- /dev/null
+++ b/Classes/Source/CDataScanner.h
@@ -0,0 +1,71 @@
+//
+// CDataScanner.h
+// TouchCode
+//
+// Created by Jonathan Wight on 04/16/08.
+// Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+// NSScanner
+
+@interface CDataScanner : NSObject {
+ NSData *data;
+
+ u_int8_t *start;
+ u_int8_t *end;
+ u_int8_t *current;
+ NSUInteger length;
+}
+
+@property (readwrite, nonatomic, retain) NSData *data;
+@property (readwrite, nonatomic, assign) NSUInteger scanLocation;
+@property (readonly, nonatomic, assign) NSUInteger bytesRemaining;
+@property (readonly, nonatomic, assign) BOOL isAtEnd;
+
+- (id)initWithData:(NSData *)inData;
+
+- (unichar)currentCharacter;
+- (unichar)scanCharacter;
+- (BOOL)scanCharacter:(unichar)inCharacter;
+
+- (BOOL)scanUTF8String:(const char *)inString intoString:(NSString **)outValue;
+- (BOOL)scanString:(NSString *)inString intoString:(NSString **)outValue;
+- (BOOL)scanCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue; // inSet must only contain 7-bit ASCII characters
+
+- (BOOL)scanUpToString:(NSString *)string intoString:(NSString **)outValue;
+- (BOOL)scanUpToCharactersFromSet:(NSCharacterSet *)set intoString:(NSString **)outValue; // inSet must only contain 7-bit ASCII characters
+
+- (BOOL)scanNumber:(NSNumber **)outValue;
+- (BOOL)scanDecimalNumber:(NSDecimalNumber **)outValue;
+
+- (BOOL)scanDataOfLength:(NSUInteger)inLength intoData:(NSData **)outData;
+
+- (void)skipWhitespace;
+
+- (NSString *)remainingString;
+- (NSData *)remainingData;
+
+@end
diff --git a/Classes/Source/CDataScanner.m b/Classes/Source/CDataScanner.m
new file mode 100644
index 0000000..b3cee6f
--- /dev/null
+++ b/Classes/Source/CDataScanner.m
@@ -0,0 +1,340 @@
+//
+// CDataScanner.m
+// TouchCode
+//
+// Created by Jonathan Wight on 04/16/08.
+// Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+#import "CDataScanner_Extensions.h"
+
+@interface CDataScanner ()
+@end
+
+#pragma mark -
+
+inline static unichar CharacterAtPointer(void *start, void *end)
+ {
+ #pragma unused(end)
+
+ const u_int8_t theByte = *(u_int8_t *)start;
+ if (theByte & 0x80)
+ {
+ // TODO -- UNICODE!!!! (well in theory nothing todo here)
+ }
+ const unichar theCharacter = theByte;
+ return(theCharacter);
+ }
+
+ static NSCharacterSet *sDoubleCharacters = NULL;
+
+ @implementation CDataScanner
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ }
+ return(self);
+ }
+
+- (id)initWithData:(NSData *)inData;
+ {
+ if ((self = [self init]) != NULL)
+ {
+ [self setData:inData];
+ }
+ return(self);
+ }
+
+ + (void)initialize
+ {
+ if (sDoubleCharacters == NULL)
+ {
+ sDoubleCharacters = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789eE-+."] retain];
+ }
+ }
+
+- (void)dealloc
+ {
+ [data release];
+ data = NULL;
+ //
+ [super dealloc];
+ }
+
+- (NSUInteger)scanLocation
+ {
+ return(current - start);
+ }
+
+- (NSUInteger)bytesRemaining
+ {
+ return(end - current);
+ }
+
+- (NSData *)data
+ {
+ return(data);
+ }
+
+- (void)setData:(NSData *)inData
+ {
+ if (data != inData)
+ {
+ [data release];
+ data = [inData retain];
+ }
+
+ if (data)
+ {
+ start = (u_int8_t *)data.bytes;
+ end = start + data.length;
+ current = start;
+ length = data.length;
+ }
+ else
+ {
+ start = NULL;
+ end = NULL;
+ current = NULL;
+ length = 0;
+ }
+ }
+
+- (void)setScanLocation:(NSUInteger)inScanLocation
+ {
+ current = start + inScanLocation;
+ }
+
+- (BOOL)isAtEnd
+ {
+ return(self.scanLocation >= length);
+ }
+
+- (unichar)currentCharacter
+ {
+ return(CharacterAtPointer(current, end));
+ }
+
+#pragma mark -
+
+- (unichar)scanCharacter
+ {
+ const unichar theCharacter = CharacterAtPointer(current++, end);
+ return(theCharacter);
+ }
+
+- (BOOL)scanCharacter:(unichar)inCharacter
+ {
+ unichar theCharacter = CharacterAtPointer(current, end);
+ if (theCharacter == inCharacter)
+ {
+ ++current;
+ return(YES);
+ }
+ else
+ return(NO);
+ }
+
+- (BOOL)scanUTF8String:(const char *)inString intoString:(NSString **)outValue
+ {
+ const size_t theLength = strlen(inString);
+ if ((size_t)(end - current) < theLength)
+ return(NO);
+ if (strncmp((char *)current, inString, theLength) == 0)
+ {
+ current += theLength;
+ if (outValue)
+ *outValue = [NSString stringWithUTF8String:inString];
+ return(YES);
+ }
+ return(NO);
+ }
+
+- (BOOL)scanString:(NSString *)inString intoString:(NSString **)outValue
+ {
+ if ((size_t)(end - current) < inString.length)
+ return(NO);
+ if (strncmp((char *)current, [inString UTF8String], inString.length) == 0)
+ {
+ current += inString.length;
+ if (outValue)
+ *outValue = inString;
+ return(YES);
+ }
+ return(NO);
+ }
+
+- (BOOL)scanCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue
+ {
+ u_int8_t *P;
+ for (P = current; P < end && [inSet characterIsMember:*P] == YES; ++P)
+ ;
+
+ if (P == current)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = P;
+
+ return(YES);
+ }
+
+- (BOOL)scanUpToString:(NSString *)inString intoString:(NSString **)outValue
+ {
+ const char *theToken = [inString UTF8String];
+ const char *theResult = strnstr((char *)current, theToken, end - current);
+ if (theResult == NULL)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:theResult - (char *)current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = (u_int8_t *)theResult;
+
+ return(YES);
+ }
+
+- (BOOL)scanUpToCharactersFromSet:(NSCharacterSet *)inSet intoString:(NSString **)outValue
+ {
+ u_int8_t *P;
+ for (P = current; P < end && [inSet characterIsMember:*P] == NO; ++P)
+ ;
+
+ if (P == current)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = P;
+
+ return(YES);
+ }
+
+- (BOOL)scanNumber:(NSNumber **)outValue
+ {
+ NSString *theString = NULL;
+ if ([self scanCharactersFromSet:sDoubleCharacters intoString:&theString])
+ {
+ if ([theString rangeOfString:@"."].location != NSNotFound)
+ {
+ if (outValue)
+ {
+ *outValue = [NSDecimalNumber decimalNumberWithString:theString];
+ }
+ return(YES);
+ }
+ else if ([theString rangeOfString:@"-"].location != NSNotFound)
+ {
+ if (outValue != NULL)
+ {
+ *outValue = [NSNumber numberWithLongLong:[theString longLongValue]];
+ }
+ return(YES);
+ }
+ else
+ {
+ if (outValue != NULL)
+ {
+ *outValue = [NSNumber numberWithUnsignedLongLong:strtoull([theString UTF8String], NULL, 0)];
+ }
+ return(YES);
+ }
+
+ }
+ return(NO);
+ }
+
+- (BOOL)scanDecimalNumber:(NSDecimalNumber **)outValue;
+ {
+ NSString *theString = NULL;
+ if ([self scanCharactersFromSet:sDoubleCharacters intoString:&theString])
+ {
+ if (outValue)
+ {
+ *outValue = [NSDecimalNumber decimalNumberWithString:theString];
+ }
+ return(YES);
+ }
+ return(NO);
+ }
+
+- (BOOL)scanDataOfLength:(NSUInteger)inLength intoData:(NSData **)outData;
+ {
+ if (self.bytesRemaining < inLength)
+ {
+ return(NO);
+ }
+
+ if (outData)
+ {
+ *outData = [NSData dataWithBytes:current length:inLength];
+ }
+
+ current += inLength;
+ return(YES);
+ }
+
+
+- (void)skipWhitespace
+ {
+ u_int8_t *P;
+ for (P = current; P < end && (isspace(*P)); ++P)
+ ;
+
+ current = P;
+ }
+
+- (NSString *)remainingString
+ {
+ NSData *theRemainingData = [NSData dataWithBytes:current length:end - current];
+ NSString *theString = [[[NSString alloc] initWithData:theRemainingData encoding:NSUTF8StringEncoding] autorelease];
+ return(theString);
+ }
+
+- (NSData *)remainingData;
+ {
+ NSData *theRemainingData = [NSData dataWithBytes:current length:end - current];
+ return(theRemainingData);
+ }
+
+ @end
diff --git a/Classes/Source/Experimental/CFilteringJSONSerializer.h b/Classes/Source/Experimental/CFilteringJSONSerializer.h
new file mode 100644
index 0000000..f004a79
--- /dev/null
+++ b/Classes/Source/Experimental/CFilteringJSONSerializer.h
@@ -0,0 +1,25 @@
+//
+// CFilteringJSONSerializer.h
+// CouchNotes
+//
+// Created by Jonathan Wight on 06/20/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import "CJSONSerializer.h"
+
+typedef NSString *(^JSONConversionTest)(id inObject);
+typedef id (^JSONConversionConverter)(id inObject); // TODO replace with value transformers.
+
+@interface CFilteringJSONSerializer : CJSONSerializer {
+ NSSet *tests;
+ NSDictionary *convertersByName;
+}
+
+@property (readwrite, nonatomic, retain) NSSet *tests;
+@property (readwrite, nonatomic, retain) NSDictionary *convertersByName;
+
+- (void)addTest:(JSONConversionTest)inTest;
+- (void)addConverter:(JSONConversionConverter)inConverter forName:(NSString *)inName;
+
+@end
diff --git a/Classes/Source/Experimental/CFilteringJSONSerializer.m b/Classes/Source/Experimental/CFilteringJSONSerializer.m
new file mode 100644
index 0000000..1ee2a3d
--- /dev/null
+++ b/Classes/Source/Experimental/CFilteringJSONSerializer.m
@@ -0,0 +1,87 @@
+//
+// CFilteringJSONSerializer.m
+// CouchNotes
+//
+// Created by Jonathan Wight on 06/20/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import "CFilteringJSONSerializer.h"
+
+@implementation CFilteringJSONSerializer
+
+@synthesize tests;
+@synthesize convertersByName;
+
+- (void)dealloc
+ {
+ [tests release];
+ tests = NULL;
+ //
+ [convertersByName release];
+ convertersByName = NULL;
+ //
+ [super dealloc];
+ }
+
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError
+ {
+ NSData *theData = NULL;
+ for (JSONConversionTest theTest in self.tests)
+ {
+ NSString *theName = theTest(inObject);
+ if (theName != NULL)
+ {
+ id theObject = NULL;
+ JSONConversionConverter theConverter = [self.convertersByName objectForKey:theName];
+ if (theConverter)
+ {
+ theObject = theConverter(inObject);
+ }
+
+ if (theObject)
+ {
+ if ([theObject isKindOfClass:[NSData class]])
+ {
+ theData = theObject;
+ break;
+ }
+ else
+ {
+ NSError *theError = NULL;
+ theData = [super serializeObject:theObject error:&theError];
+ if (theData != NULL)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (theData == NULL)
+ {
+ theData = [super serializeObject:inObject error:outError];
+ }
+
+ return(theData);
+ }
+
+- (void)addTest:(JSONConversionTest)inTest
+ {
+ inTest = [[inTest copy] autorelease];
+ NSSet *theTests = [self.tests setByAddingObject:inTest];
+ self.tests = theTests;
+ }
+
+- (void)addConverter:(JSONConversionConverter)inConverter forName:(NSString *)inName
+ {
+ NSMutableDictionary *theConvertersByName = [[self.convertersByName mutableCopy] autorelease];
+
+ inConverter = [[inConverter copy] autorelease];
+ [theConvertersByName setObject:inConverter forKey:inName];
+ self.convertersByName = theConvertersByName;
+ }
+
+
+@end
diff --git a/Classes/Source/Experimental/CJSONDeserializer_BlocksExtensions.h b/Classes/Source/Experimental/CJSONDeserializer_BlocksExtensions.h
new file mode 100644
index 0000000..17631af
--- /dev/null
+++ b/Classes/Source/Experimental/CJSONDeserializer_BlocksExtensions.h
@@ -0,0 +1,16 @@
+//
+// CJSONDeserializer_BlocksExtensions.h
+// TouchJSON
+//
+// Created by Jonathan Wight on 10/15/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import "CJSONDeserializer.h"
+
+@interface CJSONDeserializer (CJSONDeserializer_BlocksExtensions)
+
+- (void)deserializeAsDictionary:(NSData *)inData completionBlock:(void (^)(id result, NSError *error))block;
+- (void)deserializeAsArray:(NSData *)inData completionBlock:(void (^)(id result, NSError *error))block;
+
+@end
diff --git a/Classes/Source/Experimental/CJSONDeserializer_BlocksExtensions.m b/Classes/Source/Experimental/CJSONDeserializer_BlocksExtensions.m
new file mode 100644
index 0000000..7ea774c
--- /dev/null
+++ b/Classes/Source/Experimental/CJSONDeserializer_BlocksExtensions.m
@@ -0,0 +1,63 @@
+//
+// CJSONDeserializer_BlocksExtensions.m
+// TouchJSON
+//
+// Created by Jonathan Wight on 10/15/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import "CJSONDeserializer_BlocksExtensions.h"
+
+#import "CJSONScanner.h"
+
+@implementation CJSONDeserializer (CJSONDeserializer_BlocksExtensions)
+
+- (void)deserializeAsDictionary:(NSData *)inData completionBlock:(void (^)(id result, NSError *error))block {
+
+ NSError *noDataError = nil;
+ if (inData == NULL || [inData length] == 0) {
+ noDataError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+ block(nil, noDataError);
+ }
+
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+
+ NSError *deserializationError = nil;
+ self.scanner.data = inData;
+ NSDictionary *theDictionary = NULL;
+ BOOL successful = [self.scanner scanJSONDictionary:&theDictionary error:&deserializationError];
+
+ dispatch_async(dispatch_get_main_queue (), ^{
+ if (successful)
+ block(theDictionary, nil);
+ else
+ block(nil, deserializationError);
+ });
+ }];
+}
+
+- (void)deserializeAsArray:(NSData *)inData completionBlock:(void (^)(id result, NSError *error))block {
+
+ NSError *nullInDataError = nil;
+ if (inData == NULL || [inData length] == 0) {
+ nullInDataError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+ block(nil, nullInDataError);
+ }
+
+ [[NSOperationQueue mainQueue] addOperationWithBlock:^{
+
+ NSError *deserializationError = nil;
+ self.scanner.data = inData;
+ NSArray *theArray = NULL;
+ BOOL successful = [self.scanner scanJSONArray:&theArray error:&deserializationError];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (successful)
+ block(theArray, nil);
+ else
+ block(nil, deserializationError);
+ });
+ }];
+}
+
+@end
diff --git a/Classes/Source/Experimental/CJSONSerialization.h b/Classes/Source/Experimental/CJSONSerialization.h
new file mode 100644
index 0000000..83c9bb2
--- /dev/null
+++ b/Classes/Source/Experimental/CJSONSerialization.h
@@ -0,0 +1,34 @@
+//
+// CJSONSerialization.h
+// TouchJSON
+//
+// Created by Jonathan Wight on 03/04/11.
+// Copyright 2011 toxicsoftware.com. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+enum {
+ kCJSONReadingMutableContainers = 0x1,
+ kCJSONReadingMutableLeaves = 0x2,
+ kCJSONReadingAllowFragments = 0x04,
+};
+typedef NSUInteger EJSONReadingOptions;
+
+enum {
+ kCJJSONWritingPrettyPrinted = 0x1
+};
+typedef NSUInteger EJSONWritingOptions;
+
+
+@interface CJSONSerialization : NSObject {
+
+}
+
++ (BOOL)isValidJSONObject:(id)obj;
++ (NSData *)dataWithJSONObject:(id)obj options:(EJSONWritingOptions)opt error:(NSError **)error;
++ (id)JSONObjectWithData:(NSData *)data options:(EJSONReadingOptions)opt error:(NSError **)error;
++ (NSInteger)writeJSONObject:(id)obj toStream:(NSOutputStream *)stream options:(EJSONWritingOptions)opt error:(NSError **)error;
++ (id)JSONObjectWithStream:(NSInputStream *)stream options:(EJSONReadingOptions)opt error:(NSError **)error;
+
+@end
diff --git a/Classes/Source/Experimental/CJSONSerialization.m b/Classes/Source/Experimental/CJSONSerialization.m
new file mode 100644
index 0000000..5f603f6
--- /dev/null
+++ b/Classes/Source/Experimental/CJSONSerialization.m
@@ -0,0 +1,59 @@
+//
+// CJSONSerialization.m
+// TouchJSON
+//
+// Created by Jonathan Wight on 03/04/11.
+// Copyright 2011 toxicsoftware.com. All rights reserved.
+//
+
+#import "CJSONSerialization.h"
+
+#import "CJSONDeserializer.h"
+#import "CJSONSerializer.h"
+
+@implementation CJSONSerialization
+
++ (BOOL)isValidJSONObject:(id)obj
+ {
+ CJSONSerializer *theSerializer = [CJSONSerializer serializer];
+ return([theSerializer isValidJSONObject:obj]);
+ }
+
++ (NSData *)dataWithJSONObject:(id)obj options:(EJSONWritingOptions)opt error:(NSError **)error
+ {
+ #pragma unused (opt)
+
+ CJSONSerializer *theSerializer = [CJSONSerializer serializer];
+ NSData *theData = [theSerializer serializeObject:obj error:error];
+ return(theData);
+ }
+
++ (id)JSONObjectWithData:(NSData *)data options:(EJSONReadingOptions)opt error:(NSError **)error
+ {
+ CJSONDeserializer *theDeserializer = [CJSONDeserializer deserializer];
+ theDeserializer.options = (opt & kCJSONReadingMutableContainers ? 0 : kJSONDeserializationOptions_MutableContainers)
+ | (opt & kCJSONReadingMutableLeaves ? 0 : kJSONDeserializationOptions_MutableLeaves);
+ id theObject = [theDeserializer deserialize:data error:error];
+ return(theObject);
+ }
+
++ (NSInteger)writeJSONObject:(id)obj toStream:(NSOutputStream *)stream options:(EJSONWritingOptions)opt error:(NSError **)error
+ {
+ // TODO -- this is a quick work around.
+ NSInteger theSize = -1;
+ NSData *theData = [self dataWithJSONObject:obj options:opt error:error];
+ if (theData)
+ {
+ theSize = [stream write:[theData bytes] maxLength:[theData length]];
+ }
+ return(theSize);
+ }
+
++ (id)JSONObjectWithStream:(NSInputStream *)stream options:(EJSONReadingOptions)opt error:(NSError **)error
+ {
+ #pragma unused (stream, opt, error)
+ // TODO -- how much to read? Ugh.
+ return(NULL);
+ }
+
+@end
diff --git a/Classes/Source/Experimental/CJSONSerializedData.h b/Classes/Source/Experimental/CJSONSerializedData.h
new file mode 100644
index 0000000..4bba1be
--- /dev/null
+++ b/Classes/Source/Experimental/CJSONSerializedData.h
@@ -0,0 +1,25 @@
+//
+// CJSONSerializedData.h
+// TouchMetricsTest
+//
+// Created by Jonathan Wight on 10/31/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol CJSONSerializable <NSObject>
+@property (readonly, nonatomic, retain) NSData *serializedJSONData;
+@end
+
+#pragma mark -
+
+@interface CJSONSerializedData : NSObject <CJSONSerializable> {
+ NSData *data;
+}
+
+@property (readonly, nonatomic, retain) NSData *data;
+
+- (id)initWithData:(NSData *)inData;
+
+@end
diff --git a/Classes/Source/Experimental/CJSONSerializedData.m b/Classes/Source/Experimental/CJSONSerializedData.m
new file mode 100644
index 0000000..881899d
--- /dev/null
+++ b/Classes/Source/Experimental/CJSONSerializedData.m
@@ -0,0 +1,42 @@
+//
+// CJSONSerializedData.m
+// TouchMetricsTest
+//
+// Created by Jonathan Wight on 10/31/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import "CJSONSerializedData.h"
+
+@interface CJSONSerializedData ()
+@end
+
+#pragma mark -
+
+@implementation CJSONSerializedData
+
+@synthesize data;
+
+- (id)initWithData:(NSData *)inData
+ {
+ if ((self = [super init]) != NULL)
+ {
+ data = [inData retain];
+ }
+ return(self);
+ }
+
+- (void)dealloc
+ {
+ [data release];
+ data = NULL;
+ //
+ [super dealloc];
+ }
+
+- (NSData *)serializedJSONData
+ {
+ return(self.data);
+ }
+
+@end
diff --git a/Classes/Source/Extensions/CDataScanner_Extensions.h b/Classes/Source/Extensions/CDataScanner_Extensions.h
new file mode 100644
index 0000000..cde1dbb
--- /dev/null
+++ b/Classes/Source/Extensions/CDataScanner_Extensions.h
@@ -0,0 +1,40 @@
+//
+// CDataScanner_Extensions.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/08/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+@interface CDataScanner (CDataScanner_Extensions)
+
+- (BOOL)scanCStyleComment:(NSString **)outComment;
+- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment;
+
+- (NSUInteger)lineOfScanLocation;
+- (NSDictionary *)userInfoForScanLocation;
+
+@end
diff --git a/Classes/Source/Extensions/CDataScanner_Extensions.m b/Classes/Source/Extensions/CDataScanner_Extensions.m
new file mode 100644
index 0000000..90dbbda
--- /dev/null
+++ b/Classes/Source/Extensions/CDataScanner_Extensions.m
@@ -0,0 +1,135 @@
+//
+// CDataScanner_Extensions.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/08/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner_Extensions.h"
+
+#define LF 0x000a // Line Feed
+#define FF 0x000c // Form Feed
+#define CR 0x000d // Carriage Return
+#define NEL 0x0085 // Next Line
+#define LS 0x2028 // Line Separator
+#define PS 0x2029 // Paragraph Separator
+
+@implementation CDataScanner (CDataScanner_Extensions)
+
+- (BOOL)scanCStyleComment:(NSString **)outComment
+{
+if ([self scanString:@"/*" intoString:NULL] == YES)
+ {
+ NSString *theComment = NULL;
+ if ([self scanUpToString:@"*/" intoString:&theComment] == NO)
+ [NSException raise:NSGenericException format:@"Started to scan a C style comment but it wasn't terminated."];
+
+ if ([theComment rangeOfString:@"/*"].location != NSNotFound)
+ [NSException raise:NSGenericException format:@"C style comments should not be nested."];
+
+ if ([self scanString:@"*/" intoString:NULL] == NO)
+ [NSException raise:NSGenericException format:@"C style comment did not end correctly."];
+
+ if (outComment != NULL)
+ *outComment = theComment;
+
+ return(YES);
+ }
+else
+ {
+ return(NO);
+ }
+}
+
+- (BOOL)scanCPlusPlusStyleComment:(NSString **)outComment
+ {
+ if ([self scanString:@"//" intoString:NULL] == YES)
+ {
+ unichar theCharacters[] = { LF, FF, CR, NEL, LS, PS, };
+ NSCharacterSet *theLineBreaksCharacterSet = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithCharacters:theCharacters length:sizeof(theCharacters) / sizeof(*theCharacters)]];
+
+ NSString *theComment = NULL;
+ [self scanUpToCharactersFromSet:theLineBreaksCharacterSet intoString:&theComment];
+ [self scanCharactersFromSet:theLineBreaksCharacterSet intoString:NULL];
+
+ if (outComment != NULL)
+ *outComment = theComment;
+
+ return(YES);
+ }
+ else
+ {
+ return(NO);
+ }
+ }
+
+- (NSUInteger)lineOfScanLocation
+ {
+ NSUInteger theLine = 0;
+ for (const u_int8_t *C = start; C < current; ++C)
+ {
+ // TODO: JIW What about MS-DOS line endings you bastard! (Also other unicode line endings)
+ if (*C == '\n' || *C == '\r')
+ {
+ ++theLine;
+ }
+ }
+ return(theLine);
+ }
+
+- (NSDictionary *)userInfoForScanLocation
+ {
+ NSUInteger theLine = 0;
+ const u_int8_t *theLineStart = start;
+ for (const u_int8_t *C = start; C < current; ++C)
+ {
+ if (*C == '\n' || *C == '\r')
+ {
+ theLineStart = C - 1;
+ ++theLine;
+ }
+ }
+
+ NSUInteger theCharacter = current - theLineStart;
+
+ NSRange theStartRange = NSIntersectionRange((NSRange){ .location = MAX((NSInteger)self.scanLocation - 20, 0), .length = 20 + (NSInteger)self.scanLocation - 20 }, (NSRange){ .location = 0, .length = self.data.length });
+ NSRange theEndRange = NSIntersectionRange((NSRange){ .location = self.scanLocation, .length = 20 }, (NSRange){ .location = 0, .length = self.data.length });
+
+
+ NSString *theSnippet = [NSString stringWithFormat:@"%@!HERE>!%@",
+ [[[NSString alloc] initWithData:[self.data subdataWithRange:theStartRange] encoding:NSUTF8StringEncoding] autorelease],
+ [[[NSString alloc] initWithData:[self.data subdataWithRange:theEndRange] encoding:NSUTF8StringEncoding] autorelease]
+ ];
+
+ NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithUnsignedInteger:theLine], @"line",
+ [NSNumber numberWithUnsignedInteger:theCharacter], @"character",
+ [NSNumber numberWithUnsignedInteger:self.scanLocation], @"location",
+ theSnippet, @"snippet",
+ NULL];
+ return(theUserInfo);
+ }
+
+@end
diff --git a/Classes/Source/Extensions/NSDictionary_JSONExtensions.h b/Classes/Source/Extensions/NSDictionary_JSONExtensions.h
new file mode 100644
index 0000000..6e611d0
--- /dev/null
+++ b/Classes/Source/Extensions/NSDictionary_JSONExtensions.h
@@ -0,0 +1,37 @@
+//
+// NSDictionary_JSONExtensions.h
+// TouchCode
+//
+// Created by Jonathan Wight on 04/17/08.
+// Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSDictionary (NSDictionary_JSONExtensions)
+
++ (id)dictionaryWithJSONData:(NSData *)inData error:(NSError **)outError;
++ (id)dictionaryWithJSONString:(NSString *)inJSON error:(NSError **)outError;
+
+@end
diff --git a/Classes/Source/Extensions/NSDictionary_JSONExtensions.m b/Classes/Source/Extensions/NSDictionary_JSONExtensions.m
new file mode 100644
index 0000000..c0bb43c
--- /dev/null
+++ b/Classes/Source/Extensions/NSDictionary_JSONExtensions.m
@@ -0,0 +1,47 @@
+//
+// NSDictionary_JSONExtensions.m
+// TouchCode
+//
+// Created by Jonathan Wight on 04/17/08.
+// Copyright 2008 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "NSDictionary_JSONExtensions.h"
+
+#import "CJSONDeserializer.h"
+
+@implementation NSDictionary (NSDictionary_JSONExtensions)
+
++ (id)dictionaryWithJSONData:(NSData *)inData error:(NSError **)outError
+ {
+ return([[CJSONDeserializer deserializer] deserialize:inData error:outError]);
+ }
+
++ (id)dictionaryWithJSONString:(NSString *)inJSON error:(NSError **)outError;
+ {
+ NSData *theData = [inJSON dataUsingEncoding:NSUTF8StringEncoding];
+ return([self dictionaryWithJSONData:theData error:outError]);
+ }
+
+@end
diff --git a/Classes/Source/JSON/CJSONDeserializer.h b/Classes/Source/JSON/CJSONDeserializer.h
new file mode 100644
index 0000000..0c3ed02
--- /dev/null
+++ b/Classes/Source/JSON/CJSONDeserializer.h
@@ -0,0 +1,63 @@
+//
+// CJSONDeserializer.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/15/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "CJSONScanner.h"
+
+extern NSString *const kJSONDeserializerErrorDomain /* = @"CJSONDeserializerErrorDomain" */;
+
+enum {
+ kJSONDeserializationOptions_MutableContainers = kJSONScannerOptions_MutableContainers,
+ kJSONDeserializationOptions_MutableLeaves = kJSONScannerOptions_MutableLeaves,
+};
+typedef NSUInteger EJSONDeserializationOptions;
+
+@class CJSONScanner;
+
+@interface CJSONDeserializer : NSObject {
+ CJSONScanner *scanner;
+ EJSONDeserializationOptions options;
+}
+
+@property (readwrite, nonatomic, retain) CJSONScanner *scanner;
+/// Object to return instead when a null encountered in the JSON. Defaults to NSNull. Setting to null causes the scanner to skip null values.
+@property (readwrite, nonatomic, retain) id nullObject;
+/// JSON must be encoded in Unicode (UTF-8, UTF-16 or UTF-32). Use this if you expect to get the JSON in another encoding.
+@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding;
+@property (readwrite, nonatomic, assign) EJSONDeserializationOptions options;
+
++ (id)deserializer;
+
+- (id)deserialize:(NSData *)inData error:(NSError **)outError;
+
+- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError;
+- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError;
+
+@end
diff --git a/Classes/Source/JSON/CJSONDeserializer.m b/Classes/Source/JSON/CJSONDeserializer.m
new file mode 100644
index 0000000..27a2d03
--- /dev/null
+++ b/Classes/Source/JSON/CJSONDeserializer.m
@@ -0,0 +1,161 @@
+//
+// CJSONDeserializer.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/15/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONDeserializer.h"
+
+#import "CJSONScanner.h"
+#import "CDataScanner.h"
+
+NSString *const kJSONDeserializerErrorDomain = @"CJSONDeserializerErrorDomain";
+
+@interface CJSONDeserializer ()
+@end
+
+@implementation CJSONDeserializer
+
+@synthesize scanner;
+@synthesize options;
+
++ (id)deserializer
+ {
+ return([[[self alloc] init] autorelease]);
+ }
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ }
+ return(self);
+ }
+
+- (void)dealloc
+ {
+ [scanner release];
+ scanner = NULL;
+ //
+ [super dealloc];
+ }
+
+#pragma mark -
+
+- (CJSONScanner *)scanner
+ {
+ if (scanner == NULL)
+ {
+ scanner = [[CJSONScanner alloc] init];
+ }
+ return(scanner);
+ }
+
+- (id)nullObject
+ {
+ return(self.scanner.nullObject);
+ }
+
+- (void)setNullObject:(id)inNullObject
+ {
+ self.scanner.nullObject = inNullObject;
+ }
+
+#pragma mark -
+
+- (NSStringEncoding)allowedEncoding
+ {
+ return(self.scanner.allowedEncoding);
+ }
+
+- (void)setAllowedEncoding:(NSStringEncoding)inAllowedEncoding
+ {
+ self.scanner.allowedEncoding = inAllowedEncoding;
+ }
+
+#pragma mark -
+
+- (id)deserialize:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ id theObject = NULL;
+ if ([self.scanner scanJSONObject:&theObject error:outError] == YES)
+ return(theObject);
+ else
+ return(NULL);
+ }
+
+- (id)deserializeAsDictionary:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ NSDictionary *theDictionary = NULL;
+ if ([self.scanner scanJSONDictionary:&theDictionary error:outError] == YES)
+ return(theDictionary);
+ else
+ return(NULL);
+ }
+
+- (id)deserializeAsArray:(NSData *)inData error:(NSError **)outError
+ {
+ if (inData == NULL || [inData length] == 0)
+ {
+ if (outError)
+ *outError = [NSError errorWithDomain:kJSONDeserializerErrorDomain code:kJSONScannerErrorCode_NothingToScan userInfo:NULL];
+
+ return(NULL);
+ }
+ if ([self.scanner setData:inData error:outError] == NO)
+ {
+ return(NULL);
+ }
+ NSArray *theArray = NULL;
+ if ([self.scanner scanJSONArray:&theArray error:outError] == YES)
+ return(theArray);
+ else
+ return(NULL);
+ }
+
+@end
diff --git a/Classes/Source/JSON/CJSONScanner.h b/Classes/Source/JSON/CJSONScanner.h
new file mode 100644
index 0000000..d410893
--- /dev/null
+++ b/Classes/Source/JSON/CJSONScanner.h
@@ -0,0 +1,95 @@
+//
+// CJSONScanner.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CDataScanner.h"
+
+enum {
+ kJSONScannerOptions_MutableContainers = 0x1,
+ kJSONScannerOptions_MutableLeaves = 0x2,
+};
+typedef NSUInteger EJSONScannerOptions;
+
+/// CDataScanner subclass that understands JSON syntax natively. You should generally use CJSONDeserializer instead of this class. (TODO - this could have been a category?)
+@interface CJSONScanner : CDataScanner {
+ BOOL strictEscapeCodes;
+ id nullObject;
+ NSStringEncoding allowedEncoding;
+ EJSONScannerOptions options;
+}
+
+@property (readwrite, nonatomic, assign) BOOL strictEscapeCodes;
+@property (readwrite, nonatomic, retain) id nullObject;
+@property (readwrite, nonatomic, assign) NSStringEncoding allowedEncoding;
+@property (readwrite, nonatomic, assign) EJSONScannerOptions options;
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError;
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError;
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError;
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError;
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError;
+
+@end
+
+extern NSString *const kJSONScannerErrorDomain /* = @"kJSONScannerErrorDomain" */;
+
+typedef enum {
+
+ // Fundamental scanning errors
+ kJSONScannerErrorCode_NothingToScan = -11,
+ kJSONScannerErrorCode_CouldNotDecodeData = -12,
+ kJSONScannerErrorCode_CouldNotSerializeData = -13,
+ kJSONScannerErrorCode_CouldNotSerializeObject = -14,
+ kJSONScannerErrorCode_CouldNotScanObject = -15,
+
+ // Dictionary scanning
+ kJSONScannerErrorCode_DictionaryStartCharacterMissing = -101,
+ kJSONScannerErrorCode_DictionaryKeyScanFailed = -102,
+ kJSONScannerErrorCode_DictionaryKeyNotTerminated = -103,
+ kJSONScannerErrorCode_DictionaryValueScanFailed = -104,
+ kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter = -105,
+ kJSONScannerErrorCode_DictionaryNotTerminated = -106,
+
+ // Array scanning
+ kJSONScannerErrorCode_ArrayStartCharacterMissing = -201,
+ kJSONScannerErrorCode_ArrayValueScanFailed = -202,
+ kJSONScannerErrorCode_ArrayValueIsNull = -203,
+ kJSONScannerErrorCode_ArrayNotTerminated = -204,
+
+ // String scanning
+ kJSONScannerErrorCode_StringNotStartedWithBackslash = -301,
+ kJSONScannerErrorCode_StringUnicodeNotDecoded = -302,
+ kJSONScannerErrorCode_StringUnknownEscapeCode = -303,
+ kJSONScannerErrorCode_StringNotTerminated = -304,
+
+ // Number scanning
+ kJSONScannerErrorCode_NumberNotScannable = -401
+
+} EJSONScannerErrorCode;
diff --git a/Classes/Source/JSON/CJSONScanner.m b/Classes/Source/JSON/CJSONScanner.m
new file mode 100644
index 0000000..c5ffeb4
--- /dev/null
+++ b/Classes/Source/JSON/CJSONScanner.m
@@ -0,0 +1,676 @@
+//
+// CJSONScanner.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONScanner.h"
+
+#import "CDataScanner_Extensions.h"
+
+#if !defined(TREAT_COMMENTS_AS_WHITESPACE)
+#define TREAT_COMMENTS_AS_WHITESPACE 0
+#endif // !defined(TREAT_COMMENTS_AS_WHITESPACE)
+
+NSString *const kJSONScannerErrorDomain = @"kJSONScannerErrorDomain";
+
+inline static int HexToInt(char inCharacter)
+ {
+ int theValues[] = { 0x0 /* 48 '0' */, 0x1 /* 49 '1' */, 0x2 /* 50 '2' */, 0x3 /* 51 '3' */, 0x4 /* 52 '4' */, 0x5 /* 53 '5' */, 0x6 /* 54 '6' */, 0x7 /* 55 '7' */, 0x8 /* 56 '8' */, 0x9 /* 57 '9' */, -1 /* 58 ':' */, -1 /* 59 ';' */, -1 /* 60 '<' */, -1 /* 61 '=' */, -1 /* 62 '>' */, -1 /* 63 '?' */, -1 /* 64 '@' */, 0xa /* 65 'A' */, 0xb /* 66 'B' */, 0xc /* 67 'C' */, 0xd /* 68 'D' */, 0xe /* 69 'E' */, 0xf /* 70 'F' */, -1 /* 71 'G' */, -1 /* 72 'H' */, -1 /* 73 'I' */, -1 /* 74 'J' */, -1 /* 75 'K' */, -1 /* 76 'L' */, -1 /* 77 'M' */, -1 /* 78 'N' */, -1 /* 79 'O' */, -1 /* 80 'P' */, -1 /* 81 'Q' */, -1 /* 82 'R' */, -1 /* 83 'S' */, -1 /* 84 'T' */, -1 /* 85 'U' */, -1 /* 86 'V' */, -1 /* 87 'W' */, -1 /* 88 'X' */, -1 /* 89 'Y' */, -1 /* 90 'Z' */, -1 /* 91 '[' */, -1 /* 92 '\' */, -1 /* 93 ']' */, -1 /* 94 '^' */, -1 /* 95 '_' */, -1 /* 96 '`' */, 0xa /* 97 'a' */, 0xb /* 98 'b' */, 0xc /* 99 'c' */, 0xd /* 100 'd' */, 0xe /* 101 'e' */, 0xf /* 102 'f' */, };
+ if (inCharacter >= '0' && inCharacter <= 'f')
+ return(theValues[inCharacter - '0']);
+ else
+ return(-1);
+ }
+
+@interface CJSONScanner ()
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue;
+@end
+
+#pragma mark -
+
+@implementation CJSONScanner
+
+@synthesize strictEscapeCodes;
+@synthesize nullObject;
+@synthesize allowedEncoding;
+@synthesize options;
+
+- (id)init
+ {
+ if ((self = [super init]) != NULL)
+ {
+ strictEscapeCodes = NO;
+ nullObject = [[NSNull null] retain];
+ }
+ return(self);
+ }
+
+- (void)dealloc
+ {
+ [nullObject release];
+ nullObject = NULL;
+ //
+ [super dealloc];
+ }
+
+#pragma mark -
+
+- (BOOL)setData:(NSData *)inData error:(NSError **)outError;
+ {
+ NSData *theData = inData;
+ if (theData && theData.length >= 4)
+ {
+ // This code is lame, but it works. Because the first character of any JSON string will always be a (ascii) control character we can work out the Unicode encoding by the bit pattern. See section 3 of http://www.ietf.org/rfc/rfc4627.txt
+ const char *theChars = theData.bytes;
+ NSStringEncoding theEncoding = NSUTF8StringEncoding;
+ if (theChars[0] != 0 && theChars[1] == 0)
+ {
+ if (theChars[2] != 0 && theChars[3] == 0)
+ theEncoding = NSUTF16LittleEndianStringEncoding;
+ else if (theChars[2] == 0 && theChars[3] == 0)
+ theEncoding = NSUTF32LittleEndianStringEncoding;
+ }
+ else if (theChars[0] == 0 && theChars[2] == 0 && theChars[3] != 0)
+ {
+ if (theChars[1] == 0)
+ theEncoding = NSUTF32BigEndianStringEncoding;
+ else if (theChars[1] != 0)
+ theEncoding = NSUTF16BigEndianStringEncoding;
+ }
+
+ NSString *theString = [[NSString alloc] initWithData:theData encoding:theEncoding];
+ if (theString == NULL && self.allowedEncoding != 0)
+ {
+ theString = [[NSString alloc] initWithData:theData encoding:self.allowedEncoding];
+ }
+ theData = [theString dataUsingEncoding:NSUTF8StringEncoding];
+ [theString release];
+ }
+
+ if (theData)
+ {
+ [super setData:theData];
+ return(YES);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan data. Data wasn't encoded properly?", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotDecodeData userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+ }
+
+- (void)setData:(NSData *)inData
+ {
+ [self setData:inData error:NULL];
+ }
+
+#pragma mark -
+
+- (BOOL)scanJSONObject:(id *)outObject error:(NSError **)outError
+ {
+ BOOL theResult = YES;
+
+ [self skipWhitespace];
+
+ id theObject = NULL;
+
+ const unichar C = [self currentCharacter];
+ switch (C)
+ {
+ case 't':
+ if ([self scanUTF8String:"true" intoString:NULL])
+ {
+ theObject = [NSNumber numberWithBool:YES];
+ }
+ break;
+ case 'f':
+ if ([self scanUTF8String:"false" intoString:NULL])
+ {
+ theObject = [NSNumber numberWithBool:NO];
+ }
+ break;
+ case 'n':
+ if ([self scanUTF8String:"null" intoString:NULL])
+ {
+ theObject = self.nullObject;
+ }
+ break;
+ case '\"':
+ case '\'':
+ theResult = [self scanJSONStringConstant:&theObject error:outError];
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ theResult = [self scanJSONNumberConstant:&theObject error:outError];
+ break;
+ case '{':
+ theResult = [self scanJSONDictionary:&theObject error:outError];
+ break;
+ case '[':
+ theResult = [self scanJSONArray:&theObject error:outError];
+ break;
+ default:
+ theResult = NO;
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan object. Character not a valid JSON character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_CouldNotScanObject userInfo:theUserInfo];
+ }
+ break;
+ }
+
+ if (outObject != NULL)
+ *outObject = theObject;
+
+ return(theResult);
+ }
+
+- (BOOL)scanJSONDictionary:(NSDictionary **)outDictionary error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:'{'] == NO)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Dictionary that does not start with '{' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryStartCharacterMissing userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+
+ NSMutableDictionary *theDictionary = [[NSMutableDictionary alloc] init];
+
+ while ([self currentCharacter] != '}')
+ {
+ [self skipWhitespace];
+
+ if ([self currentCharacter] == '}')
+ break;
+
+ NSString *theKey = NULL;
+ if ([self scanJSONStringConstant:&theKey error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Failed to scan a key.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyScanFailed userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:':'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Key was not terminated with a ':' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyNotTerminated userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ id theValue = NULL;
+ if ([self scanJSONObject:&theValue error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Failed to scan a value.", NSLocalizedDescriptionKey,
+ NULL];
+
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryValueScanFailed userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ if (theValue == NULL && self.nullObject == NULL)
+ {
+ // If the value is a null and nullObject is also null then we're skipping this key/value pair.
+ }
+ else
+ {
+ [theDictionary setValue:theValue forKey:theKey];
+ }
+
+ [self skipWhitespace];
+ if ([self scanCharacter:','] == NO)
+ {
+ if ([self currentCharacter] != '}')
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Key value pairs not delimited with a ',' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryKeyValuePairNoDelimiter userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+ break;
+ }
+ else
+ {
+ [self skipWhitespace];
+ if ([self currentCharacter] == '}')
+ break;
+ }
+ }
+
+ if ([self scanCharacter:'}'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan dictionary. Dictionary not terminated by a '}' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_DictionaryNotTerminated userInfo:theUserInfo];
+ }
+ [theDictionary release];
+ return(NO);
+ }
+
+ if (outDictionary != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableContainers)
+ {
+ *outDictionary = [theDictionary autorelease];
+ }
+ else
+ {
+ *outDictionary = [[theDictionary copy] autorelease];
+ [theDictionary release];
+ }
+ }
+ else
+ {
+ [theDictionary release];
+ }
+
+ return(YES);
+ }
+
+- (BOOL)scanJSONArray:(NSArray **)outArray error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:'['] == NO)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not started by a '[' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayStartCharacterMissing userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+
+ NSMutableArray *theArray = [[NSMutableArray alloc] init];
+
+ [self skipWhitespace];
+ while ([self currentCharacter] != ']')
+ {
+ NSString *theValue = NULL;
+ if ([self scanJSONObject:&theValue error:outError] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Could not scan a value.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueScanFailed userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ if (theValue == NULL)
+ {
+ if (self.nullObject != NULL)
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Value is NULL.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayValueIsNull userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+ }
+ else
+ {
+ [theArray addObject:theValue];
+ }
+
+ [self skipWhitespace];
+ if ([self scanCharacter:','] == NO)
+ {
+ [self skipWhitespace];
+ if ([self currentCharacter] != ']')
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ break;
+ }
+ [self skipWhitespace];
+ }
+
+ [self skipWhitespace];
+
+ if ([self scanCharacter:']'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan array. Array not terminated by a ']' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_ArrayNotTerminated userInfo:theUserInfo];
+ }
+ [theArray release];
+ return(NO);
+ }
+
+ if (outArray != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableContainers)
+ {
+ *outArray = [theArray autorelease];
+ }
+ else
+ {
+ *outArray = [[theArray copy] autorelease];
+ [theArray release];
+ }
+ }
+ else
+ {
+ [theArray release];
+ }
+ return(YES);
+ }
+
+- (BOOL)scanJSONStringConstant:(NSString **)outStringConstant error:(NSError **)outError
+ {
+ NSUInteger theScanLocation = [self scanLocation];
+
+ [self skipWhitespace];
+
+ NSMutableString *theString = [[NSMutableString alloc] init];
+
+ if ([self scanCharacter:'"'] == NO)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. String not started by a '\"' character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotStartedWithBackslash userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+
+ while ([self scanCharacter:'"'] == NO)
+ {
+ NSString *theStringChunk = NULL;
+ if ([self scanNotQuoteCharactersIntoString:&theStringChunk])
+ {
+ CFStringAppend((CFMutableStringRef)theString, (CFStringRef)theStringChunk);
+ }
+ else if ([self scanCharacter:'\\'] == YES)
+ {
+ unichar theCharacter = [self scanCharacter];
+ switch (theCharacter)
+ {
+ case '"':
+ case '\\':
+ case '/':
+ break;
+ case 'b':
+ theCharacter = '\b';
+ break;
+ case 'f':
+ theCharacter = '\f';
+ break;
+ case 'n':
+ theCharacter = '\n';
+ break;
+ case 'r':
+ theCharacter = '\r';
+ break;
+ case 't':
+ theCharacter = '\t';
+ break;
+ case 'u':
+ {
+ theCharacter = 0;
+
+ int theShift;
+ for (theShift = 12; theShift >= 0; theShift -= 4)
+ {
+ const int theDigit = HexToInt([self scanCharacter]);
+ if (theDigit == -1)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. Unicode character could not be decoded.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnicodeNotDecoded userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ theCharacter |= (theDigit << theShift);
+ }
+ }
+ break;
+ default:
+ {
+ if (strictEscapeCodes == YES)
+ {
+ [self setScanLocation:theScanLocation];
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. Unknown escape code.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringUnknownEscapeCode userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ }
+ break;
+ }
+ CFStringAppendCharacters((CFMutableStringRef)theString, &theCharacter, 1);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan string constant. No terminating double quote character.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_StringNotTerminated userInfo:theUserInfo];
+ }
+ [theString release];
+ return(NO);
+ }
+ }
+
+ if (outStringConstant != NULL)
+ {
+ if (self.options & kJSONScannerOptions_MutableLeaves)
+ {
+ *outStringConstant = [theString autorelease];
+ }
+ else
+ {
+ *outStringConstant = [[theString copy] autorelease];
+ [theString release];
+ }
+ }
+ else
+ {
+ [theString release];
+ }
+
+ return(YES);
+ }
+
+- (BOOL)scanJSONNumberConstant:(NSNumber **)outNumberConstant error:(NSError **)outError
+ {
+ NSNumber *theNumber = NULL;
+
+ [self skipWhitespace];
+
+ if ([self scanNumber:&theNumber] == YES)
+ {
+ if (outNumberConstant != NULL)
+ *outNumberConstant = theNumber;
+ return(YES);
+ }
+ else
+ {
+ if (outError)
+ {
+ NSMutableDictionary *theUserInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
+ @"Could not scan number constant.", NSLocalizedDescriptionKey,
+ NULL];
+ [theUserInfo addEntriesFromDictionary:self.userInfoForScanLocation];
+ *outError = [NSError errorWithDomain:kJSONScannerErrorDomain code:kJSONScannerErrorCode_NumberNotScannable userInfo:theUserInfo];
+ }
+ return(NO);
+ }
+ }
+
+#if TREAT_COMMENTS_AS_WHITESPACE
+- (void)skipWhitespace
+ {
+ [super skipWhitespace];
+ [self scanCStyleComment:NULL];
+ [self scanCPlusPlusStyleComment:NULL];
+ [super skipWhitespace];
+ }
+#endif // TREAT_COMMENTS_AS_WHITESPACE
+
+#pragma mark -
+
+- (BOOL)scanNotQuoteCharactersIntoString:(NSString **)outValue
+ {
+ u_int8_t *P;
+ for (P = current; P < end && *P != '\"' && *P != '\\'; ++P)
+ ;
+
+ if (P == current)
+ {
+ return(NO);
+ }
+
+ if (outValue)
+ {
+ *outValue = [[[NSString alloc] initWithBytes:current length:P - current encoding:NSUTF8StringEncoding] autorelease];
+ }
+
+ current = P;
+
+ return(YES);
+ }
+
+@end
diff --git a/Classes/Source/JSON/CJSONSerializer.h b/Classes/Source/JSON/CJSONSerializer.h
new file mode 100644
index 0000000..748a85c
--- /dev/null
+++ b/Classes/Source/JSON/CJSONSerializer.h
@@ -0,0 +1,53 @@
+//
+// CJSONSerializer.h
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface CJSONSerializer : NSObject {
+}
+
++ (id)serializer;
+
+- (BOOL)isValidJSONObject:(id)inObject;
+
+/// Take any JSON compatible object (generally NSNull, NSNumber, NSString, NSArray and NSDictionary) and produce an NSData containing the serialized JSON.
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError;
+
+- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError;
+- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError;
+- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError;
+- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError;
+- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError;
+
+@end
+
+typedef enum {
+ CJSONSerializerErrorCouldNotSerializeDataType = -1,
+ CJSONSerializerErrorCouldNotSerializeObject = -1
+} CJSONSerializerError;
diff --git a/Classes/Source/JSON/CJSONSerializer.m b/Classes/Source/JSON/CJSONSerializer.m
new file mode 100644
index 0000000..952b3c2
--- /dev/null
+++ b/Classes/Source/JSON/CJSONSerializer.m
@@ -0,0 +1,342 @@
+//
+// CJSONSerializer.m
+// TouchCode
+//
+// Created by Jonathan Wight on 12/07/2005.
+// Copyright 2005 toxicsoftware.com. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#import "CJSONSerializer.h"
+
+#import "JSONRepresentation.h"
+
+static NSData *kNULL = NULL;
+static NSData *kFalse = NULL;
+static NSData *kTrue = NULL;
+
+@implementation CJSONSerializer
+
++ (void)initialize
+ {
+ NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
+
+ if (self == [CJSONSerializer class])
+ {
+ if (kNULL == NULL)
+ kNULL = [[NSData alloc] initWithBytesNoCopy:(void *)"null" length:4 freeWhenDone:NO];
+ if (kFalse == NULL)
+ kFalse = [[NSData alloc] initWithBytesNoCopy:(void *)"false" length:5 freeWhenDone:NO];
+ if (kTrue == NULL)
+ kTrue = [[NSData alloc] initWithBytesNoCopy:(void *)"true" length:4 freeWhenDone:NO];
+
+ [thePool release];
+ }
+ }
+
++ (id)serializer
+ {
+ return([[[self alloc] init] autorelease]);
+ }
+
+- (BOOL)isValidJSONObject:(id)inObject
+ {
+ if ([inObject isKindOfClass:[NSNull class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSNumber class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSString class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSArray class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSDictionary class]])
+ {
+ return(YES);
+ }
+ else if ([inObject isKindOfClass:[NSData class]])
+ {
+ return(YES);
+ }
+ else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)])
+ {
+ return(YES);
+ }
+ else
+ {
+ return(NO);
+ }
+ }
+
+- (NSData *)serializeObject:(id)inObject error:(NSError **)outError
+ {
+ NSData *theResult = NULL;
+
+ if ([inObject isKindOfClass:[NSNull class]])
+ {
+ theResult = [self serializeNull:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSNumber class]])
+ {
+ theResult = [self serializeNumber:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSString class]])
+ {
+ theResult = [self serializeString:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSArray class]])
+ {
+ theResult = [self serializeArray:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSDictionary class]])
+ {
+ theResult = [self serializeDictionary:inObject error:outError];
+ }
+ else if ([inObject isKindOfClass:[NSData class]])
+ {
+ NSString *theString = [[[NSString alloc] initWithData:inObject encoding:NSUTF8StringEncoding] autorelease];
+ theResult = [self serializeString:theString error:outError];
+ }
+ else if ([inObject respondsToSelector:@selector(JSONDataRepresentation)])
+ {
+ theResult = [inObject JSONDataRepresentation];
+ }
+ else
+ {
+ if (outError)
+ {
+ NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:@"Cannot serialize data of type '%@'", NSStringFromClass([inObject class])], NSLocalizedDescriptionKey,
+ NULL];
+ *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeDataType userInfo:theUserInfo];
+ }
+ return(NULL);
+ }
+ if (theResult == NULL)
+ {
+ if (outError)
+ {
+ NSDictionary *theUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithFormat:@"Could not serialize object '%@'", inObject], NSLocalizedDescriptionKey,
+ NULL];
+ *outError = [NSError errorWithDomain:@"TODO_DOMAIN" code:CJSONSerializerErrorCouldNotSerializeObject userInfo:theUserInfo];
+ }
+ return(NULL);
+ }
+ return(theResult);
+ }
+
+- (NSData *)serializeNull:(NSNull *)inNull error:(NSError **)outError
+ {
+ #pragma unused (inNull, outError)
+ return(kNULL);
+ }
+
+- (NSData *)serializeNumber:(NSNumber *)inNumber error:(NSError **)outError
+ {
+ #pragma unused (outError)
+ NSData *theResult = NULL;
+ switch (CFNumberGetType((CFNumberRef)inNumber))
+ {
+ case kCFNumberCharType:
+ {
+ int theValue = [inNumber intValue];
+ if (theValue == 0)
+ theResult = kFalse;
+ else if (theValue == 1)
+ theResult = kTrue;
+ else
+ theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding];
+ }
+ break;
+ case kCFNumberFloat32Type:
+ case kCFNumberFloat64Type:
+ case kCFNumberFloatType:
+ case kCFNumberDoubleType:
+ case kCFNumberSInt8Type:
+ case kCFNumberSInt16Type:
+ case kCFNumberSInt32Type:
+ case kCFNumberSInt64Type:
+ case kCFNumberShortType:
+ case kCFNumberIntType:
+ case kCFNumberLongType:
+ case kCFNumberLongLongType:
+ case kCFNumberCFIndexType:
+ default:
+ theResult = [[inNumber stringValue] dataUsingEncoding:NSASCIIStringEncoding];
+ break;
+ }
+ return(theResult);
+ }
+
+- (NSData *)serializeString:(NSString *)inString error:(NSError **)outError
+ {
+ #pragma unused (outError)
+
+ const char *theUTF8String = [inString UTF8String];
+
+ NSMutableData *theData = [NSMutableData dataWithLength:strlen(theUTF8String) * 2 + 2];
+
+ char *theOutputStart = [theData mutableBytes];
+ char *OUT = theOutputStart;
+
+ *OUT++ = '"';
+
+ for (const char *IN = theUTF8String; IN && *IN != '\0'; ++IN)
+ {
+ switch (*IN)
+ {
+ case '\\':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '\\';
+ }
+ break;
+ case '\"':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '\"';
+ }
+ break;
+ case '/':
+ {
+ *OUT++ = '\\';
+ *OUT++ = '/';
+ }
+ break;
+ case '\b':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'b';
+ }
+ break;
+ case '\f':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'f';
+ }
+ break;
+ case '\n':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'n';
+ }
+ break;
+ case '\r':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 'r';
+ }
+ break;
+ case '\t':
+ {
+ *OUT++ = '\\';
+ *OUT++ = 't';
+ }
+ break;
+ default:
+ {
+ *OUT++ = *IN;
+ }
+ break;
+ }
+ }
+
+ *OUT++ = '"';
+
+ theData.length = OUT - theOutputStart;
+ return(theData);
+ }
+
+- (NSData *)serializeArray:(NSArray *)inArray error:(NSError **)outError
+ {
+ NSMutableData *theData = [NSMutableData data];
+
+ [theData appendBytes:"[" length:1];
+
+ NSEnumerator *theEnumerator = [inArray objectEnumerator];
+ id theValue = NULL;
+ NSUInteger i = 0;
+ while ((theValue = [theEnumerator nextObject]) != NULL)
+ {
+ NSData *theValueData = [self serializeObject:theValue error:outError];
+ if (theValueData == NULL)
+ {
+ return(NULL);
+ }
+ [theData appendData:theValueData];
+ if (++i < [inArray count])
+ [theData appendBytes:"," length:1];
+ }
+
+ [theData appendBytes:"]" length:1];
+
+ return(theData);
+ }
+
+- (NSData *)serializeDictionary:(NSDictionary *)inDictionary error:(NSError **)outError
+ {
+ NSMutableData *theData = [NSMutableData data];
+
+ [theData appendBytes:"{" length:1];
+
+ NSArray *theKeys = [inDictionary allKeys];
+ NSEnumerator *theEnumerator = [theKeys objectEnumerator];
+ NSString *theKey = NULL;
+ while ((theKey = [theEnumerator nextObject]) != NULL)
+ {
+ id theValue = [inDictionary objectForKey:theKey];
+
+ NSData *theKeyData = [self serializeString:theKey error:outError];
+ if (theKeyData == NULL)
+ {
+ return(NULL);
+ }
+ NSData *theValueData = [self serializeObject:theValue error:outError];
+ if (theValueData == NULL)
+ {
+ return(NULL);
+ }
+
+
+ [theData appendData:theKeyData];
+ [theData appendBytes:":" length:1];
+ [theData appendData:theValueData];
+
+ if (theKey != [theKeys lastObject])
+ [theData appendData:[@"," dataUsingEncoding:NSASCIIStringEncoding]];
+ }
+
+ [theData appendBytes:"}" length:1];
+
+ return(theData);
+ }
+
+@end
diff --git a/Classes/Source/JSON/JSONRepresentation.h b/Classes/Source/JSON/JSONRepresentation.h
new file mode 100644
index 0000000..a83d76f
--- /dev/null
+++ b/Classes/Source/JSON/JSONRepresentation.h
@@ -0,0 +1,18 @@
+//
+// JSONRepresentation.h
+// TouchJSON
+//
+// Created by Jonathan Wight on 10/15/10.
+// Copyright 2010 toxicsoftware.com. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol JSONRepresentation
+
+@optional
+- (id)initWithJSONDataRepresentation:(NSData *)inJSONData;
+
+- (NSData *)JSONDataRepresentation;
+
+@end