Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
grgcombs committed Apr 14, 2014
1 parent c241276 commit f070ac6
Show file tree
Hide file tree
Showing 27 changed files with 2,194 additions and 1 deletion.
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,26 @@
#
# Pods/

# Xcode
.DS_Store
build/*
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
#*.xcworkspace
!default.xcworkspace
xcuserdata
profile
*.moved-aside
.svn
/Builds
xcuserdata/*
/DerivedData
build
*.orig
*.xccheckout
377 changes: 377 additions & 0 deletions JSONTools.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "37BDF4BC18FAF1DD00BF9705"
BuildableName = "libJSONTools.a"
BlueprintName = "JSONTools"
ReferencedContainer = "container:JSONTools.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "37BDF4CC18FAF1DD00BF9705"
BuildableName = "JSONToolsTests.xctest"
BlueprintName = "JSONToolsTests"
ReferencedContainer = "container:JSONTools.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>JSONTools.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>37BDF4BC18FAF1DD00BF9705</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>37BDF4CC18FAF1DD00BF9705</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>
32 changes: 32 additions & 0 deletions JSONTools/JSONPatch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// JSONPatch.h
// inspired by https://github.com/Starcounter-Jack/JSON-Patch
//
// JSONTools
//
// Copyright (C) 2014 Gregory Combs [gcombs at gmail]
// See LICENSE.txt for details.

#import "JSONTools.h"

@interface JSONPatch : NSObject

/**
* Implements IETF RFC6902 - JSON Patch
* @see https://tools.ietf.org/html/rfc6902
*
* @param patches An array of one or more patch dictionaries in the form of:
* `{"op":"add", "path": "/foo/0/bar", "value": "thing"}`
* - `op` is one of: "add", "remove", "copy", "move", "test", "_get"
* - `path` is a JSON Pointer (RFC 6901) (see JSONPointer.h)
* - `value` is an objective-c object (
*
* @param collection A ***mutable*** dictionary or ***mutable*** array to patch
*
* @return For all but "_get", the result is an NSNumber boolean indicating patch success.
* However, for "_get" operations, the result will be the collection's content
* corresponding to the supplied patch JSON Pointer (i.e. "path")
*/
+ (id)applyPatches:(NSArray *)patches toCollection:(id)collection;

@end
179 changes: 179 additions & 0 deletions JSONTools/JSONPatch.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//
// JSONPatch.m
// inspired by https://github.com/Starcounter-Jack/JSON-Patch
//
// JSONTools
//
// Copyright (C) 2014 Gregory Combs [gcombs at gmail]
// See LICENSE.txt for details.

#import "JSONPatch.h"
#import "JSONPatchDictionary.h"
#import "JSONPatchArray.h"
#import "JSONPointer.h"

@implementation JSONPatch

+ (id)applyPatches:(NSArray *)patches toCollection:(id)collection
{
if (!collection ||
![collection respondsToSelector:@selector(mutableCopy)])
{
return nil;
}

id result = nil;

for (NSDictionary *patch in patches)
{
JSONPatchInfo *patchInfo = [JSONPatchInfo newPatchInfoWithDictionary:patch];
if (!patchInfo)
break;

NSArray *pathKeys = [patchInfo.path componentsSeparatedByString:@"/"];
id object = collection;
NSInteger t = 1;

while (YES) {
if (![object isKindOfClass:[NSMutableDictionary class]] &&
![object isKindOfClass:[NSMutableArray class]] &&
[object respondsToSelector:@selector(mutableCopy)])
{
object = [object mutableCopy];
}

if ([object isKindOfClass:[NSMutableArray class]])
{
NSString *component = pathKeys[t];
NSInteger index = [(NSMutableArray *)object indexForJSONPointerComponent:component];
if (index == NSNotFound)
break;

t++;

if (t >= pathKeys.count)
{
BOOL stop = NO;
result = [self applyPatch:patchInfo array:object index:index collection:collection stop:&stop];
if (stop) {
return result;
}
break;
}
object = (NSMutableArray *)object[index];
}
else if ([object isKindOfClass:[NSMutableDictionary class]])
{
NSString *component = [(NSMutableDictionary *)object keyForJSONPointerComponent:pathKeys[t]];

t++;

if (t >= pathKeys.count)
{
BOOL stop = NO;
result = [self applyPatch:patchInfo dictionary:object key:component collection:collection stop:&stop];
if (stop) {
return result;
}
break;
}
object = (NSMutableDictionary *)object[component];
}
}
}
return result;
}

#pragma mark - Singular Patch

+ (id)applyPatch:(JSONPatchInfo *)patchInfo array:(NSMutableArray *)array index:(NSInteger)index collection:(id)collection stop:(BOOL *)stop
{
BOOL success = NO;
switch (patchInfo.op)
{
case JSONPatchOperationGet:
case JSONPatchOperationTest:
*stop = YES;
return [JSONPatchArray applyPatchInfo:patchInfo object:array index:index];
break;
case JSONPatchOperationAdd:
case JSONPatchOperationReplace:
case JSONPatchOperationRemove:
success = ([[JSONPatchArray applyPatchInfo:patchInfo object:array index:index] boolValue]);
break;
case JSONPatchOperationCopy:
success = [self applyCopyPatch:patchInfo toCollection:collection];
break;
case JSONPatchOperationMove:
success = [self applyMovePatch:patchInfo toCollection:collection];
break;
case JSONPatchOperationUndefined:
break;
}
if (!success)
return nil;
return @(success);
}

+ (id)applyPatch:(JSONPatchInfo *)patchInfo dictionary:(NSMutableDictionary *)dictionary key:(NSString *)key collection:(id)collection stop:(BOOL *)stop
{
BOOL success = NO;
switch (patchInfo.op)
{
case JSONPatchOperationGet:
case JSONPatchOperationTest:
*stop = YES;
return [JSONPatchDictionary applyPatchInfo:patchInfo object:dictionary key:key];
break;
case JSONPatchOperationAdd:
case JSONPatchOperationReplace:
case JSONPatchOperationRemove:
success = ([[JSONPatchDictionary applyPatchInfo:patchInfo object:dictionary key:key] boolValue]);
break;
case JSONPatchOperationCopy:
success = [self applyCopyPatch:patchInfo toCollection:collection];
break;
case JSONPatchOperationMove:
success = [self applyMovePatch:patchInfo toCollection:collection];
break;
case JSONPatchOperationUndefined:
break;
}
if (!success)
return nil;
return @(success);
}

#pragma mark - Aggregated Operations

+ (BOOL)applyCopyPatch:(JSONPatchInfo *)patchInfo toCollection:(id)collection
{
id fromValue = [self applyPatches:@[@{@"op": @"_get",
@"path": patchInfo.fromPath}] toCollection:collection];
if (!fromValue) {
return NO;
}
id toResult = [self applyPatches:@[@{@"op": @"add",
@"path": patchInfo.path,
@"value": fromValue}] toCollection:collection];
return (toResult != NULL);
}

+ (BOOL)applyMovePatch:(JSONPatchInfo *)patchInfo toCollection:(id)collection
{
id fromValue = [self applyPatches:@[@{@"op": @"_get",
@"path": patchInfo.fromPath}] toCollection:collection];
if (!fromValue)
{
return NO;
}
id removeResult = [self applyPatches:@[@{@"op": @"remove",
@"path": patchInfo.fromPath}] toCollection:collection];
id toResult = [self applyPatches:@[@{@"op": @"add",
@"path": patchInfo.path,
@"value": fromValue}] toCollection:collection];
return (toResult != NULL &&
removeResult != NULL);
}

@end
15 changes: 15 additions & 0 deletions JSONTools/JSONPatchArray.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// JSONPatchArray.h
// inspired by https://github.com/Starcounter-Jack/JSON-Patch
//
// JSONTools
//
// Copyright (C) 2014 Gregory Combs [gcombs at gmail]
// See LICENSE.txt for details.

#import "JSONTools.h"
#import "JSONPatchInfo.h"

@interface JSONPatchArray: NSObject
+ (id)applyPatchInfo:(JSONPatchInfo *)info object:(NSMutableArray *)object index:(NSInteger)index;
@end
Loading

0 comments on commit f070ac6

Please sign in to comment.