aboutsummaryrefslogtreecommitdiffstats
path: root/iPhone/CordovaLib/Classes
diff options
context:
space:
mode:
Diffstat (limited to 'iPhone/CordovaLib/Classes')
-rwxr-xr-xiPhone/CordovaLib/Classes/CDV.h3
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVAvailability.h4
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVCamera.h4
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVCamera.m159
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVCapture.m5
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVCommandDelegate.h7
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVCommandDelegateImpl.m24
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVCommandQueue.m4
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVConfigParser.h5
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVConfigParser.m23
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVContact.m49
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVDevice.m3
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVEcho.m14
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVFile.h2
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVFile.m341
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVFileTransfer.h5
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVFileTransfer.m173
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVInAppBrowser.h21
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVInAppBrowser.m162
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVInvokedUrlCommand.m30
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVJSON.h (renamed from iPhone/CordovaLib/Classes/CDVCordovaView.h)11
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVJSON.m77
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVLocalStorage.h2
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVLocalStorage.m48
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVLocation.m14
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVPlugin.h4
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVPlugin.m51
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVPluginResult.h2
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVPluginResult.m40
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVSound.h8
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVSound.m156
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVSplashScreen.h6
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVSplashScreen.m151
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVURLProtocol.h3
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVURLProtocol.m83
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVUserAgentUtil.h (renamed from iPhone/CordovaLib/Classes/CDVCordovaView.m)22
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVUserAgentUtil.m120
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVViewController.h18
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVViewController.m341
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVWebViewDelegate.h37
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVWebViewDelegate.m157
-rwxr-xr-xiPhone/CordovaLib/Classes/JSON/JSONKit.h251
-rwxr-xr-xiPhone/CordovaLib/Classes/JSON/JSONKit.m3061
43 files changed, 1673 insertions, 4028 deletions
diff --git a/iPhone/CordovaLib/Classes/CDV.h b/iPhone/CordovaLib/Classes/CDV.h
index 5367799..5a0ae6a 100755
--- a/iPhone/CordovaLib/Classes/CDV.h
+++ b/iPhone/CordovaLib/Classes/CDV.h
@@ -32,7 +32,6 @@
#import "CDVConnection.h"
#import "CDVContact.h"
#import "CDVContacts.h"
-#import "CDVCordovaView.h"
#import "CDVDebug.h"
#import "CDVDebugConsole.h"
#import "CDVDevice.h"
@@ -55,4 +54,4 @@
#import "NSMutableArray+QueueAdditions.h"
#import "UIDevice+Extensions.h"
-#import "JSONKit.h"
+#import "CDVJSON.h"
diff --git a/iPhone/CordovaLib/Classes/CDVAvailability.h b/iPhone/CordovaLib/Classes/CDVAvailability.h
index d2e7f1b..33c6799 100755
--- a/iPhone/CordovaLib/Classes/CDVAvailability.h
+++ b/iPhone/CordovaLib/Classes/CDVAvailability.h
@@ -35,6 +35,8 @@
#define __CORDOVA_2_1_0 20100
#define __CORDOVA_2_2_0 20200
#define __CORDOVA_2_3_0 20300
+#define __CORDOVA_2_4_0 20400
+#define __CORDOVA_2_5_0 20500
#define __CORDOVA_NA 99999 /* not available */
/*
@@ -45,7 +47,7 @@
#endif
*/
#ifndef CORDOVA_VERSION_MIN_REQUIRED
- #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_2_3_0
+ #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_2_5_0
#endif
/*
diff --git a/iPhone/CordovaLib/Classes/CDVCamera.h b/iPhone/CordovaLib/Classes/CDVCamera.h
index 2905a8a..204d25f 100755
--- a/iPhone/CordovaLib/Classes/CDVCamera.h
+++ b/iPhone/CordovaLib/Classes/CDVCamera.h
@@ -22,7 +22,8 @@
enum CDVDestinationType {
DestinationTypeDataUrl = 0,
- DestinationTypeFileUri
+ DestinationTypeFileUri,
+ DestinationTypeNativeUri
};
typedef NSUInteger CDVDestinationType;
@@ -79,6 +80,7 @@ typedef NSUInteger CDVMediaType;
- (void)takePicture:(CDVInvokedUrlCommand*)command;
- (void)postImage:(UIImage*)anImage withFilename:(NSString*)filename toUrl:(NSURL*)url;
- (void)cleanup:(CDVInvokedUrlCommand*)command;
+- (void)repositionPopover:(CDVInvokedUrlCommand*)command;
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info;
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo;
diff --git a/iPhone/CordovaLib/Classes/CDVCamera.m b/iPhone/CordovaLib/Classes/CDVCamera.m
index cfbf415..aabe844 100755
--- a/iPhone/CordovaLib/Classes/CDVCamera.m
+++ b/iPhone/CordovaLib/Classes/CDVCamera.m
@@ -93,6 +93,13 @@ static NSSet* org_apache_cordova_validArrowDirections;
targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]);
}
+ // If a popover is already open, close it; we only want one at a time.
+ if (([[self pickerController] popoverController] != nil) && [[[self pickerController] popoverController] isPopoverVisible]) {
+ [[[self pickerController] popoverController] dismissPopoverAnimated:YES];
+ [[[self pickerController] popoverController] setDelegate:nil];
+ [[self pickerController] setPopoverController:nil];
+ }
+
CDVCameraPicker* cameraPicker = [[CDVCameraPicker alloc] init];
self.pickerController = cameraPicker;
@@ -128,28 +135,8 @@ static NSSet* org_apache_cordova_validArrowDirections;
if (cameraPicker.popoverController == nil) {
cameraPicker.popoverController = [[NSClassFromString (@"UIPopoverController")alloc] initWithContentViewController:cameraPicker];
}
- int x = 0;
- int y = 32;
- int width = 320;
- int height = 480;
- UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny;
NSDictionary* options = [command.arguments objectAtIndex:10 withDefault:nil];
- if (options) {
- x = [options integerValueForKey:@"x" defaultValue:0];
- y = [options integerValueForKey:@"y" defaultValue:32];
- width = [options integerValueForKey:@"width" defaultValue:320];
- height = [options integerValueForKey:@"height" defaultValue:480];
- arrowDirection = [options integerValueForKey:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny];
- if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithInt:arrowDirection]]) {
- arrowDirection = UIPopoverArrowDirectionAny;
- }
- }
-
- cameraPicker.popoverController.delegate = self;
- [cameraPicker.popoverController presentPopoverFromRect:CGRectMake(x, y, width, height)
- inView:[self.webView superview]
- permittedArrowDirections:arrowDirection
- animated:YES];
+ [self displayPopover:options];
} else {
if ([self.viewController respondsToSelector:@selector(presentViewController:::)]) {
[self.viewController presentViewController:cameraPicker animated:YES completion:nil];
@@ -160,6 +147,39 @@ static NSSet* org_apache_cordova_validArrowDirections;
self.hasPendingOperation = YES;
}
+- (void)repositionPopover:(CDVInvokedUrlCommand*)command
+{
+ NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:nil];
+
+ [self displayPopover:options];
+}
+
+- (void)displayPopover:(NSDictionary*)options
+{
+ int x = 0;
+ int y = 32;
+ int width = 320;
+ int height = 480;
+ UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny;
+
+ if (options) {
+ x = [options integerValueForKey:@"x" defaultValue:0];
+ y = [options integerValueForKey:@"y" defaultValue:32];
+ width = [options integerValueForKey:@"width" defaultValue:320];
+ height = [options integerValueForKey:@"height" defaultValue:480];
+ arrowDirection = [options integerValueForKey:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny];
+ if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithInt:arrowDirection]]) {
+ arrowDirection = UIPopoverArrowDirectionAny;
+ }
+ }
+
+ [[[self pickerController] popoverController] setDelegate:self];
+ [[[self pickerController] popoverController] presentPopoverFromRect:CGRectMake(x, y, width, height)
+ inView:[self.webView superview]
+ permittedArrowDirections:arrowDirection
+ animated:YES];
+}
+
- (void)cleanup:(CDVInvokedUrlCommand*)command
{
// empty the tmp directory
@@ -232,63 +252,68 @@ static NSSet* org_apache_cordova_validArrowDirections;
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
// IMAGE TYPE
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
- // get the image
- UIImage* image = nil;
- if (cameraPicker.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) {
- image = [info objectForKey:UIImagePickerControllerEditedImage];
+ if (cameraPicker.returnType == DestinationTypeNativeUri) {
+ NSString* nativeUri = [(NSURL*)[info objectForKey:UIImagePickerControllerReferenceURL] absoluteString];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
} else {
- image = [info objectForKey:UIImagePickerControllerOriginalImage];
- }
+ // get the image
+ UIImage* image = nil;
+ if (cameraPicker.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) {
+ image = [info objectForKey:UIImagePickerControllerEditedImage];
+ } else {
+ image = [info objectForKey:UIImagePickerControllerOriginalImage];
+ }
- if (cameraPicker.saveToPhotoAlbum) {
- UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
- }
+ if (cameraPicker.saveToPhotoAlbum) {
+ UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
+ }
- if (cameraPicker.correctOrientation) {
- image = [self imageCorrectedForCaptureOrientation:image];
- }
+ if (cameraPicker.correctOrientation) {
+ image = [self imageCorrectedForCaptureOrientation:image];
+ }
- UIImage* scaledImage = nil;
+ UIImage* scaledImage = nil;
- if ((cameraPicker.targetSize.width > 0) && (cameraPicker.targetSize.height > 0)) {
- // if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping
- if (cameraPicker.cropToSize) {
- scaledImage = [self imageByScalingAndCroppingForSize:image toSize:cameraPicker.targetSize];
- } else {
- scaledImage = [self imageByScalingNotCroppingForSize:image toSize:cameraPicker.targetSize];
+ if ((cameraPicker.targetSize.width > 0) && (cameraPicker.targetSize.height > 0)) {
+ // if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping
+ if (cameraPicker.cropToSize) {
+ scaledImage = [self imageByScalingAndCroppingForSize:image toSize:cameraPicker.targetSize];
+ } else {
+ scaledImage = [self imageByScalingNotCroppingForSize:image toSize:cameraPicker.targetSize];
+ }
}
- }
- NSData* data = nil;
+ NSData* data = nil;
- if (cameraPicker.encodingType == EncodingTypePNG) {
- data = UIImagePNGRepresentation(scaledImage == nil ? image : scaledImage);
- } else {
- data = UIImageJPEGRepresentation(scaledImage == nil ? image : scaledImage, cameraPicker.quality / 100.0f);
- }
+ if (cameraPicker.encodingType == EncodingTypePNG) {
+ data = UIImagePNGRepresentation(scaledImage == nil ? image : scaledImage);
+ } else {
+ data = UIImageJPEGRepresentation(scaledImage == nil ? image : scaledImage, cameraPicker.quality / 100.0f);
+ }
- if (cameraPicker.returnType == DestinationTypeFileUri) {
- // write to temp directory and return URI
- // get the temp directory path
- NSString* docsPath = [NSTemporaryDirectory ()stringByStandardizingPath];
- NSError* err = nil;
- NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by apple (vs [NSFileManager defaultManager]) to be threadsafe
- // generate unique file name
- NSString* filePath;
-
- int i = 1;
- do {
- filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, cameraPicker.encodingType == EncodingTypePNG ? @"png":@"jpg"];
- } while ([fileMgr fileExistsAtPath:filePath]);
-
- // save file
- if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
+ if (cameraPicker.returnType == DestinationTypeFileUri) {
+ // write to temp directory and return URI
+ // get the temp directory path
+ NSString* docsPath = [NSTemporaryDirectory ()stringByStandardizingPath];
+ NSError* err = nil;
+ NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by apple (vs [NSFileManager defaultManager]) to be threadsafe
+ // generate unique file name
+ NSString* filePath;
+
+ int i = 1;
+ do {
+ filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, cameraPicker.encodingType == EncodingTypePNG ? @"png":@"jpg"];
+ } while ([fileMgr fileExistsAtPath:filePath]);
+
+ // save file
+ if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
+ } else {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]];
+ }
} else {
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[data base64EncodedString]];
}
- } else {
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[data base64EncodedString]];
}
}
// NOT IMAGE TYPE (MOVIE)
diff --git a/iPhone/CordovaLib/Classes/CDVCapture.m b/iPhone/CordovaLib/Classes/CDVCapture.m
index 6d52042..ed9f664 100755
--- a/iPhone/CordovaLib/Classes/CDVCapture.m
+++ b/iPhone/CordovaLib/Classes/CDVCapture.m
@@ -18,9 +18,8 @@
*/
#import "CDVCapture.h"
-#import "JSONKit.h"
+#import "CDVJSON.h"
#import "CDVAvailability.h"
-#import "CDVViewController.h"
#define kW3CMediaFormatHeight @"height"
#define kW3CMediaFormatWidth @"width"
@@ -329,7 +328,7 @@
movieArray ? (NSObject*) movieArray:[NSNull null], @"video",
audioArray ? (NSObject*) audioArray:[NSNull null], @"audio",
nil];
- NSString* jsString = [NSString stringWithFormat:@"navigator.device.capture.setSupportedModes(%@);", [modes cdvjk_JSONString]];
+ NSString* jsString = [NSString stringWithFormat:@"navigator.device.capture.setSupportedModes(%@);", [modes JSONString]];
[self.commandDelegate evalJs:jsString];
}
diff --git a/iPhone/CordovaLib/Classes/CDVCommandDelegate.h b/iPhone/CordovaLib/Classes/CDVCommandDelegate.h
index 4a53f98..e177c63 100755
--- a/iPhone/CordovaLib/Classes/CDVCommandDelegate.h
+++ b/iPhone/CordovaLib/Classes/CDVCommandDelegate.h
@@ -22,9 +22,12 @@
@class CDVPlugin;
@class CDVPluginResult;
+@class CDVWhitelist;
@protocol CDVCommandDelegate <NSObject>
+@property (nonatomic, readonly) NSDictionary* settings;
+
- (NSString*)pathForResource:(NSString*)resourcepath;
- (id)getCommandInstance:(NSString*)pluginName;
- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className CDV_DEPRECATED(2.2, "Use CDVViewController to register plugins, or use config.xml.");
@@ -44,5 +47,9 @@
- (void)evalJs:(NSString*)js scheduledOnRunLoop:(BOOL)scheduledOnRunLoop;
// Runs the given block on a background thread using a shared thread-pool.
- (void)runInBackground:(void (^)())block;
+// Returns the User-Agent of the associated UIWebView.
+- (NSString*)userAgent;
+// Returns whether the given URL passes the white-list.
+- (BOOL)URLIsWhitelisted:(NSURL*)url;
@end
diff --git a/iPhone/CordovaLib/Classes/CDVCommandDelegateImpl.m b/iPhone/CordovaLib/Classes/CDVCommandDelegateImpl.m
index 76413bd..e399289 100755
--- a/iPhone/CordovaLib/Classes/CDVCommandDelegateImpl.m
+++ b/iPhone/CordovaLib/Classes/CDVCommandDelegateImpl.m
@@ -18,7 +18,7 @@
*/
#import "CDVCommandDelegateImpl.h"
-
+#import "CDVJSON.h"
#import "CDVCommandQueue.h"
#import "CDVPluginResult.h"
#import "CDVViewController.h"
@@ -78,13 +78,17 @@
- (void)sendPluginResult:(CDVPluginResult*)result callbackId:(NSString*)callbackId
{
+ // This occurs when there is are no win/fail callbacks for the call.
+ if ([@"INVALID" isEqualToString:callbackId]) {
+ return;
+ }
int status = [result.status intValue];
BOOL keepCallback = [result.keepCallback boolValue];
id message = result.message == nil ? [NSNull null] : result.message;
// Use an array to encode the message as JSON.
message = [NSArray arrayWithObject:message];
- NSString* encodedMessage = [message cdvjk_JSONString];
+ NSString* encodedMessage = [message JSONString];
// And then strip off the outer []s.
encodedMessage = [encodedMessage substringWithRange:NSMakeRange(1, [encodedMessage length] - 2)];
NSString* js = [NSString stringWithFormat:@"cordova.require('cordova/exec').nativeCallback('%@',%d,%@,%d)",
@@ -128,4 +132,20 @@
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}
+- (NSString*)userAgent
+{
+ return [_viewController userAgent];
+}
+
+- (BOOL)URLIsWhitelisted:(NSURL*)url
+{
+ return ![_viewController.whitelist schemeIsAllowed:[url scheme]] ||
+ [_viewController.whitelist URLIsAllowed:url];
+}
+
+- (NSDictionary*)settings
+{
+ return _viewController.settings;
+}
+
@end
diff --git a/iPhone/CordovaLib/Classes/CDVCommandQueue.m b/iPhone/CordovaLib/Classes/CDVCommandQueue.m
index d88a730..a8a58b7 100755
--- a/iPhone/CordovaLib/Classes/CDVCommandQueue.m
+++ b/iPhone/CordovaLib/Classes/CDVCommandQueue.m
@@ -87,14 +87,14 @@
for (NSUInteger i = 0; i < [_queue count]; ++i) {
// Parse the returned JSON array.
- NSArray* commandBatch = [[_queue objectAtIndex:i] cdvjk_mutableObjectFromJSONString];
+ NSArray* commandBatch = [[_queue objectAtIndex:i] JSONObject];
// Iterate over and execute all of the commands.
for (NSArray* jsonEntry in commandBatch) {
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
if (![self execute:command]) {
#ifdef DEBUG
- NSString* commandJson = [jsonEntry cdvjk_JSONString];
+ NSString* commandJson = [jsonEntry JSONString];
static NSUInteger maxLogLength = 1024;
NSString* commandString = ([commandJson length] > maxLogLength) ?
[NSString stringWithFormat:@"%@[...]", [commandJson substringToIndex:maxLogLength]] :
diff --git a/iPhone/CordovaLib/Classes/CDVConfigParser.h b/iPhone/CordovaLib/Classes/CDVConfigParser.h
index 23cdd01..7392580 100755
--- a/iPhone/CordovaLib/Classes/CDVConfigParser.h
+++ b/iPhone/CordovaLib/Classes/CDVConfigParser.h
@@ -17,11 +17,12 @@
under the License.
*/
-@interface CDVConfigParser : NSObject <NSXMLParserDelegate> {
-}
+@interface CDVConfigParser : NSObject <NSXMLParserDelegate>{}
@property (nonatomic, readonly, strong) NSMutableDictionary* pluginsDict;
@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
@property (nonatomic, readonly, strong) NSMutableArray* whitelistHosts;
+@property (nonatomic, readonly, strong) NSMutableArray* startupPluginNames;
+@property (nonatomic, readonly, strong) NSString* startPage;
@end
diff --git a/iPhone/CordovaLib/Classes/CDVConfigParser.m b/iPhone/CordovaLib/Classes/CDVConfigParser.m
index b596c9d..6fd5913 100755
--- a/iPhone/CordovaLib/Classes/CDVConfigParser.m
+++ b/iPhone/CordovaLib/Classes/CDVConfigParser.m
@@ -24,20 +24,23 @@
@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginsDict;
@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
@property (nonatomic, readwrite, strong) NSMutableArray* whitelistHosts;
+@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames;
+@property (nonatomic, readwrite, strong) NSString* startPage;
@end
@implementation CDVConfigParser
-@synthesize pluginsDict, settings, whitelistHosts;
+@synthesize pluginsDict, settings, whitelistHosts, startPage, startupPluginNames;
- (id)init
{
self = [super init];
if (self != nil) {
- self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:4];
- self.settings = [[NSMutableDictionary alloc] initWithCapacity:4];
- self.whitelistHosts = [[NSMutableArray alloc] initWithCapacity:1];
+ self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:30];
+ self.settings = [[NSMutableDictionary alloc] initWithCapacity:30];
+ self.whitelistHosts = [[NSMutableArray alloc] initWithCapacity:30];
+ self.startupPluginNames = [[NSMutableArray alloc] initWithCapacity:8];
}
return self;
}
@@ -45,11 +48,17 @@
- (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict
{
if ([elementName isEqualToString:@"preference"]) {
- [settings setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]];
+ settings[attributeDict[@"name"]] = attributeDict[@"value"];
} else if ([elementName isEqualToString:@"plugin"]) {
- [pluginsDict setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]];
+ NSString* name = [attributeDict[@"name"] lowercaseString];
+ pluginsDict[name] = attributeDict[@"value"];
+ if ([@"true" isEqualToString:attributeDict[@"onload"]]) {
+ [self.startupPluginNames addObject:name];
+ }
} else if ([elementName isEqualToString:@"access"]) {
- [whitelistHosts addObject:[attributeDict objectForKey:@"origin"]];
+ [whitelistHosts addObject:attributeDict[@"origin"]];
+ } else if ([elementName isEqualToString:@"content"]) {
+ self.startPage = attributeDict[@"src"];
}
}
diff --git a/iPhone/CordovaLib/Classes/CDVContact.m b/iPhone/CordovaLib/Classes/CDVContact.m
index 9389207..9efaf10 100755
--- a/iPhone/CordovaLib/Classes/CDVContact.m
+++ b/iPhone/CordovaLib/Classes/CDVContact.m
@@ -1146,7 +1146,7 @@ static NSDictionary* org_apache_cordova_contacts_defaultFields = nil;
if (fields == nil) { // no name fields requested
return nil;
}
- id __weak value;
+ CFStringRef value;
NSObject* addresses;
ABMultiValueRef multi = ABRecordCopyValue(self.record, kABPersonAddressProperty);
CFIndex count = multi ? ABMultiValueGetCount(multi) : 0;
@@ -1174,7 +1174,13 @@ static NSDictionary* org_apache_cordova_contacts_defaultFields = nil;
id key = [[CDVContact defaultW3CtoAB] valueForKey:k];
if (key && ![k isKindOfClass:[NSNull class]]) {
bFound = CFDictionaryGetValueIfPresent(dict, (__bridge const void*)key, (void*)&value);
- [newAddress setObject:(bFound && value != NULL) ? (id) value:[NSNull null] forKey:k];
+ if (bFound && (value != NULL)) {
+ CFRetain(value);
+ [newAddress setObject:(__bridge id)value forKey:k];
+ CFRelease(value);
+ } else {
+ [newAddress setObject:[NSNull null] forKey:k];
+ }
} else {
// was a property that iPhone doesn't support
[newAddress setObject:[NSNull null] forKey:k];
@@ -1221,16 +1227,28 @@ static NSDictionary* org_apache_cordova_contacts_defaultFields = nil;
NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithCapacity:3];
// iOS has label property (work, home, other) for each IM but W3C contact API doesn't use
CFDictionaryRef dict = (CFDictionaryRef)ABMultiValueCopyValueAtIndex(multi, i);
- NSString* __weak value; // all values should be CFStringRefs / NSString*
+ CFStringRef value; // all values should be CFStringRefs / NSString*
bool bFound;
if ([fields containsObject:kW3ContactFieldValue]) {
// value = user name
bFound = CFDictionaryGetValueIfPresent(dict, kABPersonInstantMessageUsernameKey, (void*)&value);
- [newDict setObject:(bFound && value != NULL) ? (id) value:[NSNull null] forKey:kW3ContactFieldValue];
+ if (bFound && (value != NULL)) {
+ CFRetain(value);
+ [newDict setObject:(__bridge id)value forKey:kW3ContactFieldValue];
+ CFRelease(value);
+ } else {
+ [newDict setObject:[NSNull null] forKey:kW3ContactFieldValue];
+ }
}
if ([fields containsObject:kW3ContactFieldType]) {
bFound = CFDictionaryGetValueIfPresent(dict, kABPersonInstantMessageServiceKey, (void*)&value);
- [newDict setObject:(bFound && value != NULL) ? (id)[[CDVContact class] convertPropertyLabelToContactType:value]:[NSNull null] forKey:kW3ContactFieldType];
+ if (bFound && (value != NULL)) {
+ CFRetain(value);
+ [newDict setObject:(id)[[CDVContact class] convertPropertyLabelToContactType:(__bridge NSString*)value] forKey:kW3ContactFieldType];
+ CFRelease(value);
+ } else {
+ [newDict setObject:[NSNull null] forKey:kW3ContactFieldType];
+ }
}
// always set ID
id identifier = [NSNumber numberWithUnsignedInt:ABMultiValueGetIdentifierAtIndex(multi, i)];
@@ -1311,13 +1329,14 @@ static NSDictionary* org_apache_cordova_contacts_defaultFields = nil;
// get the temp directory path
NSString* docsPath = [NSTemporaryDirectory ()stringByStandardizingPath];
NSError* err = nil;
- NSFileManager* fileMgr = [[NSFileManager alloc] init];
- // generate unique file name
- NSString* filePath;
- int i = 1;
- do {
- filePath = [NSString stringWithFormat:@"%@/photo_%03d.jpg", docsPath, i++];
- } while ([fileMgr fileExistsAtPath:filePath]);
+ NSString* filePath = [NSString stringWithFormat:@"%@/photo_XXXXX", docsPath];
+ char template[filePath.length + 1];
+ strcpy(template, [filePath cStringUsingEncoding:NSASCIIStringEncoding]);
+ mkstemp(template);
+ filePath = [[NSFileManager defaultManager]
+ stringWithFileSystemRepresentation:template
+ length:strlen(template)];
+
// save file
if ([data writeToFile:filePath options:NSAtomicWrite error:&err]) {
photos = [NSMutableArray arrayWithCapacity:1];
@@ -1694,7 +1713,7 @@ static NSDictionary* org_apache_cordova_contacts_defaultFields = nil;
for (NSString* member in fields) {
NSString* abKey = [[CDVContact defaultW3CtoAB] valueForKey:member]; // im and address fields are all strings
- NSString* __weak abValue = nil;
+ CFStringRef abValue = nil;
if (abKey) {
NSString* testString = nil;
if ([member isEqualToString:kW3ContactImType]) {
@@ -1708,8 +1727,10 @@ static NSDictionary* org_apache_cordova_contacts_defaultFields = nil;
if (testString != nil) {
BOOL bExists = CFDictionaryGetValueIfPresent(dict, (__bridge const void*)abKey, (void*)&abValue);
if (bExists) {
+ CFRetain(abValue);
NSPredicate* containPred = [NSPredicate predicateWithFormat:@"SELF contains[cd] %@", testString];
- bFound = [containPred evaluateWithObject:abValue];
+ bFound = [containPred evaluateWithObject:(__bridge id)abValue];
+ CFRelease(abValue);
}
}
}
diff --git a/iPhone/CordovaLib/Classes/CDVDevice.m b/iPhone/CordovaLib/Classes/CDVDevice.m
index 3fa4bb3..cc7ad89 100755
--- a/iPhone/CordovaLib/Classes/CDVDevice.m
+++ b/iPhone/CordovaLib/Classes/CDVDevice.m
@@ -58,7 +58,8 @@
NSDictionary* temp = [CDVViewController getBundlePlist:@"Settings"];
if ([temp respondsToSelector:@selector(JSONString)]) {
- NSString* js = [NSString stringWithFormat:@"window.Settings = %@;", [temp cdvjk_JSONString]];
+ NSLog(@"Deprecation warning: window.Setting will be removed Aug 2013. Refer to https://issues.apache.org/jira/browse/CB-2433");
+ NSString* js = [NSString stringWithFormat:@"window.Settings = %@;", [temp JSONString]];
[self.commandDelegate evalJs:js];
}
diff --git a/iPhone/CordovaLib/Classes/CDVEcho.m b/iPhone/CordovaLib/Classes/CDVEcho.m
index 9cda957..916e315 100755
--- a/iPhone/CordovaLib/Classes/CDVEcho.m
+++ b/iPhone/CordovaLib/Classes/CDVEcho.m
@@ -24,7 +24,8 @@
- (void)echo:(CDVInvokedUrlCommand*)command
{
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[command.arguments objectAtIndex:0]];
+ id message = [command.arguments objectAtIndex:0];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
@@ -36,9 +37,18 @@
- (void)echoAsync:(CDVInvokedUrlCommand*)command
{
- CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[command.arguments objectAtIndex:0]];
+ id message = [command.arguments objectAtIndex:0];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
[self performSelector:@selector(echoAsyncHelper:) withObject:[NSArray arrayWithObjects:pluginResult, command.callbackId, nil] afterDelay:0];
}
+- (void)echoArrayBuffer:(CDVInvokedUrlCommand*)command
+{
+ id message = [command.arguments objectAtIndex:0];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArrayBuffer:message];
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
@end
diff --git a/iPhone/CordovaLib/Classes/CDVFile.h b/iPhone/CordovaLib/Classes/CDVFile.h
index 204c9f0..4862921 100755
--- a/iPhone/CordovaLib/Classes/CDVFile.h
+++ b/iPhone/CordovaLib/Classes/CDVFile.h
@@ -42,6 +42,8 @@ enum CDVFileSystemType {
};
typedef int CDVFileSystemType;
+extern NSString* const kCDVAssetsLibraryPrefix;
+
@interface CDVFile : CDVPlugin {
NSString* appDocsPath;
NSString* appLibraryPath;
diff --git a/iPhone/CordovaLib/Classes/CDVFile.m b/iPhone/CordovaLib/Classes/CDVFile.m
index 1e5d009..d52405d 100755
--- a/iPhone/CordovaLib/Classes/CDVFile.m
+++ b/iPhone/CordovaLib/Classes/CDVFile.m
@@ -20,8 +20,11 @@
#import "CDVFile.h"
#import "NSArray+Comparisons.h"
#import "NSDictionary+Extensions.h"
-#import "JSONKit.h"
+#import "CDVJSON.h"
#import "NSData+Base64.h"
+#import <AssetsLibrary/ALAsset.h>
+#import <AssetsLibrary/ALAssetRepresentation.h>
+#import <AssetsLibrary/ALAssetsLibrary.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "CDVAvailability.h"
#import "sys/xattr.h"
@@ -32,6 +35,8 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
#endif
+NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
+
@implementation CDVFile
@synthesize appDocsPath, appLibraryPath, appTempPath, persistentPath, temporaryPath, userHasAllowed;
@@ -329,6 +334,13 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
NSString* requestedPath = [command.arguments objectAtIndex:1];
NSDictionary* options = [command.arguments objectAtIndex:2 withDefault:nil];
+ // return unsupported result for assets-library URLs
+ if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
CDVPluginResult* result = nil;
BOOL bDirRequest = NO;
BOOL create = NO;
@@ -425,6 +437,13 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
// arguments are URL encoded
NSString* fullPath = [command.arguments objectAtIndex:0];
+ // we don't (yet?) support getting the parent of an asset
+ if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
CDVPluginResult* result = nil;
NSString* newPath = nil;
@@ -461,11 +480,40 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
{
// arguments
NSString* argPath = [command.arguments objectAtIndex:0];
+ __block CDVPluginResult* result = nil;
+
+ if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ // In this case, we need to use an asynchronous method to retrieve the file.
+ // Because of this, we can't just assign to `result` and send it at the end of the method.
+ // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) {
+ if (asset) {
+ // We have the asset! Retrieve the metadata and send it off.
+ NSDate* date = [asset valueForProperty:ALAssetPropertyDate];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[date timeIntervalSince1970] * 1000];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ } else {
+ // We couldn't find the asset. Send the appropriate error.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }
+ };
+ // TODO(maxw): Consider making this a class variable since it's the same every time.
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) {
+ // Retrieving the asset failed for some reason. Send the appropriate error.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:[NSURL URLWithString:argPath] resultBlock:resultBlock failureBlock:failureBlock];
+ return;
+ }
+
NSString* testPath = argPath; // [self getFullPath: argPath];
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSError* __autoreleasing error = nil;
- CDVPluginResult* result = nil;
NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:testPath error:&error];
@@ -477,7 +525,7 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
} else {
// didn't get fileAttribs
CDVFileError errorCode = ABORT_ERR;
- NSLog(@"error getting metadata: %@", [error localizedDescription]);
+ NSLog (@"error getting metadata: %@", [error localizedDescription]);
if ([error code] == NSFileNoSuchFileError) {
errorCode = NOT_FOUND_ERR;
}
@@ -500,27 +548,29 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
// arguments
NSString* filePath = [command.arguments objectAtIndex:0];
NSDictionary* options = [command.arguments objectAtIndex:1 withDefault:nil];
-
CDVPluginResult* result = nil;
BOOL ok = NO;
- // we only care about this iCloud key for now.
- // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
- NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
- id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
-
- if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
- if (IsAtLeastiOSVersion(@"5.1")) {
- NSURL* url = [NSURL fileURLWithPath:filePath];
- NSError* __autoreleasing error = nil;
-
- ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
- } else { // below 5.1 (deprecated - only really supported in 5.01)
- u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
- if (value == 0) { // remove the attribute (allow backup, the default)
- ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
- } else { // set the attribute (skip backup)
- ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
+ // setMetadata doesn't make sense for asset library files
+ if (![filePath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ // we only care about this iCloud key for now.
+ // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
+ NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
+ id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
+
+ if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
+ if (IsAtLeastiOSVersion(@"5.1")) {
+ NSURL* url = [NSURL fileURLWithPath:filePath];
+ NSError* __autoreleasing error = nil;
+
+ ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
+ } else { // below 5.1 (deprecated - only really supported in 5.01)
+ u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
+ if (value == 0) { // remove the attribute (allow backup, the default)
+ ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
+ } else { // set the attribute (skip backup)
+ ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
+ }
}
}
}
@@ -539,19 +589,21 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
* 0 - NSString* fullPath
*
* returns NO_MODIFICATION_ALLOWED_ERR if is top level directory or no permission to delete dir
- * returns INVALID_MODIFICATION_ERR if is dir and is not empty
+ * returns INVALID_MODIFICATION_ERR if is non-empty dir or asset library file
* returns NOT_FOUND_ERR if file or dir is not found
*/
- (void)remove:(CDVInvokedUrlCommand*)command
{
// arguments
NSString* fullPath = [command.arguments objectAtIndex:0];
-
CDVPluginResult* result = nil;
CDVFileError errorCode = 0; // !! 0 not currently defined
- // error if try to remove top level (documents or tmp) dir
- if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) {
+ // return error for assets-library URLs
+ if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ errorCode = INVALID_MODIFICATION_ERR;
+ } else if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) {
+ // error if try to remove top level (documents or tmp) dir
errorCode = NO_MODIFICATION_ALLOWED_ERR;
} else {
NSFileManager* fileMgr = [[NSFileManager alloc] init];
@@ -587,6 +639,13 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
// arguments
NSString* fullPath = [command.arguments objectAtIndex:0];
+ // return unsupported result for assets-library URLs
+ if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
CDVPluginResult* result = nil;
// error if try to remove top level (documents or tmp) dir
@@ -693,7 +752,7 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
// optional argument
NSString* newName = ([arguments count] > 2) ? [arguments objectAtIndex:2] : [srcFullPath lastPathComponent]; // use last component from appPath if new name not provided
- CDVPluginResult* result = nil;
+ __block CDVPluginResult* result = nil;
CDVFileError errCode = 0; // !! Currently 0 is not defined, use this to signal error !!
/*NSString* destRootPath = nil;
@@ -710,12 +769,59 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
errCode = ENCODING_ERR;
} else {
NSString* newFullPath = [destRootPath stringByAppendingPathComponent:newName];
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
if ([newFullPath isEqualToString:srcFullPath]) {
// source and destination can not be the same
errCode = INVALID_MODIFICATION_ERR;
- } else {
- NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ } else if ([srcFullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ if (bCopy) {
+ // Copying (as opposed to moving) an assets library file is okay.
+ // In this case, we need to use an asynchronous method to retrieve the file.
+ // Because of this, we can't just assign to `result` and send it at the end of the method.
+ // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) {
+ if (asset) {
+ // We have the asset! Get the data and try to copy it over.
+ if (![fileMgr fileExistsAtPath:destRootPath]) {
+ // The destination directory doesn't exist.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ } else if ([fileMgr fileExistsAtPath:newFullPath]) {
+ // A file already exists at the destination path.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:PATH_EXISTS_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+ // We're good to go! Write the file to the new destination.
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ Byte* buffer = (Byte*)malloc ([assetRepresentation size]);
+ NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
+ NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+ [data writeToFile:newFullPath atomically:YES];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getDirectoryEntry:newFullPath isDirectory:NO]];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ } else {
+ // We couldn't find the asset. Send the appropriate error.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }
+ };
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) {
+ // Retrieving the asset failed for some reason. Send the appropriate error.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:[NSURL URLWithString:srcFullPath] resultBlock:resultBlock failureBlock:failureBlock];
+ return;
+ } else {
+ // Moving an assets library file is not doable, since we can't remove it.
+ errCode = INVALID_MODIFICATION_ERR;
+ }
+ } else {
BOOL bSrcIsDir = NO;
BOOL bDestIsDir = NO;
BOOL bNewIsDir = NO;
@@ -838,30 +944,67 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
// arguments
NSString* argPath = [command.arguments objectAtIndex:0];
- CDVPluginResult* result = nil;
+ __block CDVPluginResult* result = nil;
NSString* fullPath = argPath; // [self getFullPath: argPath];
if (fullPath) {
- NSFileManager* fileMgr = [[NSFileManager alloc] init];
- BOOL bIsDir = NO;
- // make sure it exists and is not a directory
- BOOL bExists = [fileMgr fileExistsAtPath:fullPath isDirectory:&bIsDir];
- if (!bExists || bIsDir) {
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ // In this case, we need to use an asynchronous method to retrieve the file.
+ // Because of this, we can't just assign to `result` and send it at the end of the method.
+ // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) {
+ if (asset) {
+ // We have the asset! Populate the dictionary and send it off.
+ NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
+ [fileInfo setObject:argPath forKey:@"fullPath"];
+ NSString* filename = [assetRepresentation filename];
+ [fileInfo setObject:filename forKey:@"name"];
+ [fileInfo setObject:[self getMimeTypeFromPath:filename] forKey:@"type"];
+ NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
+ NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
+ [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ } else {
+ // We couldn't find the asset. Send the appropriate error.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }
+ };
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) {
+ // Retrieving the asset failed for some reason. Send the appropriate error.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:[NSURL URLWithString:argPath] resultBlock:resultBlock failureBlock:failureBlock];
+ return;
} else {
- // create dictionary of file info
- NSError* __autoreleasing error = nil;
- NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:fullPath error:&error];
- NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
- [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"];
- [fileInfo setObject:argPath forKey:@"fullPath"];
- [fileInfo setObject:@"" forKey:@"type"]; // can't easily get the mimetype unless create URL, send request and read response so skipping
- [fileInfo setObject:[argPath lastPathComponent] forKey:@"name"];
- NSDate* modDate = [fileAttrs fileModificationDate];
- NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000];
- [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
+ NSFileManager* fileMgr = [[NSFileManager alloc] init];
+ BOOL bIsDir = NO;
+ // make sure it exists and is not a directory
+ BOOL bExists = [fileMgr fileExistsAtPath:fullPath isDirectory:&bIsDir];
+ if (!bExists || bIsDir) {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ } else {
+ // create dictionary of file info
+ NSError* __autoreleasing error = nil;
+ NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:fullPath error:&error];
+ NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+ [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"];
+ [fileInfo setObject:argPath forKey:@"fullPath"];
+ [fileInfo setObject:@"" forKey:@"type"]; // can't easily get the mimetype unless create URL, send request and read response so skipping
+ [fileInfo setObject:[argPath lastPathComponent] forKey:@"name"];
+ NSDate* modDate = [fileAttrs fileModificationDate];
+ NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000];
+ [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
+ }
}
}
if (!result) {
@@ -875,6 +1018,13 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
// arguments
NSString* fullPath = [command.arguments objectAtIndex:0];
+ // return unsupported result for assets-library URLs
+ if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
CDVPluginResult* result = nil;
NSFileManager* fileMgr = [[NSFileManager alloc] init];
@@ -908,21 +1058,45 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
* NSArray* arguments
* 0 - NSString* fullPath
* 1 - NSString* encoding - NOT USED, iOS reads and writes using UTF8!
+ * 2 - NSString* start - OPTIONAL, only provided when not == 0.
+ * 3 - NSString* end - OPTIONAL, only provided when not == length.
*/
- (void)readAsText:(CDVInvokedUrlCommand*)command
{
// arguments
NSString* argPath = [command.arguments objectAtIndex:0];
+ NSInteger start = 0;
+ NSInteger end = -1;
+
+ if ([command.arguments count] >= 3) {
+ start = [[command.arguments objectAtIndex:2] integerValue];
+ }
+ if ([command.arguments count] >= 4) {
+ end = [[command.arguments objectAtIndex:3] integerValue];
+ }
+
// NSString* encoding = [command.arguments objectAtIndex:2]; // not currently used
CDVPluginResult* result = nil;
NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:argPath];
- if (!file) {
+ if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ // can't read assets-library URLs as text
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
+ } else if (!file) {
// invalid path entry
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
} else {
- NSData* readData = [file readDataToEndOfFile];
+ if (start > 0) {
+ [file seekToFileOffset:start];
+ }
+
+ NSData* readData;
+ if (end < 0) {
+ readData = [file readDataToEndOfFile];
+ } else {
+ readData = [file readDataOfLength:(end - start)];
+ }
[file closeFile];
NSString* pNStrBuff = nil;
@@ -933,7 +1107,7 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
pNStrBuff = @"";
}
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[pNStrBuff stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:pNStrBuff];
}
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
@@ -950,12 +1124,51 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
{
// arguments
NSString* argPath = [command.arguments objectAtIndex:0];
+ NSInteger start = 0;
+ NSInteger end = -1;
+
+ if ([command.arguments count] >= 2) {
+ start = [[command.arguments objectAtIndex:1] integerValue];
+ }
+ if ([command.arguments count] >= 3) {
+ end = [[command.arguments objectAtIndex:2] integerValue];
+ }
CDVFileError errCode = ABORT_ERR;
- CDVPluginResult* result = nil;
+ __block CDVPluginResult* result = nil;
if (!argPath) {
errCode = SYNTAX_ERR;
+ } else if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ // In this case, we need to use an asynchronous method to retrieve the file.
+ // Because of this, we can't just assign to `result` and send it at the end of the method.
+ // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) {
+ if (asset) {
+ // We have the asset! Get the data and send it off.
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ Byte* buffer = (Byte*)malloc ([assetRepresentation size]);
+ NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
+ NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+ NSString* mimeType = [self getMimeTypeFromPath:[assetRepresentation filename]];
+ NSString* dataString = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, [data base64EncodedString]];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:dataString];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ } else {
+ // We couldn't find the asset. Send the appropriate error.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }
+ };
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) {
+ // Retrieving the asset failed for some reason. Send the appropriate error.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:[NSURL URLWithString:argPath] resultBlock:resultBlock failureBlock:failureBlock];
+ return;
} else {
NSString* mimeType = [self getMimeTypeFromPath:argPath];
if (!mimeType) {
@@ -963,7 +1176,17 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
errCode = ENCODING_ERR;
} else {
NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:argPath];
- NSData* readData = [file readDataToEndOfFile];
+ if (start > 0) {
+ [file seekToFileOffset:start];
+ }
+
+ NSData* readData;
+ if (end < 0) {
+ readData = [file readDataToEndOfFile];
+ } else {
+ readData = [file readDataOfLength:(end - start)];
+ }
+
[file closeFile];
if (readData) {
NSString* output = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, [readData base64EncodedString]];
@@ -1014,6 +1237,13 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
NSString* argPath = [command.arguments objectAtIndex:0];
unsigned long long pos = (unsigned long long)[[command.arguments objectAtIndex:1] longLongValue];
+ // assets-library files can't be truncated
+ if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
NSString* appFile = argPath; // [self getFullPath:argPath];
unsigned long long newPos = [self truncateFile:appFile atPosition:pos];
@@ -1054,6 +1284,13 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
NSString* argData = [arguments objectAtIndex:1];
unsigned long long pos = (unsigned long long)[[arguments objectAtIndex:2] longLongValue];
+ // text can't be written into assets-library files
+ if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
NSString* fullPath = argPath; // [self getFullPath:argPath];
[self truncateFile:fullPath atPosition:pos];
diff --git a/iPhone/CordovaLib/Classes/CDVFileTransfer.h b/iPhone/CordovaLib/Classes/CDVFileTransfer.h
index 76c6a95..f96bb7d 100755
--- a/iPhone/CordovaLib/Classes/CDVFileTransfer.h
+++ b/iPhone/CordovaLib/Classes/CDVFileTransfer.h
@@ -50,7 +50,8 @@ extern NSString* const kOptionsKeyCookie;
- (NSMutableDictionary*)createFileTransferError:(int)code
AndSource :(NSString*)source
AndTarget :(NSString*)target
- AndHttpStatus :(int)httpStatus;
+ AndHttpStatus :(int)httpStatus
+ AndBody :(NSString*)body;
@property (readonly) NSMutableDictionary* activeTransfers;
@end
@@ -64,8 +65,10 @@ extern NSString* const kOptionsKeyCookie;
@property (nonatomic, copy) NSString* objectId;
@property (nonatomic, copy) NSString* source;
@property (nonatomic, copy) NSString* target;
+@property (nonatomic, copy) NSString* mimeType;
@property (assign) int responseCode; // atomic
@property (nonatomic, assign) NSInteger bytesTransfered;
@property (nonatomic, assign) NSInteger bytesExpected;
+@property (nonatomic, assign) BOOL trustAllHosts;
@end;
diff --git a/iPhone/CordovaLib/Classes/CDVFileTransfer.m b/iPhone/CordovaLib/Classes/CDVFileTransfer.m
index b25177a..4ccdce6 100755
--- a/iPhone/CordovaLib/Classes/CDVFileTransfer.m
+++ b/iPhone/CordovaLib/Classes/CDVFileTransfer.m
@@ -19,7 +19,10 @@
#import "CDV.h"
-#include <CFNetwork/CFNetwork.h>
+#import <AssetsLibrary/ALAsset.h>
+#import <AssetsLibrary/ALAssetRepresentation.h>
+#import <AssetsLibrary/ALAssetsLibrary.h>
+#import <CFNetwork/CFNetwork.h>
@interface CDVFileTransfer ()
// Sets the requests headers for the request.
@@ -27,7 +30,7 @@
// Creates a delegate to handle an upload.
- (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command;
// Creates an NSData* for the file for the given upload arguments.
-- (NSData*)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command;
+- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command;
@end
// Buffer size to use for streaming uploads.
@@ -87,7 +90,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
{
[req setValue:@"XMLHttpRequest" forHTTPHeaderField:@"X-Requested-With"];
- NSString* userAgent = [[self.webView request] valueForHTTPHeaderField:@"User-Agent"];
+ NSString* userAgent = [self.commandDelegate userAgent];
if (userAgent) {
[req setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
@@ -130,17 +133,10 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
NSString* fileName = [arguments objectAtIndex:3 withDefault:@"no-filename"];
NSString* mimeType = [arguments objectAtIndex:4 withDefault:nil];
NSDictionary* options = [arguments objectAtIndex:5 withDefault:nil];
- // NSString* trustAllHosts = (NSString*)[arguments objectAtIndex:6]; // allow self-signed certs
+ // BOOL trustAllHosts = [[arguments objectAtIndex:6 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs
BOOL chunkedMode = [[arguments objectAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue];
NSDictionary* headers = [arguments objectAtIndex:8 withDefault:nil];
- // CFStreamCreateBoundPair crashes on iOS < 5.
- if (!IsAtLeastiOSVersion(@"5")) {
- // TODO(agrieve): See if it's okay license-wise to include the work-around code from:
- // http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections/Listings/PostController_m.html
- chunkedMode = NO;
- }
-
CDVPluginResult* result = nil;
CDVFileTransferError errorCode = 0;
@@ -245,6 +241,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
{
NSString* source = [command.arguments objectAtIndex:0];
NSString* server = [command.arguments objectAtIndex:1];
+ BOOL trustAllHosts = [[command.arguments objectAtIndex:6 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs
NSString* objectId = [command.arguments objectAtIndex:9];
CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init];
@@ -255,29 +252,65 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
delegate.objectId = objectId;
delegate.source = source;
delegate.target = server;
+ delegate.trustAllHosts = trustAllHosts;
+
return delegate;
}
-- (NSData*)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command
+- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command
{
NSString* target = (NSString*)[command.arguments objectAtIndex:0];
NSError* __autoreleasing err = nil;
- // Extract the path part out of a file: URL.
- NSString* filePath = [target hasPrefix:@"/"] ? [target copy] : [[NSURL URLWithString:target] path];
- // Memory map the file so that it can be read efficiently even if it is large.
- NSData* fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&err];
+ // return unsupported result for assets-library URLs
+ if ([target hasPrefix:kCDVAssetsLibraryPrefix]) {
+ // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) {
+ if (asset) {
+ // We have the asset! Get the data and send it off.
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ Byte* buffer = (Byte*)malloc ([assetRepresentation size]);
+ NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
+ NSData* fileData = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+ [self uploadData:fileData command:command];
+ } else {
+ // We couldn't find the asset. Send the appropriate error.
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }
+ };
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) {
+ // Retrieving the asset failed for some reason. Send the appropriate error.
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:[NSURL URLWithString:target] resultBlock:resultBlock failureBlock:failureBlock];
+ return;
+ } else {
+ // Extract the path part out of a file: URL.
+ NSString* filePath = [target hasPrefix:@"/"] ? [target copy] : [[NSURL URLWithString:target] path];
+
+ // Memory map the file so that it can be read efficiently even if it is large.
+ NSData* fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&err];
- if (err != nil) {
- NSLog(@"Error opening file %@: %@", target, err);
+ if (err != nil) {
+ NSLog (@"Error opening file %@: %@", target, err);
+ }
+ [self uploadData:fileData command:command];
}
- return fileData;
}
- (void)upload:(CDVInvokedUrlCommand*)command
{
// fileData and req are split into helper functions to ease the unit testing of delegateForUpload.
- NSData* fileData = [self fileDataForUploadCommand:command];
+ // First, get the file data. This method will call `uploadData:command`.
+ [self fileDataForUploadCommand:command];
+}
+
+- (void)uploadData:(NSData*)fileData command:(CDVInvokedUrlCommand*)command
+{
NSURLRequest* req = [self requestForUploadCommand:command fileData:fileData];
if (req == nil) {
@@ -302,9 +335,9 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
if (delegate != nil) {
[delegate.connection cancel];
[activeTransfers removeObjectForKey:objectId];
-
- //delete uncomplete file
- NSFileManager *fileMgr = [NSFileManager defaultManager];
+
+ // delete uncomplete file
+ NSFileManager* fileMgr = [NSFileManager defaultManager];
[fileMgr removeItemAtPath:delegate.target error:nil];
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:CONNECTION_ABORTED AndSource:delegate.source AndTarget:delegate.target]];
@@ -317,8 +350,16 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
DLog(@"File Transfer downloading file...");
NSString* sourceUrl = [command.arguments objectAtIndex:0];
NSString* filePath = [command.arguments objectAtIndex:1];
- // NSString* trustAllHosts = (NSString*)[arguments objectAtIndex:6]; // allow self-signed certs
+ BOOL trustAllHosts = [[command.arguments objectAtIndex:2 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs
NSString* objectId = [command.arguments objectAtIndex:3];
+
+ // return unsupported result for assets-library URLs
+ if ([filePath hasPrefix:kCDVAssetsLibraryPrefix]) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"download not supported for assets-library URLs."];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ return;
+ }
+
CDVPluginResult* result = nil;
CDVFileTransferError errorCode = 0;
@@ -356,6 +397,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
delegate.objectId = objectId;
delegate.source = sourceUrl;
delegate.target = filePath;
+ delegate.trustAllHosts = trustAllHosts;
delegate.connection = [NSURLConnection connectionWithRequest:req delegate:delegate];
@@ -382,13 +424,15 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
AndSource:(NSString*)source
AndTarget:(NSString*)target
AndHttpStatus:(int)httpStatus
+ AndBody:(NSString*)body
{
- NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:4];
+ NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:5];
[result setObject:[NSNumber numberWithInt:code] forKey:@"code"];
[result setObject:source forKey:@"source"];
[result setObject:target forKey:@"target"];
[result setObject:[NSNumber numberWithInt:httpStatus] forKey:@"http_status"];
+ [result setObject:body forKey:@"body"];
NSLog(@"FileTransferError %@", result);
return result;
@@ -412,7 +456,8 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSString* uploadResponse = nil;
- BOOL downloadResponse;
+ NSString* downloadResponse = nil;
+ BOOL downloadWriteOK = NO;
NSMutableDictionary* uploadResult;
CDVPluginResult* result = nil;
NSError* __autoreleasing error = nil;
@@ -423,9 +468,10 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
NSLog(@"File Transfer Finished with response code %d", self.responseCode);
if (self.direction == CDV_TRANSFER_UPLOAD) {
+ uploadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+
if ((self.responseCode >= 200) && (self.responseCode < 300)) {
// create dictionary to return FileUploadResult object
- uploadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
uploadResult = [NSMutableDictionary dictionaryWithCapacity:3];
if (uploadResponse != nil) {
[uploadResult setObject:uploadResponse forKey:@"response"];
@@ -434,7 +480,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
[uploadResult setObject:[NSNumber numberWithInt:self.responseCode] forKey:@"responseCode"];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadResult];
} else {
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:uploadResponse]];
}
}
if (self.direction == CDV_TRANSFER_DOWNLOAD) {
@@ -450,11 +496,12 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
[[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:nil];
}
- downloadResponse = [self.responseData writeToFile:self.target options:NSDataWritingFileProtectionNone error:&error];
+ downloadWriteOK = [self.responseData writeToFile:self.target options:NSDataWritingFileProtectionNone error:&error];
- if (downloadResponse == NO) {
+ if (downloadWriteOK == NO) {
// send our results back
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+ downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
} else {
DLog(@"File Transfer Download success");
@@ -465,10 +512,13 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
}
@catch(id exception) {
// jump back to main thread
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+ downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
}
} else {
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+ downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
}
}
@@ -480,23 +530,29 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
+ self.mimeType = [response MIMEType];
+
// required for iOS 4.3, for some reason; response is
// a plain NSURLResponse, not the HTTP subclass
- if (![response isKindOfClass:[NSHTTPURLResponse class]]) {
- self.responseCode = 403;
+ if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
+ NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
+
+ self.responseCode = [httpResponse statusCode];
self.bytesExpected = [response expectedContentLength];
- return;
+ } else if ([response.URL isFileURL]) {
+ NSDictionary* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:[response.URL path] error:nil];
+ self.responseCode = 200;
+ self.bytesExpected = [attr[NSFileSize] longLongValue];
+ } else {
+ self.responseCode = 200;
+ self.bytesExpected = NSURLResponseUnknownLength;
}
-
- NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
-
- self.responseCode = [httpResponse statusCode];
- self.bytesExpected = [response expectedContentLength];
}
- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
{
- CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+ NSString* body = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:body]];
NSLog(@"File Transfer Error: %@", [error localizedDescription]);
@@ -537,33 +593,20 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
self.bytesTransfered = totalBytesWritten;
}
-/* TESTING ONLY CODE
-// use ONLY for testing with self signed certificates
-// uncomment and modify server name in connection didReceiveAuthenticationChallenge
-- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
+// for self signed certificates
+- (void)connection:(NSURLConnection*)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
{
- return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
-}
-
-- (void)connection:(NSURLConnection *) connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
-{
- if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
- {
- //NSLog(@"challenge host: %@", challenge.protectionSpace.host);
- // we only trust our own domain
- if ([challenge.protectionSpace.host isEqualToString:@"serverName.domain.com"]){
- NSURLCredential* myCredential = [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust];
-
- [challenge.sender useCredential:myCredential forAuthenticationChallenge:challenge];
-
+ if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
+ if (self.trustAllHosts) {
+ NSURLCredential* credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+ [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
}
+ [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
+ } else {
+ [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
}
-
- [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
-// uncomment the above two methods for testing servers with self signed certificates
-// END TESTING ONLY CODE
- */
+
- (id)init
{
if ((self = [super init])) {
diff --git a/iPhone/CordovaLib/Classes/CDVInAppBrowser.h b/iPhone/CordovaLib/Classes/CDVInAppBrowser.h
index 51199ed..9ff460a 100755
--- a/iPhone/CordovaLib/Classes/CDVInAppBrowser.h
+++ b/iPhone/CordovaLib/Classes/CDVInAppBrowser.h
@@ -31,7 +31,7 @@
@end
-@interface CDVInAppBrowser : CDVPlugin <CDVInAppBrowserNavigationDelegate>{}
+@interface CDVInAppBrowser : CDVPlugin <CDVInAppBrowserNavigationDelegate>
@property (nonatomic, retain) CDVInAppBrowserViewController* inAppBrowserViewController;
@property (nonatomic, copy) NSString* callbackId;
@@ -41,7 +41,13 @@
@end
-@interface CDVInAppBrowserViewController : UIViewController <UIWebViewDelegate>{}
+@interface CDVInAppBrowserViewController : UIViewController <UIWebViewDelegate>{
+ @private
+ NSURL* _requestedURL;
+ NSString* _userAgent;
+ NSString* _prevUserAgent;
+ NSInteger _userAgentLockToken;
+}
@property (nonatomic, strong) IBOutlet UIWebView* webView;
@property (nonatomic, strong) IBOutlet UIBarButtonItem* closeButton;
@@ -53,19 +59,26 @@
@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate;
@property (nonatomic, weak) id <CDVInAppBrowserNavigationDelegate> navigationDelegate;
-@property (nonatomic, strong) NSString* userAgent;
- (void)close;
- (void)navigateTo:(NSURL*)url;
- (void)showLocationBar:(BOOL)show;
-- (id)initWithUserAgent:(NSString*)userAgent;
+- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent;
@end
@interface CDVInAppBrowserOptions : NSObject {}
@property (nonatomic, assign) BOOL location;
+@property (nonatomic, copy) NSString* presentationstyle;
+@property (nonatomic, copy) NSString* transitionstyle;
+
+@property (nonatomic, assign) BOOL enableviewportscale;
+@property (nonatomic, assign) BOOL mediaplaybackrequiresuseraction;
+@property (nonatomic, assign) BOOL allowinlinemediaplayback;
+@property (nonatomic, assign) BOOL keyboarddisplayrequiresuseraction;
+@property (nonatomic, assign) BOOL suppressesincrementalrendering;
+ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options;
diff --git a/iPhone/CordovaLib/Classes/CDVInAppBrowser.m b/iPhone/CordovaLib/Classes/CDVInAppBrowser.m
index 22fd1fc..14671a5 100755
--- a/iPhone/CordovaLib/Classes/CDVInAppBrowser.m
+++ b/iPhone/CordovaLib/Classes/CDVInAppBrowser.m
@@ -19,7 +19,7 @@
#import "CDVInAppBrowser.h"
#import "CDVPluginResult.h"
-#import "CDVViewController.h"
+#import "CDVUserAgentUtil.h"
#define kInAppBrowserTargetSelf @"_self"
#define kInAppBrowserTargetSystem @"_system"
@@ -91,8 +91,8 @@
- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
{
if (self.inAppBrowserViewController == nil) {
- NSString* originalUA = [CDVViewController originalUserAgent];
- self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:originalUA];
+ NSString* originalUA = [CDVUserAgentUtil originalUserAgent];
+ self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:originalUA prevUserAgent:[self.commandDelegate userAgent]];
self.inAppBrowserViewController.navigationDelegate = self;
if ([self.viewController conformsToProtocol:@protocol(CDVScreenOrientationDelegate)]) {
@@ -103,6 +103,37 @@
CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options];
[self.inAppBrowserViewController showLocationBar:browserOptions.location];
+ // Set Presentation Style
+ UIModalPresentationStyle presentationStyle = UIModalPresentationFullScreen; // default
+ if (browserOptions.presentationstyle != nil) {
+ if ([browserOptions.presentationstyle isEqualToString:@"pagesheet"]) {
+ presentationStyle = UIModalPresentationPageSheet;
+ } else if ([browserOptions.presentationstyle isEqualToString:@"formsheet"]) {
+ presentationStyle = UIModalPresentationFormSheet;
+ }
+ }
+ self.inAppBrowserViewController.modalPresentationStyle = presentationStyle;
+
+ // Set Transition Style
+ UIModalTransitionStyle transitionStyle = UIModalTransitionStyleCoverVertical; // default
+ if (browserOptions.transitionstyle != nil) {
+ if ([browserOptions.transitionstyle isEqualToString:@"fliphorizontal"]) {
+ transitionStyle = UIModalTransitionStyleFlipHorizontal;
+ } else if ([browserOptions.transitionstyle isEqualToString:@"crossdissolve"]) {
+ transitionStyle = UIModalTransitionStyleCrossDissolve;
+ }
+ }
+ self.inAppBrowserViewController.modalTransitionStyle = transitionStyle;
+
+ // UIWebView options
+ self.inAppBrowserViewController.webView.scalesPageToFit = browserOptions.enableviewportscale;
+ self.inAppBrowserViewController.webView.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction;
+ self.inAppBrowserViewController.webView.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback;
+ if (IsAtLeastiOSVersion(@"6.0")) {
+ self.inAppBrowserViewController.webView.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction;
+ self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering;
+ }
+
if (self.viewController.modalViewController != self.inAppBrowserViewController) {
[self.viewController presentModalViewController:self.inAppBrowserViewController animated:YES];
}
@@ -111,18 +142,7 @@
- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options
{
- BOOL passesWhitelist = YES;
-
- if ([self.viewController isKindOfClass:[CDVViewController class]]) {
- CDVViewController* vc = (CDVViewController*)self.viewController;
- if ([vc.whitelist schemeIsAllowed:[url scheme]]) {
- passesWhitelist = [vc.whitelist URLIsAllowed:url];
- }
- } else { // something went wrong, we can't get the whitelist
- passesWhitelist = NO;
- }
-
- if (passesWhitelist) {
+ if ([self.commandDelegate URLIsWhitelisted:url]) {
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
} else { // this assumes the InAppBrowser can be excepted from the white-list
@@ -172,6 +192,9 @@
[self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
}
+ // Don't recycle the ViewController since it may be consuming a lot of memory.
+ // Also - this is required for the PDF/User-Agent bug work-around.
+ self.inAppBrowserViewController = nil;
}
@end
@@ -180,11 +203,12 @@
@implementation CDVInAppBrowserViewController
-- (id)initWithUserAgent:(NSString*)userAgent
+- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent
{
self = [super init];
if (self != nil) {
- self.userAgent = userAgent;
+ _userAgent = userAgent;
+ _prevUserAgent = prevUserAgent;
[self createViews];
}
@@ -199,31 +223,23 @@
webViewBounds.size.height -= FOOTER_HEIGHT;
- if (!self.webView) {
- // setting the UserAgent must occur before the UIWebView is instantiated.
- // This is read per instantiation, so it does not affect the main Cordova UIWebView
- NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:self.userAgent, @"UserAgent", nil];
- [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
-
- self.webView = [[UIWebView alloc] initWithFrame:webViewBounds];
- self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
-
- [self.view addSubview:self.webView];
- [self.view sendSubviewToBack:self.webView];
-
- self.webView.delegate = self;
- self.webView.scalesPageToFit = TRUE;
- self.webView.backgroundColor = [UIColor whiteColor];
-
- self.webView.clearsContextBeforeDrawing = YES;
- self.webView.clipsToBounds = YES;
- self.webView.contentMode = UIViewContentModeScaleToFill;
- self.webView.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
- self.webView.multipleTouchEnabled = YES;
- self.webView.opaque = YES;
- self.webView.scalesPageToFit = NO;
- self.webView.userInteractionEnabled = YES;
- }
+ self.webView = [[UIWebView alloc] initWithFrame:webViewBounds];
+ self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
+
+ [self.view addSubview:self.webView];
+ [self.view sendSubviewToBack:self.webView];
+
+ self.webView.delegate = self;
+ self.webView.backgroundColor = [UIColor whiteColor];
+
+ self.webView.clearsContextBeforeDrawing = YES;
+ self.webView.clipsToBounds = YES;
+ self.webView.contentMode = UIViewContentModeScaleToFill;
+ self.webView.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
+ self.webView.multipleTouchEnabled = YES;
+ self.webView.opaque = YES;
+ self.webView.scalesPageToFit = NO;
+ self.webView.userInteractionEnabled = YES;
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
self.spinner.alpha = 1.000;
@@ -346,11 +362,14 @@
- (void)viewDidUnload
{
[self.webView loadHTMLString:nil baseURL:nil];
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
[super viewDidUnload];
}
- (void)close
{
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
+
if ([self respondsToSelector:@selector(presentingViewController)]) {
[[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
} else {
@@ -366,7 +385,17 @@
{
NSURLRequest* request = [NSURLRequest requestWithURL:url];
- [self.webView loadRequest:request];
+ _requestedURL = url;
+
+ if (_userAgentLockToken != 0) {
+ [self.webView loadRequest:request];
+ } else {
+ [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
+ _userAgentLockToken = lockToken;
+ [CDVUserAgentUtil setUserAgent:_userAgent lockToken:lockToken];
+ [self.webView loadRequest:request];
+ }];
+ }
}
- (void)goBack:(id)sender
@@ -392,7 +421,11 @@
[self.spinner startAnimating];
if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserLoadStart:)]) {
- [self.navigationDelegate browserLoadStart:theWebView.request.URL];
+ NSURL* url = theWebView.request.URL;
+ if (url == nil) {
+ url = _requestedURL;
+ }
+ [self.navigationDelegate browserLoadStart:url];
}
}
@@ -406,8 +439,25 @@
[self.spinner stopAnimating];
+ // Work around a bug where the first time a PDF is opened, all UIWebViews
+ // reload their User-Agent from NSUserDefaults.
+ // This work-around makes the following assumptions:
+ // 1. The app has only a single Cordova Webview. If not, then the app should
+ // take it upon themselves to load a PDF in the background as a part of
+ // their start-up flow.
+ // 2. That the PDF does not require any additional network requests. We change
+ // the user-agent here back to that of the CDVViewController, so requests
+ // from it must pass through its white-list. This *does* break PDFs that
+ // contain links to other remote PDF/websites.
+ // More info at https://issues.apache.org/jira/browse/CB-2225
+ BOOL isPDF = [@"true" isEqualToString:[theWebView stringByEvaluatingJavaScriptFromString:@"document.body==null"]];
+ if (isPDF) {
+ [CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken];
+ }
+
if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserLoadStop:)]) {
- [self.navigationDelegate browserLoadStop:theWebView.request.URL];
+ NSURL* url = theWebView.request.URL;
+ [self.navigationDelegate browserLoadStop:url];
}
}
@@ -460,6 +510,12 @@
if (self = [super init]) {
// default values
self.location = YES;
+
+ self.enableviewportscale = NO;
+ self.mediaplaybackrequiresuseraction = NO;
+ self.allowinlinemediaplayback = NO;
+ self.keyboarddisplayrequiresuseraction = YES;
+ self.suppressesincrementalrendering = NO;
}
return self;
@@ -478,12 +534,22 @@
if ([keyvalue count] == 2) {
NSString* key = [[keyvalue objectAtIndex:0] lowercaseString];
- NSString* value = [keyvalue objectAtIndex:1];
- BOOL valueBool = [[value lowercaseString] isEqualToString:@"yes"];
+ NSString* value = [[keyvalue objectAtIndex:1] lowercaseString];
+
+ BOOL isBoolean = [value isEqualToString:@"yes"] || [value isEqualToString:@"no"];
+ NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
+ [numberFormatter setAllowsFloats:YES];
+ BOOL isNumber = [numberFormatter numberFromString:value] != nil;
// set the property according to the key name
if ([obj respondsToSelector:NSSelectorFromString(key)]) {
- [obj setValue:[NSNumber numberWithBool:valueBool] forKey:key];
+ if (isNumber) {
+ [obj setValue:[numberFormatter numberFromString:value] forKey:key];
+ } else if (isBoolean) {
+ [obj setValue:[NSNumber numberWithBool:[value isEqualToString:@"yes"]] forKey:key];
+ } else {
+ [obj setValue:value forKey:key];
+ }
}
}
}
diff --git a/iPhone/CordovaLib/Classes/CDVInvokedUrlCommand.m b/iPhone/CordovaLib/Classes/CDVInvokedUrlCommand.m
index 833baad..6c7a856 100755
--- a/iPhone/CordovaLib/Classes/CDVInvokedUrlCommand.m
+++ b/iPhone/CordovaLib/Classes/CDVInvokedUrlCommand.m
@@ -18,7 +18,8 @@
*/
#import "CDVInvokedUrlCommand.h"
-#import "JSONKit.h"
+#import "CDVJSON.h"
+#import "NSData+Base64.h"
@implementation CDVInvokedUrlCommand
@@ -58,9 +59,36 @@
_className = className;
_methodName = methodName;
}
+ [self massageArguments];
return self;
}
+- (void)massageArguments
+{
+ NSMutableArray* newArgs = nil;
+
+ for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) {
+ id arg = [_arguments objectAtIndex:i];
+ if (![arg isKindOfClass:[NSDictionary class]]) {
+ continue;
+ }
+ NSDictionary* dict = arg;
+ NSString* type = [dict objectForKey:@"CDVType"];
+ if (!type || ![type isEqualToString:@"ArrayBuffer"]) {
+ continue;
+ }
+ NSString* data = [dict objectForKey:@"data"];
+ if (!data) {
+ continue;
+ }
+ if (newArgs == nil) {
+ newArgs = [NSMutableArray arrayWithArray:_arguments];
+ _arguments = newArgs;
+ }
+ [newArgs replaceObjectAtIndex:i withObject:[NSData dataFromBase64String:data]];
+ }
+}
+
- (void)legacyArguments:(NSMutableArray**)legacyArguments andDict:(NSMutableDictionary**)legacyDict
{
NSMutableArray* newArguments = [NSMutableArray arrayWithArray:_arguments];
diff --git a/iPhone/CordovaLib/Classes/CDVCordovaView.h b/iPhone/CordovaLib/Classes/CDVJSON.h
index 6318b21..eaa895e 100755
--- a/iPhone/CordovaLib/Classes/CDVCordovaView.h
+++ b/iPhone/CordovaLib/Classes/CDVJSON.h
@@ -17,7 +17,14 @@
under the License.
*/
-#import <UIKit/UIKit.h>
+@interface NSArray (CDVJSONSerializing)
+- (NSString*)JSONString;
+@end
+
+@interface NSDictionary (CDVJSONSerializing)
+- (NSString*)JSONString;
+@end
-@interface CDVCordovaView : UIWebView {}
+@interface NSString (CDVJSONSerializing)
+- (id)JSONObject;
@end
diff --git a/iPhone/CordovaLib/Classes/CDVJSON.m b/iPhone/CordovaLib/Classes/CDVJSON.m
new file mode 100755
index 0000000..78267e5
--- /dev/null
+++ b/iPhone/CordovaLib/Classes/CDVJSON.m
@@ -0,0 +1,77 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import "CDVJSON.h"
+#import <Foundation/NSJSONSerialization.h>
+
+@implementation NSArray (CDVJSONSerializing)
+
+- (NSString*)JSONString
+{
+ NSError* error = nil;
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
+ options:NSJSONWritingPrettyPrinted
+ error:&error];
+
+ if (error != nil) {
+ NSLog(@"NSArray JSONString error: %@", [error localizedDescription]);
+ return nil;
+ } else {
+ return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ }
+}
+
+@end
+
+@implementation NSDictionary (CDVJSONSerializing)
+
+- (NSString*)JSONString
+{
+ NSError* error = nil;
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
+ options:NSJSONWritingPrettyPrinted
+ error:&error];
+
+ if (error != nil) {
+ NSLog(@"NSDictionary JSONString error: %@", [error localizedDescription]);
+ return nil;
+ } else {
+ return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ }
+}
+
+@end
+
+@implementation NSString (CDVJSONSerializing)
+
+- (id)JSONObject
+{
+ NSError* error = nil;
+ id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding]
+ options:kNilOptions
+ error:&error];
+
+ if (error != nil) {
+ NSLog(@"NSString JSONObject error: %@", [error localizedDescription]);
+ }
+
+ return object;
+}
+
+@end
diff --git a/iPhone/CordovaLib/Classes/CDVLocalStorage.h b/iPhone/CordovaLib/Classes/CDVLocalStorage.h
index e5e3112..cc6613f 100755
--- a/iPhone/CordovaLib/Classes/CDVLocalStorage.h
+++ b/iPhone/CordovaLib/Classes/CDVLocalStorage.h
@@ -22,7 +22,7 @@
#define kCDVLocalStorageErrorDomain @"kCDVLocalStorageErrorDomain"
#define kCDVLocalStorageFileOperationError 1
-@interface CDVLocalStorage : CDVPlugin <UIWebViewDelegate>
+@interface CDVLocalStorage : CDVPlugin
@property (nonatomic, readonly, strong) NSMutableArray* backupInfo;
diff --git a/iPhone/CordovaLib/Classes/CDVLocalStorage.m b/iPhone/CordovaLib/Classes/CDVLocalStorage.m
index 217f611..68175f1 100755
--- a/iPhone/CordovaLib/Classes/CDVLocalStorage.m
+++ b/iPhone/CordovaLib/Classes/CDVLocalStorage.m
@@ -31,21 +31,13 @@
@synthesize backupInfo, webviewDelegate;
-- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings
+- (void)pluginInitialize
{
- self = (CDVLocalStorage*)[super initWithWebView:theWebView settings:classSettings];
- if (self) {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive)
- name:UIApplicationWillResignActiveNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive)
+ name:UIApplicationWillResignActiveNotification object:nil];
+ BOOL cloudBackup = [@"cloud" isEqualToString:self.commandDelegate.settings[@"BackupWebStorage"]];
- self.backupInfo = [[self class] createBackupInfoWithCloudBackup:[(NSString*)[classSettings objectForKey:@"backupType"] isEqualToString:@"cloud"]];
-
- // over-ride current webview delegate (for restore reasons)
- self.webviewDelegate = theWebView.delegate;
- theWebView.delegate = self;
- }
-
- return self;
+ self.backupInfo = [[self class] createBackupInfoWithCloudBackup:cloudBackup];
}
#pragma mark -
@@ -411,37 +403,9 @@
[self onResignActive];
}
-#pragma mark -
-#pragma mark UIWebviewDelegate implementation and forwarding
-
-- (void)webViewDidStartLoad:(UIWebView*)theWebView
+- (void)onReset
{
[self restore:nil];
-
- return [self.webviewDelegate webViewDidStartLoad:theWebView];
-}
-
-- (void)webViewDidFinishLoad:(UIWebView*)theWebView
-{
- return [self.webviewDelegate webViewDidFinishLoad:theWebView];
-}
-
-- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
-{
- return [self.webviewDelegate webView:theWebView didFailLoadWithError:error];
-}
-
-- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
-{
- return [self.webviewDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
-}
-
-#pragma mark -
-#pragma mark Over-rides
-
-- (void)dealloc
-{
- [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock:
}
@end
diff --git a/iPhone/CordovaLib/Classes/CDVLocation.m b/iPhone/CordovaLib/Classes/CDVLocation.m
index 9814f35..07af30e 100755
--- a/iPhone/CordovaLib/Classes/CDVLocation.m
+++ b/iPhone/CordovaLib/Classes/CDVLocation.m
@@ -18,7 +18,6 @@
*/
#import "CDVLocation.h"
-#import "CDVViewController.h"
#import "NSArray+Comparisons.h"
#pragma mark Constants
@@ -477,17 +476,8 @@
// helper method to check the orientation and start updating headings
- (void)startHeadingWithFilter:(CLLocationDegrees)filter
{
- if ([self.locationManager respondsToSelector:@selector(headingOrientation)]) {
- UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
- if (currentOrientation != UIDeviceOrientationUnknown) {
- CDVViewController* cdvViewController = (CDVViewController*)self.viewController;
-
- if ([cdvViewController supportsOrientation:currentOrientation]) {
- self.locationManager.headingOrientation = (CLDeviceOrientation)currentOrientation;
- // FYI UIDeviceOrientation and CLDeviceOrientation enums are currently the same
- }
- }
- }
+ // FYI UIDeviceOrientation and CLDeviceOrientation enums are currently the same
+ self.locationManager.headingOrientation = (CLDeviceOrientation)self.viewController.interfaceOrientation;
self.locationManager.headingFilter = filter;
[self.locationManager startUpdatingHeading];
self.headingData.headingStatus = HEADINGSTARTING;
diff --git a/iPhone/CordovaLib/Classes/CDVPlugin.h b/iPhone/CordovaLib/Classes/CDVPlugin.h
index 8c59a2b..f5b50eb 100755
--- a/iPhone/CordovaLib/Classes/CDVPlugin.h
+++ b/iPhone/CordovaLib/Classes/CDVPlugin.h
@@ -23,6 +23,7 @@
#import "NSMutableArray+QueueAdditions.h"
#import "CDVCommandDelegate.h"
+#define CDVPageDidLoadNotification @"CDVPageDidLoadNotification"
#define CDVPluginHandleOpenURLNotification @"CDVPluginHandleOpenURLNotification"
#define CDVPluginResetNotification @"CDVPluginResetNotification"
#define CDVLocalNotification @"CDVLocalNotification"
@@ -30,14 +31,13 @@
@interface CDVPlugin : NSObject {}
@property (nonatomic, weak) UIWebView* webView;
-@property (nonatomic, strong) NSDictionary* settings;
@property (nonatomic, weak) UIViewController* viewController;
@property (nonatomic, weak) id <CDVCommandDelegate> commandDelegate;
@property (readonly, assign) BOOL hasPendingOperation;
-- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings;
- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView;
+- (void)pluginInitialize;
- (void)handleOpenURL:(NSNotification*)notification;
- (void)onAppTerminate;
diff --git a/iPhone/CordovaLib/Classes/CDVPlugin.m b/iPhone/CordovaLib/Classes/CDVPlugin.m
index 53023c7..a42d241 100755
--- a/iPhone/CordovaLib/Classes/CDVPlugin.m
+++ b/iPhone/CordovaLib/Classes/CDVPlugin.m
@@ -26,16 +26,12 @@
@end
@implementation CDVPlugin
-@synthesize webView, settings, viewController, commandDelegate, hasPendingOperation;
+@synthesize webView, viewController, commandDelegate, hasPendingOperation;
+// Do not override these methods. Use pluginInitialize instead.
- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings
{
- self = [self initWithWebView:theWebView];
- if (self) {
- self.settings = classSettings;
- self.hasPendingOperation = NO;
- }
- return self;
+ return [self initWithWebView:theWebView];
}
- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView
@@ -48,26 +44,29 @@
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:nil];
self.webView = theWebView;
-
- // You can listen to more app notifications, see:
- // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4
-
- /*
- // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
-
- // Added in 2.3.0+
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil];
-
- */
}
return self;
}
+- (void)pluginInitialize
+{
+ // You can listen to more app notifications, see:
+ // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4
+
+ // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler
+
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
+
+ // Added in 2.3.0
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil];
+
+ // Added in 2.5.0
+ // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:nil];
+}
+
- (void)dispose
{
viewController = nil;
@@ -140,9 +139,9 @@
}
// default implementation does nothing, ideally, we are not registered for notification if we aren't going to do anything.
-//- (void)didReceiveLocalNotification:(NSNotification *)notification
-//{
+// - (void)didReceiveLocalNotification:(NSNotification *)notification
+// {
// // UILocalNotification* localNotification = [notification object]; // get the payload as a LocalNotification
-//}
+// }
@end
diff --git a/iPhone/CordovaLib/Classes/CDVPluginResult.h b/iPhone/CordovaLib/Classes/CDVPluginResult.h
index 6dd7d45..8683205 100755
--- a/iPhone/CordovaLib/Classes/CDVPluginResult.h
+++ b/iPhone/CordovaLib/Classes/CDVPluginResult.h
@@ -44,7 +44,9 @@ typedef enum {
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage;
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode;
+ (void)setVerbose:(BOOL)verbose;
diff --git a/iPhone/CordovaLib/Classes/CDVPluginResult.m b/iPhone/CordovaLib/Classes/CDVPluginResult.m
index 5343dce..d9ba08f 100755
--- a/iPhone/CordovaLib/Classes/CDVPluginResult.m
+++ b/iPhone/CordovaLib/Classes/CDVPluginResult.m
@@ -18,8 +18,9 @@
*/
#import "CDVPluginResult.h"
-#import "JSONKit.h"
+#import "CDVJSON.h"
#import "CDVDebug.h"
+#import "NSData+Base64.h"
@interface CDVPluginResult ()
@@ -88,11 +89,26 @@ static NSArray* org_apache_cordova_CommandStatusMsgs;
return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]];
}
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage
+{
+ return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]];
+}
+
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage
{
return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
}
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage
+{
+ NSDictionary* arrDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"ArrayBuffer", @"CDVType",
+ [theMessage base64EncodedString], @"data",
+ nil];
+
+ return [[self alloc] initWithStatus:statusOrdinal message:arrDict];
+}
+
+ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode
{
NSDictionary* errDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:errorCode] forKey:@"code"];
@@ -107,11 +123,23 @@ static NSArray* org_apache_cordova_CommandStatusMsgs;
- (NSString*)toJSONString
{
- NSString* resultString = [[NSDictionary dictionaryWithObjectsAndKeys:
- self.status, @"status",
- self.message ? self. message:[NSNull null], @"message",
- self.keepCallback, @"keepCallback",
- nil] cdvjk_JSONString];
+ NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
+ self.status, @"status",
+ self.message ? self. message:[NSNull null], @"message",
+ self.keepCallback, @"keepCallback",
+ nil];
+
+ NSError* error = nil;
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dict
+ options:NSJSONWritingPrettyPrinted
+ error:&error];
+ NSString* resultString = nil;
+
+ if (error != nil) {
+ NSLog(@"toJSONString error: %@", [error localizedDescription]);
+ } else {
+ resultString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ }
if ([[self class] isVerbose]) {
NSLog(@"PluginResult:toJSONString - %@", resultString);
diff --git a/iPhone/CordovaLib/Classes/CDVSound.h b/iPhone/CordovaLib/Classes/CDVSound.h
index 6551621..8dcf98e 100755
--- a/iPhone/CordovaLib/Classes/CDVSound.h
+++ b/iPhone/CordovaLib/Classes/CDVSound.h
@@ -98,7 +98,13 @@ typedef NSUInteger CDVMediaMsg;
- (BOOL)hasAudioSession;
// helper methods
-- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId;
+- (NSURL*)urlForRecording:(NSString*)resourcePath;
+- (NSURL*)urlForPlaying:(NSString*)resourcePath;
+- (NSURL*)urlForResource:(NSString*)resourcePath CDV_DEPRECATED(2.5, "Use specific api for playing or recording");
+
+- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId CDV_DEPRECATED(2.5, "Use updated audioFileForResource api");
+
+- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId doValidation:(BOOL)bValidate forRecording:(BOOL)bRecord;
- (BOOL)prepareToPlay:(CDVAudioFile*)audioFile withId:(NSString*)mediaId;
- (NSString*)createMediaErrorWithCode:(CDVMediaError)code message:(NSString*)message;
diff --git a/iPhone/CordovaLib/Classes/CDVSound.m b/iPhone/CordovaLib/Classes/CDVSound.m
index f7a3adf..99515d7 100755
--- a/iPhone/CordovaLib/Classes/CDVSound.m
+++ b/iPhone/CordovaLib/Classes/CDVSound.m
@@ -18,19 +18,18 @@
*/
#import "CDVSound.h"
-#import "CDVViewController.h"
#import "NSArray+Comparisons.h"
+#import "CDVJSON.h"
#define DOCUMENTS_SCHEME_PREFIX @"documents://"
#define HTTP_SCHEME_PREFIX @"http://"
#define HTTPS_SCHEME_PREFIX @"https://"
+#define RECORDING_WAV @"wav"
@implementation CDVSound
@synthesize soundCache, avSession;
-// Maps a url for a resource path
-// "Naked" resource paths are assumed to be from the www folder as its base
- (NSURL*)urlForResource:(NSString*)resourcePath
{
NSURL* resourceURL = nil;
@@ -43,7 +42,8 @@
NSLog(@"Will use resource '%@' from the Internet.", resourcePath);
resourceURL = [NSURL URLWithString:resourcePath];
} else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) {
- filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", [CDVViewController applicationDocumentsDirectory]]];
+ NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+ filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]];
NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath);
} else {
// attempt to find file path in www directory
@@ -70,9 +70,101 @@
return resourceURL;
}
-// Creates or gets the cached audio file resource object
+// Maps a url for a resource path for recording
+- (NSURL*)urlForRecording:(NSString*)resourcePath
+{
+ NSURL* resourceURL = nil;
+ NSString* filePath = nil;
+ NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+
+ // first check for correct extension
+ if ([[resourcePath pathExtension] caseInsensitiveCompare:RECORDING_WAV] != NSOrderedSame) {
+ resourceURL = nil;
+ NSLog(@"Resource for recording must have %@ extension", RECORDING_WAV);
+ } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) {
+ // try to find Documents:// resources
+ filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]];
+ NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath);
+ } else {
+ // if resourcePath is not from FileSystem put in tmp dir, else attempt to use provided resource path
+ NSString* tmpPath = [NSTemporaryDirectory ()stringByStandardizingPath];
+ BOOL isTmp = [resourcePath rangeOfString:tmpPath].location != NSNotFound;
+ BOOL isDoc = [resourcePath rangeOfString:docsPath].location != NSNotFound;
+ if (!isTmp && !isDoc) {
+ // put in temp dir
+ filePath = [NSString stringWithFormat:@"%@/%@", tmpPath, resourcePath];
+ } else {
+ filePath = resourcePath;
+ }
+ }
+
+ if (filePath != nil) {
+ // create resourceURL
+ resourceURL = [NSURL fileURLWithPath:filePath];
+ }
+ return resourceURL;
+}
+
+// Maps a url for a resource path for playing
+// "Naked" resource paths are assumed to be from the www folder as its base
+- (NSURL*)urlForPlaying:(NSString*)resourcePath
+{
+ NSURL* resourceURL = nil;
+ NSString* filePath = nil;
+
+ // first try to find HTTP:// or Documents:// resources
+
+ if ([resourcePath hasPrefix:HTTP_SCHEME_PREFIX] || [resourcePath hasPrefix:HTTPS_SCHEME_PREFIX]) {
+ // if it is a http url, use it
+ NSLog(@"Will use resource '%@' from the Internet.", resourcePath);
+ resourceURL = [NSURL URLWithString:resourcePath];
+ } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) {
+ NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+ filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]];
+ NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath);
+ } else {
+ // attempt to find file path in www directory or LocalFileSystem.TEMPORARY directory
+ filePath = [self.commandDelegate pathForResource:resourcePath];
+ if (filePath == nil) {
+ // see if this exists in the documents/temp directory from a previous recording
+ NSString* testPath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory ()stringByStandardizingPath], resourcePath];
+ if ([[NSFileManager defaultManager] fileExistsAtPath:testPath]) {
+ // inefficient as existence will be checked again below but only way to determine if file exists from previous recording
+ filePath = testPath;
+ NSLog(@"Will attempt to use file resource from LocalFileSystem.TEMPORARY directory");
+ } else {
+ // attempt to use path provided
+ filePath = resourcePath;
+ NSLog(@"Will attempt to use file resource '%@'", filePath);
+ }
+ } else {
+ NSLog(@"Found resource '%@' in the web folder.", filePath);
+ }
+ }
+ // check that file exists for all but HTTP_SHEME_PREFIX
+ if (filePath != nil) {
+ // create resourceURL
+ resourceURL = [NSURL fileURLWithPath:filePath];
+ // try to access file
+ NSFileManager* fMgr = [NSFileManager defaultManager];
+ if (![fMgr fileExistsAtPath:filePath]) {
+ resourceURL = nil;
+ NSLog(@"Unknown resource '%@'", resourcePath);
+ }
+ }
+
+ return resourceURL;
+}
+
- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId
{
+ // will maintain backwards compatibility with original implementation
+ return [self audioFileForResource:resourcePath withId:mediaId doValidation:YES forRecording:NO];
+}
+
+// Creates or gets the cached audio file resource object
+- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId doValidation:(BOOL)bValidate forRecording:(BOOL)bRecord
+{
BOOL bError = NO;
CDVMediaError errcode = MEDIA_ERR_NONE_SUPPORTED;
NSString* errMsg = @"";
@@ -87,31 +179,37 @@
}
if (audioFile == nil) {
// validate resourcePath and create
-
if ((resourcePath == nil) || ![resourcePath isKindOfClass:[NSString class]] || [resourcePath isEqualToString:@""]) {
bError = YES;
errcode = MEDIA_ERR_ABORTED;
errMsg = @"invalid media src argument";
} else {
- resourceURL = [self urlForResource:resourcePath];
+ audioFile = [[CDVAudioFile alloc] init];
+ audioFile.resourcePath = resourcePath;
+ audioFile.resourceURL = nil; // validate resourceURL when actually play or record
+ [[self soundCache] setObject:audioFile forKey:mediaId];
+ }
+ }
+ if (bValidate && (audioFile.resourceURL == nil)) {
+ if (bRecord) {
+ resourceURL = [self urlForRecording:resourcePath];
+ } else {
+ resourceURL = [self urlForPlaying:resourcePath];
}
-
if (resourceURL == nil) {
bError = YES;
errcode = MEDIA_ERR_ABORTED;
errMsg = [NSString stringWithFormat:@"Cannot use audio file from resource '%@'", resourcePath];
- }
- if (bError) {
- // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, errcode];
- jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:errcode message:errMsg]];
- [self.commandDelegate evalJs:jsString];
} else {
- audioFile = [[CDVAudioFile alloc] init];
- audioFile.resourcePath = resourcePath;
audioFile.resourceURL = resourceURL;
- [[self soundCache] setObject:audioFile forKey:mediaId];
}
}
+
+ if (bError) {
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:errcode message:errMsg]];
+ [self.commandDelegate evalJs:jsString];
+ }
+
return audioFile;
}
@@ -141,7 +239,7 @@
[errorDict setObject:[NSNumber numberWithUnsignedInt:code] forKey:@"code"];
[errorDict setObject:message ? message:@"" forKey:@"message"];
- return [errorDict cdvjk_JSONString];
+ return [errorDict JSONString];
}
- (void)create:(CDVInvokedUrlCommand*)command
@@ -149,7 +247,7 @@
NSString* mediaId = [command.arguments objectAtIndex:0];
NSString* resourcePath = [command.arguments objectAtIndex:1];
- CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId];
+ CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId doValidation:NO forRecording:NO];
if (audioFile == nil) {
NSString* errorMessage = [NSString stringWithFormat:@"Failed to initialize Media file with path %@", resourcePath];
@@ -193,9 +291,8 @@
BOOL bError = NO;
NSString* jsString = nil;
- CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId];
-
- if (audioFile != nil) {
+ CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId doValidation:YES forRecording:NO];
+ if ((audioFile != nil) && (audioFile.resourceURL != nil)) {
if (audioFile.player == nil) {
bError = [self prepareToPlay:audioFile withId:mediaId];
}
@@ -274,7 +371,12 @@
if ([resourceURL isFileURL]) {
audioFile.player = [[CDVAudioPlayer alloc] initWithContentsOfURL:resourceURL error:&playerError];
} else {
- NSURLRequest* request = [NSURLRequest requestWithURL:resourceURL];
+ NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:resourceURL];
+ NSString* userAgent = [self.commandDelegate userAgent];
+ if (userAgent) {
+ [request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
+ }
+
NSURLResponse* __autoreleasing response = nil;
NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&playerError];
if (playerError) {
@@ -411,11 +513,11 @@
#pragma unused(callbackId)
NSString* mediaId = [command.arguments objectAtIndex:0];
- CDVAudioFile* audioFile = [self audioFileForResource:[command.arguments objectAtIndex:1] withId:mediaId];
+ CDVAudioFile* audioFile = [self audioFileForResource:[command.arguments objectAtIndex:1] withId:mediaId doValidation:YES forRecording:YES];
NSString* jsString = nil;
NSString* errorMsg = @"";
- if (audioFile != nil) {
+ if ((audioFile != nil) && (audioFile.resourceURL != nil)) {
NSError* __autoreleasing error = nil;
if (audioFile.recorder != nil) {
@@ -462,9 +564,9 @@
jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
}
} else {
- // file does not exist
- NSLog(@"Could not start recording audio, file '%@' does not exist.", audioFile.resourcePath);
- jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:@"File to record to does not exist"]];
+ // file did not validate
+ NSString* errorMsg = [NSString stringWithFormat:@"Could not record audio at '%@'", audioFile.resourcePath];
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
}
if (jsString) {
[self.commandDelegate evalJs:jsString];
diff --git a/iPhone/CordovaLib/Classes/CDVSplashScreen.h b/iPhone/CordovaLib/Classes/CDVSplashScreen.h
index b0d8615..a0868a0 100755
--- a/iPhone/CordovaLib/Classes/CDVSplashScreen.h
+++ b/iPhone/CordovaLib/Classes/CDVSplashScreen.h
@@ -20,7 +20,11 @@
#import <Foundation/Foundation.h>
#import "CDVPlugin.h"
-@interface CDVSplashScreen : CDVPlugin {}
+@interface CDVSplashScreen : CDVPlugin {
+ UIActivityIndicatorView* _activityView;
+ UIImageView* _imageView;
+ UIView* _parentView;
+}
- (void)show:(CDVInvokedUrlCommand*)command;
- (void)hide:(CDVInvokedUrlCommand*)command;
diff --git a/iPhone/CordovaLib/Classes/CDVSplashScreen.m b/iPhone/CordovaLib/Classes/CDVSplashScreen.m
index 2512328..cba1b53 100755
--- a/iPhone/CordovaLib/Classes/CDVSplashScreen.m
+++ b/iPhone/CordovaLib/Classes/CDVSplashScreen.m
@@ -18,32 +18,157 @@
*/
#import "CDVSplashScreen.h"
-#import "CDVViewController.h"
+
+#define kSplashScreenStateShow 0
+#define kSplashScreenStateHide 1
+
+#define kSplashScreenDurationDefault 0.25f
@implementation CDVSplashScreen
-- (void)__show:(BOOL)show
+- (void)pluginInitialize
{
- // Legacy support - once deprecated classes removed, clean this up
- id <UIApplicationDelegate> delegate = [[UIApplication sharedApplication] delegate];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
- if ([delegate respondsToSelector:@selector(viewController)]) {
- id vc = [delegate performSelector:@selector(viewController)];
- if ([vc isKindOfClass:[CDVViewController class]]) {
- ((CDVViewController*)vc).imageView.hidden = !show;
- ((CDVViewController*)vc).activityView.hidden = !show;
- }
- }
+ [self show:nil];
}
- (void)show:(CDVInvokedUrlCommand*)command
{
- [self __show:YES];
+ [self updateSplashScreenWithState:kSplashScreenStateShow];
}
- (void)hide:(CDVInvokedUrlCommand*)command
{
- [self __show:NO];
+ [self updateSplashScreenWithState:kSplashScreenStateHide];
+}
+
+- (void)pageDidLoad
+{
+ id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:@"AutoHideSplashScreen"];
+
+ // if value is missing, default to yes
+ if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) {
+ [self hide:nil];
+ }
+}
+
+- (void)onOrientationWillChange:(NSNotification*)notification
+{
+ if (_imageView != nil) {
+ UIInterfaceOrientation orientation = [notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] intValue];
+ [self updateSplashImageForOrientation:orientation];
+ }
+}
+
+- (void)createViews
+{
+ /*
+ * The Activity View is the top spinning throbber in the status/battery bar. We init it with the default Grey Style.
+ *
+ * whiteLarge = UIActivityIndicatorViewStyleWhiteLarge
+ * white = UIActivityIndicatorViewStyleWhite
+ * gray = UIActivityIndicatorViewStyleGray
+ *
+ */
+ NSString* topActivityIndicator = [self.commandDelegate.settings objectForKey:@"TopActivityIndicator"];
+ UIActivityIndicatorViewStyle topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
+
+ if ([topActivityIndicator isEqualToString:@"whiteLarge"]) {
+ topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhiteLarge;
+ } else if ([topActivityIndicator isEqualToString:@"white"]) {
+ topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhite;
+ } else if ([topActivityIndicator isEqualToString:@"gray"]) {
+ topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
+ }
+
+ _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle];
+ _activityView.tag = 2;
+ _activityView.center = self.viewController.view.center;
+ [_activityView startAnimating];
+
+ _imageView = [[UIImageView alloc] init];
+ [self.viewController.view addSubview:_imageView];
+ [self.viewController.view.superview addSubview:_activityView];
+ [self.viewController.view.superview layoutSubviews];
+}
+
+- (void)updateSplashImageForOrientation:(UIInterfaceOrientation)orientation
+{
+ // IPHONE (default)
+ NSString* imageName = @"Default";
+
+ if (CDV_IsIPhone5()) {
+ imageName = [imageName stringByAppendingString:@"-568h"];
+ } else if (CDV_IsIPad()) {
+ // set default to portrait upside down
+ imageName = @"Default-Portrait"; // @"Default-PortraitUpsideDown.png";
+
+ if (orientation == UIInterfaceOrientationLandscapeLeft) {
+ imageName = @"Default-Landscape.png"; // @"Default-LandscapeLeft.png";
+ } else if (orientation == UIInterfaceOrientationLandscapeRight) {
+ imageName = @"Default-Landscape.png"; // @"Default-LandscapeRight.png";
+ }
+ }
+
+ _imageView.image = [UIImage imageNamed:imageName];
+ _imageView.frame = CGRectMake(0, 0, _imageView.image.size.width, _imageView.image.size.height);
+}
+
+- (void)updateSplashScreenWithState:(int)state
+{
+ float toAlpha = state == kSplashScreenStateShow ? 1.0f : 0.0f;
+ BOOL hidden = state == kSplashScreenStateShow ? NO : YES;
+
+ id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:@"FadeSplashScreen"];
+ id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:@"FadeSplashScreenDuration"];
+
+ float fadeDuration = fadeSplashScreenDuration == nil ? kSplashScreenDurationDefault : [fadeSplashScreenDuration floatValue];
+
+ if ((fadeSplashScreenValue == nil) || ![fadeSplashScreenValue boolValue]) {
+ fadeDuration = 0;
+ }
+ if (hidden && (_imageView == nil)) {
+ return;
+ } else if (_imageView == nil) {
+ [self createViews];
+ fadeDuration = 0;
+ }
+
+ if (!hidden) {
+ [self updateSplashImageForOrientation:self.viewController.interfaceOrientation];
+ }
+
+ if (fadeDuration == 0) {
+ [_imageView setHidden:hidden];
+ [_activityView setHidden:hidden];
+ } else {
+ if (state == kSplashScreenStateShow) {
+ // reset states
+ [_imageView setHidden:NO];
+ [_activityView setHidden:NO];
+ [_imageView setAlpha:0.0f];
+ [_activityView setAlpha:0.0f];
+ }
+
+ [UIView transitionWithView:self.viewController.view
+ duration:fadeDuration
+ options:UIViewAnimationOptionTransitionNone
+ animations:^(void) {
+ [_imageView setAlpha:toAlpha];
+ [_activityView setAlpha:toAlpha];
+ }
+ completion:^(BOOL finished) {
+ if (state == kSplashScreenStateHide) {
+ // Clean-up resources.
+ [_imageView removeFromSuperview];
+ [_activityView removeFromSuperview];
+ _imageView = nil;
+ _activityView = nil;
+ }
+ }];
+ }
}
@end
diff --git a/iPhone/CordovaLib/Classes/CDVURLProtocol.h b/iPhone/CordovaLib/Classes/CDVURLProtocol.h
index ce8df38..5444f6d 100755
--- a/iPhone/CordovaLib/Classes/CDVURLProtocol.h
+++ b/iPhone/CordovaLib/Classes/CDVURLProtocol.h
@@ -24,9 +24,6 @@
@interface CDVURLProtocol : NSURLProtocol {}
-+ (void)registerPGHttpURLProtocol CDV_DEPRECATED (2.0, "This is now a no-op and should be removed.");
-+ (void)registerURLProtocol CDV_DEPRECATED (2.0, "This is now a no-op and should be removed.");
-
+ (void)registerViewController:(CDVViewController*)viewController;
+ (void)unregisterViewController:(CDVViewController*)viewController;
@end
diff --git a/iPhone/CordovaLib/Classes/CDVURLProtocol.m b/iPhone/CordovaLib/Classes/CDVURLProtocol.m
index a645288..1959c77 100755
--- a/iPhone/CordovaLib/Classes/CDVURLProtocol.m
+++ b/iPhone/CordovaLib/Classes/CDVURLProtocol.m
@@ -17,14 +17,17 @@
under the License.
*/
+#import <AssetsLibrary/ALAsset.h>
+#import <AssetsLibrary/ALAssetRepresentation.h>
+#import <AssetsLibrary/ALAssetsLibrary.h>
+#import <MobileCoreServices/MobileCoreServices.h>
#import "CDVURLProtocol.h"
#import "CDVCommandQueue.h"
#import "CDVWhitelist.h"
#import "CDVViewController.h"
+#import "CDVFile.h"
@interface CDVHTTPURLResponse : NSHTTPURLResponse
-- (id)initWithUnauthorizedURL:(NSURL*)url;
-- (id)initWithBlankResponse:(NSURL*)url;
@property (nonatomic) NSInteger statusCode;
@end
@@ -106,7 +109,9 @@ static CDVViewController *viewControllerForRequest(NSURLRequest* request)
NSURL* theUrl = [theRequest URL];
CDVViewController* viewController = viewControllerForRequest(theRequest);
- if (viewController != nil) {
+ if ([[theUrl absoluteString] hasPrefix:kCDVAssetsLibraryPrefix]) {
+ return YES;
+ } else if (viewController != nil) {
if ([[theUrl path] isEqualToString:@"/!gap_exec"]) {
NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"];
NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"];
@@ -151,21 +156,35 @@ static CDVViewController *viewControllerForRequest(NSURLRequest* request)
NSURL* url = [[self request] URL];
if ([[url path] isEqualToString:@"/!gap_exec"]) {
- CDVHTTPURLResponse* response = [[CDVHTTPURLResponse alloc] initWithBlankResponse:url];
- [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
- [[self client] URLProtocolDidFinishLoading:self];
+ [self sendResponseWithResponseCode:200 data:nil mimeType:nil];
+ return;
+ } else if ([[url absoluteString] hasPrefix:kCDVAssetsLibraryPrefix]) {
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) {
+ if (asset) {
+ // We have the asset! Get the data and send it along.
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass ((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
+ Byte* buffer = (Byte*)malloc ([assetRepresentation size]);
+ NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
+ NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+ [self sendResponseWithResponseCode:200 data:data mimeType:MIMEType];
+ } else {
+ // Retrieving the asset failed for some reason. Send an error.
+ [self sendResponseWithResponseCode:404 data:nil mimeType:nil];
+ }
+ };
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) {
+ // Retrieving the asset failed for some reason. Send an error.
+ [self sendResponseWithResponseCode:401 data:nil mimeType:nil];
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:url resultBlock:resultBlock failureBlock:failureBlock];
return;
}
NSString* body = [gWhitelist errorStringForURL:url];
-
- CDVHTTPURLResponse* response = [[CDVHTTPURLResponse alloc] initWithUnauthorizedURL:url];
-
- [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
-
- [[self client] URLProtocol:self didLoadData:[body dataUsingEncoding:NSASCIIStringEncoding]];
-
- [[self client] URLProtocolDidFinishLoading:self];
+ [self sendResponseWithResponseCode:401 data:[body dataUsingEncoding:NSASCIIStringEncoding] mimeType:nil];
}
- (void)stopLoading
@@ -178,29 +197,31 @@ static CDVViewController *viewControllerForRequest(NSURLRequest* request)
return NO;
}
-@end
-
-@implementation CDVHTTPURLResponse
-@synthesize statusCode;
-
-- (id)initWithUnauthorizedURL:(NSURL*)url
+- (void)sendResponseWithResponseCode:(NSInteger)statusCode data:(NSData*)data mimeType:(NSString*)mimeType
{
- self = [super initWithURL:url MIMEType:@"text/plain" expectedContentLength:-1 textEncodingName:@"UTF-8"];
- if (self) {
- self.statusCode = 401;
+ if (mimeType == nil) {
+ mimeType = @"text/plain";
}
- return self;
-}
+ NSString* encodingName = [@"text/plain" isEqualToString:mimeType] ? @"UTF-8" : nil;
+ CDVHTTPURLResponse* response =
+ [[CDVHTTPURLResponse alloc] initWithURL:[[self request] URL]
+ MIMEType:mimeType
+ expectedContentLength:[data length]
+ textEncodingName:encodingName];
+ response.statusCode = statusCode;
-- (id)initWithBlankResponse:(NSURL*)url
-{
- self = [super initWithURL:url MIMEType:@"text/plain" expectedContentLength:-1 textEncodingName:@"UTF-8"];
- if (self) {
- self.statusCode = 200;
+ [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+ if (data != nil) {
+ [[self client] URLProtocol:self didLoadData:data];
}
- return self;
+ [[self client] URLProtocolDidFinishLoading:self];
}
+@end
+
+@implementation CDVHTTPURLResponse
+@synthesize statusCode;
+
- (NSDictionary*)allHeaderFields
{
return nil;
diff --git a/iPhone/CordovaLib/Classes/CDVCordovaView.m b/iPhone/CordovaLib/Classes/CDVUserAgentUtil.h
index 3dff80a..4de382f 100755
--- a/iPhone/CordovaLib/Classes/CDVCordovaView.m
+++ b/iPhone/CordovaLib/Classes/CDVUserAgentUtil.h
@@ -17,21 +17,11 @@
under the License.
*/
-#import "CDVCordovaView.h"
-
-@implementation CDVCordovaView
-
-- (void)loadRequest:(NSURLRequest*)request
-{
- [super loadRequest:request];
-}
-
-/*
-// Only override drawRect: if you perform custom drawing.
-// An empty implementation adversely affects performance during animation.
-- (void)drawRect:(CGRect)rect {
- // Drawing code.
-}
-*/
+#import <Foundation/Foundation.h>
+@interface CDVUserAgentUtil : NSObject
++ (NSString*)originalUserAgent;
++ (void)acquireLock:(void (^)(NSInteger lockToken))block;
++ (void)releaseLock:(NSInteger*)lockToken;
++ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken;
@end
diff --git a/iPhone/CordovaLib/Classes/CDVUserAgentUtil.m b/iPhone/CordovaLib/Classes/CDVUserAgentUtil.m
new file mode 100755
index 0000000..5c43c51
--- /dev/null
+++ b/iPhone/CordovaLib/Classes/CDVUserAgentUtil.m
@@ -0,0 +1,120 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import "CDVUserAgentUtil.h"
+
+#import <UIKit/UIKit.h>
+
+// #define VerboseLog NSLog
+#define VerboseLog(...) do {} while (0)
+
+static NSString* const kCdvUserAgentKey = @"Cordova-User-Agent";
+static NSString* const kCdvUserAgentVersionKey = @"Cordova-User-Agent-Version";
+
+static NSString* gOriginalUserAgent = nil;
+static NSInteger gNextLockToken = 0;
+static NSInteger gCurrentLockToken = 0;
+static NSMutableArray* gPendingSetUserAgentBlocks = nil;
+
+@implementation CDVUserAgentUtil
+
++ (NSString*)originalUserAgent
+{
+ if (gOriginalUserAgent == nil) {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppLocaleDidChange:)
+ name:NSCurrentLocaleDidChangeNotification object:nil];
+
+ NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
+ NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
+ NSString* localeStr = [[NSLocale currentLocale] localeIdentifier];
+ NSString* systemAndLocale = [NSString stringWithFormat:@"%@ %@", systemVersion, localeStr];
+
+ NSString* cordovaUserAgentVersion = [userDefaults stringForKey:kCdvUserAgentVersionKey];
+ gOriginalUserAgent = [userDefaults stringForKey:kCdvUserAgentKey];
+ BOOL cachedValueIsOld = ![systemAndLocale isEqualToString:cordovaUserAgentVersion];
+
+ if ((gOriginalUserAgent == nil) || cachedValueIsOld) {
+ UIWebView* sampleWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
+ gOriginalUserAgent = [sampleWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
+
+ [userDefaults setObject:gOriginalUserAgent forKey:kCdvUserAgentKey];
+ [userDefaults setObject:systemAndLocale forKey:kCdvUserAgentVersionKey];
+
+ [userDefaults synchronize];
+ }
+ }
+ return gOriginalUserAgent;
+}
+
++ (void)onAppLocaleDidChange:(NSNotification*)notification
+{
+ // TODO: We should figure out how to update the user-agent of existing UIWebViews when this happens.
+ // Maybe use the PDF bug (noted in setUserAgent:).
+ gOriginalUserAgent = nil;
+}
+
++ (void)acquireLock:(void (^)(NSInteger lockToken))block
+{
+ if (gCurrentLockToken == 0) {
+ gCurrentLockToken = ++gNextLockToken;
+ VerboseLog(@"Gave lock %d", gCurrentLockToken);
+ block(gCurrentLockToken);
+ } else {
+ if (gPendingSetUserAgentBlocks == nil) {
+ gPendingSetUserAgentBlocks = [[NSMutableArray alloc] initWithCapacity:4];
+ }
+ VerboseLog(@"Waiting for lock");
+ [gPendingSetUserAgentBlocks addObject:block];
+ }
+}
+
++ (void)releaseLock:(NSInteger*)lockToken
+{
+ if (*lockToken == 0) {
+ return;
+ }
+ NSAssert(gCurrentLockToken == *lockToken, @"Got token %d, expected %d", *lockToken, gCurrentLockToken);
+
+ VerboseLog(@"Released lock %d", *lockToken);
+ if ([gPendingSetUserAgentBlocks count] > 0) {
+ void (^block)() = [gPendingSetUserAgentBlocks objectAtIndex:0];
+ [gPendingSetUserAgentBlocks removeObjectAtIndex:0];
+ gCurrentLockToken = ++gNextLockToken;
+ NSLog (@"Gave lock %d", gCurrentLockToken);
+ block(gCurrentLockToken);
+ } else {
+ gCurrentLockToken = 0;
+ }
+ *lockToken = 0;
+}
+
++ (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken
+{
+ NSAssert(gCurrentLockToken == lockToken, @"Got token %d, expected %d", lockToken, gCurrentLockToken);
+ VerboseLog(@"User-Agent set to: %@", value);
+
+ // Setting the UserAgent must occur before a UIWebView is instantiated.
+ // It is read per instantiation, so it does not affect previously created views.
+ // Except! When a PDF is loaded, all currently active UIWebViews reload their
+ // User-Agent from the NSUserDefaults some time after the DidFinishLoad of the PDF bah!
+ NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:value, @"UserAgent", nil];
+ [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
+}
+
+@end
diff --git a/iPhone/CordovaLib/Classes/CDVViewController.h b/iPhone/CordovaLib/Classes/CDVViewController.h
index 8318398..82e22f6 100755
--- a/iPhone/CordovaLib/Classes/CDVViewController.h
+++ b/iPhone/CordovaLib/Classes/CDVViewController.h
@@ -17,9 +17,8 @@
under the License.
*/
-#import "CDVCordovaView.h"
-
-#import "JSONKit.h"
+#import <UIKit/UIKit.h>
+#import <Foundation/NSJSONSerialization.h>
#import "CDVAvailability.h"
#import "CDVInvokedUrlCommand.h"
#import "CDVCommandDelegate.h"
@@ -37,19 +36,15 @@
NSString* _userAgent;
}
-@property (nonatomic, strong) IBOutlet CDVCordovaView* webView;
+@property (nonatomic, strong) IBOutlet UIWebView* webView;
@property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects;
@property (nonatomic, readonly, strong) NSDictionary* pluginsMap;
-@property (nonatomic, readonly, strong) NSDictionary* settings;
+@property (nonatomic, readonly, strong) NSMutableDictionary* settings;
@property (nonatomic, readonly, strong) NSXMLParser* configParser;
@property (nonatomic, readonly, strong) CDVWhitelist* whitelist; // readonly for public
@property (nonatomic, readonly, assign) BOOL loadFromString;
-@property (nonatomic, readwrite, copy)NSString * invokeString CDV_DEPRECATED(2.0, "Use window.handleOpenURL(url instead. It is called when the app is launched through a custom scheme url.");
-
-@property (nonatomic, readwrite, assign) BOOL useSplashScreen;
-@property (nonatomic, readonly, strong) IBOutlet UIActivityIndicatorView* activityView;
-@property (nonatomic, readonly, strong) UIImageView* imageView;
+@property (nonatomic, readwrite, assign) BOOL useSplashScreen CDV_DEPRECATED(2.5, "Add/Remove the SplashScreen plugin instead of setting this property.");
@property (nonatomic, readwrite, copy) NSString* wwwFolderName;
@property (nonatomic, readwrite, copy) NSString* startPage;
@@ -59,11 +54,10 @@
+ (NSDictionary*)getBundlePlist:(NSString*)plistName;
+ (NSString*)applicationDocumentsDirectory;
-+ (NSString*)originalUserAgent;
- (void)printMultitaskingInfo;
- (void)createGapView;
-- (CDVCordovaView*)newCordovaViewWithFrame:(CGRect)bounds;
+- (UIWebView*)newCordovaViewWithFrame:(CGRect)bounds;
- (void)javascriptAlert:(NSString*)text;
- (NSString*)appURLScheme;
diff --git a/iPhone/CordovaLib/Classes/CDVViewController.m b/iPhone/CordovaLib/Classes/CDVViewController.m
index f1b36df..bec716d 100755
--- a/iPhone/CordovaLib/Classes/CDVViewController.m
+++ b/iPhone/CordovaLib/Classes/CDVViewController.m
@@ -22,36 +22,38 @@
#import "CDVCommandQueue.h"
#import "CDVCommandDelegateImpl.h"
#import "CDVConfigParser.h"
+#import "CDVUserAgentUtil.h"
+#import "CDVWebViewDelegate.h"
#define degreesToRadian(x) (M_PI * (x) / 180.0)
-#define CDV_USER_AGENT_KEY @"Cordova-User-Agent"
-#define CDV_USER_AGENT_VERSION_KEY @"Cordova-User-Agent-Version"
-static NSString* gOriginalUserAgent = nil;
-
-@interface CDVViewController ()
+@interface CDVViewController () {
+ NSInteger _userAgentLockToken;
+ CDVWebViewDelegate* _webViewDelegate;
+}
@property (nonatomic, readwrite, strong) NSXMLParser* configParser;
-@property (nonatomic, readwrite, strong) NSDictionary* settings;
+@property (nonatomic, readwrite, strong) NSMutableDictionary* settings;
@property (nonatomic, readwrite, strong) CDVWhitelist* whitelist;
@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects;
+@property (nonatomic, readwrite, strong) NSArray* startupPluginNames;
@property (nonatomic, readwrite, strong) NSDictionary* pluginsMap;
@property (nonatomic, readwrite, strong) NSArray* supportedOrientations;
@property (nonatomic, readwrite, assign) BOOL loadFromString;
-@property (nonatomic, readwrite, strong) IBOutlet UIActivityIndicatorView* activityView;
-@property (nonatomic, readwrite, strong) UIImageView* imageView;
@property (readwrite, assign) BOOL initialized;
+@property (atomic, strong) NSURL* openURL;
+
@end
@implementation CDVViewController
@synthesize webView, supportedOrientations;
-@synthesize pluginObjects, pluginsMap, whitelist;
+@synthesize pluginObjects, pluginsMap, whitelist, startupPluginNames;
@synthesize configParser, settings, loadFromString;
-@synthesize imageView, activityView, useSplashScreen;
-@synthesize wwwFolderName, startPage, invokeString, initialized;
+@synthesize useSplashScreen;
+@synthesize wwwFolderName, startPage, initialized, openURL;
@synthesize commandDelegate = _commandDelegate;
@synthesize commandQueue = _commandQueue;
@@ -60,38 +62,30 @@ static NSString* gOriginalUserAgent = nil;
if ((self != nil) && !self.initialized) {
_commandQueue = [[CDVCommandQueue alloc] initWithViewController:self];
_commandDelegate = [[CDVCommandDelegateImpl alloc] initWithViewController:self];
- [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedOrientationChange)
- name:UIDeviceOrientationDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillTerminate:)
name:UIApplicationWillTerminateNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillResignActive:)
name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:)
name:UIApplicationDidBecomeActiveNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppLocaleDidChange:)
- name:NSCurrentLocaleDidChangeNotification object:nil];
-
- if (IsAtLeastiOSVersion(@"4.0")) {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:)
- name:UIApplicationWillEnterForegroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:)
- name:UIApplicationDidEnterBackgroundNotification object:nil];
- }
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:)
+ name:UIApplicationWillEnterForegroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:)
+ name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil];
// read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist
self.supportedOrientations = [self parseInterfaceOrientations:
[[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]];
- self.wwwFolderName = @"www";
- self.startPage = @"index.html";
-
[self printMultitaskingInfo];
[self printDeprecationNotice];
self.initialized = YES;
// load config.xml settings
[self loadSettings];
+ useSplashScreen = YES;
}
}
@@ -167,12 +161,20 @@ static NSString* gOriginalUserAgent = nil;
[configParser parse];
// Get the plugin dictionary, whitelist and settings from the delegate.
- self.pluginsMap = [delegate.pluginsDict dictionaryWithLowercaseKeys];
+ self.pluginsMap = delegate.pluginsDict;
+ self.startupPluginNames = delegate.startupPluginNames;
self.whitelist = [[CDVWhitelist alloc] initWithArray:delegate.whitelistHosts];
self.settings = delegate.settings;
+ // And the start folder/page.
+ self.wwwFolderName = @"www";
+ self.startPage = delegate.startPage;
+ if (self.startPage == nil) {
+ self.startPage = @"index.html";
+ }
+
// Initialize the plugin objects dict.
- self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:4];
+ self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20];
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
@@ -203,13 +205,14 @@ static NSString* gOriginalUserAgent = nil;
NSString* backupWebStorageType = @"cloud"; // default value
- id backupWebStorage = [self.settings objectForKey:@"BackupWebStorage"];
+ id backupWebStorage = self.settings[@"BackupWebStorage"];
if ([backupWebStorage isKindOfClass:[NSString class]]) {
backupWebStorageType = backupWebStorage;
} else if ([backupWebStorage isKindOfClass:[NSNumber class]]) {
NSLog(@"Deprecated: BackupWebStorage boolean property is a string property now (none, local, cloud). A boolean value of 'true' will be mapped to 'cloud'. Consult the docs: http://docs.cordova.io/en/edge/guide_project-settings_ios_index.md.html#Project%%20Settings%%20for%%20iOS");
backupWebStorageType = [(NSNumber*) backupWebStorage boolValue] ? @"cloud" : @"none";
}
+ self.settings[@"BackupWebStorage"] = backupWebStorageType;
if (IsAtLeastiOSVersion(@"5.1")) {
[CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType];
@@ -244,8 +247,7 @@ static NSString* gOriginalUserAgent = nil;
*/
if (IsAtLeastiOSVersion(@"5.1") && (([backupWebStorageType isEqualToString:@"local"]) ||
([backupWebStorageType isEqualToString:@"cloud"] && !IsAtLeastiOSVersion(@"6.0")))) {
- [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView settings:[NSDictionary dictionaryWithObjectsAndKeys:
- @"backupType", backupWebStorageType, nil]] withClassName:NSStringFromClass([CDVLocalStorage class])];
+ [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass([CDVLocalStorage class])];
}
/*
@@ -305,15 +307,27 @@ static NSString* gOriginalUserAgent = nil;
}
}
- // /////////////////
+ for (NSString* pluginName in self.startupPluginNames) {
+ [self getCommandInstance:pluginName];
+ }
- if (!loadErr) {
- NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
- [self.webView loadRequest:appReq];
- } else {
- NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
- [self.webView loadHTMLString:html baseURL:nil];
+ // TODO: Remove this explicit instantiation once we move to cordova-CLI.
+ if (useSplashScreen) {
+ [self getCommandInstance:@"splashscreen"];
}
+
+ // /////////////////
+ [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
+ _userAgentLockToken = lockToken;
+ [CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
+ if (!loadErr) {
+ NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
+ [self.webView loadRequest:appReq];
+ } else {
+ NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
+ [self.webView loadHTMLString:html baseURL:nil];
+ }
+ }];
}
- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations
@@ -411,55 +425,15 @@ static NSString* gOriginalUserAgent = nil;
return [self.supportedOrientations containsObject:[NSNumber numberWithInt:orientation]];
}
-/**
- Called by UIKit when the device starts to rotate to a new orientation. This fires the \c setOrientation
- method on the Orientation object in JavaScript. Look at the JavaScript documentation for more information.
- */
-- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
-{
- if (!IsAtLeastiOSVersion(@"5.0")) {
- NSString* jsCallback = [NSString stringWithFormat:
- @"window.__defineGetter__('orientation',function(){ return %d; }); \
- cordova.fireWindowEvent('orientationchange');"
- , [self mapIosOrientationToJsOrientation:fromInterfaceOrientation]];
- [self.commandDelegate evalJs:jsCallback];
- }
-}
-
-- (CDVCordovaView*)newCordovaViewWithFrame:(CGRect)bounds
-{
- return [[CDVCordovaView alloc] initWithFrame:bounds];
-}
-
-+ (NSString*)originalUserAgent
+- (UIWebView*)newCordovaViewWithFrame:(CGRect)bounds
{
- if (gOriginalUserAgent == nil) {
- NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
- NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
- NSString* localeStr = [[NSLocale currentLocale] localeIdentifier];
- NSString* systemAndLocale = [NSString stringWithFormat:@"%@ %@", systemVersion, localeStr];
-
- NSString* cordovaUserAgentVersion = [userDefaults stringForKey:CDV_USER_AGENT_VERSION_KEY];
- gOriginalUserAgent = [userDefaults stringForKey:CDV_USER_AGENT_KEY];
- BOOL cachedValueIsOld = ![systemAndLocale isEqualToString:cordovaUserAgentVersion];
-
- if ((gOriginalUserAgent == nil) || cachedValueIsOld) {
- UIWebView* sampleWebView = [[UIWebView alloc] initWithFrame:CGRectZero];
- gOriginalUserAgent = [sampleWebView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
-
- [userDefaults setObject:gOriginalUserAgent forKey:CDV_USER_AGENT_KEY];
- [userDefaults setObject:systemAndLocale forKey:CDV_USER_AGENT_VERSION_KEY];
-
- [userDefaults synchronize];
- }
- }
- return gOriginalUserAgent;
+ return [[UIWebView alloc] initWithFrame:bounds];
}
- (NSString*)userAgent
{
if (_userAgent == nil) {
- NSString* originalUserAgent = [[self class] originalUserAgent];
+ NSString* originalUserAgent = [CDVUserAgentUtil originalUserAgent];
// Use our address as a unique number to append to the User-Agent.
_userAgent = [NSString stringWithFormat:@"%@ (%lld)", originalUserAgent, (long long)self];
}
@@ -473,18 +447,14 @@ static NSString* gOriginalUserAgent = nil;
webViewBounds.origin = self.view.bounds.origin;
if (!self.webView) {
- // setting the UserAgent must occur before the UIWebView is instantiated.
- // This is read per instantiation, so it does not affect the main Cordova UIWebView
- NSDictionary* dict = [[NSDictionary alloc] initWithObjectsAndKeys:self.userAgent, @"UserAgent", nil];
- [[NSUserDefaults standardUserDefaults] registerDefaults:dict];
-
self.webView = [self newCordovaViewWithFrame:webViewBounds];
self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
[self.view addSubview:self.webView];
[self.view sendSubviewToBack:self.webView];
- self.webView.delegate = self;
+ _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self];
+ self.webView.delegate = _webViewDelegate;
// register this viewcontroller with the NSURLProtocol, only after the User-Agent is set
[CDVURLProtocol registerViewController:self];
@@ -523,6 +493,7 @@ static NSString* gOriginalUserAgent = nil;
self.webView.delegate = nil;
self.webView = nil;
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
}
#pragma mark UIWebViewDelegate
@@ -533,43 +504,41 @@ static NSString* gOriginalUserAgent = nil;
*/
- (void)webViewDidStartLoad:(UIWebView*)theWebView
{
+ NSLog(@"Resetting plugins due to page load.");
[_commandQueue resetRequestId];
- [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:nil]];
+ [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:self.webView]];
}
/**
- Called when the webview finishes loading. This stops the activity view and closes the imageview
+ Called when the webview finishes loading. This stops the activity view.
*/
- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
+ NSLog(@"Finished load of: %@", theWebView.request.URL);
+ // It's safe to release the lock even if this is just a sub-frame that's finished loading.
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
+
+ // The .onNativeReady().fire() will work when cordova.js is already loaded.
+ // The _nativeReady = true; is used when this is run before cordova.js is loaded.
+ NSString* nativeReady = @"try{cordova.require('cordova/channel').onNativeReady.fire();}catch(e){window._nativeReady = true;}";
+ // Don't use [commandDelegate evalJs] here since it relies on cordova.js being loaded already.
+ [self.webView stringByEvaluatingJavaScriptFromString:nativeReady];
+
/*
* Hide the Top Activity THROBBER in the Battery Bar
*/
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
- id autoHideSplashScreenValue = [self.settings objectForKey:@"AutoHideSplashScreen"];
- // if value is missing, default to yes
- if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) {
- self.imageView.hidden = YES;
- self.activityView.hidden = YES;
- [self.view.superview bringSubviewToFront:self.webView];
- }
- [self didRotateFromInterfaceOrientation:(UIInterfaceOrientation)[[UIDevice currentDevice] orientation]];
+ [self processOpenUrl];
- // The .onNativeReady().fire() will work when cordova.js is already loaded.
- // The _nativeReady = true; is used when this is run before cordova.js is loaded.
- NSString* nativeReady = @"try{cordova.require('cordova/channel').onNativeReady.fire();}catch(e){window._nativeReady = true;}";
- [self.commandDelegate evalJs:nativeReady];
+ [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:nil]];
}
-- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error
+- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
{
- NSLog(@"Failed to load webpage with error: %@", [error localizedDescription]);
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
- /*
- if ([error code] != NSURLErrorCancelled)
- alert([error localizedDescription]);
- */
+ NSLog(@"Failed to load webpage with error: %@", [error localizedDescription]);
}
- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
@@ -625,8 +594,6 @@ static NSString* gOriginalUserAgent = nil;
* Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview.
*/
else {
- // BOOL isIFrame = ([theWebView.request.mainDocumentURL absoluteString] == nil);
-
if ([self.whitelist schemeIsAllowed:[url scheme]]) {
return [self.whitelist URLIsAllowed:url];
} else {
@@ -677,131 +644,6 @@ static NSString* gOriginalUserAgent = nil;
return basePath;
}
-- (void)showSplashScreen
-{
- CGRect screenBounds = [[UIScreen mainScreen] bounds];
- NSString* launchImageFile = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchImageFile"];
-
- if (launchImageFile == nil) { // fallback if no launch image was specified
- if (CDV_IsIPhone5()) {
- // iPhone 5 or iPod Touch 6th-gen
- launchImageFile = @"Default-568h";
- } else {
- launchImageFile = @"Default";
- }
- }
-
- NSString* orientedLaunchImageFile = nil;
- CGAffineTransform startupImageTransform = CGAffineTransformIdentity;
- UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
- CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
- UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
- UIImage* launchImage = nil;
-
- // default to center of screen as in the original implementation. This will produce the 20px jump
- CGPoint center = CGPointMake((screenBounds.size.width / 2), (screenBounds.size.height / 2));
-
- if (CDV_IsIPad()) {
- if (!UIDeviceOrientationIsValidInterfaceOrientation(deviceOrientation)) {
- deviceOrientation = (UIDeviceOrientation)statusBarOrientation;
- }
-
- switch (deviceOrientation) {
- case UIDeviceOrientationLandscapeLeft: // this is where the home button is on the right (yeah, I know, confusing)
- {
- orientedLaunchImageFile = [NSString stringWithFormat:@"%@-Landscape", launchImageFile];
- startupImageTransform = CGAffineTransformMakeRotation(degreesToRadian(90));
- center.x -= MIN(statusBarFrame.size.width, statusBarFrame.size.height) / 2;
- }
- break;
-
- case UIDeviceOrientationLandscapeRight: // this is where the home button is on the left (yeah, I know, confusing)
- {
- orientedLaunchImageFile = [NSString stringWithFormat:@"%@-Landscape", launchImageFile];
- startupImageTransform = CGAffineTransformMakeRotation(degreesToRadian(-90));
- center.x += MIN(statusBarFrame.size.width, statusBarFrame.size.height) / 2;
- }
- break;
-
- case UIDeviceOrientationPortraitUpsideDown:
- {
- orientedLaunchImageFile = [NSString stringWithFormat:@"%@-Portrait", launchImageFile];
- startupImageTransform = CGAffineTransformMakeRotation(degreesToRadian(180));
- center.y -= MIN(statusBarFrame.size.width, statusBarFrame.size.height) / 2;
- }
- break;
-
- case UIDeviceOrientationPortrait:
- default:
- {
- orientedLaunchImageFile = [NSString stringWithFormat:@"%@-Portrait", launchImageFile];
- startupImageTransform = CGAffineTransformIdentity;
- center.y += MIN(statusBarFrame.size.width, statusBarFrame.size.height) / 2;
- }
- break;
- }
- } else { // not iPad
- orientedLaunchImageFile = launchImageFile;
- }
-
- launchImage = [UIImage imageNamed:[[self class] resolveImageResource:orientedLaunchImageFile]];
- if (launchImage == nil) {
- NSLog(@"WARNING: Splash-screen image '%@' was not found. Orientation: %d, iPad: %d", orientedLaunchImageFile, deviceOrientation, CDV_IsIPad());
- }
-
- self.imageView = [[UIImageView alloc] initWithImage:launchImage];
- self.imageView.tag = 1;
- self.imageView.center = center;
-
- self.imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin);
- [self.imageView setTransform:startupImageTransform];
- [self.view.superview addSubview:self.imageView];
-
- /*
- * The Activity View is the top spinning throbber in the status/battery bar. We init it with the default Grey Style.
- *
- * whiteLarge = UIActivityIndicatorViewStyleWhiteLarge
- * white = UIActivityIndicatorViewStyleWhite
- * gray = UIActivityIndicatorViewStyleGray
- *
- */
- NSString* topActivityIndicator = [self.settings objectForKey:@"TopActivityIndicator"];
- UIActivityIndicatorViewStyle topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
-
- if ([topActivityIndicator isEqualToString:@"whiteLarge"]) {
- topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhiteLarge;
- } else if ([topActivityIndicator isEqualToString:@"white"]) {
- topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhite;
- } else if ([topActivityIndicator isEqualToString:@"gray"]) {
- topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
- }
-
- self.activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle];
- self.activityView.tag = 2;
-
- id showSplashScreenSpinnerValue = [self.settings objectForKey:@"ShowSplashScreenSpinner"];
- // backwards compatibility - if key is missing, default to true
- if ((showSplashScreenSpinnerValue == nil) || [showSplashScreenSpinnerValue boolValue]) {
- [self.view.superview addSubview:self.activityView];
- }
-
- self.activityView.center = self.view.center;
- [self.activityView startAnimating];
-
- [self.view.superview layoutSubviews];
-}
-
-BOOL gSplashScreenShown = NO;
-- (void)receivedOrientationChange
-{
- if (self.imageView == nil) {
- gSplashScreenShown = YES;
- if (self.useSplashScreen) {
- [self showSplashScreen];
- }
- }
-}
-
#pragma mark CordovaCommands
- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className
@@ -815,6 +657,7 @@ BOOL gSplashScreenShown = NO;
}
[self.pluginObjects setObject:plugin forKey:className];
+ [plugin pluginInitialize];
}
/**
@@ -835,16 +678,9 @@ BOOL gSplashScreenShown = NO;
id obj = [self.pluginObjects objectForKey:className];
if (!obj) {
- // attempt to load the settings for this command class
- NSDictionary* classSettings = [self.settings objectForKey:className];
-
- if (classSettings) {
- obj = [[NSClassFromString (className)alloc] initWithWebView:webView settings:classSettings];
- } else {
- obj = [[NSClassFromString (className)alloc] initWithWebView:webView];
- }
+ obj = [[NSClassFromString (className)alloc] initWithWebView:webView];
- if ((obj != nil) && [obj isKindOfClass:[CDVPlugin class]]) {
+ if (obj != nil) {
[self registerPlugin:obj withClassName:className];
} else {
NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName);
@@ -956,9 +792,21 @@ BOOL gSplashScreenShown = NO;
[self.commandDelegate evalJs:@"cordova.fireDocumentEvent('pause', null, true);" scheduledOnRunLoop:NO];
}
-- (void)onAppLocaleDidChange:(NSNotification*)notification
+// ///////////////////////
+
+- (void)handleOpenURL:(NSNotification*)notification
+{
+ self.openURL = notification.object;
+}
+
+- (void)processOpenUrl
{
- gOriginalUserAgent = nil;
+ if (self.openURL) {
+ // calls into javascript global function 'handleOpenURL'
+ NSString* jsString = [NSString stringWithFormat:@"handleOpenURL(\"%@\");", [self.openURL description]];
+ [self.webView stringByEvaluatingJavaScriptFromString:jsString];
+ self.openURL = nil;
+ }
}
// ///////////////////////
@@ -972,9 +820,10 @@ BOOL gSplashScreenShown = NO;
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSCurrentLocaleDidChangeNotification object:nil];
-
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:CDVPluginHandleOpenURLNotification object:nil];
self.webView.delegate = nil;
self.webView = nil;
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
[_commandQueue dispose];
[[self.pluginObjects allValues] makeObjectsPerformSelector:@selector(dispose)];
}
diff --git a/iPhone/CordovaLib/Classes/CDVWebViewDelegate.h b/iPhone/CordovaLib/Classes/CDVWebViewDelegate.h
new file mode 100755
index 0000000..8a89a22
--- /dev/null
+++ b/iPhone/CordovaLib/Classes/CDVWebViewDelegate.h
@@ -0,0 +1,37 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import <UIKit/UIKit.h>
+
+/**
+ * Distinguishes top-level navigations from sub-frame navigations.
+ * shouldStartLoadWithRequest is called for every request, but didStartLoad
+ * and didFinishLoad is called only for top-level navigations.
+ * Relevant bug: CB-2389
+ */
+@interface CDVWebViewDelegate : NSObject <UIWebViewDelegate>{
+ __weak NSObject <UIWebViewDelegate>* _delegate;
+ NSInteger _loadCount;
+ NSInteger _state;
+ NSInteger _curLoadToken;
+}
+
+- (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate;
+
+@end
diff --git a/iPhone/CordovaLib/Classes/CDVWebViewDelegate.m b/iPhone/CordovaLib/Classes/CDVWebViewDelegate.m
new file mode 100755
index 0000000..9ee8186
--- /dev/null
+++ b/iPhone/CordovaLib/Classes/CDVWebViewDelegate.m
@@ -0,0 +1,157 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import "CDVWebViewDelegate.h"
+#import "CDVAvailability.h"
+
+typedef enum {
+ STATE_NORMAL,
+ STATE_SHOULD_LOAD_MISSING,
+ STATE_WAITING_FOR_START,
+ STATE_WAITING_FOR_FINISH
+} State;
+
+@implementation CDVWebViewDelegate
+
+- (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate
+{
+ self = [super init];
+ if (self != nil) {
+ _delegate = delegate;
+ _loadCount = -1;
+ _state = STATE_NORMAL;
+ }
+ return self;
+}
+
+- (BOOL)isPageLoaded:(UIWebView*)webView
+{
+ NSString* readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];
+
+ return [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"];
+}
+
+- (BOOL)isJsLoadTokenSet:(UIWebView*)webView
+{
+ NSString* loadToken = [webView stringByEvaluatingJavaScriptFromString:@"window.__cordovaLoadToken"];
+
+ return [[NSString stringWithFormat:@"%d", _curLoadToken] isEqualToString:loadToken];
+}
+
+- (void)setLoadToken:(UIWebView*)webView
+{
+ _curLoadToken += 1;
+ [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.__cordovaLoadToken=%d", _curLoadToken]];
+}
+
+- (void)pollForPageLoadStart:(UIWebView*)webView
+{
+ if ((_state != STATE_WAITING_FOR_START) && (_state != STATE_SHOULD_LOAD_MISSING)) {
+ return;
+ }
+ if (![self isJsLoadTokenSet:webView]) {
+ _state = STATE_WAITING_FOR_FINISH;
+ [self setLoadToken:webView];
+ [_delegate webViewDidStartLoad:webView];
+ [self pollForPageLoadFinish:webView];
+ }
+}
+
+- (void)pollForPageLoadFinish:(UIWebView*)webView
+{
+ if (_state != STATE_WAITING_FOR_FINISH) {
+ return;
+ }
+ if ([self isPageLoaded:webView]) {
+ _state = STATE_SHOULD_LOAD_MISSING;
+ [_delegate webViewDidFinishLoad:webView];
+ } else {
+ [self performSelector:@selector(pollForPageLoaded) withObject:webView afterDelay:50];
+ }
+}
+
+- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+ BOOL shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
+
+ if (shouldLoad) {
+ BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
+ if (isTopLevelNavigation) {
+ _loadCount = 0;
+ _state = STATE_NORMAL;
+ }
+ }
+ return shouldLoad;
+}
+
+- (void)webViewDidStartLoad:(UIWebView*)webView
+{
+ if (_state == STATE_NORMAL) {
+ if (_loadCount == 0) {
+ [_delegate webViewDidStartLoad:webView];
+ _loadCount += 1;
+ } else if (_loadCount > 0) {
+ _loadCount += 1;
+ } else if (!IsAtLeastiOSVersion(@"6.0")) {
+ // If history.go(-1) is used pre-iOS6, the shouldStartLoadWithRequest function is not called.
+ // Without shouldLoad, we can't distinguish an iframe from a top-level navigation.
+ // We could try to distinguish using [UIWebView canGoForward], but that's too much complexity,
+ // and would work only on the first time it was used.
+
+ // Our work-around is to set a JS variable and poll until it disappears (from a naviagtion).
+ _state = STATE_WAITING_FOR_START;
+ [self setLoadToken:webView];
+ }
+ } else {
+ [self pollForPageLoadStart:webView];
+ [self pollForPageLoadFinish:webView];
+ }
+}
+
+- (void)webViewDidFinishLoad:(UIWebView*)webView
+{
+ if (_state == STATE_NORMAL) {
+ if (_loadCount == 1) {
+ [_delegate webViewDidFinishLoad:webView];
+ _loadCount -= 1;
+ } else if (_loadCount > 1) {
+ _loadCount -= 1;
+ }
+ } else {
+ [self pollForPageLoadStart:webView];
+ [self pollForPageLoadFinish:webView];
+ }
+}
+
+- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error
+{
+ if (_state == STATE_NORMAL) {
+ if (_loadCount == 1) {
+ [_delegate webView:webView didFailLoadWithError:error];
+ _loadCount -= 1;
+ } else if (_loadCount > 1) {
+ _loadCount -= 1;
+ }
+ } else {
+ [self pollForPageLoadStart:webView];
+ [self pollForPageLoadFinish:webView];
+ }
+}
+
+@end
diff --git a/iPhone/CordovaLib/Classes/JSON/JSONKit.h b/iPhone/CordovaLib/Classes/JSON/JSONKit.h
deleted file mode 100755
index 2b245cd..0000000
--- a/iPhone/CordovaLib/Classes/JSON/JSONKit.h
+++ /dev/null
@@ -1,251 +0,0 @@
-//
-// JSONKit.h
-// http://github.com/johnezang/JSONKit
-// Dual licensed under either the terms of the BSD License, or alternatively
-// under the terms of the Apache License, Version 2.0, as specified below.
-//
-
-/*
- Copyright (c) 2011, John Engelhart
-
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the Zang Industries nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-/*
- Copyright 2011 John Engelhart
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-#include <stddef.h>
-#include <stdint.h>
-#include <limits.h>
-#include <TargetConditionals.h>
-#include <AvailabilityMacros.h>
-
-#ifdef __OBJC__
-#import <Foundation/NSArray.h>
-#import <Foundation/NSData.h>
-#import <Foundation/NSDictionary.h>
-#import <Foundation/NSError.h>
-#import <Foundation/NSObjCRuntime.h>
-#import <Foundation/NSString.h>
-#endif // __OBJC__
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-// For Mac OS X < 10.5.
-#ifndef NSINTEGER_DEFINED
-#define NSINTEGER_DEFINED
-#if defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
-typedef long NSInteger;
-typedef unsigned long NSUInteger;
-#define NSIntegerMin LONG_MIN
-#define NSIntegerMax LONG_MAX
-#define NSUIntegerMax ULONG_MAX
-#else // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
-typedef int NSInteger;
-typedef unsigned int NSUInteger;
-#define NSIntegerMin INT_MIN
-#define NSIntegerMax INT_MAX
-#define NSUIntegerMax UINT_MAX
-#endif // defined(__LP64__) || defined(NS_BUILD_32_LIKE_64)
-#endif // NSINTEGER_DEFINED
-
-
-#ifndef _CDVJSONKIT_H_
-#define _CDVJSONKIT_H_
-
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__APPLE_CC__) && (__APPLE_CC__ >= 5465)
-#define CDVJK_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
-#else
-#define CDVJK_DEPRECATED_ATTRIBUTE
-#endif
-
-#define CDVJSONKIT_VERSION_MAJOR 1
-#define CDVJSONKIT_VERSION_MINOR 4
-
-typedef NSUInteger CDVJKFlags;
-
-/*
- CDVJKParseOptionComments : Allow C style // and /_* ... *_/ (without a _, obviously) comments in JSON.
- CDVJKParseOptionUnicodeNewlines : Allow Unicode recommended (?:\r\n|[\n\v\f\r\x85\p{Zl}\p{Zp}]) newlines.
- CDVJKParseOptionLooseUnicode : Normally the decoder will stop with an error at any malformed Unicode.
- This option allows JSON with malformed Unicode to be parsed without reporting an error.
- Any malformed Unicode is replaced with \uFFFD, or "REPLACEMENT CHARACTER".
- */
-
-enum {
- CDVJKParseOptionNone = 0,
- CDVJKParseOptionStrict = 0,
- CDVJKParseOptionComments = (1 << 0),
- CDVJKParseOptionUnicodeNewlines = (1 << 1),
- CDVJKParseOptionLooseUnicode = (1 << 2),
- CDVJKParseOptionPermitTextAfterValidJSON = (1 << 3),
- CDVJKParseOptionValidFlags = (CDVJKParseOptionComments | CDVJKParseOptionUnicodeNewlines | CDVJKParseOptionLooseUnicode | CDVJKParseOptionPermitTextAfterValidJSON),
-};
-typedef CDVJKFlags CDVJKParseOptionFlags;
-
-enum {
- CDVJKSerializeOptionNone = 0,
- CDVJKSerializeOptionPretty = (1 << 0),
- CDVJKSerializeOptionEscapeUnicode = (1 << 1),
- CDVJKSerializeOptionEscapeForwardSlashes = (1 << 4),
- CDVJKSerializeOptionValidFlags = (CDVJKSerializeOptionPretty | CDVJKSerializeOptionEscapeUnicode | CDVJKSerializeOptionEscapeForwardSlashes),
-};
-typedef CDVJKFlags CDVJKSerializeOptionFlags;
-
-#ifdef __OBJC__
-
-typedef struct CDVJKParseState CDVJKParseState; // Opaque internal, private type.
-
-// As a general rule of thumb, if you use a method that doesn't accept a CDVJKParseOptionFlags argument, it defaults to CDVJKParseOptionStrict
-
-@interface CDVJSONDecoder : NSObject {
- CDVJKParseState *parseState;
-}
-+ (id)decoder;
-+ (id)decoderWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags;
-- (id)initWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags;
-- (void)clearCache;
-
-// The parse... methods were deprecated in v1.4 in favor of the v1.4 objectWith... methods.
-- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length CDVJK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length: instead.
-- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error CDVJK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithUTF8String:length:error: instead.
-// The NSData MUST be UTF8 encoded JSON.
-- (id)parseJSONData:(NSData *)jsonData CDVJK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithData: instead.
-- (id)parseJSONData:(NSData *)jsonData error:(NSError **)error CDVJK_DEPRECATED_ATTRIBUTE; // Deprecated in JSONKit v1.4. Use objectWithData:error: instead.
-
-// Methods that return immutable collection objects.
-- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length;
-- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error;
-// The NSData MUST be UTF8 encoded JSON.
-- (id)objectWithData:(NSData *)jsonData;
-- (id)objectWithData:(NSData *)jsonData error:(NSError **)error;
-
-// Methods that return mutable collection objects.
-- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length;
-- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error;
-// The NSData MUST be UTF8 encoded JSON.
-- (id)mutableObjectWithData:(NSData *)jsonData;
-- (id)mutableObjectWithData:(NSData *)jsonData error:(NSError **)error;
-
-@end
-
-////////////
-#pragma mark Deserializing methods
-////////////
-
-@interface NSString (CDVJSONKitDeserializing)
-- (id)cdvjk_objectFromJSONString;
-- (id)cdvjk_objectFromJSONStringWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags;
-- (id)cdvjk_objectFromJSONStringWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags error:(NSError **)error;
-- (id)cdvjk_mutableObjectFromJSONString;
-- (id)cdvjk_mutableObjectFromJSONStringWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags;
-- (id)cdvjk_mutableObjectFromJSONStringWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags error:(NSError **)error;
-@end
-
-@interface NSData (CDVJSONKitDeserializing)
-// The NSData MUST be UTF8 encoded JSON.
-- (id)cdvjk_objectFromJSONData;
-- (id)cdvjk_objectFromJSONDataWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags;
-- (id)cdvjk_objectFromJSONDataWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags error:(NSError **)error;
-- (id)cdvjk_mutableObjectFromJSONData;
-- (id)cdvjk_mutableObjectFromJSONDataWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags;
-- (id)cdvjk_mutableObjectFromJSONDataWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags error:(NSError **)error;
-@end
-
-////////////
-#pragma mark Serializing methods
-////////////
-
-@interface NSString (CDVJSONKitSerializing)
-// Convenience methods for those that need to serialize the receiving NSString (i.e., instead of having to serialize a NSArray with a single NSString, you can "serialize to JSON" just the NSString).
-// Normally, a string that is serialized to JSON has quotation marks surrounding it, which you may or may not want when serializing a single string, and can be controlled with includeQuotes:
-// includeQuotes:YES `a "test"...` -> `"a \"test\"..."`
-// includeQuotes:NO `a "test"...` -> `a \"test\"...`
-- (NSData *)cdvjk_JSONData; // Invokes JSONDataWithOptions:CDVJKSerializeOptionNone includeQuotes:YES
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error;
-- (NSString *)cdvjk_JSONString; // Invokes JSONStringWithOptions:CDVJKSerializeOptionNone includeQuotes:YES
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error;
-@end
-
-@interface NSArray (CDVJSONKitSerializing)
-- (NSData *)cdvjk_JSONData;
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions error:(NSError **)error;
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
-- (NSString *)cdvjk_JSONString;
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions error:(NSError **)error;
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
-@end
-
-@interface NSDictionary (CDVJSONKitSerializing)
-- (NSData *)cdvjk_JSONData;
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions error:(NSError **)error;
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
-- (NSString *)cdvjk_JSONString;
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions error:(NSError **)error;
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
-@end
-
-#ifdef __BLOCKS__
-
-@interface NSArray (CDVJSONKitSerializingBlockAdditions)
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
-@end
-
-@interface NSDictionary (CDVJSONKitSerializingBlockAdditions)
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error;
-@end
-
-#endif
-
-
-#endif // __OBJC__
-
-#endif // _CDVJSONKIT_H_
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
diff --git a/iPhone/CordovaLib/Classes/JSON/JSONKit.m b/iPhone/CordovaLib/Classes/JSON/JSONKit.m
deleted file mode 100755
index 15a7171..0000000
--- a/iPhone/CordovaLib/Classes/JSON/JSONKit.m
+++ /dev/null
@@ -1,3061 +0,0 @@
-//
-// JSONKit.m
-// http://github.com/johnezang/JSONKit
-// Dual licensed under either the terms of the BSD License, or alternatively
-// under the terms of the Apache License, Version 2.0, as specified below.
-//
-
-/*
- Copyright (c) 2011, John Engelhart
-
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- * Neither the name of the Zang Industries nor the names of its
- contributors may be used to endorse or promote products derived from
- this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-/*
- Copyright 2011 John Engelhart
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-*/
-
-
-/*
- Acknowledgments:
-
- The bulk of the UTF8 / UTF32 conversion and verification comes
- from ConvertUTF.[hc]. It has been modified from the original sources.
-
- The original sources were obtained from http://www.unicode.org/.
- However, the web site no longer seems to host the files. Instead,
- the Unicode FAQ http://www.unicode.org/faq//utf_bom.html#gen4
- points to International Components for Unicode (ICU)
- http://site.icu-project.org/ as an example of how to write a UTF
- converter.
-
- The decision to use the ConvertUTF.[ch] code was made to leverage
- "proven" code. Hopefully the local modifications are bug free.
-
- The code in isValidCodePoint() is derived from the ICU code in
- utf.h for the macros U_IS_UNICODE_NONCHAR and U_IS_UNICODE_CHAR.
-
- From the original ConvertUTF.[ch]:
-
- * Copyright 2001-2004 Unicode, Inc.
- *
- * Disclaimer
- *
- * This source code is provided as is by Unicode, Inc. No claims are
- * made as to fitness for any particular purpose. No warranties of any
- * kind are expressed or implied. The recipient agrees to determine
- * applicability of information provided. If this file has been
- * purchased on magnetic or optical media from Unicode, Inc., the
- * sole remedy for any claim will be exchange of defective media
- * within 90 days of receipt.
- *
- * Limitations on Rights to Redistribute This Code
- *
- * Unicode, Inc. hereby grants the right to freely use the information
- * supplied in this file in the creation of products supporting the
- * Unicode Standard, and to make copies of this file in any form
- * for internal or external distribution as long as this notice
- * remains attached.
-
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <assert.h>
-#include <sys/errno.h>
-#include <math.h>
-#include <limits.h>
-#include <objc/runtime.h>
-
-#import "JSONKit.h"
-
-//#include <CoreFoundation/CoreFoundation.h>
-#include <CoreFoundation/CFString.h>
-#include <CoreFoundation/CFArray.h>
-#include <CoreFoundation/CFDictionary.h>
-#include <CoreFoundation/CFNumber.h>
-
-//#import <Foundation/Foundation.h>
-#import <Foundation/NSArray.h>
-#import <Foundation/NSAutoreleasePool.h>
-#import <Foundation/NSData.h>
-#import <Foundation/NSDictionary.h>
-#import <Foundation/NSException.h>
-#import <Foundation/NSNull.h>
-#import <Foundation/NSObjCRuntime.h>
-
-#ifndef __cdv_has_feature
-#define __cdv_has_feature(x) 0
-#endif
-
-#ifdef CDVJK_ENABLE_CF_TRANSFER_OWNERSHIP_CALLBACKS
-#warning As of CDVJSONKit v1.4, CDVJK_ENABLE_CF_TRANSFER_OWNERSHIP_CALLBACKS is no longer required. It is no longer a valid option.
-#endif
-
-#ifdef __OBJC_GC__
-#error CDVJSONKit does not support Objective-C Garbage Collection
-#endif
-
-#if __has_feature(objc_arc)
-#error CDVJSONKit does not support Objective-C Automatic Reference Counting (ARC)
-#endif
-
-// The following checks are really nothing more than sanity checks.
-// CDVJSONKit technically has a few problems from a "strictly C99 conforming" standpoint, though they are of the pedantic nitpicking variety.
-// In practice, though, for the compilers and architectures we can reasonably expect this code to be compiled for, these pedantic nitpicks aren't really a problem.
-// Since we're limited as to what we can do with pre-processor #if checks, these checks are not nearly as through as they should be.
-
-#if (UINT_MAX != 0xffffffffU) || (INT_MIN != (-0x7fffffff-1)) || (ULLONG_MAX != 0xffffffffffffffffULL) || (LLONG_MIN != (-0x7fffffffffffffffLL-1LL))
-#error CDVJSONKit requires the C 'int' and 'long long' types to be 32 and 64 bits respectively.
-#endif
-
-#if !defined(__LP64__) && ((UINT_MAX != ULONG_MAX) || (INT_MAX != LONG_MAX) || (INT_MIN != LONG_MIN) || (WORD_BIT != LONG_BIT))
-#error CDVJSONKit requires the C 'int' and 'long' types to be the same on 32-bit architectures.
-#endif
-
-// Cocoa / Foundation uses NS*Integer as the type for a lot of arguments. We make sure that NS*Integer is something we are expecting and is reasonably compatible with size_t / ssize_t
-
-#if (NSUIntegerMax != ULONG_MAX) || (NSIntegerMax != LONG_MAX) || (NSIntegerMin != LONG_MIN)
-#error CDVJSONKit requires NSInteger and NSUInteger to be the same size as the C 'long' type.
-#endif
-
-#if (NSUIntegerMax != SIZE_MAX) || (NSIntegerMax != SSIZE_MAX)
-#error CDVJSONKit requires NSInteger and NSUInteger to be the same size as the C 'size_t' type.
-#endif
-
-
-// For DJB hash.
-#define CDVJK_HASH_INIT (1402737925UL)
-
-// Use __builtin_clz() instead of trailingBytesForUTF8[] table lookup.
-#define CDVJK_FAST_TRAILING_BYTES
-
-// CDVJK_CACHE_SLOTS must be a power of 2. Default size is 1024 slots.
-#define CDVJK_CACHE_SLOTS_BITS (10)
-#define CDVJK_CACHE_SLOTS (1UL << CDVJK_CACHE_SLOTS_BITS)
-// CDVJK_CACHE_PROBES is the number of probe attempts.
-#define CDVJK_CACHE_PROBES (4UL)
-// CDVJK_INIT_CACHE_AGE must be (1 << AGE) - 1
-#define CDVJK_INIT_CACHE_AGE (0)
-
-// CDVJK_TOKENBUFFER_SIZE is the default stack size for the temporary buffer used to hold "non-simple" strings (i.e., contains \ escapes)
-#define CDVJK_TOKENBUFFER_SIZE (1024UL * 2UL)
-
-// CDVJK_STACK_OBJS is the default number of spaces reserved on the stack for temporarily storing pointers to Obj-C objects before they can be transferred to a NSArray / NSDictionary.
-#define CDVJK_STACK_OBJS (1024UL * 1UL)
-
-#define CDVJK_JSONBUFFER_SIZE (1024UL * 4UL)
-#define CDVJK_UTF8BUFFER_SIZE (1024UL * 16UL)
-
-#define CDVJK_ENCODE_CACHE_SLOTS (1024UL)
-
-
-#if defined (__GNUC__) && (__GNUC__ >= 4)
-#define CDVJK_ATTRIBUTES(attr, ...) __attribute__((attr, ##__VA_ARGS__))
-#define CDVJK_EXPECTED(cond, expect) __builtin_expect((long)(cond), (expect))
-#define CDVJK_EXPECT_T(cond) CDVJK_EXPECTED(cond, 1U)
-#define CDVJK_EXPECT_F(cond) CDVJK_EXPECTED(cond, 0U)
-#define CDVJK_PREFETCH(ptr) __builtin_prefetch(ptr)
-#else // defined (__GNUC__) && (__GNUC__ >= 4)
-#define CDVJK_ATTRIBUTES(attr, ...)
-#define CDVJK_EXPECTED(cond, expect) (cond)
-#define CDVJK_EXPECT_T(cond) (cond)
-#define CDVJK_EXPECT_F(cond) (cond)
-#define CDVJK_PREFETCH(ptr)
-#endif // defined (__GNUC__) && (__GNUC__ >= 4)
-
-#define CDVJK_STATIC_INLINE static __inline__ CDVJK_ATTRIBUTES(always_inline)
-#define CDVJK_ALIGNED(arg) CDVJK_ATTRIBUTES(aligned(arg))
-#define CDVJK_UNUSED_ARG CDVJK_ATTRIBUTES(unused)
-#define CDVJK_WARN_UNUSED CDVJK_ATTRIBUTES(warn_unused_result)
-#define CDVJK_WARN_UNUSED_CONST CDVJK_ATTRIBUTES(warn_unused_result, const)
-#define CDVJK_WARN_UNUSED_PURE CDVJK_ATTRIBUTES(warn_unused_result, pure)
-#define CDVJK_WARN_UNUSED_SENTINEL CDVJK_ATTRIBUTES(warn_unused_result, sentinel)
-#define CDVJK_NONNULL_ARGS(arg, ...) CDVJK_ATTRIBUTES(nonnull(arg, ##__VA_ARGS__))
-#define CDVJK_WARN_UNUSED_NONNULL_ARGS(arg, ...) CDVJK_ATTRIBUTES(warn_unused_result, nonnull(arg, ##__VA_ARGS__))
-#define CDVJK_WARN_UNUSED_CONST_NONNULL_ARGS(arg, ...) CDVJK_ATTRIBUTES(warn_unused_result, const, nonnull(arg, ##__VA_ARGS__))
-#define CDVJK_WARN_UNUSED_PURE_NONNULL_ARGS(arg, ...) CDVJK_ATTRIBUTES(warn_unused_result, pure, nonnull(arg, ##__VA_ARGS__))
-
-#if defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
-#define CDVJK_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) CDVJK_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__), alloc_size(as))
-#else // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
-#define CDVJK_ALLOC_SIZE_NON_NULL_ARGS_WARN_UNUSED(as, nn, ...) CDVJK_ATTRIBUTES(warn_unused_result, nonnull(nn, ##__VA_ARGS__))
-#endif // defined (__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3)
-
-
-@class CDVJKArray, CDVJKDictionaryEnumerator, CDVJKDictionary;
-
-enum {
- CDVJSONNumberStateStart = 0,
- CDVJSONNumberStateFinished = 1,
- CDVJSONNumberStateError = 2,
- CDVJSONNumberStateWholeNumberStart = 3,
- CDVJSONNumberStateWholeNumberMinus = 4,
- CDVJSONNumberStateWholeNumberZero = 5,
- CDVJSONNumberStateWholeNumber = 6,
- CDVJSONNumberStatePeriod = 7,
- CDVJSONNumberStateFractionalNumberStart = 8,
- CDVJSONNumberStateFractionalNumber = 9,
- CDVJSONNumberStateExponentStart = 10,
- CDVJSONNumberStateExponentPlusMinus = 11,
- CDVJSONNumberStateExponent = 12,
-};
-
-enum {
- CDVJSONStringStateStart = 0,
- CDVJSONStringStateParsing = 1,
- CDVJSONStringStateFinished = 2,
- CDVJSONStringStateError = 3,
- CDVJSONStringStateEscape = 4,
- CDVJSONStringStateEscapedUnicode1 = 5,
- CDVJSONStringStateEscapedUnicode2 = 6,
- CDVJSONStringStateEscapedUnicode3 = 7,
- CDVJSONStringStateEscapedUnicode4 = 8,
- CDVJSONStringStateEscapedUnicodeSurrogate1 = 9,
- CDVJSONStringStateEscapedUnicodeSurrogate2 = 10,
- CDVJSONStringStateEscapedUnicodeSurrogate3 = 11,
- CDVJSONStringStateEscapedUnicodeSurrogate4 = 12,
- CDVJSONStringStateEscapedNeedEscapeForSurrogate = 13,
- CDVJSONStringStateEscapedNeedEscapedUForSurrogate = 14,
-};
-
-enum {
- CDVJKParseAcceptValue = (1 << 0),
- CDVJKParseAcceptComma = (1 << 1),
- CDVJKParseAcceptEnd = (1 << 2),
- CDVJKParseAcceptValueOrEnd = (CDVJKParseAcceptValue | CDVJKParseAcceptEnd),
- CDVJKParseAcceptCommaOrEnd = (CDVJKParseAcceptComma | CDVJKParseAcceptEnd),
-};
-
-enum {
- CDVJKClassUnknown = 0,
- CDVJKClassString = 1,
- CDVJKClassNumber = 2,
- CDVJKClassArray = 3,
- CDVJKClassDictionary = 4,
- CDVJKClassNull = 5,
-};
-
-enum {
- CDVJKManagedBufferOnStack = 1,
- CDVJKManagedBufferOnHeap = 2,
- CDVJKManagedBufferLocationMask = (0x3),
- CDVJKManagedBufferLocationShift = (0),
-
- CDVJKManagedBufferMustFree = (1 << 2),
-};
-typedef CDVJKFlags CDVJKManagedBufferFlags;
-
-enum {
- CDVJKObjectStackOnStack = 1,
- CDVJKObjectStackOnHeap = 2,
- CDVJKObjectStackLocationMask = (0x3),
- CDVJKObjectStackLocationShift = (0),
-
- CDVJKObjectStackMustFree = (1 << 2),
-};
-typedef CDVJKFlags CDVJKObjectStackFlags;
-
-enum {
- CDVJKTokenTypeInvalid = 0,
- CDVJKTokenTypeNumber = 1,
- CDVJKTokenTypeString = 2,
- CDVJKTokenTypeObjectBegin = 3,
- CDVJKTokenTypeObjectEnd = 4,
- CDVJKTokenTypeArrayBegin = 5,
- CDVJKTokenTypeArrayEnd = 6,
- CDVJKTokenTypeSeparator = 7,
- CDVJKTokenTypeComma = 8,
- CDVJKTokenTypeTrue = 9,
- CDVJKTokenTypeFalse = 10,
- CDVJKTokenTypeNull = 11,
- CDVJKTokenTypeWhiteSpace = 12,
-};
-typedef NSUInteger CDVJKTokenType;
-
-// These are prime numbers to assist with hash slot probing.
-enum {
- CDVJKValueTypeNone = 0,
- CDVJKValueTypeString = 5,
- CDVJKValueTypeLongLong = 7,
- CDVJKValueTypeUnsignedLongLong = 11,
- CDVJKValueTypeDouble = 13,
-};
-typedef NSUInteger CDVJKValueType;
-
-enum {
- CDVJKEncodeOptionAsData = 1,
- CDVJKEncodeOptionAsString = 2,
- CDVJKEncodeOptionAsTypeMask = 0x7,
- CDVJKEncodeOptionCollectionObj = (1 << 3),
- CDVJKEncodeOptionStringObj = (1 << 4),
- CDVJKEncodeOptionStringObjTrimQuotes = (1 << 5),
-
-};
-typedef NSUInteger CDVJKEncodeOptionType;
-
-typedef NSUInteger CDVJKHash;
-
-typedef struct CDVJKTokenCacheItem CDVJKTokenCacheItem;
-typedef struct CDVJKTokenCache CDVJKTokenCache;
-typedef struct CDVJKTokenValue CDVJKTokenValue;
-typedef struct CDVJKParseToken CDVJKParseToken;
-typedef struct CDVJKPtrRange CDVJKPtrRange;
-typedef struct CDVJKObjectStack CDVJKObjectStack;
-typedef struct CDVJKBuffer CDVJKBuffer;
-typedef struct CDVJKConstBuffer CDVJKConstBuffer;
-typedef struct CDVJKConstPtrRange CDVJKConstPtrRange;
-typedef struct CDVJKRange CDVJKRange;
-typedef struct CDVJKManagedBuffer CDVJKManagedBuffer;
-typedef struct CDVJKFastClassLookup CDVJKFastClassLookup;
-typedef struct CDVJKEncodeCache CDVJKEncodeCache;
-typedef struct CDVJKEncodeState CDVJKEncodeState;
-typedef struct CDVJKObjCImpCache CDVJKObjCImpCache;
-typedef struct CDVJKHashTableEntry CDVJKHashTableEntry;
-
-typedef id (*NSNumberAllocImp)(id receiver, SEL selector);
-typedef id (*NSNumberInitWithUnsignedLongLongImp)(id receiver, SEL selector, unsigned long long value);
-typedef id (*CDVJKClassFormatterIMP)(id receiver, SEL selector, id object);
-#ifdef __BLOCKS__
-typedef id (^CDVJKClassFormatterBlock)(id formatObject);
-#endif
-
-
-struct CDVJKPtrRange {
- unsigned char *ptr;
- size_t length;
-};
-
-struct CDVJKConstPtrRange {
- const unsigned char *ptr;
- size_t length;
-};
-
-struct CDVJKRange {
- size_t location, length;
-};
-
-struct CDVJKManagedBuffer {
- CDVJKPtrRange bytes;
- CDVJKManagedBufferFlags flags;
- size_t roundSizeUpToMultipleOf;
-};
-
-struct CDVJKObjectStack {
- void **objects, **keys;
- CFHashCode *cfHashes;
- size_t count, index, roundSizeUpToMultipleOf;
- CDVJKObjectStackFlags flags;
-};
-
-struct CDVJKBuffer {
- CDVJKPtrRange bytes;
-};
-
-struct CDVJKConstBuffer {
- CDVJKConstPtrRange bytes;
-};
-
-struct CDVJKTokenValue {
- CDVJKConstPtrRange ptrRange;
- CDVJKValueType type;
- CDVJKHash hash;
- union {
- long long longLongValue;
- unsigned long long unsignedLongLongValue;
- double doubleValue;
- } number;
- CDVJKTokenCacheItem *cacheItem;
-};
-
-struct CDVJKParseToken {
- CDVJKConstPtrRange tokenPtrRange;
- CDVJKTokenType type;
- CDVJKTokenValue value;
- CDVJKManagedBuffer tokenBuffer;
-};
-
-struct CDVJKTokenCacheItem {
- void *object;
- CDVJKHash hash;
- CFHashCode cfHash;
- size_t size;
- unsigned char *bytes;
- CDVJKValueType type;
-};
-
-struct CDVJKTokenCache {
- CDVJKTokenCacheItem *items;
- size_t count;
- unsigned int prng_lfsr;
- unsigned char age[CDVJK_CACHE_SLOTS];
-};
-
-struct CDVJKObjCImpCache {
- Class NSNumberClass;
- NSNumberAllocImp NSNumberAlloc;
- NSNumberInitWithUnsignedLongLongImp NSNumberInitWithUnsignedLongLong;
-};
-
-struct CDVJKParseState {
- CDVJKParseOptionFlags parseOptionFlags;
- CDVJKConstBuffer stringBuffer;
- size_t atIndex, lineNumber, lineStartIndex;
- size_t prev_atIndex, prev_lineNumber, prev_lineStartIndex;
- CDVJKParseToken token;
- CDVJKObjectStack objectStack;
- CDVJKTokenCache cache;
- CDVJKObjCImpCache objCImpCache;
- NSError *error;
- int errorIsPrev;
- BOOL mutableCollections;
-};
-
-struct CDVJKFastClassLookup {
- void *stringClass;
- void *numberClass;
- void *arrayClass;
- void *dictionaryClass;
- void *nullClass;
-};
-
-struct CDVJKEncodeCache {
- id object;
- size_t offset;
- size_t length;
-};
-
-struct CDVJKEncodeState {
- CDVJKManagedBuffer utf8ConversionBuffer;
- CDVJKManagedBuffer stringBuffer;
- size_t atIndex;
- CDVJKFastClassLookup fastClassLookup;
- CDVJKEncodeCache cache[CDVJK_ENCODE_CACHE_SLOTS];
- CDVJKSerializeOptionFlags serializeOptionFlags;
- CDVJKEncodeOptionType encodeOption;
- size_t depth;
- NSError *error;
- id classFormatterDelegate;
- SEL classFormatterSelector;
- CDVJKClassFormatterIMP classFormatterIMP;
-#ifdef __BLOCKS__
- CDVJKClassFormatterBlock classFormatterBlock;
-#endif
-};
-
-// This is a CDVJSONKit private class.
-@interface CDVJKSerializer : NSObject {
- CDVJKEncodeState *encodeState;
-}
-
-#ifdef __BLOCKS__
-#define CDVJKSERIALIZER_BLOCKS_PROTO id(^)(id object)
-#else
-#define CDVJKSERIALIZER_BLOCKS_PROTO id
-#endif
-
-+ (id)serializeObject:(id)object options:(CDVJKSerializeOptionFlags)optionFlags encodeOption:(CDVJKEncodeOptionType)encodeOption block:(CDVJKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
-- (id)serializeObject:(id)object options:(CDVJKSerializeOptionFlags)optionFlags encodeOption:(CDVJKEncodeOptionType)encodeOption block:(CDVJKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error;
-- (void)releaseState;
-
-@end
-
-struct CDVJKHashTableEntry {
- NSUInteger keyHash;
- id key, object;
-};
-
-
-typedef uint32_t UTF32; /* at least 32 bits */
-typedef uint16_t UTF16; /* at least 16 bits */
-typedef uint8_t UTF8; /* typically 8 bits */
-
-typedef enum {
- conversionOK, /* conversion successful */
- sourceExhausted, /* partial character in source, but hit end */
- targetExhausted, /* insuff. room in target for conversion */
- sourceIllegal /* source sequence is illegal/malformed */
-} CDV_ConversionResult;
-
-#define CDV_UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
-#define CDV_UNI_MAX_BMP (UTF32)0x0000FFFF
-#define CDV_UNI_MAX_UTF16 (UTF32)0x0010FFFF
-#define CDV_UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
-#define CDV_UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
-#define CDV_UNI_SUR_HIGH_START (UTF32)0xD800
-#define CDV_UNI_SUR_HIGH_END (UTF32)0xDBFF
-#define CDV_UNI_SUR_LOW_START (UTF32)0xDC00
-#define CDV_UNI_SUR_LOW_END (UTF32)0xDFFF
-
-
-#if !defined(CDVJK_FAST_TRAILING_BYTES)
-static const char trailingBytesForUTF8[256] = {
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
- 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
- 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
-};
-#endif
-
-static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
-static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
-
-#define CDVJK_AT_STRING_PTR(x) (&((x)->stringBuffer.bytes.ptr[(x)->atIndex]))
-#define CDVJK_END_STRING_PTR(x) (&((x)->stringBuffer.bytes.ptr[(x)->stringBuffer.bytes.length]))
-
-
-static CDVJKArray *_CDVJKArrayCreate(id *objects, NSUInteger count, BOOL mutableCollection);
-static void _CDVJKArrayInsertObjectAtIndex(CDVJKArray *array, id newObject, NSUInteger objectIndex);
-static void _CDVJKArrayReplaceObjectAtIndexWithObject(CDVJKArray *array, NSUInteger objectIndex, id newObject);
-static void _CDVJKArrayRemoveObjectAtIndex(CDVJKArray *array, NSUInteger objectIndex);
-
-
-static NSUInteger _CDVJKDictionaryCapacityForCount(NSUInteger count);
-static CDVJKDictionary *_CDVJKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection);
-static CDVJKHashTableEntry *_CDVJKDictionaryHashEntry(CDVJKDictionary *dictionary);
-static NSUInteger _CDVJKDictionaryCapacity(CDVJKDictionary *dictionary);
-static void _CDVJKDictionaryResizeIfNeccessary(CDVJKDictionary *dictionary);
-static void _CDVJKDictionaryRemoveObjectWithEntry(CDVJKDictionary *dictionary, CDVJKHashTableEntry *entry);
-static void _CDVJKDictionaryAddObject(CDVJKDictionary *dictionary, NSUInteger keyHash, id key, id object);
-static CDVJKHashTableEntry *_CDVJKDictionaryHashTableEntryForKey(CDVJKDictionary *dictionary, id aKey);
-
-
-static void _CDVJSONDecoderCleanup(CDVJSONDecoder *decoder);
-
-static id _CDVNSStringObjectFromJSONString(NSString *jsonString, CDVJKParseOptionFlags parseOptionFlags, NSError **error, BOOL mutableCollection);
-
-
-static void cdvjk_managedBuffer_release(CDVJKManagedBuffer *managedBuffer);
-static void cdvjk_managedBuffer_setToStackBuffer(CDVJKManagedBuffer *managedBuffer, unsigned char *ptr, size_t length);
-static unsigned char *cdvjk_managedBuffer_resize(CDVJKManagedBuffer *managedBuffer, size_t newSize);
-static void cdvjk_objectStack_release(CDVJKObjectStack *objectStack);
-static void cdvjk_objectStack_setToStackBuffer(CDVJKObjectStack *objectStack, void **objects, void **keys, CFHashCode *cfHashes, size_t count);
-static int cdvjk_objectStack_resize(CDVJKObjectStack *objectStack, size_t newCount);
-
-static void cdvjk_error(CDVJKParseState *parseState, NSString *format, ...);
-static int cdvjk_parse_string(CDVJKParseState *parseState);
-static int cdvjk_parse_number(CDVJKParseState *parseState);
-static size_t cdvjk_parse_is_newline(CDVJKParseState *parseState, const unsigned char *atCharacterPtr);
-CDVJK_STATIC_INLINE int cdvjk_parse_skip_newline(CDVJKParseState *parseState);
-CDVJK_STATIC_INLINE void cdvjk_parse_skip_whitespace(CDVJKParseState *parseState);
-static int cdvjk_parse_next_token(CDVJKParseState *parseState);
-static void cdvjk_error_parse_accept_or3(CDVJKParseState *parseState, int state, NSString *or1String, NSString *or2String, NSString *or3String);
-static void *cdvjk_create_dictionary(CDVJKParseState *parseState, size_t startingObjectIndex);
-static void *cdvjk_parse_dictionary(CDVJKParseState *parseState);
-static void *cdvjk_parse_array(CDVJKParseState *parseState);
-static void *cdvjk_object_for_token(CDVJKParseState *parseState);
-static void *cdvjk_cachedObjects(CDVJKParseState *parseState);
-CDVJK_STATIC_INLINE void cdvjk_cache_age(CDVJKParseState *parseState);
-CDVJK_STATIC_INLINE void cdvjk_set_parsed_token(CDVJKParseState *parseState, const unsigned char *ptr, size_t length, CDVJKTokenType type, size_t advanceBy);
-
-
-static void cdvjk_encode_error(CDVJKEncodeState *encodeState, NSString *format, ...);
-static int cdvjk_encode_printf(CDVJKEncodeState *encodeState, CDVJKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, ...);
-static int cdvjk_encode_write(CDVJKEncodeState *encodeState, CDVJKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format);
-static int cdvjk_encode_writePrettyPrintWhiteSpace(CDVJKEncodeState *encodeState);
-static int cdvjk_encode_write1slow(CDVJKEncodeState *encodeState, ssize_t depthChange, const char *format);
-static int cdvjk_encode_write1fast(CDVJKEncodeState *encodeState, ssize_t depthChange CDVJK_UNUSED_ARG, const char *format);
-static int cdvjk_encode_writen(CDVJKEncodeState *encodeState, CDVJKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, size_t length);
-CDVJK_STATIC_INLINE CDVJKHash cdvjk_encode_object_hash(void *objectPtr);
-CDVJK_STATIC_INLINE void cdvjk_encode_updateCache(CDVJKEncodeState *encodeState, CDVJKEncodeCache *cacheSlot, size_t startingAtIndex, id object);
-static int cdvjk_encode_add_atom_to_buffer(CDVJKEncodeState *encodeState, void *objectPtr);
-
-#define cdvjk_encode_write1(es, dc, f) (CDVJK_EXPECT_F(_jk_encode_prettyPrint) ? cdvjk_encode_write1slow(es, dc, f) : cdvjk_encode_write1fast(es, dc, f))
-
-
-CDVJK_STATIC_INLINE size_t cdvjk_min(size_t a, size_t b);
-CDVJK_STATIC_INLINE size_t cdvjk_max(size_t a, size_t b);
-CDVJK_STATIC_INLINE CDVJKHash cdvcalculateHash(CDVJKHash currentHash, unsigned char c);
-
-// CDVJSONKit v1.4 used both a CDVJKArray : NSArray and CDVJKMutableArray : NSMutableArray, and the same for the dictionary collection type.
-// However, Louis Gerbarg (via cocoa-dev) pointed out that Cocoa / Core Foundation actually implements only a single class that inherits from the
-// mutable version, and keeps an ivar bit for whether or not that instance is mutable. This means that the immutable versions of the collection
-// classes receive the mutating methods, but this is handled by having those methods throw an exception when the ivar bit is set to immutable.
-// We adopt the same strategy here. It's both cleaner and gets rid of the method swizzling hackery used in CDVJSONKit v1.4.
-
-
-// This is a workaround for issue #23 https://github.com/johnezang/JSONKit/pull/23
-// Basically, there seem to be a problem with using +load in static libraries on iOS. However, __attribute__ ((constructor)) does work correctly.
-// Since we do not require anything "special" that +load provides, and we can accomplish the same thing using __attribute__ ((constructor)), the +load logic was moved here.
-
-static Class _CDVJKArrayClass = NULL;
-static size_t _CDVJKArrayInstanceSize = 0UL;
-static Class _CDVJKDictionaryClass = NULL;
-static size_t _CDVJKDictionaryInstanceSize = 0UL;
-
-// For CDVJSONDecoder...
-static Class _CDVjk_NSNumberClass = NULL;
-static NSNumberAllocImp _CDVjk_NSNumberAllocImp = NULL;
-static NSNumberInitWithUnsignedLongLongImp _CDVjk_NSNumberInitWithUnsignedLongLongImp = NULL;
-
-extern void cdvjk_collectionClassLoadTimeInitialization(void) __attribute__ ((constructor));
-
-void cdvjk_collectionClassLoadTimeInitialization(void) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Though technically not required, the run time environment at load time initialization may be less than ideal.
-
- _CDVJKArrayClass = objc_getClass("CDVJKArray");
- _CDVJKArrayInstanceSize = cdvjk_max(16UL, class_getInstanceSize(_CDVJKArrayClass));
-
- _CDVJKDictionaryClass = objc_getClass("CDVJKDictionary");
- _CDVJKDictionaryInstanceSize = cdvjk_max(16UL, class_getInstanceSize(_CDVJKDictionaryClass));
-
- // For CDVJSONDecoder...
- _CDVjk_NSNumberClass = [NSNumber class];
- _CDVjk_NSNumberAllocImp = (NSNumberAllocImp)[NSNumber methodForSelector:@selector(alloc)];
-
- // Hacktacular. Need to do it this way due to the nature of class clusters.
- id temp_NSNumber = [NSNumber alloc];
- _CDVjk_NSNumberInitWithUnsignedLongLongImp = (NSNumberInitWithUnsignedLongLongImp)[temp_NSNumber methodForSelector:@selector(initWithUnsignedLongLong:)];
- [[temp_NSNumber init] release];
- temp_NSNumber = NULL;
-
- [pool release]; pool = NULL;
-}
-
-
-#pragma mark -
-@interface CDVJKArray : NSMutableArray <NSCopying, NSMutableCopying, NSFastEnumeration> {
- id *objects;
- NSUInteger count, capacity, mutations;
-}
-@end
-
-@implementation CDVJKArray
-
-+ (id)allocWithZone:(NSZone *)zone
-{
-#pragma unused(zone)
- [NSException raise:NSInvalidArgumentException format:@"*** - [%@ %@]: The %@ class is private to CDVJSONKit and should not be used in this fashion.", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromClass([self class])];
- return(NULL);
-}
-
-static CDVJKArray *_CDVJKArrayCreate(id *objects, NSUInteger count, BOOL mutableCollection) {
- NSCParameterAssert((objects != NULL) && (_CDVJKArrayClass != NULL) && (_CDVJKArrayInstanceSize > 0UL));
- CDVJKArray *array = NULL;
- if(CDVJK_EXPECT_T((array = (CDVJKArray *)calloc(1UL, _CDVJKArrayInstanceSize)) != NULL)) { // Directly allocate the CDVJKArray instance via calloc.
- array->isa = _CDVJKArrayClass;
- if((array = [array init]) == NULL) { return(NULL); }
- array->capacity = count;
- array->count = count;
- if(CDVJK_EXPECT_F((array->objects = (id *)malloc(sizeof(id) * array->capacity)) == NULL)) { [array autorelease]; return(NULL); }
- memcpy(array->objects, objects, array->capacity * sizeof(id));
- array->mutations = (mutableCollection == NO) ? 0UL : 1UL;
- }
- return(array);
-}
-
-// Note: The caller is responsible for -retaining the object that is to be added.
-static void _CDVJKArrayInsertObjectAtIndex(CDVJKArray *array, id newObject, NSUInteger objectIndex) {
- NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex <= array->count) && (newObject != NULL));
- if(!((array != NULL) && (array->objects != NULL) && (objectIndex <= array->count) && (newObject != NULL))) { [newObject autorelease]; return; }
- if((array->count + 1UL) >= array->capacity) {
- id *newObjects = NULL;
- if((newObjects = (id *)realloc(array->objects, sizeof(id) * (array->capacity + 16UL))) == NULL) { [NSException raise:NSMallocException format:@"Unable to resize objects array."]; }
- array->objects = newObjects;
- array->capacity += 16UL;
- memset(&array->objects[array->count], 0, sizeof(id) * (array->capacity - array->count));
- }
- array->count++;
- if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex + 1UL], &array->objects[objectIndex], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[objectIndex] = NULL; }
- array->objects[objectIndex] = newObject;
-}
-
-// Note: The caller is responsible for -retaining the object that is to be added.
-static void _CDVJKArrayReplaceObjectAtIndexWithObject(CDVJKArray *array, NSUInteger objectIndex, id newObject) {
- NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL) && (newObject != NULL));
- if(!((array != NULL) && (array->objects != NULL) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL) && (newObject != NULL))) { [newObject autorelease]; return; }
- CFRelease(array->objects[objectIndex]);
- array->objects[objectIndex] = NULL;
- array->objects[objectIndex] = newObject;
-}
-
-static void _CDVJKArrayRemoveObjectAtIndex(CDVJKArray *array, NSUInteger objectIndex) {
- NSCParameterAssert((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL));
- if(!((array != NULL) && (array->objects != NULL) && (array->count > 0UL) && (array->count <= array->capacity) && (objectIndex < array->count) && (array->objects[objectIndex] != NULL))) { return; }
- CFRelease(array->objects[objectIndex]);
- array->objects[objectIndex] = NULL;
- if((objectIndex + 1UL) < array->count) { memmove(&array->objects[objectIndex], &array->objects[objectIndex + 1UL], sizeof(id) * ((array->count - 1UL) - objectIndex)); array->objects[array->count - 1UL] = NULL; }
- array->count--;
-}
-
-- (void)dealloc
-{
- if(CDVJK_EXPECT_T(objects != NULL)) {
- NSUInteger atObject = 0UL;
- for(atObject = 0UL; atObject < count; atObject++) { if(CDVJK_EXPECT_T(objects[atObject] != NULL)) { CFRelease(objects[atObject]); objects[atObject] = NULL; } }
- free(objects); objects = NULL;
- }
-
- [super dealloc];
-}
-
-- (NSUInteger)count
-{
- NSParameterAssert((objects != NULL) && (count <= capacity));
- return(count);
-}
-
-- (void)getObjects:(id *)objectsPtr range:(NSRange)range
-{
- NSParameterAssert((objects != NULL) && (count <= capacity));
- if((objectsPtr == NULL) && (NSMaxRange(range) > 0UL)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: pointer to objects array is NULL but range length is %u", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSMaxRange(range)]; }
- if((range.location > count) || (NSMaxRange(range) > count)) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%u) beyond bounds (%u)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSMaxRange(range), count]; }
- if (objectsPtr != NULL) {
- memcpy(objectsPtr, objects + range.location, range.length * sizeof(id));
- }
-}
-
-- (id)objectAtIndex:(NSUInteger)objectIndex
-{
- if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%u) beyond bounds (%u)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objectIndex, count]; }
- NSParameterAssert((objects != NULL) && (count <= capacity) && (objects[objectIndex] != NULL));
- return(objects[objectIndex]);
-}
-
-- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
-{
- NSParameterAssert((state != NULL) && (stackbuf != NULL) && (len > 0UL) && (objects != NULL) && (count <= capacity));
- if(CDVJK_EXPECT_F(state->state == 0UL)) { state->mutationsPtr = (unsigned long *)&mutations; state->itemsPtr = stackbuf; }
- if(CDVJK_EXPECT_F(state->state >= count)) { return(0UL); }
-
- NSUInteger enumeratedCount = 0UL;
- while(CDVJK_EXPECT_T(enumeratedCount < len) && CDVJK_EXPECT_T(state->state < count)) { NSParameterAssert(objects[state->state] != NULL); stackbuf[enumeratedCount++] = objects[state->state++]; }
-
- return(enumeratedCount);
-}
-
-- (void)insertObject:(id)anObject atIndex:(NSUInteger)objectIndex
-{
- if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- if(objectIndex > count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%u) beyond bounds (%lu)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objectIndex, count + 1UL]; }
-#ifdef __clang_analyzer__
- [anObject retain]; // Stupid clang analyzer... Issue #19.
-#else
- anObject = [anObject retain];
-#endif
- _CDVJKArrayInsertObjectAtIndex(self, anObject, objectIndex);
- mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
-}
-
-- (void)removeObjectAtIndex:(NSUInteger)objectIndex
-{
- if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%u) beyond bounds (%u)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objectIndex, count]; }
- _CDVJKArrayRemoveObjectAtIndex(self, objectIndex);
- mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
-}
-
-- (void)replaceObjectAtIndex:(NSUInteger)objectIndex withObject:(id)anObject
-{
- if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- if(objectIndex >= count) { [NSException raise:NSRangeException format:@"*** -[%@ %@]: index (%u) beyond bounds (%u)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), objectIndex, count]; }
-#ifdef __clang_analyzer__
- [anObject retain]; // Stupid clang analyzer... Issue #19.
-#else
- anObject = [anObject retain];
-#endif
- _CDVJKArrayReplaceObjectAtIndexWithObject(self, objectIndex, anObject);
- mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
- NSParameterAssert((objects != NULL) && (count <= capacity));
- return((mutations == 0UL) ? [self retain] : [(NSArray *)[NSArray allocWithZone:zone] initWithObjects:objects count:count]);
-}
-
-- (id)mutableCopyWithZone:(NSZone *)zone
-{
- NSParameterAssert((objects != NULL) && (count <= capacity));
- return([(NSMutableArray *)[NSMutableArray allocWithZone:zone] initWithObjects:objects count:count]);
-}
-
-@end
-
-
-#pragma mark -
-@interface CDVJKDictionaryEnumerator : NSEnumerator {
- id collection;
- NSUInteger nextObject;
-}
-
-- (id)initWithJKDictionary:(CDVJKDictionary *)initDictionary;
-- (NSArray *)allObjects;
-- (id)nextObject;
-
-@end
-
-@implementation CDVJKDictionaryEnumerator
-
-- (id)initWithJKDictionary:(CDVJKDictionary *)initDictionary
-{
- NSParameterAssert(initDictionary != NULL);
- if((self = [super init]) == NULL) { return(NULL); }
- if((collection = (id)CFRetain(initDictionary)) == NULL) { [self autorelease]; return(NULL); }
- return(self);
-}
-
-- (void)dealloc
-{
- if(collection != NULL) { CFRelease(collection); collection = NULL; }
- [super dealloc];
-}
-
-- (NSArray *)allObjects
-{
- NSParameterAssert(collection != NULL);
- NSUInteger count = [collection count], atObject = 0UL;
- id objects[count];
-
- while((objects[atObject] = [self nextObject]) != NULL) { NSParameterAssert(atObject < count); atObject++; }
-
- return([NSArray arrayWithObjects:objects count:atObject]);
-}
-
-- (id)nextObject
-{
- NSParameterAssert((collection != NULL) && (_CDVJKDictionaryHashEntry(collection) != NULL));
- CDVJKHashTableEntry *entry = _CDVJKDictionaryHashEntry(collection);
- NSUInteger capacity = _CDVJKDictionaryCapacity(collection);
- id returnObject = NULL;
-
- if(entry != NULL) { while((nextObject < capacity) && ((returnObject = entry[nextObject++].key) == NULL)) { /* ... */ } }
-
- return(returnObject);
-}
-
-@end
-
-#pragma mark -
-@interface CDVJKDictionary : NSMutableDictionary <NSCopying, NSMutableCopying, NSFastEnumeration> {
- NSUInteger count, capacity, mutations;
- CDVJKHashTableEntry *entry;
-}
-@end
-
-@implementation CDVJKDictionary
-
-+ (id)allocWithZone:(NSZone *)zone
-{
-#pragma unused(zone)
- [NSException raise:NSInvalidArgumentException format:@"*** - [%@ %@]: The %@ class is private to CDVJSONKit and should not be used in this fashion.", NSStringFromClass([self class]), NSStringFromSelector(_cmd), NSStringFromClass([self class])];
- return(NULL);
-}
-
-// These values are taken from Core Foundation CF-550 CFBasicHash.m. As a bonus, they align very well with our CDVJKHashTableEntry struct too.
-static const NSUInteger cdvjk_dictionaryCapacities[] = {
- 0UL, 3UL, 7UL, 13UL, 23UL, 41UL, 71UL, 127UL, 191UL, 251UL, 383UL, 631UL, 1087UL, 1723UL,
- 2803UL, 4523UL, 7351UL, 11959UL, 19447UL, 31231UL, 50683UL, 81919UL, 132607UL,
- 214519UL, 346607UL, 561109UL, 907759UL, 1468927UL, 2376191UL, 3845119UL,
- 6221311UL, 10066421UL, 16287743UL, 26354171UL, 42641881UL, 68996069UL,
- 111638519UL, 180634607UL, 292272623UL, 472907251UL
-};
-
-static NSUInteger _CDVJKDictionaryCapacityForCount(NSUInteger count) {
- NSUInteger bottom = 0UL, top = sizeof(cdvjk_dictionaryCapacities) / sizeof(NSUInteger), mid = 0UL, tableSize = lround(floor((count) * 1.33));
- while(top > bottom) { mid = (top + bottom) / 2UL; if(cdvjk_dictionaryCapacities[mid] < tableSize) { bottom = mid + 1UL; } else { top = mid; } }
- return(cdvjk_dictionaryCapacities[bottom]);
-}
-
-static void _CDVJKDictionaryResizeIfNeccessary(CDVJKDictionary *dictionary) {
- NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity));
-
- NSUInteger capacityForCount = 0UL;
- if(dictionary->capacity < (capacityForCount = _CDVJKDictionaryCapacityForCount(dictionary->count + 1UL))) { // resize
- NSUInteger oldCapacity = dictionary->capacity;
-#ifndef NS_BLOCK_ASSERTIONS
- NSUInteger oldCount = dictionary->count;
-#endif
- CDVJKHashTableEntry *oldEntry = dictionary->entry;
- if(CDVJK_EXPECT_F((dictionary->entry = (CDVJKHashTableEntry *)calloc(1UL, sizeof(CDVJKHashTableEntry) * capacityForCount)) == NULL)) { [NSException raise:NSMallocException format:@"Unable to allocate memory for hash table."]; }
- dictionary->capacity = capacityForCount;
- dictionary->count = 0UL;
-
- NSUInteger idx = 0UL;
- for(idx = 0UL; idx < oldCapacity; idx++) { if(oldEntry[idx].key != NULL) { _CDVJKDictionaryAddObject(dictionary, oldEntry[idx].keyHash, oldEntry[idx].key, oldEntry[idx].object); oldEntry[idx].keyHash = 0UL; oldEntry[idx].key = NULL; oldEntry[idx].object = NULL; } }
- NSCParameterAssert((oldCount == dictionary->count));
- free(oldEntry); oldEntry = NULL;
- }
-}
-
-static CDVJKDictionary *_CDVJKDictionaryCreate(id *keys, NSUInteger *keyHashes, id *objects, NSUInteger count, BOOL mutableCollection) {
- NSCParameterAssert((keys != NULL) && (keyHashes != NULL) && (objects != NULL) && (_CDVJKDictionaryClass != NULL) && (_CDVJKDictionaryInstanceSize > 0UL));
- CDVJKDictionary *dictionary = NULL;
- if(CDVJK_EXPECT_T((dictionary = (CDVJKDictionary *)calloc(1UL, _CDVJKDictionaryInstanceSize)) != NULL)) { // Directly allocate the CDVJKDictionary instance via calloc.
- dictionary->isa = _CDVJKDictionaryClass;
- if((dictionary = [dictionary init]) == NULL) { return(NULL); }
- dictionary->capacity = _CDVJKDictionaryCapacityForCount(count);
- dictionary->count = 0UL;
-
- if(CDVJK_EXPECT_F((dictionary->entry = (CDVJKHashTableEntry *)calloc(1UL, sizeof(CDVJKHashTableEntry) * dictionary->capacity)) == NULL)) { [dictionary autorelease]; return(NULL); }
-
- NSUInteger idx = 0UL;
- for(idx = 0UL; idx < count; idx++) { _CDVJKDictionaryAddObject(dictionary, keyHashes[idx], keys[idx], objects[idx]); }
-
- dictionary->mutations = (mutableCollection == NO) ? 0UL : 1UL;
- }
- return(dictionary);
-}
-
-- (void)dealloc
-{
- if(CDVJK_EXPECT_T(entry != NULL)) {
- NSUInteger atEntry = 0UL;
- for(atEntry = 0UL; atEntry < capacity; atEntry++) {
- if(CDVJK_EXPECT_T(entry[atEntry].key != NULL)) { CFRelease(entry[atEntry].key); entry[atEntry].key = NULL; }
- if(CDVJK_EXPECT_T(entry[atEntry].object != NULL)) { CFRelease(entry[atEntry].object); entry[atEntry].object = NULL; }
- }
-
- free(entry); entry = NULL;
- }
-
- [super dealloc];
-}
-
-static CDVJKHashTableEntry *_CDVJKDictionaryHashEntry(CDVJKDictionary *dictionary) {
- NSCParameterAssert(dictionary != NULL);
- return(dictionary->entry);
-}
-
-static NSUInteger _CDVJKDictionaryCapacity(CDVJKDictionary *dictionary) {
- NSCParameterAssert(dictionary != NULL);
- return(dictionary->capacity);
-}
-
-static void _CDVJKDictionaryRemoveObjectWithEntry(CDVJKDictionary *dictionary, CDVJKHashTableEntry *entry) {
- NSCParameterAssert((dictionary != NULL) && (entry != NULL) && (entry->key != NULL) && (entry->object != NULL) && (dictionary->count > 0UL) && (dictionary->count <= dictionary->capacity));
- CFRelease(entry->key); entry->key = NULL;
- CFRelease(entry->object); entry->object = NULL;
- entry->keyHash = 0UL;
- dictionary->count--;
- // In order for certain invariants that are used to speed up the search for a particular key, we need to "re-add" all the entries in the hash table following this entry until we hit a NULL entry.
- NSUInteger removeIdx = entry - dictionary->entry, idx = 0UL;
- NSCParameterAssert((removeIdx < dictionary->capacity));
- for(idx = 0UL; idx < dictionary->capacity; idx++) {
- NSUInteger entryIdx = (removeIdx + idx + 1UL) % dictionary->capacity;
- CDVJKHashTableEntry *atEntry = &dictionary->entry[entryIdx];
- if(atEntry->key == NULL) { break; }
- NSUInteger keyHash = atEntry->keyHash;
- id key = atEntry->key, object = atEntry->object;
- NSCParameterAssert(object != NULL);
- atEntry->keyHash = 0UL;
- atEntry->key = NULL;
- atEntry->object = NULL;
- NSUInteger addKeyEntry = keyHash % dictionary->capacity, addIdx = 0UL;
- for(addIdx = 0UL; addIdx < dictionary->capacity; addIdx++) {
- CDVJKHashTableEntry *atAddEntry = &dictionary->entry[((addKeyEntry + addIdx) % dictionary->capacity)];
- if(CDVJK_EXPECT_T(atAddEntry->key == NULL)) { NSCParameterAssert((atAddEntry->keyHash == 0UL) && (atAddEntry->object == NULL)); atAddEntry->key = key; atAddEntry->object = object; atAddEntry->keyHash = keyHash; break; }
- }
- }
-}
-
-static void _CDVJKDictionaryAddObject(CDVJKDictionary *dictionary, NSUInteger keyHash, id key, id object) {
- NSCParameterAssert((dictionary != NULL) && (key != NULL) && (object != NULL) && (dictionary->count < dictionary->capacity) && (dictionary->entry != NULL));
- NSUInteger keyEntry = keyHash % dictionary->capacity, idx = 0UL;
- for(idx = 0UL; idx < dictionary->capacity; idx++) {
- NSUInteger entryIdx = (keyEntry + idx) % dictionary->capacity;
- CDVJKHashTableEntry *atEntry = &dictionary->entry[entryIdx];
- if(CDVJK_EXPECT_F(atEntry->keyHash == keyHash) && CDVJK_EXPECT_T(atEntry->key != NULL) && (CDVJK_EXPECT_F(key == atEntry->key) || CDVJK_EXPECT_F(CFEqual(atEntry->key, key)))) { _CDVJKDictionaryRemoveObjectWithEntry(dictionary, atEntry); }
- if(CDVJK_EXPECT_T(atEntry->key == NULL)) { NSCParameterAssert((atEntry->keyHash == 0UL) && (atEntry->object == NULL)); atEntry->key = key; atEntry->object = object; atEntry->keyHash = keyHash; dictionary->count++; return; }
- }
-
- // We should never get here. If we do, we -release the key / object because it's our responsibility.
- CFRelease(key);
- CFRelease(object);
-}
-
-- (NSUInteger)count
-{
- return(count);
-}
-
-static CDVJKHashTableEntry *_CDVJKDictionaryHashTableEntryForKey(CDVJKDictionary *dictionary, id aKey) {
- NSCParameterAssert((dictionary != NULL) && (dictionary->entry != NULL) && (dictionary->count <= dictionary->capacity));
- if((aKey == NULL) || (dictionary->capacity == 0UL)) { return(NULL); }
- NSUInteger keyHash = CFHash(aKey), keyEntry = (keyHash % dictionary->capacity), idx = 0UL;
- CDVJKHashTableEntry *atEntry = NULL;
- for(idx = 0UL; idx < dictionary->capacity; idx++) {
- atEntry = &dictionary->entry[(keyEntry + idx) % dictionary->capacity];
- if(CDVJK_EXPECT_T(atEntry->keyHash == keyHash) && CDVJK_EXPECT_T(atEntry->key != NULL) && ((atEntry->key == aKey) || CFEqual(atEntry->key, aKey))) { NSCParameterAssert(atEntry->object != NULL); return(atEntry); break; }
- if(CDVJK_EXPECT_F(atEntry->key == NULL)) { NSCParameterAssert(atEntry->object == NULL); return(NULL); break; } // If the key was in the table, we would have found it by now.
- }
- return(NULL);
-}
-
-- (id)objectForKey:(id)aKey
-{
- NSParameterAssert((entry != NULL) && (count <= capacity));
- CDVJKHashTableEntry *entryForKey = _CDVJKDictionaryHashTableEntryForKey(self, aKey);
- return((entryForKey != NULL) ? entryForKey->object : NULL);
-}
-
-- (void)getObjects:(id *)objects andKeys:(id *)keys
-{
- NSParameterAssert((entry != NULL) && (count <= capacity));
- NSUInteger atEntry = 0UL; NSUInteger arrayIdx = 0UL;
- for(atEntry = 0UL; atEntry < capacity; atEntry++) {
- if(CDVJK_EXPECT_T(entry[atEntry].key != NULL)) {
- NSCParameterAssert((entry[atEntry].object != NULL) && (arrayIdx < count));
- if(CDVJK_EXPECT_T(keys != NULL)) { keys[arrayIdx] = entry[atEntry].key; }
- if(CDVJK_EXPECT_T(objects != NULL)) { objects[arrayIdx] = entry[atEntry].object; }
- arrayIdx++;
- }
- }
-}
-
-- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
-{
- NSParameterAssert((state != NULL) && (stackbuf != NULL) && (len > 0UL) && (entry != NULL) && (count <= capacity));
- if(CDVJK_EXPECT_F(state->state == 0UL)) { state->mutationsPtr = (unsigned long *)&mutations; state->itemsPtr = stackbuf; }
- if(CDVJK_EXPECT_F(state->state >= capacity)) { return(0UL); }
-
- NSUInteger enumeratedCount = 0UL;
- while(CDVJK_EXPECT_T(enumeratedCount < len) && CDVJK_EXPECT_T(state->state < capacity)) { if(CDVJK_EXPECT_T(entry[state->state].key != NULL)) { stackbuf[enumeratedCount++] = entry[state->state].key; } state->state++; }
-
- return(enumeratedCount);
-}
-
-- (NSEnumerator *)keyEnumerator
-{
- return([[[CDVJKDictionaryEnumerator alloc] initWithJKDictionary:self] autorelease]);
-}
-
-- (void)setObject:(id)anObject forKey:(id)aKey
-{
- if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- if(anObject == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to insert nil value (key: %@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd), aKey]; }
-
- _CDVJKDictionaryResizeIfNeccessary(self);
-#ifndef __clang_analyzer__
- aKey = [aKey copy]; // Why on earth would clang complain that this -copy "might leak",
- anObject = [anObject retain]; // but this -retain doesn't!?
-#endif // __clang_analyzer__
- _CDVJKDictionaryAddObject(self, CFHash(aKey), aKey, anObject);
- mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
-}
-
-- (void)removeObjectForKey:(id)aKey
-{
- if(mutations == 0UL) { [NSException raise:NSInternalInconsistencyException format:@"*** -[%@ %@]: mutating method sent to immutable object", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- if(aKey == NULL) { [NSException raise:NSInvalidArgumentException format:@"*** -[%@ %@]: attempt to remove nil key", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]; }
- CDVJKHashTableEntry *entryForKey = _CDVJKDictionaryHashTableEntryForKey(self, aKey);
- if(entryForKey != NULL) {
- _CDVJKDictionaryRemoveObjectWithEntry(self, entryForKey);
- mutations = (mutations == NSUIntegerMax) ? 1UL : mutations + 1UL;
- }
-}
-
-- (id)copyWithZone:(NSZone *)zone
-{
- NSParameterAssert((entry != NULL) && (count <= capacity));
- return((mutations == 0UL) ? [self retain] : [[NSDictionary allocWithZone:zone] initWithDictionary:self]);
-}
-
-- (id)mutableCopyWithZone:(NSZone *)zone
-{
- NSParameterAssert((entry != NULL) && (count <= capacity));
- return([[NSMutableDictionary allocWithZone:zone] initWithDictionary:self]);
-}
-
-@end
-
-
-
-#pragma mark -
-
-CDVJK_STATIC_INLINE size_t cdvjk_min(size_t a, size_t b) { return((a < b) ? a : b); }
-CDVJK_STATIC_INLINE size_t cdvjk_max(size_t a, size_t b) { return((a > b) ? a : b); }
-
-CDVJK_STATIC_INLINE CDVJKHash cdvcalculateHash(CDVJKHash currentHash, unsigned char c) { return(((currentHash << 5) + currentHash) + c); }
-
-static void cdvjk_error(CDVJKParseState *parseState, NSString *format, ...) {
- NSCParameterAssert((parseState != NULL) && (format != NULL));
-
- va_list varArgsList;
- va_start(varArgsList, format);
- NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
- va_end(varArgsList);
-
-#if 0
- const unsigned char *lineStart = parseState->stringBuffer.bytes.ptr + parseState->lineStartIndex;
- const unsigned char *lineEnd = lineStart;
- const unsigned char *atCharacterPtr = NULL;
-
- for(atCharacterPtr = lineStart; atCharacterPtr < CDVJK_END_STRING_PTR(parseState); atCharacterPtr++) { lineEnd = atCharacterPtr; if(cdvjk_parse_is_newline(parseState, atCharacterPtr)) { break; } }
-
- NSString *lineString = @"", *carretString = @"";
- if(lineStart < CDVJK_END_STRING_PTR(parseState)) {
- lineString = [[[NSString alloc] initWithBytes:lineStart length:(lineEnd - lineStart) encoding:NSUTF8StringEncoding] autorelease];
- carretString = [NSString stringWithFormat:@"%*.*s^", (int)(parseState->atIndex - parseState->lineStartIndex), (int)(parseState->atIndex - parseState->lineStartIndex), " "];
- }
-#endif
-
- if(parseState->error == NULL) {
- parseState->error = [NSError errorWithDomain:@"CDVJKErrorDomain" code:-1L userInfo:
- [NSDictionary dictionaryWithObjectsAndKeys:
- formatString, NSLocalizedDescriptionKey,
- [NSNumber numberWithUnsignedLong:parseState->atIndex], @"CDVJKAtIndexKey",
- [NSNumber numberWithUnsignedLong:parseState->lineNumber], @"CDVJKLineNumberKey",
- //lineString, @"CDVJKErrorLine0Key",
- //carretString, @"CDVJKErrorLine1Key",
- NULL]];
- }
-}
-
-#pragma mark -
-#pragma mark Buffer and Object Stack management functions
-
-static void cdvjk_managedBuffer_release(CDVJKManagedBuffer *managedBuffer) {
- if((managedBuffer->flags & CDVJKManagedBufferMustFree)) {
- if(managedBuffer->bytes.ptr != NULL) { free(managedBuffer->bytes.ptr); managedBuffer->bytes.ptr = NULL; }
- managedBuffer->flags &= ~CDVJKManagedBufferMustFree;
- }
-
- managedBuffer->bytes.ptr = NULL;
- managedBuffer->bytes.length = 0UL;
- managedBuffer->flags &= ~CDVJKManagedBufferLocationMask;
-}
-
-static void cdvjk_managedBuffer_setToStackBuffer(CDVJKManagedBuffer *managedBuffer, unsigned char *ptr, size_t length) {
- cdvjk_managedBuffer_release(managedBuffer);
- managedBuffer->bytes.ptr = ptr;
- managedBuffer->bytes.length = length;
- managedBuffer->flags = (managedBuffer->flags & ~CDVJKManagedBufferLocationMask) | CDVJKManagedBufferOnStack;
-}
-
-static unsigned char *cdvjk_managedBuffer_resize(CDVJKManagedBuffer *managedBuffer, size_t newSize) {
- size_t roundedUpNewSize = newSize;
-
- if(managedBuffer->roundSizeUpToMultipleOf > 0UL) { roundedUpNewSize = newSize + ((managedBuffer->roundSizeUpToMultipleOf - (newSize % managedBuffer->roundSizeUpToMultipleOf)) % managedBuffer->roundSizeUpToMultipleOf); }
-
- if((roundedUpNewSize != managedBuffer->bytes.length) && (roundedUpNewSize > managedBuffer->bytes.length)) {
- if((managedBuffer->flags & CDVJKManagedBufferLocationMask) == CDVJKManagedBufferOnStack) {
- NSCParameterAssert((managedBuffer->flags & CDVJKManagedBufferMustFree) == 0);
- unsigned char *newBuffer = NULL, *oldBuffer = managedBuffer->bytes.ptr;
-
- if((newBuffer = (unsigned char *)malloc(roundedUpNewSize)) == NULL) { return(NULL); }
- memcpy(newBuffer, oldBuffer, cdvjk_min(managedBuffer->bytes.length, roundedUpNewSize));
- managedBuffer->flags = (managedBuffer->flags & ~CDVJKManagedBufferLocationMask) | (CDVJKManagedBufferOnHeap | CDVJKManagedBufferMustFree);
- managedBuffer->bytes.ptr = newBuffer;
- managedBuffer->bytes.length = roundedUpNewSize;
- } else {
- NSCParameterAssert(((managedBuffer->flags & CDVJKManagedBufferMustFree) != 0) && ((managedBuffer->flags & CDVJKManagedBufferLocationMask) == CDVJKManagedBufferOnHeap));
- if((managedBuffer->bytes.ptr = (unsigned char *)reallocf(managedBuffer->bytes.ptr, roundedUpNewSize)) == NULL) { return(NULL); }
- managedBuffer->bytes.length = roundedUpNewSize;
- }
- }
-
- return(managedBuffer->bytes.ptr);
-}
-
-
-
-static void cdvjk_objectStack_release(CDVJKObjectStack *objectStack) {
- NSCParameterAssert(objectStack != NULL);
-
- NSCParameterAssert(objectStack->index <= objectStack->count);
- size_t atIndex = 0UL;
- for(atIndex = 0UL; atIndex < objectStack->index; atIndex++) {
- if(objectStack->objects[atIndex] != NULL) { CFRelease(objectStack->objects[atIndex]); objectStack->objects[atIndex] = NULL; }
- if(objectStack->keys[atIndex] != NULL) { CFRelease(objectStack->keys[atIndex]); objectStack->keys[atIndex] = NULL; }
- }
- objectStack->index = 0UL;
-
- if(objectStack->flags & CDVJKObjectStackMustFree) {
- NSCParameterAssert((objectStack->flags & CDVJKObjectStackLocationMask) == CDVJKObjectStackOnHeap);
- if(objectStack->objects != NULL) { free(objectStack->objects); objectStack->objects = NULL; }
- if(objectStack->keys != NULL) { free(objectStack->keys); objectStack->keys = NULL; }
- if(objectStack->cfHashes != NULL) { free(objectStack->cfHashes); objectStack->cfHashes = NULL; }
- objectStack->flags &= ~CDVJKObjectStackMustFree;
- }
-
- objectStack->objects = NULL;
- objectStack->keys = NULL;
- objectStack->cfHashes = NULL;
-
- objectStack->count = 0UL;
- objectStack->flags &= ~CDVJKObjectStackLocationMask;
-}
-
-static void cdvjk_objectStack_setToStackBuffer(CDVJKObjectStack *objectStack, void **objects, void **keys, CFHashCode *cfHashes, size_t count) {
- NSCParameterAssert((objectStack != NULL) && (objects != NULL) && (keys != NULL) && (cfHashes != NULL) && (count > 0UL));
- cdvjk_objectStack_release(objectStack);
- objectStack->objects = objects;
- objectStack->keys = keys;
- objectStack->cfHashes = cfHashes;
- objectStack->count = count;
- objectStack->flags = (objectStack->flags & ~CDVJKObjectStackLocationMask) | CDVJKObjectStackOnStack;
-#ifndef NS_BLOCK_ASSERTIONS
- size_t idx;
- for(idx = 0UL; idx < objectStack->count; idx++) { objectStack->objects[idx] = NULL; objectStack->keys[idx] = NULL; objectStack->cfHashes[idx] = 0UL; }
-#endif
-}
-
-static int cdvjk_objectStack_resize(CDVJKObjectStack *objectStack, size_t newCount) {
- size_t roundedUpNewCount = newCount;
- int returnCode = 0;
-
- void **newObjects = NULL, **newKeys = NULL;
- CFHashCode *newCFHashes = NULL;
-
- if(objectStack->roundSizeUpToMultipleOf > 0UL) { roundedUpNewCount = newCount + ((objectStack->roundSizeUpToMultipleOf - (newCount % objectStack->roundSizeUpToMultipleOf)) % objectStack->roundSizeUpToMultipleOf); }
-
- if((roundedUpNewCount != objectStack->count) && (roundedUpNewCount > objectStack->count)) {
- if((objectStack->flags & CDVJKObjectStackLocationMask) == CDVJKObjectStackOnStack) {
- NSCParameterAssert((objectStack->flags & CDVJKObjectStackMustFree) == 0);
-
- if((newObjects = (void ** )calloc(1UL, roundedUpNewCount * sizeof(void * ))) == NULL) { returnCode = 1; goto errorExit; }
- memcpy(newObjects, objectStack->objects, cdvjk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
- if((newKeys = (void ** )calloc(1UL, roundedUpNewCount * sizeof(void * ))) == NULL) { returnCode = 1; goto errorExit; }
- memcpy(newKeys, objectStack->keys, cdvjk_min(objectStack->count, roundedUpNewCount) * sizeof(void *));
-
- if((newCFHashes = (CFHashCode *)calloc(1UL, roundedUpNewCount * sizeof(CFHashCode))) == NULL) { returnCode = 1; goto errorExit; }
- memcpy(newCFHashes, objectStack->cfHashes, cdvjk_min(objectStack->count, roundedUpNewCount) * sizeof(CFHashCode));
-
- objectStack->flags = (objectStack->flags & ~CDVJKObjectStackLocationMask) | (CDVJKObjectStackOnHeap | CDVJKObjectStackMustFree);
- objectStack->objects = newObjects; newObjects = NULL;
- objectStack->keys = newKeys; newKeys = NULL;
- objectStack->cfHashes = newCFHashes; newCFHashes = NULL;
- objectStack->count = roundedUpNewCount;
- } else {
- NSCParameterAssert(((objectStack->flags & CDVJKObjectStackMustFree) != 0) && ((objectStack->flags & CDVJKObjectStackLocationMask) == CDVJKObjectStackOnHeap));
- if((newObjects = (void ** )realloc(objectStack->objects, roundedUpNewCount * sizeof(void * ))) != NULL) { objectStack->objects = newObjects; newObjects = NULL; } else { returnCode = 1; goto errorExit; }
- if((newKeys = (void ** )realloc(objectStack->keys, roundedUpNewCount * sizeof(void * ))) != NULL) { objectStack->keys = newKeys; newKeys = NULL; } else { returnCode = 1; goto errorExit; }
- if((newCFHashes = (CFHashCode *)realloc(objectStack->cfHashes, roundedUpNewCount * sizeof(CFHashCode))) != NULL) { objectStack->cfHashes = newCFHashes; newCFHashes = NULL; } else { returnCode = 1; goto errorExit; }
-
-#ifndef NS_BLOCK_ASSERTIONS
- size_t idx;
- for(idx = objectStack->count; idx < roundedUpNewCount; idx++) { objectStack->objects[idx] = NULL; objectStack->keys[idx] = NULL; objectStack->cfHashes[idx] = 0UL; }
-#endif
- objectStack->count = roundedUpNewCount;
- }
- }
-
- errorExit:
- if(newObjects != NULL) { free(newObjects); newObjects = NULL; }
- if(newKeys != NULL) { free(newKeys); newKeys = NULL; }
- if(newCFHashes != NULL) { free(newCFHashes); newCFHashes = NULL; }
-
- return(returnCode);
-}
-
-////////////
-#pragma mark -
-#pragma mark Unicode related functions
-
-CDVJK_STATIC_INLINE CDV_ConversionResult cdvisValidCodePoint(UTF32 *u32CodePoint) {
- CDV_ConversionResult result = conversionOK;
- UTF32 ch = *u32CodePoint;
-
- if(CDVJK_EXPECT_F(ch >= CDV_UNI_SUR_HIGH_START) && (CDVJK_EXPECT_T(ch <= CDV_UNI_SUR_LOW_END))) { result = sourceIllegal; ch = CDV_UNI_REPLACEMENT_CHAR; goto finished; }
- if(CDVJK_EXPECT_F(ch >= 0xFDD0U) && (CDVJK_EXPECT_F(ch <= 0xFDEFU) || CDVJK_EXPECT_F((ch & 0xFFFEU) == 0xFFFEU)) && CDVJK_EXPECT_T(ch <= 0x10FFFFU)) { result = sourceIllegal; ch = CDV_UNI_REPLACEMENT_CHAR; goto finished; }
- if(CDVJK_EXPECT_F(ch == 0U)) { result = sourceIllegal; ch = CDV_UNI_REPLACEMENT_CHAR; goto finished; }
-
- finished:
- *u32CodePoint = ch;
- return(result);
-}
-
-
-static int cdvisLegalUTF8(const UTF8 *source, size_t length) {
- const UTF8 *srcptr = source + length;
- UTF8 a;
-
- switch(length) {
- default: return(0); // Everything else falls through when "true"...
- case 4: if(CDVJK_EXPECT_F(((a = (*--srcptr)) < 0x80) || (a > 0xBF))) { return(0); }
- case 3: if(CDVJK_EXPECT_F(((a = (*--srcptr)) < 0x80) || (a > 0xBF))) { return(0); }
- case 2: if(CDVJK_EXPECT_F( (a = (*--srcptr)) > 0xBF )) { return(0); }
-
- switch(*source) { // no fall-through in this inner switch
- case 0xE0: if(CDVJK_EXPECT_F(a < 0xA0)) { return(0); } break;
- case 0xED: if(CDVJK_EXPECT_F(a > 0x9F)) { return(0); } break;
- case 0xF0: if(CDVJK_EXPECT_F(a < 0x90)) { return(0); } break;
- case 0xF4: if(CDVJK_EXPECT_F(a > 0x8F)) { return(0); } break;
- default: if(CDVJK_EXPECT_F(a < 0x80)) { return(0); }
- }
-
- case 1: if(CDVJK_EXPECT_F((CDVJK_EXPECT_T(*source < 0xC2)) && CDVJK_EXPECT_F(*source >= 0x80))) { return(0); }
- }
-
- if(CDVJK_EXPECT_F(*source > 0xF4)) { return(0); }
-
- return(1);
-}
-
-static CDV_ConversionResult cdvConvertSingleCodePointInUTF8(const UTF8 *sourceStart, const UTF8 *sourceEnd, UTF8 const **nextUTF8, UTF32 *convertedUTF32) {
- CDV_ConversionResult result = conversionOK;
- const UTF8 *source = sourceStart;
- UTF32 ch = 0UL;
-
-#if !defined(CDVJK_FAST_TRAILING_BYTES)
- unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
-#else
- unsigned short extraBytesToRead = __builtin_clz(((*source)^0xff) << 25);
-#endif
-
- if(CDVJK_EXPECT_F((source + extraBytesToRead + 1) > sourceEnd) || CDVJK_EXPECT_F(!cdvisLegalUTF8(source, extraBytesToRead + 1))) {
- source++;
- while((source < sourceEnd) && (((*source) & 0xc0) == 0x80) && ((source - sourceStart) < (extraBytesToRead + 1))) { source++; }
- NSCParameterAssert(source <= sourceEnd);
- result = ((source < sourceEnd) && (((*source) & 0xc0) != 0x80)) ? sourceIllegal : ((sourceStart + extraBytesToRead + 1) > sourceEnd) ? sourceExhausted : sourceIllegal;
- ch = CDV_UNI_REPLACEMENT_CHAR;
- goto finished;
- }
-
- switch(extraBytesToRead) { // The cases all fall through.
- case 5: ch += *source++; ch <<= 6;
- case 4: ch += *source++; ch <<= 6;
- case 3: ch += *source++; ch <<= 6;
- case 2: ch += *source++; ch <<= 6;
- case 1: ch += *source++; ch <<= 6;
- case 0: ch += *source++;
- }
- ch -= offsetsFromUTF8[extraBytesToRead];
-
- result = cdvisValidCodePoint(&ch);
-
- finished:
- *nextUTF8 = source;
- *convertedUTF32 = ch;
-
- return(result);
-}
-
-
-static CDV_ConversionResult cdvConvertUTF32toUTF8 (UTF32 u32CodePoint, UTF8 **targetStart, UTF8 *targetEnd) {
- const UTF32 byteMask = 0xBF, byteMark = 0x80;
- CDV_ConversionResult result = conversionOK;
- UTF8 *target = *targetStart;
- UTF32 ch = u32CodePoint;
- unsigned short bytesToWrite = 0;
-
- result = cdvisValidCodePoint(&ch);
-
- // Figure out how many bytes the result will require. Turn any illegally large UTF32 things (> Plane 17) into replacement chars.
- if(ch < (UTF32)0x80) { bytesToWrite = 1; }
- else if(ch < (UTF32)0x800) { bytesToWrite = 2; }
- else if(ch < (UTF32)0x10000) { bytesToWrite = 3; }
- else if(ch <= CDV_UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4; }
- else { bytesToWrite = 3; ch = CDV_UNI_REPLACEMENT_CHAR; result = sourceIllegal; }
-
- target += bytesToWrite;
- if (target > targetEnd) { target -= bytesToWrite; result = targetExhausted; goto finished; }
-
- switch (bytesToWrite) { // note: everything falls through.
- case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
- case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
- }
-
- target += bytesToWrite;
-
- finished:
- *targetStart = target;
- return(result);
-}
-
-CDVJK_STATIC_INLINE int cdvjk_string_add_unicodeCodePoint(CDVJKParseState *parseState, uint32_t unicodeCodePoint, size_t *tokenBufferIdx, CDVJKHash *stringHash) {
- UTF8 *u8s = &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx];
- CDV_ConversionResult result;
-
- if((result = cdvConvertUTF32toUTF8(unicodeCodePoint, &u8s, (parseState->token.tokenBuffer.bytes.ptr + parseState->token.tokenBuffer.bytes.length))) != conversionOK) { if(result == targetExhausted) { return(1); } }
- size_t utf8len = u8s - &parseState->token.tokenBuffer.bytes.ptr[*tokenBufferIdx], nextIdx = (*tokenBufferIdx) + utf8len;
-
- while(*tokenBufferIdx < nextIdx) { *stringHash = cdvcalculateHash(*stringHash, parseState->token.tokenBuffer.bytes.ptr[(*tokenBufferIdx)++]); }
-
- return(0);
-}
-
-////////////
-#pragma mark -
-#pragma mark Decoding / parsing / deserializing functions
-
-static int cdvjk_parse_string(CDVJKParseState *parseState) {
- NSCParameterAssert((parseState != NULL) && (CDVJK_AT_STRING_PTR(parseState) <= CDVJK_END_STRING_PTR(parseState)));
- const unsigned char *stringStart = CDVJK_AT_STRING_PTR(parseState) + 1;
- const unsigned char *endOfBuffer = CDVJK_END_STRING_PTR(parseState);
- const unsigned char *atStringCharacter = stringStart;
- unsigned char *tokenBuffer = parseState->token.tokenBuffer.bytes.ptr;
- size_t tokenStartIndex = parseState->atIndex;
- size_t tokenBufferIdx = 0UL;
-
- int onlySimpleString = 1, stringState = CDVJSONStringStateStart;
- uint16_t escapedUnicode1 = 0U, escapedUnicode2 = 0U;
- uint32_t escapedUnicodeCodePoint = 0U;
- CDVJKHash stringHash = CDVJK_HASH_INIT;
-
- while(1) {
- unsigned long currentChar;
-
- if(CDVJK_EXPECT_F(atStringCharacter == endOfBuffer)) { /* XXX Add error message */ stringState = CDVJSONStringStateError; goto finishedParsing; }
-
- if(CDVJK_EXPECT_F((currentChar = *atStringCharacter++) >= 0x80UL)) {
- const unsigned char *nextValidCharacter = NULL;
- UTF32 u32ch = 0U;
- CDV_ConversionResult result;
-
- if(CDVJK_EXPECT_F((result = cdvConvertSingleCodePointInUTF8(atStringCharacter - 1, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { goto switchToSlowPath; }
- stringHash = cdvcalculateHash(stringHash, currentChar);
- while(atStringCharacter < nextValidCharacter) { NSCParameterAssert(CDVJK_AT_STRING_PTR(parseState) <= CDVJK_END_STRING_PTR(parseState)); stringHash = cdvcalculateHash(stringHash, *atStringCharacter++); }
- continue;
- } else {
- if(CDVJK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = CDVJSONStringStateFinished; goto finishedParsing; }
-
- if(CDVJK_EXPECT_F(currentChar == (unsigned long)'\\')) {
- switchToSlowPath:
- onlySimpleString = 0;
- stringState = CDVJSONStringStateParsing;
- tokenBufferIdx = (atStringCharacter - stringStart) - 1L;
- if(CDVJK_EXPECT_F((tokenBufferIdx + 16UL) > parseState->token.tokenBuffer.bytes.length)) { if((tokenBuffer = cdvjk_managedBuffer_resize(&parseState->token.tokenBuffer, tokenBufferIdx + 1024UL)) == NULL) { cdvjk_error(parseState, @"Internal error: Unable to resize temporary buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = CDVJSONStringStateError; goto finishedParsing; } }
- memcpy(tokenBuffer, stringStart, tokenBufferIdx);
- goto slowMatch;
- }
-
- if(CDVJK_EXPECT_F(currentChar < 0x20UL)) { cdvjk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = CDVJSONStringStateError; goto finishedParsing; }
-
- stringHash = cdvcalculateHash(stringHash, currentChar);
- }
- }
-
- slowMatch:
-
- for(atStringCharacter = (stringStart + ((atStringCharacter - stringStart) - 1L)); (atStringCharacter < endOfBuffer) && (tokenBufferIdx < parseState->token.tokenBuffer.bytes.length); atStringCharacter++) {
- if((tokenBufferIdx + 16UL) > parseState->token.tokenBuffer.bytes.length) { if((tokenBuffer = cdvjk_managedBuffer_resize(&parseState->token.tokenBuffer, tokenBufferIdx + 1024UL)) == NULL) { cdvjk_error(parseState, @"Internal error: Unable to resize temporary buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = CDVJSONStringStateError; goto finishedParsing; } }
-
- NSCParameterAssert(tokenBufferIdx < parseState->token.tokenBuffer.bytes.length);
-
- unsigned long currentChar = (*atStringCharacter), escapedChar;
-
- if(CDVJK_EXPECT_T(stringState == CDVJSONStringStateParsing)) {
- if(CDVJK_EXPECT_T(currentChar >= 0x20UL)) {
- if(CDVJK_EXPECT_T(currentChar < (unsigned long)0x80)) { // Not a UTF8 sequence
- if(CDVJK_EXPECT_F(currentChar == (unsigned long)'"')) { stringState = CDVJSONStringStateFinished; atStringCharacter++; goto finishedParsing; }
- if(CDVJK_EXPECT_F(currentChar == (unsigned long)'\\')) { stringState = CDVJSONStringStateEscape; continue; }
- stringHash = cdvcalculateHash(stringHash, currentChar);
- tokenBuffer[tokenBufferIdx++] = currentChar;
- continue;
- } else { // UTF8 sequence
- const unsigned char *nextValidCharacter = NULL;
- UTF32 u32ch = 0U;
- CDV_ConversionResult result;
-
- if(CDVJK_EXPECT_F((result = cdvConvertSingleCodePointInUTF8(atStringCharacter, endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) {
- if((result == sourceIllegal) && ((parseState->parseOptionFlags & CDVJKParseOptionLooseUnicode) == 0)) { cdvjk_error(parseState, @"Illegal UTF8 sequence found in \"\" string."); stringState = CDVJSONStringStateError; goto finishedParsing; }
- if(result == sourceExhausted) { cdvjk_error(parseState, @"End of buffer reached while parsing UTF8 in \"\" string."); stringState = CDVJSONStringStateError; goto finishedParsing; }
- if(cdvjk_string_add_unicodeCodePoint(parseState, u32ch, &tokenBufferIdx, &stringHash)) { cdvjk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = CDVJSONStringStateError; goto finishedParsing; }
- atStringCharacter = nextValidCharacter - 1;
- continue;
- } else {
- while(atStringCharacter < nextValidCharacter) { tokenBuffer[tokenBufferIdx++] = *atStringCharacter; stringHash = cdvcalculateHash(stringHash, *atStringCharacter++); }
- atStringCharacter--;
- continue;
- }
- }
- } else { // currentChar < 0x20
- cdvjk_error(parseState, @"Invalid character < 0x20 found in string: 0x%2.2x.", currentChar); stringState = CDVJSONStringStateError; goto finishedParsing;
- }
-
- } else { // stringState != CDVJSONStringStateParsing
- int isSurrogate = 1;
-
- switch(stringState) {
- case CDVJSONStringStateEscape:
- switch(currentChar) {
- case 'u': escapedUnicode1 = 0U; escapedUnicode2 = 0U; escapedUnicodeCodePoint = 0U; stringState = CDVJSONStringStateEscapedUnicode1; break;
-
- case 'b': escapedChar = '\b'; goto parsedEscapedChar;
- case 'f': escapedChar = '\f'; goto parsedEscapedChar;
- case 'n': escapedChar = '\n'; goto parsedEscapedChar;
- case 'r': escapedChar = '\r'; goto parsedEscapedChar;
- case 't': escapedChar = '\t'; goto parsedEscapedChar;
- case '\\': escapedChar = '\\'; goto parsedEscapedChar;
- case '/': escapedChar = '/'; goto parsedEscapedChar;
- case '"': escapedChar = '"'; goto parsedEscapedChar;
-
- parsedEscapedChar:
- stringState = CDVJSONStringStateParsing;
- stringHash = cdvcalculateHash(stringHash, escapedChar);
- tokenBuffer[tokenBufferIdx++] = escapedChar;
- break;
-
- default: cdvjk_error(parseState, @"Invalid escape sequence found in \"\" string."); stringState = CDVJSONStringStateError; goto finishedParsing; break;
- }
- break;
-
- case CDVJSONStringStateEscapedUnicode1:
- case CDVJSONStringStateEscapedUnicode2:
- case CDVJSONStringStateEscapedUnicode3:
- case CDVJSONStringStateEscapedUnicode4: isSurrogate = 0;
- case CDVJSONStringStateEscapedUnicodeSurrogate1:
- case CDVJSONStringStateEscapedUnicodeSurrogate2:
- case CDVJSONStringStateEscapedUnicodeSurrogate3:
- case CDVJSONStringStateEscapedUnicodeSurrogate4:
- {
- uint16_t hexValue = 0U;
-
- switch(currentChar) {
- case '0' ... '9': hexValue = currentChar - '0'; goto parsedHex;
- case 'a' ... 'f': hexValue = (currentChar - 'a') + 10U; goto parsedHex;
- case 'A' ... 'F': hexValue = (currentChar - 'A') + 10U; goto parsedHex;
-
- parsedHex:
- if(!isSurrogate) { escapedUnicode1 = (escapedUnicode1 << 4) | hexValue; } else { escapedUnicode2 = (escapedUnicode2 << 4) | hexValue; }
-
- if(stringState == CDVJSONStringStateEscapedUnicode4) {
- if(((escapedUnicode1 >= 0xD800U) && (escapedUnicode1 < 0xE000U))) {
- if((escapedUnicode1 >= 0xD800U) && (escapedUnicode1 < 0xDC00U)) { stringState = CDVJSONStringStateEscapedNeedEscapeForSurrogate; }
- else if((escapedUnicode1 >= 0xDC00U) && (escapedUnicode1 < 0xE000U)) {
- if((parseState->parseOptionFlags & CDVJKParseOptionLooseUnicode)) { escapedUnicodeCodePoint = CDV_UNI_REPLACEMENT_CHAR; }
- else { cdvjk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = CDVJSONStringStateError; goto finishedParsing; }
- }
- }
- else { escapedUnicodeCodePoint = escapedUnicode1; }
- }
-
- if(stringState == CDVJSONStringStateEscapedUnicodeSurrogate4) {
- if((escapedUnicode2 < 0xdc00) || (escapedUnicode2 > 0xdfff)) {
- if((parseState->parseOptionFlags & CDVJKParseOptionLooseUnicode)) { escapedUnicodeCodePoint = CDV_UNI_REPLACEMENT_CHAR; }
- else { cdvjk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = CDVJSONStringStateError; goto finishedParsing; }
- }
- else { escapedUnicodeCodePoint = ((escapedUnicode1 - 0xd800) * 0x400) + (escapedUnicode2 - 0xdc00) + 0x10000; }
- }
-
- if((stringState == CDVJSONStringStateEscapedUnicode4) || (stringState == CDVJSONStringStateEscapedUnicodeSurrogate4)) {
- if((cdvisValidCodePoint(&escapedUnicodeCodePoint) == sourceIllegal) && ((parseState->parseOptionFlags & CDVJKParseOptionLooseUnicode) == 0)) { cdvjk_error(parseState, @"Illegal \\u Unicode escape sequence."); stringState = CDVJSONStringStateError; goto finishedParsing; }
- stringState = CDVJSONStringStateParsing;
- if(cdvjk_string_add_unicodeCodePoint(parseState, escapedUnicodeCodePoint, &tokenBufferIdx, &stringHash)) { cdvjk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = CDVJSONStringStateError; goto finishedParsing; }
- }
- else if((stringState >= CDVJSONStringStateEscapedUnicode1) && (stringState <= CDVJSONStringStateEscapedUnicodeSurrogate4)) { stringState++; }
- break;
-
- default: cdvjk_error(parseState, @"Unexpected character found in \\u Unicode escape sequence. Found '%c', expected [0-9a-fA-F].", currentChar); stringState = CDVJSONStringStateError; goto finishedParsing; break;
- }
- }
- break;
-
- case CDVJSONStringStateEscapedNeedEscapeForSurrogate:
- if(currentChar == '\\') { stringState = CDVJSONStringStateEscapedNeedEscapedUForSurrogate; }
- else {
- if((parseState->parseOptionFlags & CDVJKParseOptionLooseUnicode) == 0) { cdvjk_error(parseState, @"Required a second \\u Unicode escape sequence following a surrogate \\u Unicode escape sequence."); stringState = CDVJSONStringStateError; goto finishedParsing; }
- else { stringState = CDVJSONStringStateParsing; atStringCharacter--; if(cdvjk_string_add_unicodeCodePoint(parseState, CDV_UNI_REPLACEMENT_CHAR, &tokenBufferIdx, &stringHash)) { cdvjk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = CDVJSONStringStateError; goto finishedParsing; } }
- }
- break;
-
- case CDVJSONStringStateEscapedNeedEscapedUForSurrogate:
- if(currentChar == 'u') { stringState = CDVJSONStringStateEscapedUnicodeSurrogate1; }
- else {
- if((parseState->parseOptionFlags & CDVJKParseOptionLooseUnicode) == 0) { cdvjk_error(parseState, @"Required a second \\u Unicode escape sequence following a surrogate \\u Unicode escape sequence."); stringState = CDVJSONStringStateError; goto finishedParsing; }
- else { stringState = CDVJSONStringStateParsing; atStringCharacter -= 2; if(cdvjk_string_add_unicodeCodePoint(parseState, CDV_UNI_REPLACEMENT_CHAR, &tokenBufferIdx, &stringHash)) { cdvjk_error(parseState, @"Internal error: Unable to add UTF8 sequence to internal string buffer. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = CDVJSONStringStateError; goto finishedParsing; } }
- }
- break;
-
- default: cdvjk_error(parseState, @"Internal error: Unknown stringState. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); stringState = CDVJSONStringStateError; goto finishedParsing; break;
- }
- }
- }
-
-finishedParsing:
-
- if(CDVJK_EXPECT_T(stringState == CDVJSONStringStateFinished)) {
- NSCParameterAssert((parseState->stringBuffer.bytes.ptr + tokenStartIndex) < atStringCharacter);
-
- parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + tokenStartIndex;
- parseState->token.tokenPtrRange.length = (atStringCharacter - parseState->token.tokenPtrRange.ptr);
-
- if(CDVJK_EXPECT_T(onlySimpleString)) {
- NSCParameterAssert(((parseState->token.tokenPtrRange.ptr + 1) < endOfBuffer) && (parseState->token.tokenPtrRange.length >= 2UL) && (((parseState->token.tokenPtrRange.ptr + 1) + (parseState->token.tokenPtrRange.length - 2)) < endOfBuffer));
- parseState->token.value.ptrRange.ptr = parseState->token.tokenPtrRange.ptr + 1;
- parseState->token.value.ptrRange.length = parseState->token.tokenPtrRange.length - 2UL;
- } else {
- parseState->token.value.ptrRange.ptr = parseState->token.tokenBuffer.bytes.ptr;
- parseState->token.value.ptrRange.length = tokenBufferIdx;
- }
-
- parseState->token.value.hash = stringHash;
- parseState->token.value.type = CDVJKValueTypeString;
- parseState->atIndex = (atStringCharacter - parseState->stringBuffer.bytes.ptr);
- }
-
- if(CDVJK_EXPECT_F(stringState != CDVJSONStringStateFinished)) { cdvjk_error(parseState, @"Invalid string."); }
- return(CDVJK_EXPECT_T(stringState == CDVJSONStringStateFinished) ? 0 : 1);
-}
-
-static int cdvjk_parse_number(CDVJKParseState *parseState) {
- NSCParameterAssert((parseState != NULL) && (CDVJK_AT_STRING_PTR(parseState) <= CDVJK_END_STRING_PTR(parseState)));
- const unsigned char *numberStart = CDVJK_AT_STRING_PTR(parseState);
- const unsigned char *endOfBuffer = CDVJK_END_STRING_PTR(parseState);
- const unsigned char *atNumberCharacter = NULL;
- int numberState = CDVJSONNumberStateWholeNumberStart, isFloatingPoint = 0, isNegative = 0, backup = 0;
- size_t startingIndex = parseState->atIndex;
-
- for(atNumberCharacter = numberStart; (CDVJK_EXPECT_T(atNumberCharacter < endOfBuffer)) && (CDVJK_EXPECT_T(!(CDVJK_EXPECT_F(numberState == CDVJSONNumberStateFinished) || CDVJK_EXPECT_F(numberState == CDVJSONNumberStateError)))); atNumberCharacter++) {
- unsigned long currentChar = (unsigned long)(*atNumberCharacter), lowerCaseCC = currentChar | 0x20UL;
-
- switch(numberState) {
- case CDVJSONNumberStateWholeNumberStart: if (currentChar == '-') { numberState = CDVJSONNumberStateWholeNumberMinus; isNegative = 1; break; }
- case CDVJSONNumberStateWholeNumberMinus: if (currentChar == '0') { numberState = CDVJSONNumberStateWholeNumberZero; break; }
- else if( (currentChar >= '1') && (currentChar <= '9')) { numberState = CDVJSONNumberStateWholeNumber; break; }
- else { /* XXX Add error message */ numberState = CDVJSONNumberStateError; break; }
- case CDVJSONNumberStateExponentStart: if( (currentChar == '+') || (currentChar == '-')) { numberState = CDVJSONNumberStateExponentPlusMinus; break; }
- case CDVJSONNumberStateFractionalNumberStart:
- case CDVJSONNumberStateExponentPlusMinus:if(!((currentChar >= '0') && (currentChar <= '9'))) { /* XXX Add error message */ numberState = CDVJSONNumberStateError; break; }
- else { if(numberState == CDVJSONNumberStateFractionalNumberStart) { numberState = CDVJSONNumberStateFractionalNumber; }
- else { numberState = CDVJSONNumberStateExponent; } break; }
- case CDVJSONNumberStateWholeNumberZero:
- case CDVJSONNumberStateWholeNumber: if (currentChar == '.') { numberState = CDVJSONNumberStateFractionalNumberStart; isFloatingPoint = 1; break; }
- case CDVJSONNumberStateFractionalNumber: if (lowerCaseCC == 'e') { numberState = CDVJSONNumberStateExponentStart; isFloatingPoint = 1; break; }
- case CDVJSONNumberStateExponent: if(!((currentChar >= '0') && (currentChar <= '9')) || (numberState == CDVJSONNumberStateWholeNumberZero)) { numberState = CDVJSONNumberStateFinished; backup = 1; break; }
- break;
- default: /* XXX Add error message */ numberState = CDVJSONNumberStateError; break;
- }
- }
-
- parseState->token.tokenPtrRange.ptr = parseState->stringBuffer.bytes.ptr + startingIndex;
- parseState->token.tokenPtrRange.length = (atNumberCharacter - parseState->token.tokenPtrRange.ptr) - backup;
- parseState->atIndex = (parseState->token.tokenPtrRange.ptr + parseState->token.tokenPtrRange.length) - parseState->stringBuffer.bytes.ptr;
-
- if(CDVJK_EXPECT_T(numberState == CDVJSONNumberStateFinished)) {
- unsigned char numberTempBuf[parseState->token.tokenPtrRange.length + 4UL];
- unsigned char *endOfNumber = NULL;
-
- memcpy(numberTempBuf, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length);
- numberTempBuf[parseState->token.tokenPtrRange.length] = 0;
-
- errno = 0;
-
- // Treat "-0" as a floating point number, which is capable of representing negative zeros.
- if(CDVJK_EXPECT_F(parseState->token.tokenPtrRange.length == 2UL) && CDVJK_EXPECT_F(numberTempBuf[1] == '0') && CDVJK_EXPECT_F(isNegative)) { isFloatingPoint = 1; }
-
- if(isFloatingPoint) {
- parseState->token.value.number.doubleValue = strtod((const char *)numberTempBuf, (char **)&endOfNumber); // strtod is documented to return U+2261 (identical to) 0.0 on an underflow error (along with setting errno to ERANGE).
- parseState->token.value.type = CDVJKValueTypeDouble;
- parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.doubleValue;
- parseState->token.value.ptrRange.length = sizeof(double);
- parseState->token.value.hash = (CDVJK_HASH_INIT + parseState->token.value.type);
- } else {
- if(isNegative) {
- parseState->token.value.number.longLongValue = strtoll((const char *)numberTempBuf, (char **)&endOfNumber, 10);
- parseState->token.value.type = CDVJKValueTypeLongLong;
- parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.longLongValue;
- parseState->token.value.ptrRange.length = sizeof(long long);
- parseState->token.value.hash = (CDVJK_HASH_INIT + parseState->token.value.type) + (CDVJKHash)parseState->token.value.number.longLongValue;
- } else {
- parseState->token.value.number.unsignedLongLongValue = strtoull((const char *)numberTempBuf, (char **)&endOfNumber, 10);
- parseState->token.value.type = CDVJKValueTypeUnsignedLongLong;
- parseState->token.value.ptrRange.ptr = (const unsigned char *)&parseState->token.value.number.unsignedLongLongValue;
- parseState->token.value.ptrRange.length = sizeof(unsigned long long);
- parseState->token.value.hash = (CDVJK_HASH_INIT + parseState->token.value.type) + (CDVJKHash)parseState->token.value.number.unsignedLongLongValue;
- }
- }
-
- if(CDVJK_EXPECT_F(errno != 0)) {
- numberState = CDVJSONNumberStateError;
- if(errno == ERANGE) {
- switch(parseState->token.value.type) {
- case CDVJKValueTypeDouble: cdvjk_error(parseState, @"The value '%s' could not be represented as a 'double' due to %s.", numberTempBuf, (parseState->token.value.number.doubleValue == 0.0) ? "underflow" : "overflow"); break; // see above for == 0.0.
- case CDVJKValueTypeLongLong: cdvjk_error(parseState, @"The value '%s' exceeded the minimum value that could be represented: %lld.", numberTempBuf, parseState->token.value.number.longLongValue); break;
- case CDVJKValueTypeUnsignedLongLong: cdvjk_error(parseState, @"The value '%s' exceeded the maximum value that could be represented: %llu.", numberTempBuf, parseState->token.value.number.unsignedLongLongValue); break;
- default: cdvjk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
- }
- }
- }
- if(CDVJK_EXPECT_F(endOfNumber != &numberTempBuf[parseState->token.tokenPtrRange.length]) && CDVJK_EXPECT_F(numberState != CDVJSONNumberStateError)) { numberState = CDVJSONNumberStateError; cdvjk_error(parseState, @"The conversion function did not consume all of the number tokens characters."); }
-
- size_t hashIndex = 0UL;
- for(hashIndex = 0UL; hashIndex < parseState->token.value.ptrRange.length; hashIndex++) { parseState->token.value.hash = cdvcalculateHash(parseState->token.value.hash, parseState->token.value.ptrRange.ptr[hashIndex]); }
- }
-
- if(CDVJK_EXPECT_F(numberState != CDVJSONNumberStateFinished)) { cdvjk_error(parseState, @"Invalid number."); }
- return(CDVJK_EXPECT_T((numberState == CDVJSONNumberStateFinished)) ? 0 : 1);
-}
-
-CDVJK_STATIC_INLINE void cdvjk_set_parsed_token(CDVJKParseState *parseState, const unsigned char *ptr, size_t length, CDVJKTokenType type, size_t advanceBy) {
- parseState->token.tokenPtrRange.ptr = ptr;
- parseState->token.tokenPtrRange.length = length;
- parseState->token.type = type;
- parseState->atIndex += advanceBy;
-}
-
-static size_t cdvjk_parse_is_newline(CDVJKParseState *parseState, const unsigned char *atCharacterPtr) {
- NSCParameterAssert((parseState != NULL) && (atCharacterPtr != NULL) && (atCharacterPtr >= parseState->stringBuffer.bytes.ptr) && (atCharacterPtr < CDVJK_END_STRING_PTR(parseState)));
- const unsigned char *endOfStringPtr = CDVJK_END_STRING_PTR(parseState);
-
- if(CDVJK_EXPECT_F(atCharacterPtr >= endOfStringPtr)) { return(0UL); }
-
- if(CDVJK_EXPECT_F((*(atCharacterPtr + 0)) == '\n')) { return(1UL); }
- if(CDVJK_EXPECT_F((*(atCharacterPtr + 0)) == '\r')) { if((CDVJK_EXPECT_T((atCharacterPtr + 1) < endOfStringPtr)) && ((*(atCharacterPtr + 1)) == '\n')) { return(2UL); } return(1UL); }
- if(parseState->parseOptionFlags & CDVJKParseOptionUnicodeNewlines) {
- if((CDVJK_EXPECT_F((*(atCharacterPtr + 0)) == 0xc2)) && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x85))) { return(2UL); }
- if((CDVJK_EXPECT_F((*(atCharacterPtr + 0)) == 0xe2)) && (((atCharacterPtr + 2) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == 0x80) && (((*(atCharacterPtr + 2)) == 0xa8) || ((*(atCharacterPtr + 2)) == 0xa9)))) { return(3UL); }
- }
-
- return(0UL);
-}
-
-CDVJK_STATIC_INLINE int cdvjk_parse_skip_newline(CDVJKParseState *parseState) {
- size_t newlineAdvanceAtIndex = 0UL;
- if(CDVJK_EXPECT_F((newlineAdvanceAtIndex = cdvjk_parse_is_newline(parseState, CDVJK_AT_STRING_PTR(parseState))) > 0UL)) { parseState->lineNumber++; parseState->atIndex += (newlineAdvanceAtIndex - 1UL); parseState->lineStartIndex = parseState->atIndex + 1UL; return(1); }
- return(0);
-}
-
-CDVJK_STATIC_INLINE void cdvjk_parse_skip_whitespace(CDVJKParseState *parseState) {
-#ifndef __clang_analyzer__
- NSCParameterAssert((parseState != NULL) && (CDVJK_AT_STRING_PTR(parseState) <= CDVJK_END_STRING_PTR(parseState)));
- const unsigned char *atCharacterPtr = NULL;
- const unsigned char *endOfStringPtr = CDVJK_END_STRING_PTR(parseState);
-
- for(atCharacterPtr = CDVJK_AT_STRING_PTR(parseState); (CDVJK_EXPECT_T((atCharacterPtr = CDVJK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) {
- if(((*(atCharacterPtr + 0)) == ' ') || ((*(atCharacterPtr + 0)) == '\t')) { continue; }
- if(cdvjk_parse_skip_newline(parseState)) { continue; }
- if(parseState->parseOptionFlags & CDVJKParseOptionComments) {
- if((CDVJK_EXPECT_F((*(atCharacterPtr + 0)) == '/')) && (CDVJK_EXPECT_T((atCharacterPtr + 1) < endOfStringPtr))) {
- if((*(atCharacterPtr + 1)) == '/') {
- parseState->atIndex++;
- for(atCharacterPtr = CDVJK_AT_STRING_PTR(parseState); (CDVJK_EXPECT_T((atCharacterPtr = CDVJK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) { if(cdvjk_parse_skip_newline(parseState)) { break; } }
- continue;
- }
- if((*(atCharacterPtr + 1)) == '*') {
- parseState->atIndex++;
- for(atCharacterPtr = CDVJK_AT_STRING_PTR(parseState); (CDVJK_EXPECT_T((atCharacterPtr = CDVJK_AT_STRING_PTR(parseState)) < endOfStringPtr)); parseState->atIndex++) {
- if(cdvjk_parse_skip_newline(parseState)) { continue; }
- if(((*(atCharacterPtr + 0)) == '*') && (((atCharacterPtr + 1) < endOfStringPtr) && ((*(atCharacterPtr + 1)) == '/'))) { parseState->atIndex++; break; }
- }
- continue;
- }
- }
- }
- break;
- }
-#endif
-}
-
-static int cdvjk_parse_next_token(CDVJKParseState *parseState) {
- NSCParameterAssert((parseState != NULL) && (CDVJK_AT_STRING_PTR(parseState) <= CDVJK_END_STRING_PTR(parseState)));
- const unsigned char *atCharacterPtr = NULL;
- const unsigned char *endOfStringPtr = CDVJK_END_STRING_PTR(parseState);
- unsigned char currentCharacter = 0U;
- int stopParsing = 0;
-
- parseState->prev_atIndex = parseState->atIndex;
- parseState->prev_lineNumber = parseState->lineNumber;
- parseState->prev_lineStartIndex = parseState->lineStartIndex;
-
- cdvjk_parse_skip_whitespace(parseState);
-
- if((CDVJK_AT_STRING_PTR(parseState) == endOfStringPtr)) { stopParsing = 1; }
-
- if((CDVJK_EXPECT_T(stopParsing == 0)) && (CDVJK_EXPECT_T((atCharacterPtr = CDVJK_AT_STRING_PTR(parseState)) < endOfStringPtr))) {
- currentCharacter = *atCharacterPtr;
-
- if(CDVJK_EXPECT_T(currentCharacter == '"')) { if(CDVJK_EXPECT_T((stopParsing = cdvjk_parse_string(parseState)) == 0)) { cdvjk_set_parsed_token(parseState, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length, CDVJKTokenTypeString, 0UL); } }
- else if(CDVJK_EXPECT_T(currentCharacter == ':')) { cdvjk_set_parsed_token(parseState, atCharacterPtr, 1UL, CDVJKTokenTypeSeparator, 1UL); }
- else if(CDVJK_EXPECT_T(currentCharacter == ',')) { cdvjk_set_parsed_token(parseState, atCharacterPtr, 1UL, CDVJKTokenTypeComma, 1UL); }
- else if((CDVJK_EXPECT_T(currentCharacter >= '0') && CDVJK_EXPECT_T(currentCharacter <= '9')) || CDVJK_EXPECT_T(currentCharacter == '-')) { if(CDVJK_EXPECT_T((stopParsing = cdvjk_parse_number(parseState)) == 0)) { cdvjk_set_parsed_token(parseState, parseState->token.tokenPtrRange.ptr, parseState->token.tokenPtrRange.length, CDVJKTokenTypeNumber, 0UL); } }
- else if(CDVJK_EXPECT_T(currentCharacter == '{')) { cdvjk_set_parsed_token(parseState, atCharacterPtr, 1UL, CDVJKTokenTypeObjectBegin, 1UL); }
- else if(CDVJK_EXPECT_T(currentCharacter == '}')) { cdvjk_set_parsed_token(parseState, atCharacterPtr, 1UL, CDVJKTokenTypeObjectEnd, 1UL); }
- else if(CDVJK_EXPECT_T(currentCharacter == '[')) { cdvjk_set_parsed_token(parseState, atCharacterPtr, 1UL, CDVJKTokenTypeArrayBegin, 1UL); }
- else if(CDVJK_EXPECT_T(currentCharacter == ']')) { cdvjk_set_parsed_token(parseState, atCharacterPtr, 1UL, CDVJKTokenTypeArrayEnd, 1UL); }
-
- else if(CDVJK_EXPECT_T(currentCharacter == 't')) { if(!((CDVJK_EXPECT_T((atCharacterPtr + 4UL) < endOfStringPtr)) && (CDVJK_EXPECT_T(atCharacterPtr[1] == 'r')) && (CDVJK_EXPECT_T(atCharacterPtr[2] == 'u')) && (CDVJK_EXPECT_T(atCharacterPtr[3] == 'e')))) { stopParsing = 1; /* XXX Add error message */ } else { cdvjk_set_parsed_token(parseState, atCharacterPtr, 4UL, CDVJKTokenTypeTrue, 4UL); } }
- else if(CDVJK_EXPECT_T(currentCharacter == 'f')) { if(!((CDVJK_EXPECT_T((atCharacterPtr + 5UL) < endOfStringPtr)) && (CDVJK_EXPECT_T(atCharacterPtr[1] == 'a')) && (CDVJK_EXPECT_T(atCharacterPtr[2] == 'l')) && (CDVJK_EXPECT_T(atCharacterPtr[3] == 's')) && (CDVJK_EXPECT_T(atCharacterPtr[4] == 'e')))) { stopParsing = 1; /* XXX Add error message */ } else { cdvjk_set_parsed_token(parseState, atCharacterPtr, 5UL, CDVJKTokenTypeFalse, 5UL); } }
- else if(CDVJK_EXPECT_T(currentCharacter == 'n')) { if(!((CDVJK_EXPECT_T((atCharacterPtr + 4UL) < endOfStringPtr)) && (CDVJK_EXPECT_T(atCharacterPtr[1] == 'u')) && (CDVJK_EXPECT_T(atCharacterPtr[2] == 'l')) && (CDVJK_EXPECT_T(atCharacterPtr[3] == 'l')))) { stopParsing = 1; /* XXX Add error message */ } else { cdvjk_set_parsed_token(parseState, atCharacterPtr, 4UL, CDVJKTokenTypeNull, 4UL); } }
- else { stopParsing = 1; /* XXX Add error message */ }
- }
-
- if(CDVJK_EXPECT_F(stopParsing)) { cdvjk_error(parseState, @"Unexpected token, wanted '{', '}', '[', ']', ',', ':', 'true', 'false', 'null', '\"STRING\"', 'NUMBER'."); }
- return(stopParsing);
-}
-
-static void cdvjk_error_parse_accept_or3(CDVJKParseState *parseState, int state, NSString *or1String, NSString *or2String, NSString *or3String) {
- NSString *acceptStrings[16];
- int acceptIdx = 0;
- if(state & CDVJKParseAcceptValue) { acceptStrings[acceptIdx++] = or1String; }
- if(state & CDVJKParseAcceptComma) { acceptStrings[acceptIdx++] = or2String; }
- if(state & CDVJKParseAcceptEnd) { acceptStrings[acceptIdx++] = or3String; }
- if(acceptIdx == 1) { cdvjk_error(parseState, @"Expected %@, not '%*.*s'", acceptStrings[0], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
- else if(acceptIdx == 2) { cdvjk_error(parseState, @"Expected %@ or %@, not '%*.*s'", acceptStrings[0], acceptStrings[1], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
- else if(acceptIdx == 3) { cdvjk_error(parseState, @"Expected %@, %@, or %@, not '%*.*s", acceptStrings[0], acceptStrings[1], acceptStrings[2], (int)parseState->token.tokenPtrRange.length, (int)parseState->token.tokenPtrRange.length, parseState->token.tokenPtrRange.ptr); }
-}
-
-static void *cdvjk_parse_array(CDVJKParseState *parseState) {
- size_t startingObjectIndex = parseState->objectStack.index;
- int arrayState = CDVJKParseAcceptValueOrEnd, stopParsing = 0;
- void *parsedArray = NULL;
-
- while(CDVJK_EXPECT_T((CDVJK_EXPECT_T(stopParsing == 0)) && (CDVJK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length)))) {
- if(CDVJK_EXPECT_F(parseState->objectStack.index > (parseState->objectStack.count - 4UL))) { if(cdvjk_objectStack_resize(&parseState->objectStack, parseState->objectStack.count + 128UL)) { cdvjk_error(parseState, @"Internal error: [array] objectsIndex > %zu, resize failed? %@ line %#ld", (parseState->objectStack.count - 4UL), [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break; } }
-
- if(CDVJK_EXPECT_T((stopParsing = cdvjk_parse_next_token(parseState)) == 0)) {
- void *object = NULL;
-#ifndef NS_BLOCK_ASSERTIONS
- parseState->objectStack.objects[parseState->objectStack.index] = NULL;
- parseState->objectStack.keys [parseState->objectStack.index] = NULL;
-#endif
- switch(parseState->token.type) {
- case CDVJKTokenTypeNumber:
- case CDVJKTokenTypeString:
- case CDVJKTokenTypeTrue:
- case CDVJKTokenTypeFalse:
- case CDVJKTokenTypeNull:
- case CDVJKTokenTypeArrayBegin:
- case CDVJKTokenTypeObjectBegin:
- if(CDVJK_EXPECT_F((arrayState & CDVJKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; cdvjk_error(parseState, @"Unexpected value."); stopParsing = 1; break; }
- if(CDVJK_EXPECT_F((object = cdvjk_object_for_token(parseState)) == NULL)) { cdvjk_error(parseState, @"Internal error: Object == NULL"); stopParsing = 1; break; } else { parseState->objectStack.objects[parseState->objectStack.index++] = object; arrayState = CDVJKParseAcceptCommaOrEnd; }
- break;
- case CDVJKTokenTypeArrayEnd: if(CDVJK_EXPECT_T(arrayState & CDVJKParseAcceptEnd)) { NSCParameterAssert(parseState->objectStack.index >= startingObjectIndex); parsedArray = (void *)_CDVJKArrayCreate((id *)&parseState->objectStack.objects[startingObjectIndex], (parseState->objectStack.index - startingObjectIndex), parseState->mutableCollections); } else { parseState->errorIsPrev = 1; cdvjk_error(parseState, @"Unexpected ']'."); } stopParsing = 1; break;
- case CDVJKTokenTypeComma: if(CDVJK_EXPECT_T(arrayState & CDVJKParseAcceptComma)) { arrayState = CDVJKParseAcceptValue; } else { parseState->errorIsPrev = 1; cdvjk_error(parseState, @"Unexpected ','."); stopParsing = 1; } break;
- default: parseState->errorIsPrev = 1; cdvjk_error_parse_accept_or3(parseState, arrayState, @"a value", @"a comma", @"a ']'"); stopParsing = 1; break;
- }
- }
- }
-
- if(CDVJK_EXPECT_F(parsedArray == NULL)) { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { if(parseState->objectStack.objects[idx] != NULL) { CFRelease(parseState->objectStack.objects[idx]); parseState->objectStack.objects[idx] = NULL; } } }
-#if !defined(NS_BLOCK_ASSERTIONS)
- else { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { parseState->objectStack.objects[idx] = NULL; parseState->objectStack.keys[idx] = NULL; } }
-#endif
-
- parseState->objectStack.index = startingObjectIndex;
- return(parsedArray);
-}
-
-static void *cdvjk_create_dictionary(CDVJKParseState *parseState, size_t startingObjectIndex) {
- void *parsedDictionary = NULL;
-
- parseState->objectStack.index--;
-
- parsedDictionary = _CDVJKDictionaryCreate((id *)&parseState->objectStack.keys[startingObjectIndex], (NSUInteger *)&parseState->objectStack.cfHashes[startingObjectIndex], (id *)&parseState->objectStack.objects[startingObjectIndex], (parseState->objectStack.index - startingObjectIndex), parseState->mutableCollections);
-
- return(parsedDictionary);
-}
-
-static void *cdvjk_parse_dictionary(CDVJKParseState *parseState) {
- size_t startingObjectIndex = parseState->objectStack.index;
- int dictState = CDVJKParseAcceptValueOrEnd, stopParsing = 0;
- void *parsedDictionary = NULL;
-
- while(CDVJK_EXPECT_T((CDVJK_EXPECT_T(stopParsing == 0)) && (CDVJK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length)))) {
- if(CDVJK_EXPECT_F(parseState->objectStack.index > (parseState->objectStack.count - 4UL))) { if(cdvjk_objectStack_resize(&parseState->objectStack, parseState->objectStack.count + 128UL)) { cdvjk_error(parseState, @"Internal error: [dictionary] objectsIndex > %zu, resize failed? %@ line #%ld", (parseState->objectStack.count - 4UL), [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break; } }
-
- size_t objectStackIndex = parseState->objectStack.index++;
- parseState->objectStack.keys[objectStackIndex] = NULL;
- parseState->objectStack.objects[objectStackIndex] = NULL;
- void *key = NULL, *object = NULL;
-
- if(CDVJK_EXPECT_T((CDVJK_EXPECT_T(stopParsing == 0)) && (CDVJK_EXPECT_T((stopParsing = cdvjk_parse_next_token(parseState)) == 0)))) {
- switch(parseState->token.type) {
- case CDVJKTokenTypeString:
- if(CDVJK_EXPECT_F((dictState & CDVJKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; cdvjk_error(parseState, @"Unexpected string."); stopParsing = 1; break; }
- if(CDVJK_EXPECT_F((key = cdvjk_object_for_token(parseState)) == NULL)) { cdvjk_error(parseState, @"Internal error: Key == NULL."); stopParsing = 1; break; }
- else {
- parseState->objectStack.keys[objectStackIndex] = key;
- if(CDVJK_EXPECT_T(parseState->token.value.cacheItem != NULL)) { if(CDVJK_EXPECT_F(parseState->token.value.cacheItem->cfHash == 0UL)) { parseState->token.value.cacheItem->cfHash = CFHash(key); } parseState->objectStack.cfHashes[objectStackIndex] = parseState->token.value.cacheItem->cfHash; }
- else { parseState->objectStack.cfHashes[objectStackIndex] = CFHash(key); }
- }
- break;
-
- case CDVJKTokenTypeObjectEnd: if((CDVJK_EXPECT_T(dictState & CDVJKParseAcceptEnd))) { NSCParameterAssert(parseState->objectStack.index >= startingObjectIndex); parsedDictionary = cdvjk_create_dictionary(parseState, startingObjectIndex); } else { parseState->errorIsPrev = 1; cdvjk_error(parseState, @"Unexpected '}'."); } stopParsing = 1; break;
- case CDVJKTokenTypeComma: if((CDVJK_EXPECT_T(dictState & CDVJKParseAcceptComma))) { dictState = CDVJKParseAcceptValue; parseState->objectStack.index--; continue; } else { parseState->errorIsPrev = 1; cdvjk_error(parseState, @"Unexpected ','."); stopParsing = 1; } break;
-
- default: parseState->errorIsPrev = 1; cdvjk_error_parse_accept_or3(parseState, dictState, @"a \"STRING\"", @"a comma", @"a '}'"); stopParsing = 1; break;
- }
- }
-
- if(CDVJK_EXPECT_T(stopParsing == 0)) {
- if(CDVJK_EXPECT_T((stopParsing = cdvjk_parse_next_token(parseState)) == 0)) { if(CDVJK_EXPECT_F(parseState->token.type != CDVJKTokenTypeSeparator)) { parseState->errorIsPrev = 1; cdvjk_error(parseState, @"Expected ':'."); stopParsing = 1; } }
- }
-
- if((CDVJK_EXPECT_T(stopParsing == 0)) && (CDVJK_EXPECT_T((stopParsing = cdvjk_parse_next_token(parseState)) == 0))) {
- switch(parseState->token.type) {
- case CDVJKTokenTypeNumber:
- case CDVJKTokenTypeString:
- case CDVJKTokenTypeTrue:
- case CDVJKTokenTypeFalse:
- case CDVJKTokenTypeNull:
- case CDVJKTokenTypeArrayBegin:
- case CDVJKTokenTypeObjectBegin:
- if(CDVJK_EXPECT_F((dictState & CDVJKParseAcceptValue) == 0)) { parseState->errorIsPrev = 1; cdvjk_error(parseState, @"Unexpected value."); stopParsing = 1; break; }
- if(CDVJK_EXPECT_F((object = cdvjk_object_for_token(parseState)) == NULL)) { cdvjk_error(parseState, @"Internal error: Object == NULL."); stopParsing = 1; break; } else { parseState->objectStack.objects[objectStackIndex] = object; dictState = CDVJKParseAcceptCommaOrEnd; }
- break;
- default: parseState->errorIsPrev = 1; cdvjk_error_parse_accept_or3(parseState, dictState, @"a value", @"a comma", @"a '}'"); stopParsing = 1; break;
- }
- }
- }
-
- if(CDVJK_EXPECT_F(parsedDictionary == NULL)) { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { if(parseState->objectStack.keys[idx] != NULL) { CFRelease(parseState->objectStack.keys[idx]); parseState->objectStack.keys[idx] = NULL; } if(parseState->objectStack.objects[idx] != NULL) { CFRelease(parseState->objectStack.objects[idx]); parseState->objectStack.objects[idx] = NULL; } } }
-#if !defined(NS_BLOCK_ASSERTIONS)
- else { size_t idx = 0UL; for(idx = startingObjectIndex; idx < parseState->objectStack.index; idx++) { parseState->objectStack.objects[idx] = NULL; parseState->objectStack.keys[idx] = NULL; } }
-#endif
-
- parseState->objectStack.index = startingObjectIndex;
- return(parsedDictionary);
-}
-
-static id json_parse_it(CDVJKParseState *parseState) {
- id parsedObject = NULL;
- int stopParsing = 0;
-
- while((CDVJK_EXPECT_T(stopParsing == 0)) && (CDVJK_EXPECT_T(parseState->atIndex < parseState->stringBuffer.bytes.length))) {
- if((CDVJK_EXPECT_T(stopParsing == 0)) && (CDVJK_EXPECT_T((stopParsing = cdvjk_parse_next_token(parseState)) == 0))) {
- switch(parseState->token.type) {
- case CDVJKTokenTypeArrayBegin:
- case CDVJKTokenTypeObjectBegin: parsedObject = [(id)cdvjk_object_for_token(parseState) autorelease]; stopParsing = 1; break;
- default: cdvjk_error(parseState, @"Expected either '[' or '{'."); stopParsing = 1; break;
- }
- }
- }
-
- NSCParameterAssert((parseState->objectStack.index == 0) && (CDVJK_AT_STRING_PTR(parseState) <= CDVJK_END_STRING_PTR(parseState)));
-
- if((parsedObject == NULL) && (CDVJK_AT_STRING_PTR(parseState) == CDVJK_END_STRING_PTR(parseState))) { cdvjk_error(parseState, @"Reached the end of the buffer."); }
- if(parsedObject == NULL) { cdvjk_error(parseState, @"Unable to parse CDVJSON."); }
-
- if((parsedObject != NULL) && (CDVJK_AT_STRING_PTR(parseState) < CDVJK_END_STRING_PTR(parseState))) {
- cdvjk_parse_skip_whitespace(parseState);
- if((parsedObject != NULL) && ((parseState->parseOptionFlags & CDVJKParseOptionPermitTextAfterValidJSON) == 0) && (CDVJK_AT_STRING_PTR(parseState) < CDVJK_END_STRING_PTR(parseState))) {
- cdvjk_error(parseState, @"A valid CDVJSON object was parsed but there were additional non-white-space characters remaining.");
- parsedObject = NULL;
- }
- }
-
- return(parsedObject);
-}
-
-////////////
-#pragma mark -
-#pragma mark Object cache
-
-// This uses a Galois Linear Feedback Shift Register (LFSR) PRNG to pick which item in the cache to age. It has a period of (2^32)-1.
-// NOTE: A LFSR *MUST* be initialized to a non-zero value and must always have a non-zero value.
-CDVJK_STATIC_INLINE void cdvjk_cache_age(CDVJKParseState *parseState) {
- NSCParameterAssert((parseState != NULL) && (parseState->cache.prng_lfsr != 0U));
- parseState->cache.prng_lfsr = (parseState->cache.prng_lfsr >> 1) ^ ((0U - (parseState->cache.prng_lfsr & 1U)) & 0x80200003U);
- parseState->cache.age[parseState->cache.prng_lfsr & (parseState->cache.count - 1UL)] >>= 1;
-}
-
-// The object cache is nothing more than a hash table with open addressing collision resolution that is bounded by CDVJK_CACHE_PROBES attempts.
-//
-// The hash table is a linear C array of CDVJKTokenCacheItem. The terms "item" and "bucket" are synonymous with the index in to the cache array, i.e. cache.items[bucket].
-//
-// Items in the cache have an age associated with them. The age is the number of rightmost 1 bits, i.e. 0000 = 0, 0001 = 1, 0011 = 2, 0111 = 3, 1111 = 4.
-// This allows us to use left and right shifts to add or subtract from an items age. Add = (age << 1) | 1. Subtract = age >> 0. Subtract is synonymous with "age" (i.e., age an item).
-// The reason for this is it allows us to perform saturated adds and subtractions and is branchless.
-// The primitive C type MUST be unsigned. It is currently a "char", which allows (at a minimum and in practice) 8 bits.
-//
-// A "useable bucket" is a bucket that is not in use (never populated), or has an age == 0.
-//
-// When an item is found in the cache, it's age is incremented.
-// If a useable bucket hasn't been found, the current item (bucket) is aged along with two random items.
-//
-// If a value is not found in the cache, and no useable bucket has been found, that value is not added to the cache.
-
-static void *cdvjk_cachedObjects(CDVJKParseState *parseState) {
- unsigned long bucket = parseState->token.value.hash & (parseState->cache.count - 1UL), setBucket = 0UL, useableBucket = 0UL, x = 0UL;
- void *parsedAtom = NULL;
-
- if(CDVJK_EXPECT_F(parseState->token.value.ptrRange.length == 0UL) && CDVJK_EXPECT_T(parseState->token.value.type == CDVJKValueTypeString)) { return(@""); }
-
- for(x = 0UL; x < CDVJK_CACHE_PROBES; x++) {
- if(CDVJK_EXPECT_F(parseState->cache.items[bucket].object == NULL)) { setBucket = 1UL; useableBucket = bucket; break; }
-
- if((CDVJK_EXPECT_T(parseState->cache.items[bucket].hash == parseState->token.value.hash)) && (CDVJK_EXPECT_T(parseState->cache.items[bucket].size == parseState->token.value.ptrRange.length)) && (CDVJK_EXPECT_T(parseState->cache.items[bucket].type == parseState->token.value.type)) && (CDVJK_EXPECT_T(parseState->cache.items[bucket].bytes != NULL)) && (CDVJK_EXPECT_T(memcmp(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length) == 0U))) {
- parseState->cache.age[bucket] = (parseState->cache.age[bucket] << 1) | 1U;
- parseState->token.value.cacheItem = &parseState->cache.items[bucket];
- NSCParameterAssert(parseState->cache.items[bucket].object != NULL);
- return((void *)CFRetain(parseState->cache.items[bucket].object));
- } else {
- if(CDVJK_EXPECT_F(setBucket == 0UL) && CDVJK_EXPECT_F(parseState->cache.age[bucket] == 0U)) { setBucket = 1UL; useableBucket = bucket; }
- if(CDVJK_EXPECT_F(setBucket == 0UL)) { parseState->cache.age[bucket] >>= 1; cdvjk_cache_age(parseState); cdvjk_cache_age(parseState); }
- // This is the open addressing function. The values length and type are used as a form of "double hashing" to distribute values with the same effective value hash across different object cache buckets.
- // The values type is a prime number that is relatively coprime to the other primes in the set of value types and the number of hash table buckets.
- bucket = (parseState->token.value.hash + (parseState->token.value.ptrRange.length * (x + 1UL)) + (parseState->token.value.type * (x + 1UL)) + (3UL * (x + 1UL))) & (parseState->cache.count - 1UL);
- }
- }
-
- switch(parseState->token.value.type) {
- case CDVJKValueTypeString: parsedAtom = (void *)CFStringCreateWithBytes(NULL, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length, kCFStringEncodingUTF8, 0); break;
- case CDVJKValueTypeLongLong: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberLongLongType, &parseState->token.value.number.longLongValue); break;
- case CDVJKValueTypeUnsignedLongLong:
- if(parseState->token.value.number.unsignedLongLongValue <= LLONG_MAX) { parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberLongLongType, &parseState->token.value.number.unsignedLongLongValue); }
- else { parsedAtom = (void *)parseState->objCImpCache.NSNumberInitWithUnsignedLongLong(parseState->objCImpCache.NSNumberAlloc(parseState->objCImpCache.NSNumberClass, @selector(alloc)), @selector(initWithUnsignedLongLong:), parseState->token.value.number.unsignedLongLongValue); }
- break;
- case CDVJKValueTypeDouble: parsedAtom = (void *)CFNumberCreate(NULL, kCFNumberDoubleType, &parseState->token.value.number.doubleValue); break;
- default: cdvjk_error(parseState, @"Internal error: Unknown token value type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
- }
-
- if(CDVJK_EXPECT_T(setBucket) && (CDVJK_EXPECT_T(parsedAtom != NULL))) {
- bucket = useableBucket;
- if(CDVJK_EXPECT_T((parseState->cache.items[bucket].object != NULL))) { CFRelease(parseState->cache.items[bucket].object); parseState->cache.items[bucket].object = NULL; }
-
- if(CDVJK_EXPECT_T((parseState->cache.items[bucket].bytes = (unsigned char *)reallocf(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.length)) != NULL)) {
- memcpy(parseState->cache.items[bucket].bytes, parseState->token.value.ptrRange.ptr, parseState->token.value.ptrRange.length);
- parseState->cache.items[bucket].object = (void *)CFRetain(parsedAtom);
- parseState->cache.items[bucket].hash = parseState->token.value.hash;
- parseState->cache.items[bucket].cfHash = 0UL;
- parseState->cache.items[bucket].size = parseState->token.value.ptrRange.length;
- parseState->cache.items[bucket].type = parseState->token.value.type;
- parseState->token.value.cacheItem = &parseState->cache.items[bucket];
- parseState->cache.age[bucket] = CDVJK_INIT_CACHE_AGE;
- } else { // The realloc failed, so clear the appropriate fields.
- parseState->cache.items[bucket].hash = 0UL;
- parseState->cache.items[bucket].cfHash = 0UL;
- parseState->cache.items[bucket].size = 0UL;
- parseState->cache.items[bucket].type = 0UL;
- }
- }
-
- return(parsedAtom);
-}
-
-
-static void *cdvjk_object_for_token(CDVJKParseState *parseState) {
- void *parsedAtom = NULL;
-
- parseState->token.value.cacheItem = NULL;
- switch(parseState->token.type) {
- case CDVJKTokenTypeString: parsedAtom = cdvjk_cachedObjects(parseState); break;
- case CDVJKTokenTypeNumber: parsedAtom = cdvjk_cachedObjects(parseState); break;
- case CDVJKTokenTypeObjectBegin: parsedAtom = cdvjk_parse_dictionary(parseState); break;
- case CDVJKTokenTypeArrayBegin: parsedAtom = cdvjk_parse_array(parseState); break;
- case CDVJKTokenTypeTrue: parsedAtom = (void *)kCFBooleanTrue; break;
- case CDVJKTokenTypeFalse: parsedAtom = (void *)kCFBooleanFalse; break;
- case CDVJKTokenTypeNull: parsedAtom = (void *)kCFNull; break;
- default: cdvjk_error(parseState, @"Internal error: Unknown token type. %@ line #%ld", [NSString stringWithUTF8String:__FILE__], (long)__LINE__); break;
- }
-
- return(parsedAtom);
-}
-
-#pragma mark -
-@implementation CDVJSONDecoder
-
-+ (id)decoder
-{
- return([self decoderWithParseOptions:CDVJKParseOptionStrict]);
-}
-
-+ (id)decoderWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags
-{
- return([[[self alloc] initWithParseOptions:parseOptionFlags] autorelease]);
-}
-
-- (id)init
-{
- return([self initWithParseOptions:CDVJKParseOptionStrict]);
-}
-
-- (id)initWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags
-{
- if((self = [super init]) == NULL) { return(NULL); }
-
- if(parseOptionFlags & ~CDVJKParseOptionValidFlags) { [self autorelease]; [NSException raise:NSInvalidArgumentException format:@"Invalid parse options."]; }
-
- if((parseState = (CDVJKParseState *)calloc(1UL, sizeof(CDVJKParseState))) == NULL) { goto errorExit; }
-
- parseState->parseOptionFlags = parseOptionFlags;
-
- parseState->token.tokenBuffer.roundSizeUpToMultipleOf = 4096UL;
- parseState->objectStack.roundSizeUpToMultipleOf = 2048UL;
-
- parseState->objCImpCache.NSNumberClass = _CDVjk_NSNumberClass;
- parseState->objCImpCache.NSNumberAlloc = _CDVjk_NSNumberAllocImp;
- parseState->objCImpCache.NSNumberInitWithUnsignedLongLong = _CDVjk_NSNumberInitWithUnsignedLongLongImp;
-
- parseState->cache.prng_lfsr = 1U;
- parseState->cache.count = CDVJK_CACHE_SLOTS;
- if((parseState->cache.items = (CDVJKTokenCacheItem *)calloc(1UL, sizeof(CDVJKTokenCacheItem) * parseState->cache.count)) == NULL) { goto errorExit; }
-
- return(self);
-
- errorExit:
- if(self) { [self autorelease]; self = NULL; }
- return(NULL);
-}
-
-// This is here primarily to support the NSString and NSData convenience functions so the autoreleased CDVJSONDecoder can release most of its resources before the pool pops.
-static void _JSONDecoderCleanup(CDVJSONDecoder *decoder) {
- if((decoder != NULL) && (decoder->parseState != NULL)) {
- cdvjk_managedBuffer_release(&decoder->parseState->token.tokenBuffer);
- cdvjk_objectStack_release(&decoder->parseState->objectStack);
-
- [decoder clearCache];
- if(decoder->parseState->cache.items != NULL) { free(decoder->parseState->cache.items); decoder->parseState->cache.items = NULL; }
-
- free(decoder->parseState); decoder->parseState = NULL;
- }
-}
-
-- (void)dealloc
-{
- _JSONDecoderCleanup(self);
- [super dealloc];
-}
-
-- (void)clearCache
-{
- if(CDVJK_EXPECT_T(parseState != NULL)) {
- if(CDVJK_EXPECT_T(parseState->cache.items != NULL)) {
- size_t idx = 0UL;
- for(idx = 0UL; idx < parseState->cache.count; idx++) {
- if(CDVJK_EXPECT_T(parseState->cache.items[idx].object != NULL)) { CFRelease(parseState->cache.items[idx].object); parseState->cache.items[idx].object = NULL; }
- if(CDVJK_EXPECT_T(parseState->cache.items[idx].bytes != NULL)) { free(parseState->cache.items[idx].bytes); parseState->cache.items[idx].bytes = NULL; }
- memset(&parseState->cache.items[idx], 0, sizeof(CDVJKTokenCacheItem));
- parseState->cache.age[idx] = 0U;
- }
- }
- }
-}
-
-// This needs to be completely rewritten.
-static id _CDVJKParseUTF8String(CDVJKParseState *parseState, BOOL mutableCollections, const unsigned char *string, size_t length, NSError **error) {
- NSCParameterAssert((parseState != NULL) && (string != NULL) && (parseState->cache.prng_lfsr != 0U));
- parseState->stringBuffer.bytes.ptr = string;
- parseState->stringBuffer.bytes.length = length;
- parseState->atIndex = 0UL;
- parseState->lineNumber = 1UL;
- parseState->lineStartIndex = 0UL;
- parseState->prev_atIndex = 0UL;
- parseState->prev_lineNumber = 1UL;
- parseState->prev_lineStartIndex = 0UL;
- parseState->error = NULL;
- parseState->errorIsPrev = 0;
- parseState->mutableCollections = (mutableCollections == NO) ? NO : YES;
-
- unsigned char stackTokenBuffer[CDVJK_TOKENBUFFER_SIZE] CDVJK_ALIGNED(64);
- cdvjk_managedBuffer_setToStackBuffer(&parseState->token.tokenBuffer, stackTokenBuffer, sizeof(stackTokenBuffer));
-
- void *stackObjects [CDVJK_STACK_OBJS] CDVJK_ALIGNED(64);
- void *stackKeys [CDVJK_STACK_OBJS] CDVJK_ALIGNED(64);
- CFHashCode stackCFHashes[CDVJK_STACK_OBJS] CDVJK_ALIGNED(64);
- cdvjk_objectStack_setToStackBuffer(&parseState->objectStack, stackObjects, stackKeys, stackCFHashes, CDVJK_STACK_OBJS);
-
- id parsedJSON = json_parse_it(parseState);
-
- if((error != NULL) && (parseState->error != NULL)) { *error = parseState->error; }
-
- cdvjk_managedBuffer_release(&parseState->token.tokenBuffer);
- cdvjk_objectStack_release(&parseState->objectStack);
-
- parseState->stringBuffer.bytes.ptr = NULL;
- parseState->stringBuffer.bytes.length = 0UL;
- parseState->atIndex = 0UL;
- parseState->lineNumber = 1UL;
- parseState->lineStartIndex = 0UL;
- parseState->prev_atIndex = 0UL;
- parseState->prev_lineNumber = 1UL;
- parseState->prev_lineStartIndex = 0UL;
- parseState->error = NULL;
- parseState->errorIsPrev = 0;
- parseState->mutableCollections = NO;
-
- return(parsedJSON);
-}
-
-////////////
-#pragma mark Deprecated as of v1.4
-////////////
-
-// Deprecated in CDVJSONKit v1.4. Use objectWithUTF8String:length: instead.
-- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length
-{
- return([self objectWithUTF8String:string length:length error:NULL]);
-}
-
-// Deprecated in CDVJSONKit v1.4. Use objectWithUTF8String:length:error: instead.
-- (id)parseUTF8String:(const unsigned char *)string length:(size_t)length error:(NSError **)error
-{
- return([self objectWithUTF8String:string length:length error:error]);
-}
-
-// Deprecated in CDVJSONKit v1.4. Use objectWithData: instead.
-- (id)parseJSONData:(NSData *)jsonData
-{
- return([self objectWithData:jsonData error:NULL]);
-}
-
-// Deprecated in CDVJSONKit v1.4. Use objectWithData:error: instead.
-- (id)parseJSONData:(NSData *)jsonData error:(NSError **)error
-{
- return([self objectWithData:jsonData error:error]);
-}
-
-////////////
-#pragma mark Methods that return immutable collection objects
-////////////
-
-- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length
-{
- return([self objectWithUTF8String:string length:length error:NULL]);
-}
-
-- (id)objectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error
-{
- if(parseState == NULL) { [NSException raise:NSInternalInconsistencyException format:@"parseState is NULL."]; }
- if(string == NULL) { [NSException raise:NSInvalidArgumentException format:@"The string argument is NULL."]; }
-
- return(_CDVJKParseUTF8String(parseState, NO, string, (size_t)length, error));
-}
-
-- (id)objectWithData:(NSData *)jsonData
-{
- return([self objectWithData:jsonData error:NULL]);
-}
-
-- (id)objectWithData:(NSData *)jsonData error:(NSError **)error
-{
- if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
- return([self objectWithUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
-}
-
-////////////
-#pragma mark Methods that return mutable collection objects
-////////////
-
-- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length
-{
- return([self mutableObjectWithUTF8String:string length:length error:NULL]);
-}
-
-- (id)mutableObjectWithUTF8String:(const unsigned char *)string length:(NSUInteger)length error:(NSError **)error
-{
- if(parseState == NULL) { [NSException raise:NSInternalInconsistencyException format:@"parseState is NULL."]; }
- if(string == NULL) { [NSException raise:NSInvalidArgumentException format:@"The string argument is NULL."]; }
-
- return(_CDVJKParseUTF8String(parseState, YES, string, (size_t)length, error));
-}
-
-- (id)mutableObjectWithData:(NSData *)jsonData
-{
- return([self mutableObjectWithData:jsonData error:NULL]);
-}
-
-- (id)mutableObjectWithData:(NSData *)jsonData error:(NSError **)error
-{
- if(jsonData == NULL) { [NSException raise:NSInvalidArgumentException format:@"The jsonData argument is NULL."]; }
- return([self mutableObjectWithUTF8String:(const unsigned char *)[jsonData bytes] length:[jsonData length] error:error]);
-}
-
-@end
-
-/*
- The NSString and NSData convenience methods need a little bit of explanation.
-
- Prior to CDVJSONKit v1.4, the NSString -objectFromJSONStringWithParseOptions:error: method looked like
-
- const unsigned char *utf8String = (const unsigned char *)[self UTF8String];
- if(utf8String == NULL) { return(NULL); }
- size_t utf8Length = strlen((const char *)utf8String);
- return([[JSONDecoder decoderWithParseOptions:parseOptionFlags] parseUTF8String:utf8String length:utf8Length error:error]);
-
- This changed with v1.4 to a more complicated method. The reason for this is to keep the amount of memory that is
- allocated, but not yet freed because it is dependent on the autorelease pool to pop before it can be reclaimed.
-
- In the simpler v1.3 code, this included all the bytes used to store the -UTF8String along with the CDVJSONDecoder and all its overhead.
-
- Now we use an autoreleased CFMutableData that is sized to the UTF8 length of the NSString in question and is used to hold the UTF8
- conversion of said string.
-
- Once parsed, the CFMutableData has its length set to 0. This should, hopefully, allow the CFMutableData to realloc and/or free
- the buffer.
-
- Another change made was a slight modification to CDVJSONDecoder so that most of the cleanup work that was done in -dealloc was moved
- to a private, internal function. These convenience routines keep the pointer to the autoreleased CDVJSONDecoder and calls
- _JSONDecoderCleanup() to early release the decoders resources since we already know that particular decoder is not going to be used
- again.
-
- If everything goes smoothly, this will most likely result in perhaps a few hundred bytes that are allocated but waiting for the
- autorelease pool to pop. This is compared to the thousands and easily hundreds of thousands of bytes that would have been in
- autorelease limbo. It's more complicated for us, but a win for the user.
-
- Autorelease objects are used in case things don't go smoothly. By having them autoreleased, we effectively guarantee that our
- requirement to -release the object is always met, not matter what goes wrong. The downside is having a an object or two in
- autorelease limbo, but we've done our best to minimize that impact, so it all balances out.
- */
-
-@implementation NSString (CDVJSONKitDeserializing)
-
-static id _NSStringObjectFromJSONString(NSString *jsonString, CDVJKParseOptionFlags parseOptionFlags, NSError **error, BOOL mutableCollection) {
- id returnObject = NULL;
- CFMutableDataRef mutableData = NULL;
- CDVJSONDecoder *decoder = NULL;
-
- CFIndex stringLength = CFStringGetLength((CFStringRef)jsonString);
- NSUInteger stringUTF8Length = [jsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
-
- if((mutableData = (CFMutableDataRef)[(id)CFDataCreateMutable(NULL, (NSUInteger)stringUTF8Length) autorelease]) != NULL) {
- UInt8 *utf8String = CFDataGetMutableBytePtr(mutableData);
- CFIndex usedBytes = 0L, convertedCount = 0L;
-
- convertedCount = CFStringGetBytes((CFStringRef)jsonString, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', NO, utf8String, (NSUInteger)stringUTF8Length, &usedBytes);
- if(CDVJK_EXPECT_F(convertedCount != stringLength) || CDVJK_EXPECT_F(usedBytes < 0L)) { if(error != NULL) { *error = [NSError errorWithDomain:@"CDVJKErrorDomain" code:-1L userInfo:[NSDictionary dictionaryWithObject:@"An error occurred converting the contents of a NSString to UTF8." forKey:NSLocalizedDescriptionKey]]; } goto exitNow; }
-
- if(mutableCollection == NO) { returnObject = [(decoder = [CDVJSONDecoder decoderWithParseOptions:parseOptionFlags]) objectWithUTF8String:(const unsigned char *)utf8String length:(size_t)usedBytes error:error]; }
- else { returnObject = [(decoder = [CDVJSONDecoder decoderWithParseOptions:parseOptionFlags]) mutableObjectWithUTF8String:(const unsigned char *)utf8String length:(size_t)usedBytes error:error]; }
- }
-
-exitNow:
- if(mutableData != NULL) { CFDataSetLength(mutableData, 0L); }
- if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
- return(returnObject);
-}
-
-- (id)cdvjk_objectFromJSONString
-{
- return([self cdvjk_objectFromJSONStringWithParseOptions:CDVJKParseOptionStrict error:NULL]);
-}
-
-- (id)cdvjk_objectFromJSONStringWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags
-{
- return([self cdvjk_objectFromJSONStringWithParseOptions:parseOptionFlags error:NULL]);
-}
-
-- (id)cdvjk_objectFromJSONStringWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags error:(NSError **)error
-{
- return(_NSStringObjectFromJSONString(self, parseOptionFlags, error, NO));
-}
-
-
-- (id)cdvjk_mutableObjectFromJSONString
-{
- return([self cdvjk_mutableObjectFromJSONStringWithParseOptions:CDVJKParseOptionStrict error:NULL]);
-}
-
-- (id)cdvjk_mutableObjectFromJSONStringWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags
-{
- return([self cdvjk_mutableObjectFromJSONStringWithParseOptions:parseOptionFlags error:NULL]);
-}
-
-- (id)cdvjk_mutableObjectFromJSONStringWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags error:(NSError **)error
-{
- return(_NSStringObjectFromJSONString(self, parseOptionFlags, error, YES));
-}
-
-@end
-
-@implementation NSData (CDVJSONKitDeserializing)
-
-- (id)cdvjk_objectFromJSONData
-{
- return([self cdvjk_objectFromJSONDataWithParseOptions:CDVJKParseOptionStrict error:NULL]);
-}
-
-- (id)cdvjk_objectFromJSONDataWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags
-{
- return([self cdvjk_objectFromJSONDataWithParseOptions:parseOptionFlags error:NULL]);
-}
-
-- (id)cdvjk_objectFromJSONDataWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags error:(NSError **)error
-{
- CDVJSONDecoder *decoder = NULL;
- id returnObject = [(decoder = [CDVJSONDecoder decoderWithParseOptions:parseOptionFlags]) objectWithData:self error:error];
- if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
- return(returnObject);
-}
-
-- (id)cdvjk_mutableObjectFromJSONData
-{
- return([self cdvjk_mutableObjectFromJSONDataWithParseOptions:CDVJKParseOptionStrict error:NULL]);
-}
-
-- (id)cdvjk_mutableObjectFromJSONDataWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags
-{
- return([self cdvjk_mutableObjectFromJSONDataWithParseOptions:parseOptionFlags error:NULL]);
-}
-
-- (id)cdvjk_mutableObjectFromJSONDataWithParseOptions:(CDVJKParseOptionFlags)parseOptionFlags error:(NSError **)error
-{
- CDVJSONDecoder *decoder = NULL;
- id returnObject = [(decoder = [CDVJSONDecoder decoderWithParseOptions:parseOptionFlags]) mutableObjectWithData:self error:error];
- if(decoder != NULL) { _JSONDecoderCleanup(decoder); }
- return(returnObject);
-}
-
-
-@end
-
-////////////
-#pragma mark -
-#pragma mark Encoding / deserializing functions
-
-static void cdvjk_encode_error(CDVJKEncodeState *encodeState, NSString *format, ...) {
- NSCParameterAssert((encodeState != NULL) && (format != NULL));
-
- va_list varArgsList;
- va_start(varArgsList, format);
- NSString *formatString = [[[NSString alloc] initWithFormat:format arguments:varArgsList] autorelease];
- va_end(varArgsList);
-
- if(encodeState->error == NULL) {
- encodeState->error = [NSError errorWithDomain:@"CDVJKErrorDomain" code:-1L userInfo:
- [NSDictionary dictionaryWithObjectsAndKeys:
- formatString, NSLocalizedDescriptionKey,
- NULL]];
- }
-}
-
-CDVJK_STATIC_INLINE void cdvjk_encode_updateCache(CDVJKEncodeState *encodeState, CDVJKEncodeCache *cacheSlot, size_t startingAtIndex, id object) {
- NSCParameterAssert(encodeState != NULL);
- if(CDVJK_EXPECT_T(cacheSlot != NULL)) {
- NSCParameterAssert((object != NULL) && (startingAtIndex <= encodeState->atIndex));
- cacheSlot->object = object;
- cacheSlot->offset = startingAtIndex;
- cacheSlot->length = (size_t)(encodeState->atIndex - startingAtIndex);
- }
-}
-
-static int cdvjk_encode_printf(CDVJKEncodeState *encodeState, CDVJKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, ...) {
- va_list varArgsList, varArgsListCopy;
- va_start(varArgsList, format);
- va_copy(varArgsListCopy, varArgsList);
-
- NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex) && (format != NULL));
-
- ssize_t formattedStringLength = 0L;
- int returnValue = 0;
-
- if(CDVJK_EXPECT_T((formattedStringLength = vsnprintf((char *)&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex], (encodeState->stringBuffer.bytes.length - encodeState->atIndex), format, varArgsList)) >= (ssize_t)(encodeState->stringBuffer.bytes.length - encodeState->atIndex))) {
- NSCParameterAssert(((encodeState->atIndex + (formattedStringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length));
- if(CDVJK_EXPECT_F(((encodeState->atIndex + (formattedStringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length)) && CDVJK_EXPECT_F((cdvjk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (formattedStringLength * 2UL)+ 4096UL) == NULL))) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); returnValue = 1; goto exitNow; }
- if(CDVJK_EXPECT_F((formattedStringLength = vsnprintf((char *)&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex], (encodeState->stringBuffer.bytes.length - encodeState->atIndex), format, varArgsListCopy)) >= (ssize_t)(encodeState->stringBuffer.bytes.length - encodeState->atIndex))) { cdvjk_encode_error(encodeState, @"vsnprintf failed unexpectedly."); returnValue = 1; goto exitNow; }
- }
-
-exitNow:
- va_end(varArgsList);
- va_end(varArgsListCopy);
- if(CDVJK_EXPECT_T(returnValue == 0)) { encodeState->atIndex += formattedStringLength; cdvjk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object); }
- return(returnValue);
-}
-
-static int cdvjk_encode_write(CDVJKEncodeState *encodeState, CDVJKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format) {
- NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex) && (format != NULL));
- if(CDVJK_EXPECT_F(((encodeState->atIndex + strlen(format) + 256UL) > encodeState->stringBuffer.bytes.length)) && CDVJK_EXPECT_F((cdvjk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + strlen(format) + 1024UL) == NULL))) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
-
- size_t formatIdx = 0UL;
- for(formatIdx = 0UL; format[formatIdx] != 0; formatIdx++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[formatIdx]; }
- cdvjk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
- return(0);
-}
-
-static int cdvjk_encode_writePrettyPrintWhiteSpace(CDVJKEncodeState *encodeState) {
- NSCParameterAssert((encodeState != NULL) && ((encodeState->serializeOptionFlags & CDVJKSerializeOptionPretty) != 0UL));
- if(CDVJK_EXPECT_F((encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 16UL) > encodeState->stringBuffer.bytes.length) && CDVJK_EXPECT_T(cdvjk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 4096UL) == NULL)) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
- encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\n';
- size_t depthWhiteSpace = 0UL;
- for(depthWhiteSpace = 0UL; depthWhiteSpace < (encodeState->depth * 2UL); depthWhiteSpace++) { NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length); encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
- return(0);
-}
-
-static int cdvjk_encode_write1slow(CDVJKEncodeState *encodeState, ssize_t depthChange, const char *format) {
- NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (format != NULL) && ((depthChange >= -1L) && (depthChange <= 1L)) && ((encodeState->depth == 0UL) ? (depthChange >= 0L) : 1) && ((encodeState->serializeOptionFlags & CDVJKSerializeOptionPretty) != 0UL));
- if(CDVJK_EXPECT_F((encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 16UL) > encodeState->stringBuffer.bytes.length) && CDVJK_EXPECT_F(cdvjk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + ((encodeState->depth + 1UL) * 2UL) + 4096UL) == NULL)) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
- encodeState->depth += depthChange;
- if(CDVJK_EXPECT_T(format[0] == ':')) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = ' '; }
- else {
- if(CDVJK_EXPECT_F(depthChange == -1L)) { if(CDVJK_EXPECT_F(cdvjk_encode_writePrettyPrintWhiteSpace(encodeState))) { return(1); } }
- encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0];
- if(CDVJK_EXPECT_T(depthChange != -1L)) { if(CDVJK_EXPECT_F(cdvjk_encode_writePrettyPrintWhiteSpace(encodeState))) { return(1); } }
- }
- NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
- return(0);
-}
-
-static int cdvjk_encode_write1fast(CDVJKEncodeState *encodeState, ssize_t depthChange CDVJK_UNUSED_ARG, const char *format) {
- NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && ((encodeState->serializeOptionFlags & CDVJKSerializeOptionPretty) == 0UL));
- if(CDVJK_EXPECT_T((encodeState->atIndex + 4UL) < encodeState->stringBuffer.bytes.length)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = format[0]; }
- else { return(cdvjk_encode_write(encodeState, NULL, 0UL, NULL, format)); }
- return(0);
-}
-
-static int cdvjk_encode_writen(CDVJKEncodeState *encodeState, CDVJKEncodeCache *cacheSlot, size_t startingAtIndex, id object, const char *format, size_t length) {
- NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (startingAtIndex <= encodeState->atIndex));
- if(CDVJK_EXPECT_F((encodeState->stringBuffer.bytes.length - encodeState->atIndex) < (length + 4UL))) { if(cdvjk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + 4096UL + length) == NULL) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); } }
- memcpy(encodeState->stringBuffer.bytes.ptr + encodeState->atIndex, format, length);
- encodeState->atIndex += length;
- cdvjk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, object);
- return(0);
-}
-
-CDVJK_STATIC_INLINE CDVJKHash cdvjk_encode_object_hash(void *objectPtr) {
- return( ( (((CDVJKHash)objectPtr) >> 21) ^ (((CDVJKHash)objectPtr) >> 9) ) + (((CDVJKHash)objectPtr) >> 4) );
-}
-
-static int cdvjk_encode_add_atom_to_buffer(CDVJKEncodeState *encodeState, void *objectPtr) {
- NSCParameterAssert((encodeState != NULL) && (encodeState->atIndex < encodeState->stringBuffer.bytes.length) && (objectPtr != NULL));
-
- id object = (id)objectPtr, encodeCacheObject = object;
- int isClass = CDVJKClassUnknown;
- size_t startingAtIndex = encodeState->atIndex;
-
- CDVJKHash objectHash = cdvjk_encode_object_hash(objectPtr);
- CDVJKEncodeCache *cacheSlot = &encodeState->cache[objectHash % CDVJK_ENCODE_CACHE_SLOTS];
-
- if(CDVJK_EXPECT_T(cacheSlot->object == object)) {
- NSCParameterAssert((cacheSlot->object != NULL) &&
- (cacheSlot->offset < encodeState->atIndex) && ((cacheSlot->offset + cacheSlot->length) < encodeState->atIndex) &&
- (cacheSlot->offset < encodeState->stringBuffer.bytes.length) && ((cacheSlot->offset + cacheSlot->length) < encodeState->stringBuffer.bytes.length) &&
- ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
- ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
- ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)));
- if(CDVJK_EXPECT_F(((encodeState->atIndex + cacheSlot->length + 256UL) > encodeState->stringBuffer.bytes.length)) && CDVJK_EXPECT_F((cdvjk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + cacheSlot->length + 1024UL) == NULL))) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
- NSCParameterAssert(((encodeState->atIndex + cacheSlot->length) < encodeState->stringBuffer.bytes.length) &&
- ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
- ((encodeState->stringBuffer.bytes.ptr + encodeState->atIndex + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
- ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
- ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->stringBuffer.bytes.length)) &&
- ((encodeState->stringBuffer.bytes.ptr + cacheSlot->offset + cacheSlot->length) < (encodeState->stringBuffer.bytes.ptr + encodeState->atIndex)));
- memcpy(encodeState->stringBuffer.bytes.ptr + encodeState->atIndex, encodeState->stringBuffer.bytes.ptr + cacheSlot->offset, cacheSlot->length);
- encodeState->atIndex += cacheSlot->length;
- return(0);
- }
-
- // When we encounter a class that we do not handle, and we have either a delegate or block that the user supplied to format unsupported classes,
- // we "re-run" the object check. However, we re-run the object check exactly ONCE. If the user supplies an object that isn't one of the
- // supported classes, we fail the second time (i.e., double fault error).
- BOOL rerunningAfterClassFormatter = NO;
- rerunAfterClassFormatter:;
-
- // XXX XXX XXX XXX
- //
- // We need to work around a bug in 10.7, which breaks ABI compatibility with Objective-C going back not just to 10.0, but OpenStep and even NextStep.
- //
- // It has long been documented that "the very first thing that a pointer to an Objective-C object "points to" is a pointer to that objects class".
- //
- // This is euphemistically called "tagged pointers". There are a number of highly technical problems with this, most involving long passages from
- // the C standard(s). In short, one can make a strong case, couched from the perspective of the C standard(s), that that 10.7 "tagged pointers" are
- // fundamentally Wrong and Broken, and should have never been implemented. Assuming those points are glossed over, because the change is very clearly
- // breaking ABI compatibility, this should have resulted in a minimum of a "minimum version required" bump in various shared libraries to prevent
- // causes code that used to work just fine to suddenly break without warning.
- //
- // In fact, the C standard says that the hack below is "undefined behavior"- there is no requirement that the 10.7 tagged pointer hack of setting the
- // "lower, unused bits" must be preserved when casting the result to an integer type, but this "works" because for most architectures
- // `sizeof(long) == sizeof(void *)` and the compiler uses the same representation for both. (note: this is informal, not meant to be
- // normative or pedantically correct).
- //
- // In other words, while this "works" for now, technically the compiler is not obligated to do "what we want", and a later version of the compiler
- // is not required in any way to produce the same results or behavior that earlier versions of the compiler did for the statement below.
- //
- // Fan-fucking-tastic.
- //
- // Why not just use `object_getClass()`? Because `object->isa` reduces to (typically) a *single* instruction. Calling `object_getClass()` requires
- // that the compiler potentially spill registers, establish a function call frame / environment, and finally execute a "jump subroutine" instruction.
- // Then, the called subroutine must spend half a dozen instructions in its prolog, however many instructions doing whatever it does, then half a dozen
- // instructions in its prolog. One instruction compared to dozens, maybe a hundred instructions.
- //
- // Yes, that's one to two orders of magnitude difference. Which is compelling in its own right. When going for performance, you're often happy with
- // gains in the two to three percent range.
- //
- // XXX XXX XXX XXX
-
- BOOL workAroundMacOSXABIBreakingBug = NO;
- if(CDVJK_EXPECT_F(((NSUInteger)object) & 0x1)) { workAroundMacOSXABIBreakingBug = YES; goto slowClassLookup; }
-
- if(CDVJK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.stringClass)) { isClass = CDVJKClassString; }
- else if(CDVJK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.numberClass)) { isClass = CDVJKClassNumber; }
- else if(CDVJK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.dictionaryClass)) { isClass = CDVJKClassDictionary; }
- else if(CDVJK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.arrayClass)) { isClass = CDVJKClassArray; }
- else if(CDVJK_EXPECT_T(object_getClass(object) == encodeState->fastClassLookup.nullClass)) { isClass = CDVJKClassNull; }
- else {
- slowClassLookup:
- if(CDVJK_EXPECT_T([object isKindOfClass:[NSString class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.stringClass = object_getClass(object); } isClass = CDVJKClassString; }
- else if(CDVJK_EXPECT_T([object isKindOfClass:[NSNumber class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.numberClass = object_getClass(object); } isClass = CDVJKClassNumber; }
- else if(CDVJK_EXPECT_T([object isKindOfClass:[NSDictionary class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.dictionaryClass = object_getClass(object); } isClass = CDVJKClassDictionary; }
- else if(CDVJK_EXPECT_T([object isKindOfClass:[NSArray class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.arrayClass = object_getClass(object); } isClass = CDVJKClassArray; }
- else if(CDVJK_EXPECT_T([object isKindOfClass:[NSNull class]])) { if(workAroundMacOSXABIBreakingBug == NO) { encodeState->fastClassLookup.nullClass = object_getClass(object); } isClass = CDVJKClassNull; }
- else {
- if((rerunningAfterClassFormatter == NO) && (
-#ifdef __BLOCKS__
- ((encodeState->classFormatterBlock) && ((object = encodeState->classFormatterBlock(object)) != NULL)) ||
-#endif
- ((encodeState->classFormatterIMP) && ((object = encodeState->classFormatterIMP(encodeState->classFormatterDelegate, encodeState->classFormatterSelector, object)) != NULL)) )) { rerunningAfterClassFormatter = YES; goto rerunAfterClassFormatter; }
-
- if(rerunningAfterClassFormatter == NO) { cdvjk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([encodeCacheObject class])); return(1); }
- else { cdvjk_encode_error(encodeState, @"Unable to serialize object class %@ that was returned by the unsupported class formatter. Original object class was %@.", (object == NULL) ? @"NULL" : NSStringFromClass([object class]), NSStringFromClass([encodeCacheObject class])); return(1); }
- }
- }
-
- // This is here for the benefit of the optimizer. It allows the optimizer to do loop invariant code motion for the CDVJKClassArray
- // and CDVJKClassDictionary cases when printing simple, single characters via cdvjk_encode_write(), which is actually a macro:
- // #define cdvjk_encode_write1(es, dc, f) (_jk_encode_prettyPrint ? cdvjk_encode_write1slow(es, dc, f) : cdvjk_encode_write1fast(es, dc, f))
- int _jk_encode_prettyPrint = CDVJK_EXPECT_T((encodeState->serializeOptionFlags & CDVJKSerializeOptionPretty) == 0) ? 0 : 1;
-
- switch(isClass) {
- case CDVJKClassString:
- {
- {
- const unsigned char *cStringPtr = (const unsigned char *)CFStringGetCStringPtr((CFStringRef)object, kCFStringEncodingMacRoman);
- if(cStringPtr != NULL) {
- const unsigned char *utf8String = cStringPtr;
- size_t utf8Idx = 0UL;
-
- CFIndex stringLength = CFStringGetLength((CFStringRef)object);
- if(CDVJK_EXPECT_F(((encodeState->atIndex + (stringLength * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length)) && CDVJK_EXPECT_F((cdvjk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (stringLength * 2UL) + 1024UL) == NULL))) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
-
- if(CDVJK_EXPECT_T((encodeState->encodeOption & CDVJKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
- for(utf8Idx = 0UL; utf8String[utf8Idx] != 0U; utf8Idx++) {
- NSCParameterAssert(((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex]) - encodeState->stringBuffer.bytes.ptr) < (ssize_t)encodeState->stringBuffer.bytes.length);
- NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
- if(CDVJK_EXPECT_F(utf8String[utf8Idx] >= 0x80U)) { encodeState->atIndex = startingAtIndex; goto slowUTF8Path; }
- if(CDVJK_EXPECT_F(utf8String[utf8Idx] < 0x20U)) {
- switch(utf8String[utf8Idx]) {
- case '\b': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'b'; break;
- case '\f': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'f'; break;
- case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
- case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
- case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
- default: if(CDVJK_EXPECT_F(cdvjk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
- }
- } else {
- if(CDVJK_EXPECT_F(utf8String[utf8Idx] == '\"') || CDVJK_EXPECT_F(utf8String[utf8Idx] == '\\') || (CDVJK_EXPECT_F(encodeState->serializeOptionFlags & CDVJKSerializeOptionEscapeForwardSlashes) && CDVJK_EXPECT_F(utf8String[utf8Idx] == '/'))) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; }
- encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
- }
- }
- NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length);
- if(CDVJK_EXPECT_T((encodeState->encodeOption & CDVJKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
- cdvjk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject);
- return(0);
- }
- }
-
- slowUTF8Path:
- {
- CFIndex stringLength = CFStringGetLength((CFStringRef)object);
- CFIndex maxStringUTF8Length = CFStringGetMaximumSizeForEncoding(stringLength, kCFStringEncodingUTF8) + 32L;
-
- if(CDVJK_EXPECT_F((size_t)maxStringUTF8Length > encodeState->utf8ConversionBuffer.bytes.length) && CDVJK_EXPECT_F(cdvjk_managedBuffer_resize(&encodeState->utf8ConversionBuffer, maxStringUTF8Length + 1024UL) == NULL)) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
-
- CFIndex usedBytes = 0L, convertedCount = 0L;
- convertedCount = CFStringGetBytes((CFStringRef)object, CFRangeMake(0L, stringLength), kCFStringEncodingUTF8, '?', NO, encodeState->utf8ConversionBuffer.bytes.ptr, encodeState->utf8ConversionBuffer.bytes.length - 16L, &usedBytes);
- if(CDVJK_EXPECT_F(convertedCount != stringLength) || CDVJK_EXPECT_F(usedBytes < 0L)) { cdvjk_encode_error(encodeState, @"An error occurred converting the contents of a NSString to UTF8."); return(1); }
-
- if(CDVJK_EXPECT_F((encodeState->atIndex + (maxStringUTF8Length * 2UL) + 256UL) > encodeState->stringBuffer.bytes.length) && CDVJK_EXPECT_F(cdvjk_managedBuffer_resize(&encodeState->stringBuffer, encodeState->atIndex + (maxStringUTF8Length * 2UL) + 1024UL) == NULL)) { cdvjk_encode_error(encodeState, @"Unable to resize temporary buffer."); return(1); }
-
- const unsigned char *utf8String = encodeState->utf8ConversionBuffer.bytes.ptr;
-
- size_t utf8Idx = 0UL;
- if(CDVJK_EXPECT_T((encodeState->encodeOption & CDVJKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
- for(utf8Idx = 0UL; utf8Idx < (size_t)usedBytes; utf8Idx++) {
- NSCParameterAssert(((&encodeState->stringBuffer.bytes.ptr[encodeState->atIndex]) - encodeState->stringBuffer.bytes.ptr) < (ssize_t)encodeState->stringBuffer.bytes.length);
- NSCParameterAssert(encodeState->atIndex < encodeState->stringBuffer.bytes.length);
- NSCParameterAssert((CFIndex)utf8Idx < usedBytes);
- if(CDVJK_EXPECT_F(utf8String[utf8Idx] < 0x20U)) {
- switch(utf8String[utf8Idx]) {
- case '\b': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'b'; break;
- case '\f': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'f'; break;
- case '\n': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'n'; break;
- case '\r': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 'r'; break;
- case '\t': encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = 't'; break;
- default: if(CDVJK_EXPECT_F(cdvjk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", utf8String[utf8Idx]))) { return(1); } break;
- }
- } else {
- if(CDVJK_EXPECT_F(utf8String[utf8Idx] >= 0x80U) && (encodeState->serializeOptionFlags & CDVJKSerializeOptionEscapeUnicode)) {
- const unsigned char *nextValidCharacter = NULL;
- UTF32 u32ch = 0U;
- CDV_ConversionResult result;
-
- if(CDVJK_EXPECT_F((result = cdvConvertSingleCodePointInUTF8(&utf8String[utf8Idx], &utf8String[usedBytes], (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { cdvjk_encode_error(encodeState, @"Error converting UTF8."); return(1); }
- else {
- utf8Idx = (nextValidCharacter - utf8String) - 1UL;
- if(CDVJK_EXPECT_T(u32ch <= 0xffffU)) { if(CDVJK_EXPECT_F(cdvjk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x", u32ch))) { return(1); } }
- else { if(CDVJK_EXPECT_F(cdvjk_encode_printf(encodeState, NULL, 0UL, NULL, "\\u%4.4x\\u%4.4x", (0xd7c0U + (u32ch >> 10)), (0xdc00U + (u32ch & 0x3ffU))))) { return(1); } }
- }
- } else {
- if(CDVJK_EXPECT_F(utf8String[utf8Idx] == '\"') || CDVJK_EXPECT_F(utf8String[utf8Idx] == '\\') || (CDVJK_EXPECT_F(encodeState->serializeOptionFlags & CDVJKSerializeOptionEscapeForwardSlashes) && CDVJK_EXPECT_F(utf8String[utf8Idx] == '/'))) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\\'; }
- encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = utf8String[utf8Idx];
- }
- }
- }
- NSCParameterAssert((encodeState->atIndex + 1UL) < encodeState->stringBuffer.bytes.length);
- if(CDVJK_EXPECT_T((encodeState->encodeOption & CDVJKEncodeOptionStringObjTrimQuotes) == 0UL)) { encodeState->stringBuffer.bytes.ptr[encodeState->atIndex++] = '\"'; }
- cdvjk_encode_updateCache(encodeState, cacheSlot, startingAtIndex, encodeCacheObject);
- return(0);
- }
- }
- break;
-
- case CDVJKClassNumber:
- {
- if(object == (id)kCFBooleanTrue) { return(cdvjk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "true", 4UL)); }
- else if(object == (id)kCFBooleanFalse) { return(cdvjk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "false", 5UL)); }
-
- const char *objCType = [object objCType];
- char anum[256], *aptr = &anum[255];
- int isNegative = 0;
- unsigned long long ullv;
- long long llv;
-
- if(CDVJK_EXPECT_F(objCType == NULL) || CDVJK_EXPECT_F(objCType[0] == 0) || CDVJK_EXPECT_F(objCType[1] != 0)) { cdvjk_encode_error(encodeState, @"NSNumber conversion error, unknown type. Type: '%s'", (objCType == NULL) ? "<NULL>" : objCType); return(1); }
-
- switch(objCType[0]) {
- case 'c': case 'i': case 's': case 'l': case 'q':
- if(CDVJK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &llv))) {
- if(llv < 0LL) { ullv = -llv; isNegative = 1; } else { ullv = llv; isNegative = 0; }
- goto convertNumber;
- } else { cdvjk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
- break;
- case 'C': case 'I': case 'S': case 'L': case 'Q': case 'B':
- if(CDVJK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberLongLongType, &ullv))) {
- convertNumber:
- if(CDVJK_EXPECT_F(ullv < 10ULL)) { *--aptr = ullv + '0'; } else { while(CDVJK_EXPECT_T(ullv > 0ULL)) { *--aptr = (ullv % 10ULL) + '0'; ullv /= 10ULL; NSCParameterAssert(aptr > anum); } }
- if(isNegative) { *--aptr = '-'; }
- NSCParameterAssert(aptr > anum);
- return(cdvjk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, aptr, &anum[255] - aptr));
- } else { cdvjk_encode_error(encodeState, @"Unable to get scalar value from number object."); return(1); }
- break;
- case 'f': case 'd':
- {
- double dv;
- if(CDVJK_EXPECT_T(CFNumberGetValue((CFNumberRef)object, kCFNumberDoubleType, &dv))) {
- if(CDVJK_EXPECT_F(!isfinite(dv))) { cdvjk_encode_error(encodeState, @"Floating point values must be finite. CDVJSON does not support NaN or Infinity."); return(1); }
- return(cdvjk_encode_printf(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "%.17g", dv));
- } else { cdvjk_encode_error(encodeState, @"Unable to get floating point value from number object."); return(1); }
- }
- break;
- default: cdvjk_encode_error(encodeState, @"NSNumber conversion error, unknown type. Type: '%c' / 0x%2.2x", objCType[0], objCType[0]); return(1); break;
- }
- }
- break;
-
- case CDVJKClassArray:
- {
- int printComma = 0;
- CFIndex arrayCount = CFArrayGetCount((CFArrayRef)object), idx = 0L;
- if(CDVJK_EXPECT_F(cdvjk_encode_write1(encodeState, 1L, "["))) { return(1); }
- if(CDVJK_EXPECT_F(arrayCount > 1020L)) {
- for(id arrayObject in object) { if(CDVJK_EXPECT_T(printComma)) { if(CDVJK_EXPECT_F(cdvjk_encode_write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; if(CDVJK_EXPECT_F(cdvjk_encode_add_atom_to_buffer(encodeState, arrayObject))) { return(1); } }
- } else {
- void *objects[1024];
- CFArrayGetValues((CFArrayRef)object, CFRangeMake(0L, arrayCount), (const void **)objects);
- for(idx = 0L; idx < arrayCount; idx++) { if(CDVJK_EXPECT_T(printComma)) { if(CDVJK_EXPECT_F(cdvjk_encode_write1(encodeState, 0L, ","))) { return(1); } } printComma = 1; if(CDVJK_EXPECT_F(cdvjk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); } }
- }
- return(cdvjk_encode_write1(encodeState, -1L, "]"));
- }
- break;
-
- case CDVJKClassDictionary:
- {
- int printComma = 0;
- CFIndex dictionaryCount = CFDictionaryGetCount((CFDictionaryRef)object), idx = 0L;
- id enumerateObject = CDVJK_EXPECT_F(_jk_encode_prettyPrint) ? [[object allKeys] sortedArrayUsingSelector:@selector(compare:)] : object;
-
- if(CDVJK_EXPECT_F(cdvjk_encode_write1(encodeState, 1L, "{"))) { return(1); }
- if(CDVJK_EXPECT_F(_jk_encode_prettyPrint) || CDVJK_EXPECT_F(dictionaryCount > 1020L)) {
- for(id keyObject in enumerateObject) {
- if(CDVJK_EXPECT_T(printComma)) { if(CDVJK_EXPECT_F(cdvjk_encode_write1(encodeState, 0L, ","))) { return(1); } }
- printComma = 1;
- if(CDVJK_EXPECT_F((object_getClass(keyObject) != encodeState->fastClassLookup.stringClass)) && CDVJK_EXPECT_F(([keyObject isKindOfClass:[NSString class]] == NO))) { cdvjk_encode_error(encodeState, @"Key must be a string object."); return(1); }
- if(CDVJK_EXPECT_F(cdvjk_encode_add_atom_to_buffer(encodeState, keyObject))) { return(1); }
- if(CDVJK_EXPECT_F(cdvjk_encode_write1(encodeState, 0L, ":"))) { return(1); }
- if(CDVJK_EXPECT_F(cdvjk_encode_add_atom_to_buffer(encodeState, (void *)CFDictionaryGetValue((CFDictionaryRef)object, keyObject)))) { return(1); }
- }
- } else {
- void *keys[1024], *objects[1024];
- CFDictionaryGetKeysAndValues((CFDictionaryRef)object, (const void **)keys, (const void **)objects);
- for(idx = 0L; idx < dictionaryCount; idx++) {
- if(CDVJK_EXPECT_T(printComma)) { if(CDVJK_EXPECT_F(cdvjk_encode_write1(encodeState, 0L, ","))) { return(1); } }
- printComma = 1;
- if(CDVJK_EXPECT_F(object_getClass(((id)keys[idx])) != encodeState->fastClassLookup.stringClass) && CDVJK_EXPECT_F([(id)keys[idx] isKindOfClass:[NSString class]] == NO)) { cdvjk_encode_error(encodeState, @"Key must be a string object."); return(1); }
- if(CDVJK_EXPECT_F(cdvjk_encode_add_atom_to_buffer(encodeState, keys[idx]))) { return(1); }
- if(CDVJK_EXPECT_F(cdvjk_encode_write1(encodeState, 0L, ":"))) { return(1); }
- if(CDVJK_EXPECT_F(cdvjk_encode_add_atom_to_buffer(encodeState, objects[idx]))) { return(1); }
- }
- }
- return(cdvjk_encode_write1(encodeState, -1L, "}"));
- }
- break;
-
- case CDVJKClassNull: return(cdvjk_encode_writen(encodeState, cacheSlot, startingAtIndex, encodeCacheObject, "null", 4UL)); break;
-
- default: cdvjk_encode_error(encodeState, @"Unable to serialize object class %@.", NSStringFromClass([object class])); return(1); break;
- }
-
- return(0);
-}
-
-
-@implementation CDVJKSerializer
-
-+ (id)serializeObject:(id)object options:(CDVJKSerializeOptionFlags)optionFlags encodeOption:(CDVJKEncodeOptionType)encodeOption block:(CDVJKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error
-{
- return([[[[self alloc] init] autorelease] serializeObject:object options:optionFlags encodeOption:encodeOption block:block delegate:delegate selector:selector error:error]);
-}
-
-- (id)serializeObject:(id)object options:(CDVJKSerializeOptionFlags)optionFlags encodeOption:(CDVJKEncodeOptionType)encodeOption block:(CDVJKSERIALIZER_BLOCKS_PROTO)block delegate:(id)delegate selector:(SEL)selector error:(NSError **)error
-{
-#ifndef __BLOCKS__
-#pragma unused(block)
-#endif
- NSParameterAssert((object != NULL) && (encodeState == NULL) && ((delegate != NULL) ? (block == NULL) : 1) && ((block != NULL) ? (delegate == NULL) : 1) &&
- (((encodeOption & CDVJKEncodeOptionCollectionObj) != 0UL) ? (((encodeOption & CDVJKEncodeOptionStringObj) == 0UL) && ((encodeOption & CDVJKEncodeOptionStringObjTrimQuotes) == 0UL)) : 1) &&
- (((encodeOption & CDVJKEncodeOptionStringObj) != 0UL) ? ((encodeOption & CDVJKEncodeOptionCollectionObj) == 0UL) : 1));
-
- id returnObject = NULL;
-
- if(encodeState != NULL) { [self releaseState]; }
- if((encodeState = (struct CDVJKEncodeState *)calloc(1UL, sizeof(CDVJKEncodeState))) == NULL) { [NSException raise:NSMallocException format:@"Unable to allocate state structure."]; return(NULL); }
-
- if((error != NULL) && (*error != NULL)) { *error = NULL; }
-
- if(delegate != NULL) {
- if(selector == NULL) { [NSException raise:NSInvalidArgumentException format:@"The delegate argument is not NULL, but the selector argument is NULL."]; }
- if([delegate respondsToSelector:selector] == NO) { [NSException raise:NSInvalidArgumentException format:@"The serializeUnsupportedClassesUsingDelegate: delegate does not respond to the selector argument."]; }
- encodeState->classFormatterDelegate = delegate;
- encodeState->classFormatterSelector = selector;
- encodeState->classFormatterIMP = (CDVJKClassFormatterIMP)[delegate methodForSelector:selector];
- NSCParameterAssert(encodeState->classFormatterIMP != NULL);
- }
-
-#ifdef __BLOCKS__
- encodeState->classFormatterBlock = block;
-#endif
- encodeState->serializeOptionFlags = optionFlags;
- encodeState->encodeOption = encodeOption;
- encodeState->stringBuffer.roundSizeUpToMultipleOf = (1024UL * 32UL);
- encodeState->utf8ConversionBuffer.roundSizeUpToMultipleOf = 4096UL;
-
- unsigned char stackJSONBuffer[CDVJK_JSONBUFFER_SIZE] CDVJK_ALIGNED(64);
- cdvjk_managedBuffer_setToStackBuffer(&encodeState->stringBuffer, stackJSONBuffer, sizeof(stackJSONBuffer));
-
- unsigned char stackUTF8Buffer[CDVJK_UTF8BUFFER_SIZE] CDVJK_ALIGNED(64);
- cdvjk_managedBuffer_setToStackBuffer(&encodeState->utf8ConversionBuffer, stackUTF8Buffer, sizeof(stackUTF8Buffer));
-
- if(((encodeOption & CDVJKEncodeOptionCollectionObj) != 0UL) && (([object isKindOfClass:[NSArray class]] == NO) && ([object isKindOfClass:[NSDictionary class]] == NO))) { cdvjk_encode_error(encodeState, @"Unable to serialize object class %@, expected a NSArray or NSDictionary.", NSStringFromClass([object class])); goto errorExit; }
- if(((encodeOption & CDVJKEncodeOptionStringObj) != 0UL) && ([object isKindOfClass:[NSString class]] == NO)) { cdvjk_encode_error(encodeState, @"Unable to serialize object class %@, expected a NSString.", NSStringFromClass([object class])); goto errorExit; }
-
- if(cdvjk_encode_add_atom_to_buffer(encodeState, object) == 0) {
- BOOL stackBuffer = ((encodeState->stringBuffer.flags & CDVJKManagedBufferMustFree) == 0UL) ? YES : NO;
-
- if((encodeState->atIndex < 2UL))
- if((stackBuffer == NO) && ((encodeState->stringBuffer.bytes.ptr = (unsigned char *)reallocf(encodeState->stringBuffer.bytes.ptr, encodeState->atIndex + 16UL)) == NULL)) { cdvjk_encode_error(encodeState, @"Unable to realloc buffer"); goto errorExit; }
-
- switch((encodeOption & CDVJKEncodeOptionAsTypeMask)) {
- case CDVJKEncodeOptionAsData:
- if(stackBuffer == YES) { if((returnObject = [(id)CFDataCreate( NULL, encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex) autorelease]) == NULL) { cdvjk_encode_error(encodeState, @"Unable to create NSData object"); } }
- else { if((returnObject = [(id)CFDataCreateWithBytesNoCopy( NULL, encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, NULL) autorelease]) == NULL) { cdvjk_encode_error(encodeState, @"Unable to create NSData object"); } }
- break;
-
- case CDVJKEncodeOptionAsString:
- if(stackBuffer == YES) { if((returnObject = [(id)CFStringCreateWithBytes( NULL, (const UInt8 *)encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, kCFStringEncodingUTF8, NO) autorelease]) == NULL) { cdvjk_encode_error(encodeState, @"Unable to create NSString object"); } }
- else { if((returnObject = [(id)CFStringCreateWithBytesNoCopy(NULL, (const UInt8 *)encodeState->stringBuffer.bytes.ptr, (CFIndex)encodeState->atIndex, kCFStringEncodingUTF8, NO, NULL) autorelease]) == NULL) { cdvjk_encode_error(encodeState, @"Unable to create NSString object"); } }
- break;
-
- default: cdvjk_encode_error(encodeState, @"Unknown encode as type."); break;
- }
-
- if((returnObject != NULL) && (stackBuffer == NO)) { encodeState->stringBuffer.flags &= ~CDVJKManagedBufferMustFree; encodeState->stringBuffer.bytes.ptr = NULL; encodeState->stringBuffer.bytes.length = 0UL; }
- }
-
-errorExit:
- if((encodeState != NULL) && (error != NULL) && (encodeState->error != NULL)) { *error = encodeState->error; encodeState->error = NULL; }
- [self releaseState];
-
- return(returnObject);
-}
-
-- (void)releaseState
-{
- if(encodeState != NULL) {
- cdvjk_managedBuffer_release(&encodeState->stringBuffer);
- cdvjk_managedBuffer_release(&encodeState->utf8ConversionBuffer);
- free(encodeState); encodeState = NULL;
- }
-}
-
-- (void)dealloc
-{
- [self releaseState];
- [super dealloc];
-}
-
-@end
-
-@implementation NSString (CDVJSONKitSerializing)
-
-////////////
-#pragma mark Methods for serializing a single NSString.
-////////////
-
-// Useful for those who need to serialize just a NSString. Otherwise you would have to do something like [NSArray arrayWithObject:stringToBeJSONSerialized], serializing the array, and then chopping of the extra ^\[.*\]$ square brackets.
-
-// NSData returning methods...
-
-- (NSData *)cdvjk_JSONData
-{
- return([self cdvjk_JSONDataWithOptions:CDVJKSerializeOptionNone includeQuotes:YES error:NULL]);
-}
-
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsData | ((includeQuotes == NO) ? CDVJKEncodeOptionStringObjTrimQuotes : 0UL) | CDVJKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]);
-}
-
-// NSString returning methods...
-
-- (NSString *)cdvjk_JSONString
-{
- return([self cdvjk_JSONStringWithOptions:CDVJKSerializeOptionNone includeQuotes:YES error:NULL]);
-}
-
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions includeQuotes:(BOOL)includeQuotes error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsString | ((includeQuotes == NO) ? CDVJKEncodeOptionStringObjTrimQuotes : 0UL) | CDVJKEncodeOptionStringObj) block:NULL delegate:NULL selector:NULL error:error]);
-}
-
-@end
-
-@implementation NSArray (CDVJSONKitSerializing)
-
-// NSData returning methods...
-
-- (NSData *)cdvjk_JSONData
-{
- return([CDVJKSerializer serializeObject:self options:CDVJKSerializeOptionNone encodeOption:(CDVJKEncodeOptionAsData | CDVJKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
-}
-
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsData | CDVJKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
-}
-
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsData | CDVJKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
-}
-
-// NSString returning methods...
-
-- (NSString *)cdvjk_JSONString
-{
- return([CDVJKSerializer serializeObject:self options:CDVJKSerializeOptionNone encodeOption:(CDVJKEncodeOptionAsString | CDVJKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
-}
-
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsString | CDVJKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
-}
-
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsString | CDVJKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
-}
-
-@end
-
-@implementation NSDictionary (CDVJSONKitSerializing)
-
-// NSData returning methods...
-
-- (NSData *)cdvjk_JSONData
-{
- return([CDVJKSerializer serializeObject:self options:CDVJKSerializeOptionNone encodeOption:(CDVJKEncodeOptionAsData | CDVJKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
-}
-
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsData | CDVJKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
-}
-
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsData | CDVJKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
-}
-
-// NSString returning methods...
-
-- (NSString *)cdvjk_JSONString
-{
- return([CDVJKSerializer serializeObject:self options:CDVJKSerializeOptionNone encodeOption:(CDVJKEncodeOptionAsString | CDVJKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:NULL]);
-}
-
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsString | CDVJKEncodeOptionCollectionObj) block:NULL delegate:NULL selector:NULL error:error]);
-}
-
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingDelegate:(id)delegate selector:(SEL)selector error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsString | CDVJKEncodeOptionCollectionObj) block:NULL delegate:delegate selector:selector error:error]);
-}
-
-@end
-
-
-#ifdef __BLOCKS__
-
-@implementation NSArray (CDVJSONKitSerializingBlockAdditions)
-
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsData | CDVJKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
-}
-
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsString | CDVJKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
-}
-
-@end
-
-@implementation NSDictionary (CDVJSONKitSerializingBlockAdditions)
-
-- (NSData *)cdvjk_JSONDataWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsData | CDVJKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
-}
-
-- (NSString *)cdvjk_JSONStringWithOptions:(CDVJKSerializeOptionFlags)serializeOptions serializeUnsupportedClassesUsingBlock:(id(^)(id object))block error:(NSError **)error
-{
- return([CDVJKSerializer serializeObject:self options:serializeOptions encodeOption:(CDVJKEncodeOptionAsString | CDVJKEncodeOptionCollectionObj) block:block delegate:NULL selector:NULL error:error]);
-}
-
-@end
-
-#endif // __BLOCKS__
-