iCuke 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.gitmodules +3 -0
- data/README.rdoc +8 -7
- data/Rakefile +10 -30
- data/VERSION +1 -1
- data/app/.gitignore +3 -0
- data/app/LICENSE +20 -0
- data/app/Readme.markdown +3 -0
- data/app/Shared/AppDelegate_Shared.h +17 -0
- data/app/Shared/AppDelegate_Shared.m +25 -0
- data/app/{images/yellowslide.png → Shared/Icon.png} +0 -0
- data/app/Shared/ModalViewController.h +15 -0
- data/app/Shared/ModalViewController.m +96 -0
- data/app/Shared/TestViewController_Shared.h +15 -0
- data/app/Shared/TestViewController_Shared.m +43 -0
- data/app/{Info.plist → Shared/Universal-Info.plist} +17 -4
- data/app/Shared/Universal_Prefix.pch +8 -0
- data/app/Shared/main.m +14 -0
- data/app/Universal.xcodeproj/project.pbxproj +301 -0
- data/app/iPad/AppDelegate_Pad.h +15 -0
- data/app/iPad/AppDelegate_Pad.m +28 -0
- data/app/iPad/Default-Landscape.png +0 -0
- data/app/iPad/Default-Portrait.png +0 -0
- data/app/iPad/MainWindow_Pad.xib +343 -0
- data/app/iPad/TestViewController_Pad.h +15 -0
- data/app/iPad/TestViewController_Pad.m +52 -0
- data/app/iPhone/AppDelegate_Phone.h +15 -0
- data/app/iPhone/AppDelegate_Phone.m +28 -0
- data/app/{images/segment_search.png → iPhone/Default.png} +0 -0
- data/app/iPhone/MainWindow_Phone.xib +363 -0
- data/app/iPhone/TestViewController_Phone.h +15 -0
- data/app/iPhone/TestViewController_Phone.m +36 -0
- data/bin/.gitignore +1 -0
- data/bin/icuke +10 -0
- data/bin/icuke-module +17 -0
- data/ext/.gitignore +1 -0
- data/ext/Rakefile +6 -0
- data/ext/WaxSim/Simulator.h +31 -0
- data/ext/WaxSim/Simulator.m +131 -0
- data/ext/WaxSim/WaxSim.m +110 -0
- data/ext/WaxSim/WaxSim.xcodeproj/project.pbxproj +238 -0
- data/ext/WaxSim/WaxSim_Prefix.pch +7 -0
- data/ext/WaxSim/iPhoneSimulatorRemoteClient/iPhoneSimulatorRemoteClient.h +126 -0
- data/ext/iCuke/Rakefile +14 -17
- data/ext/iCuke/libicuke-sdk3.1.dylib +0 -0
- data/ext/iCuke/libicuke-sdk4.0.dylib +0 -0
- data/ext/iCuke/libicuke.dylib +0 -0
- data/ext/iCuke/{DefaultsResponse.h → sdk3.1/DefaultsResponse.h} +0 -0
- data/ext/iCuke/{DefaultsResponse.m → sdk3.1/DefaultsResponse.m} +0 -0
- data/ext/iCuke/{EventResponse.h → sdk3.1/EventResponse.h} +0 -0
- data/ext/iCuke/{EventResponse.m → sdk3.1/EventResponse.m} +0 -0
- data/ext/iCuke/sdk3.1/ModuleResponse.h +5 -0
- data/ext/iCuke/sdk3.1/ModuleResponse.m +76 -0
- data/ext/iCuke/sdk3.1/Rakefile +20 -0
- data/ext/iCuke/{Recorder.h → sdk3.1/Recorder.h} +0 -0
- data/ext/iCuke/{Recorder.m → sdk3.1/Recorder.m} +0 -0
- data/ext/iCuke/{RecorderResponse.h → sdk3.1/RecorderResponse.h} +0 -0
- data/ext/iCuke/{RecorderResponse.m → sdk3.1/RecorderResponse.m} +0 -0
- data/ext/iCuke/{SynthesizeSingleton.h → sdk3.1/SynthesizeSingleton.h} +0 -0
- data/ext/iCuke/{ViewResponse.h → sdk3.1/ViewResponse.h} +0 -0
- data/ext/iCuke/{ViewResponse.m → sdk3.1/ViewResponse.m} +0 -0
- data/ext/iCuke/{Viewer.h → sdk3.1/Viewer.h} +0 -0
- data/ext/iCuke/sdk3.1/Viewer.m +203 -0
- data/ext/iCuke/{iCukeHTTPResponseHandler.h → sdk3.1/iCukeHTTPResponseHandler.h} +0 -0
- data/ext/iCuke/{iCukeHTTPResponseHandler.m → sdk3.1/iCukeHTTPResponseHandler.m} +0 -0
- data/ext/iCuke/{iCukeHTTPServer.h → sdk3.1/iCukeHTTPServer.h} +0 -0
- data/ext/iCuke/{iCukeHTTPServer.m → sdk3.1/iCukeHTTPServer.m} +0 -0
- data/ext/iCuke/{iCukeServer.h → sdk3.1/iCukeServer.h} +0 -2
- data/ext/iCuke/{iCukeServer.m → sdk3.1/iCukeServer.m} +22 -20
- data/ext/iCuke/{json → sdk3.1/json}/JSON.h +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/NSObject+SBJSON.h +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/NSObject+SBJSON.m +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/NSString+SBJSON.h +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/NSString+SBJSON.m +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/SBJSON.h +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/SBJSON.m +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/SBJsonBase.h +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/SBJsonBase.m +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/SBJsonParser.h +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/SBJsonParser.m +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/SBJsonWriter.h +0 -0
- data/ext/iCuke/{json → sdk3.1/json}/SBJsonWriter.m +0 -0
- data/ext/iCuke/sdk4.0/DefaultsResponse.h +5 -0
- data/ext/iCuke/sdk4.0/DefaultsResponse.m +66 -0
- data/ext/iCuke/sdk4.0/EventResponse.h +5 -0
- data/ext/iCuke/sdk4.0/EventResponse.m +130 -0
- data/ext/iCuke/sdk4.0/ModuleResponse.h +5 -0
- data/ext/iCuke/sdk4.0/ModuleResponse.m +76 -0
- data/ext/iCuke/sdk4.0/Rakefile +20 -0
- data/ext/iCuke/sdk4.0/Recorder.h +17 -0
- data/ext/iCuke/sdk4.0/Recorder.m +90 -0
- data/ext/iCuke/sdk4.0/RecorderResponse.h +6 -0
- data/ext/iCuke/sdk4.0/RecorderResponse.m +68 -0
- data/ext/iCuke/sdk4.0/SynthesizeSingleton.h +68 -0
- data/ext/iCuke/sdk4.0/ViewResponse.h +5 -0
- data/ext/iCuke/sdk4.0/ViewResponse.m +84 -0
- data/ext/iCuke/sdk4.0/Viewer.h +8 -0
- data/ext/iCuke/{Viewer.m → sdk4.0/Viewer.m} +14 -0
- data/ext/iCuke/sdk4.0/iCukeHTTPResponseHandler.h +50 -0
- data/ext/iCuke/sdk4.0/iCukeHTTPResponseHandler.m +381 -0
- data/ext/iCuke/sdk4.0/iCukeHTTPServer.h +53 -0
- data/ext/iCuke/sdk4.0/iCukeHTTPServer.m +365 -0
- data/ext/iCuke/sdk4.0/iCukeServer.h +14 -0
- data/ext/iCuke/sdk4.0/iCukeServer.m +65 -0
- data/ext/iCuke/sdk4.0/json/JSON.h +50 -0
- data/ext/iCuke/sdk4.0/json/NSObject+SBJSON.h +68 -0
- data/ext/iCuke/sdk4.0/json/NSObject+SBJSON.m +53 -0
- data/ext/iCuke/sdk4.0/json/NSString+SBJSON.h +58 -0
- data/ext/iCuke/sdk4.0/json/NSString+SBJSON.m +55 -0
- data/ext/iCuke/sdk4.0/json/SBJSON.h +75 -0
- data/ext/iCuke/sdk4.0/json/SBJSON.m +212 -0
- data/ext/iCuke/sdk4.0/json/SBJsonBase.h +86 -0
- data/ext/iCuke/sdk4.0/json/SBJsonBase.m +78 -0
- data/ext/iCuke/sdk4.0/json/SBJsonParser.h +87 -0
- data/ext/iCuke/sdk4.0/json/SBJsonParser.m +475 -0
- data/ext/iCuke/sdk4.0/json/SBJsonWriter.h +129 -0
- data/ext/iCuke/sdk4.0/json/SBJsonWriter.m +228 -0
- data/features/iphone.feature +15 -0
- data/features/support/env.rb +1 -1
- data/iCuke.gemspec +118 -114
- data/lib/icuke/cucumber.rb +93 -64
- data/lib/icuke/screen.rb +110 -0
- data/lib/icuke/sdk.rb +134 -0
- data/lib/icuke/simulate.rb +9 -5
- data/lib/icuke/simulator.rb +10 -11
- data/lib/icuke/waxsim.rb +36 -0
- data/lib/icuke.rb +2 -1
- metadata +124 -129
- data/app/AlertsViewController.h +0 -59
- data/app/AlertsViewController.m +0 -341
- data/app/AppDelegate.h +0 -60
- data/app/AppDelegate.m +0 -101
- data/app/ButtonsViewController.h +0 -80
- data/app/ButtonsViewController.m +0 -448
- data/app/Constants.h +0 -58
- data/app/ControlsViewController.h +0 -72
- data/app/ControlsViewController.m +0 -379
- data/app/ImagesViewController.h +0 -62
- data/app/ImagesViewController.m +0 -137
- data/app/MainViewController.h +0 -57
- data/app/MainViewController.m +0 -262
- data/app/Picker/CustomPickerDataSource.h +0 -55
- data/app/Picker/CustomPickerDataSource.m +0 -135
- data/app/Picker/CustomView.h +0 -62
- data/app/Picker/CustomView.m +0 -119
- data/app/PickerViewController.h +0 -88
- data/app/PickerViewController.m +0 -402
- data/app/Prefix.pch +0 -5
- data/app/ReadMe.txt +0 -82
- data/app/SearchBarController.h +0 -58
- data/app/SearchBarController.m +0 -105
- data/app/SegmentViewController.h +0 -54
- data/app/SegmentViewController.m +0 -195
- data/app/TextFieldController.h +0 -67
- data/app/TextFieldController.m +0 -342
- data/app/TextViewController.h +0 -57
- data/app/TextViewController.m +0 -164
- data/app/ToolbarViewController.h +0 -74
- data/app/ToolbarViewController.m +0 -303
- data/app/TransitionViewController.h +0 -64
- data/app/TransitionViewController.m +0 -178
- data/app/UICatalog.xcodeproj/project.pbxproj +0 -615
- data/app/WebViewController.h +0 -57
- data/app/WebViewController.m +0 -173
- data/app/en.lproj/AlertsViewController.xib +0 -144
- data/app/en.lproj/ButtonsViewController.xib +0 -147
- data/app/en.lproj/ControlsViewController.xib +0 -147
- data/app/en.lproj/ImagesViewController.xib +0 -264
- data/app/en.lproj/Localizable.strings +0 -41
- data/app/en.lproj/MainWindow.xib +0 -306
- data/app/en.lproj/PickerViewController.xib +0 -415
- data/app/en.lproj/SearchBarController.xib +0 -142
- data/app/en.lproj/SegmentViewController.xib +0 -143
- data/app/en.lproj/TextFieldController.xib +0 -167
- data/app/en.lproj/TextViewController.xib +0 -149
- data/app/en.lproj/ToolbarViewController.xib +0 -491
- data/app/en.lproj/TransitionViewController.xib +0 -255
- data/app/en.lproj/WebViewController.xib +0 -141
- data/app/images/12-6AM.png +0 -0
- data/app/images/12-6PM.png +0 -0
- data/app/images/6-12AM.png +0 -0
- data/app/images/6-12PM.png +0 -0
- data/app/images/Default.png +0 -0
- data/app/images/Icon.png +0 -0
- data/app/images/UIButton_custom.png +0 -0
- data/app/images/blueButton.png +0 -0
- data/app/images/orangeslide.png +0 -0
- data/app/images/scene1.jpg +0 -0
- data/app/images/scene2.jpg +0 -0
- data/app/images/scene3.jpg +0 -0
- data/app/images/scene4.jpg +0 -0
- data/app/images/scene5.jpg +0 -0
- data/app/images/segment_check.png +0 -0
- data/app/images/segment_tools.png +0 -0
- data/app/images/slider_ball.png +0 -0
- data/app/images/whiteButton.png +0 -0
- data/app/main.m +0 -58
- data/features/uicatalog.feature +0 -20
- data/lib/icuke/com.apple.Accessibility.plist +0 -0
- data/lib/icuke/headless.rb +0 -54
- data/lib/icuke/xcode.rb +0 -164
@@ -0,0 +1,475 @@
|
|
1
|
+
/*
|
2
|
+
Copyright (C) 2009 Stig Brautaset. All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of the author nor the names of its contributors may be used
|
15
|
+
to endorse or promote products derived from this software without specific
|
16
|
+
prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
*/
|
29
|
+
|
30
|
+
#import "SBJsonParser.h"
|
31
|
+
|
32
|
+
@interface SBJsonParser ()
|
33
|
+
|
34
|
+
- (BOOL)scanValue:(NSObject **)o;
|
35
|
+
|
36
|
+
- (BOOL)scanRestOfArray:(NSMutableArray **)o;
|
37
|
+
- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o;
|
38
|
+
- (BOOL)scanRestOfNull:(NSNull **)o;
|
39
|
+
- (BOOL)scanRestOfFalse:(NSNumber **)o;
|
40
|
+
- (BOOL)scanRestOfTrue:(NSNumber **)o;
|
41
|
+
- (BOOL)scanRestOfString:(NSMutableString **)o;
|
42
|
+
|
43
|
+
// Cannot manage without looking at the first digit
|
44
|
+
- (BOOL)scanNumber:(NSNumber **)o;
|
45
|
+
|
46
|
+
- (BOOL)scanHexQuad:(unichar *)x;
|
47
|
+
- (BOOL)scanUnicodeChar:(unichar *)x;
|
48
|
+
|
49
|
+
- (BOOL)scanIsAtEnd;
|
50
|
+
|
51
|
+
@end
|
52
|
+
|
53
|
+
#define skipWhitespace(c) while (isspace(*c)) c++
|
54
|
+
#define skipDigits(c) while (isdigit(*c)) c++
|
55
|
+
|
56
|
+
|
57
|
+
@implementation SBJsonParser
|
58
|
+
|
59
|
+
static char ctrl[0x22];
|
60
|
+
|
61
|
+
+ (void)initialize
|
62
|
+
{
|
63
|
+
ctrl[0] = '\"';
|
64
|
+
ctrl[1] = '\\';
|
65
|
+
for (int i = 1; i < 0x20; i++)
|
66
|
+
ctrl[i+1] = i;
|
67
|
+
ctrl[0x21] = 0;
|
68
|
+
}
|
69
|
+
|
70
|
+
/**
|
71
|
+
@deprecated This exists in order to provide fragment support in older APIs in one more version.
|
72
|
+
It should be removed in the next major version.
|
73
|
+
*/
|
74
|
+
- (id)fragmentWithString:(id)repr {
|
75
|
+
[self clearErrorTrace];
|
76
|
+
|
77
|
+
if (!repr) {
|
78
|
+
[self addErrorWithCode:EINPUT description:@"Input was 'nil'"];
|
79
|
+
return nil;
|
80
|
+
}
|
81
|
+
|
82
|
+
depth = 0;
|
83
|
+
c = [repr UTF8String];
|
84
|
+
|
85
|
+
id o;
|
86
|
+
if (![self scanValue:&o]) {
|
87
|
+
return nil;
|
88
|
+
}
|
89
|
+
|
90
|
+
// We found some valid JSON. But did it also contain something else?
|
91
|
+
if (![self scanIsAtEnd]) {
|
92
|
+
[self addErrorWithCode:ETRAILGARBAGE description:@"Garbage after JSON"];
|
93
|
+
return nil;
|
94
|
+
}
|
95
|
+
|
96
|
+
NSAssert1(o, @"Should have a valid object from %@", repr);
|
97
|
+
return o;
|
98
|
+
}
|
99
|
+
|
100
|
+
- (id)objectWithString:(NSString *)repr {
|
101
|
+
|
102
|
+
id o = [self fragmentWithString:repr];
|
103
|
+
if (!o)
|
104
|
+
return nil;
|
105
|
+
|
106
|
+
// Check that the object we've found is a valid JSON container.
|
107
|
+
if (![o isKindOfClass:[NSDictionary class]] && ![o isKindOfClass:[NSArray class]]) {
|
108
|
+
[self addErrorWithCode:EFRAGMENT description:@"Valid fragment, but not JSON"];
|
109
|
+
return nil;
|
110
|
+
}
|
111
|
+
|
112
|
+
return o;
|
113
|
+
}
|
114
|
+
|
115
|
+
/*
|
116
|
+
In contrast to the public methods, it is an error to omit the error parameter here.
|
117
|
+
*/
|
118
|
+
- (BOOL)scanValue:(NSObject **)o
|
119
|
+
{
|
120
|
+
skipWhitespace(c);
|
121
|
+
|
122
|
+
switch (*c++) {
|
123
|
+
case '{':
|
124
|
+
return [self scanRestOfDictionary:(NSMutableDictionary **)o];
|
125
|
+
break;
|
126
|
+
case '[':
|
127
|
+
return [self scanRestOfArray:(NSMutableArray **)o];
|
128
|
+
break;
|
129
|
+
case '"':
|
130
|
+
return [self scanRestOfString:(NSMutableString **)o];
|
131
|
+
break;
|
132
|
+
case 'f':
|
133
|
+
return [self scanRestOfFalse:(NSNumber **)o];
|
134
|
+
break;
|
135
|
+
case 't':
|
136
|
+
return [self scanRestOfTrue:(NSNumber **)o];
|
137
|
+
break;
|
138
|
+
case 'n':
|
139
|
+
return [self scanRestOfNull:(NSNull **)o];
|
140
|
+
break;
|
141
|
+
case '-':
|
142
|
+
case '0'...'9':
|
143
|
+
c--; // cannot verify number correctly without the first character
|
144
|
+
return [self scanNumber:(NSNumber **)o];
|
145
|
+
break;
|
146
|
+
case '+':
|
147
|
+
[self addErrorWithCode:EPARSENUM description: @"Leading + disallowed in number"];
|
148
|
+
return NO;
|
149
|
+
break;
|
150
|
+
case 0x0:
|
151
|
+
[self addErrorWithCode:EEOF description:@"Unexpected end of string"];
|
152
|
+
return NO;
|
153
|
+
break;
|
154
|
+
default:
|
155
|
+
[self addErrorWithCode:EPARSE description: @"Unrecognised leading character"];
|
156
|
+
return NO;
|
157
|
+
break;
|
158
|
+
}
|
159
|
+
|
160
|
+
NSAssert(0, @"Should never get here");
|
161
|
+
return NO;
|
162
|
+
}
|
163
|
+
|
164
|
+
- (BOOL)scanRestOfTrue:(NSNumber **)o
|
165
|
+
{
|
166
|
+
if (!strncmp(c, "rue", 3)) {
|
167
|
+
c += 3;
|
168
|
+
*o = [NSNumber numberWithBool:YES];
|
169
|
+
return YES;
|
170
|
+
}
|
171
|
+
[self addErrorWithCode:EPARSE description:@"Expected 'true'"];
|
172
|
+
return NO;
|
173
|
+
}
|
174
|
+
|
175
|
+
- (BOOL)scanRestOfFalse:(NSNumber **)o
|
176
|
+
{
|
177
|
+
if (!strncmp(c, "alse", 4)) {
|
178
|
+
c += 4;
|
179
|
+
*o = [NSNumber numberWithBool:NO];
|
180
|
+
return YES;
|
181
|
+
}
|
182
|
+
[self addErrorWithCode:EPARSE description: @"Expected 'false'"];
|
183
|
+
return NO;
|
184
|
+
}
|
185
|
+
|
186
|
+
- (BOOL)scanRestOfNull:(NSNull **)o {
|
187
|
+
if (!strncmp(c, "ull", 3)) {
|
188
|
+
c += 3;
|
189
|
+
*o = [NSNull null];
|
190
|
+
return YES;
|
191
|
+
}
|
192
|
+
[self addErrorWithCode:EPARSE description: @"Expected 'null'"];
|
193
|
+
return NO;
|
194
|
+
}
|
195
|
+
|
196
|
+
- (BOOL)scanRestOfArray:(NSMutableArray **)o {
|
197
|
+
if (maxDepth && ++depth > maxDepth) {
|
198
|
+
[self addErrorWithCode:EDEPTH description: @"Nested too deep"];
|
199
|
+
return NO;
|
200
|
+
}
|
201
|
+
|
202
|
+
*o = [NSMutableArray arrayWithCapacity:8];
|
203
|
+
|
204
|
+
for (; *c ;) {
|
205
|
+
id v;
|
206
|
+
|
207
|
+
skipWhitespace(c);
|
208
|
+
if (*c == ']' && c++) {
|
209
|
+
depth--;
|
210
|
+
return YES;
|
211
|
+
}
|
212
|
+
|
213
|
+
if (![self scanValue:&v]) {
|
214
|
+
[self addErrorWithCode:EPARSE description:@"Expected value while parsing array"];
|
215
|
+
return NO;
|
216
|
+
}
|
217
|
+
|
218
|
+
[*o addObject:v];
|
219
|
+
|
220
|
+
skipWhitespace(c);
|
221
|
+
if (*c == ',' && c++) {
|
222
|
+
skipWhitespace(c);
|
223
|
+
if (*c == ']') {
|
224
|
+
[self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in array"];
|
225
|
+
return NO;
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}
|
229
|
+
|
230
|
+
[self addErrorWithCode:EEOF description: @"End of input while parsing array"];
|
231
|
+
return NO;
|
232
|
+
}
|
233
|
+
|
234
|
+
- (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o
|
235
|
+
{
|
236
|
+
if (maxDepth && ++depth > maxDepth) {
|
237
|
+
[self addErrorWithCode:EDEPTH description: @"Nested too deep"];
|
238
|
+
return NO;
|
239
|
+
}
|
240
|
+
|
241
|
+
*o = [NSMutableDictionary dictionaryWithCapacity:7];
|
242
|
+
|
243
|
+
for (; *c ;) {
|
244
|
+
id k, v;
|
245
|
+
|
246
|
+
skipWhitespace(c);
|
247
|
+
if (*c == '}' && c++) {
|
248
|
+
depth--;
|
249
|
+
return YES;
|
250
|
+
}
|
251
|
+
|
252
|
+
if (!(*c == '\"' && c++ && [self scanRestOfString:&k])) {
|
253
|
+
[self addErrorWithCode:EPARSE description: @"Object key string expected"];
|
254
|
+
return NO;
|
255
|
+
}
|
256
|
+
|
257
|
+
skipWhitespace(c);
|
258
|
+
if (*c != ':') {
|
259
|
+
[self addErrorWithCode:EPARSE description: @"Expected ':' separating key and value"];
|
260
|
+
return NO;
|
261
|
+
}
|
262
|
+
|
263
|
+
c++;
|
264
|
+
if (![self scanValue:&v]) {
|
265
|
+
NSString *string = [NSString stringWithFormat:@"Object value expected for key: %@", k];
|
266
|
+
[self addErrorWithCode:EPARSE description: string];
|
267
|
+
return NO;
|
268
|
+
}
|
269
|
+
|
270
|
+
[*o setObject:v forKey:k];
|
271
|
+
|
272
|
+
skipWhitespace(c);
|
273
|
+
if (*c == ',' && c++) {
|
274
|
+
skipWhitespace(c);
|
275
|
+
if (*c == '}') {
|
276
|
+
[self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in object"];
|
277
|
+
return NO;
|
278
|
+
}
|
279
|
+
}
|
280
|
+
}
|
281
|
+
|
282
|
+
[self addErrorWithCode:EEOF description: @"End of input while parsing object"];
|
283
|
+
return NO;
|
284
|
+
}
|
285
|
+
|
286
|
+
- (BOOL)scanRestOfString:(NSMutableString **)o
|
287
|
+
{
|
288
|
+
*o = [NSMutableString stringWithCapacity:16];
|
289
|
+
do {
|
290
|
+
// First see if there's a portion we can grab in one go.
|
291
|
+
// Doing this caused a massive speedup on the long string.
|
292
|
+
size_t len = strcspn(c, ctrl);
|
293
|
+
if (len) {
|
294
|
+
// check for
|
295
|
+
id t = [[NSString alloc] initWithBytesNoCopy:(char*)c
|
296
|
+
length:len
|
297
|
+
encoding:NSUTF8StringEncoding
|
298
|
+
freeWhenDone:NO];
|
299
|
+
if (t) {
|
300
|
+
[*o appendString:t];
|
301
|
+
[t release];
|
302
|
+
c += len;
|
303
|
+
}
|
304
|
+
}
|
305
|
+
|
306
|
+
if (*c == '"') {
|
307
|
+
c++;
|
308
|
+
return YES;
|
309
|
+
|
310
|
+
} else if (*c == '\\') {
|
311
|
+
unichar uc = *++c;
|
312
|
+
switch (uc) {
|
313
|
+
case '\\':
|
314
|
+
case '/':
|
315
|
+
case '"':
|
316
|
+
break;
|
317
|
+
|
318
|
+
case 'b': uc = '\b'; break;
|
319
|
+
case 'n': uc = '\n'; break;
|
320
|
+
case 'r': uc = '\r'; break;
|
321
|
+
case 't': uc = '\t'; break;
|
322
|
+
case 'f': uc = '\f'; break;
|
323
|
+
|
324
|
+
case 'u':
|
325
|
+
c++;
|
326
|
+
if (![self scanUnicodeChar:&uc]) {
|
327
|
+
[self addErrorWithCode:EUNICODE description: @"Broken unicode character"];
|
328
|
+
return NO;
|
329
|
+
}
|
330
|
+
c--; // hack.
|
331
|
+
break;
|
332
|
+
default:
|
333
|
+
[self addErrorWithCode:EESCAPE description: [NSString stringWithFormat:@"Illegal escape sequence '0x%x'", uc]];
|
334
|
+
return NO;
|
335
|
+
break;
|
336
|
+
}
|
337
|
+
CFStringAppendCharacters((CFMutableStringRef)*o, &uc, 1);
|
338
|
+
c++;
|
339
|
+
|
340
|
+
} else if (*c < 0x20) {
|
341
|
+
[self addErrorWithCode:ECTRL description: [NSString stringWithFormat:@"Unescaped control character '0x%x'", *c]];
|
342
|
+
return NO;
|
343
|
+
|
344
|
+
} else {
|
345
|
+
NSLog(@"should not be able to get here");
|
346
|
+
}
|
347
|
+
} while (*c);
|
348
|
+
|
349
|
+
[self addErrorWithCode:EEOF description:@"Unexpected EOF while parsing string"];
|
350
|
+
return NO;
|
351
|
+
}
|
352
|
+
|
353
|
+
- (BOOL)scanUnicodeChar:(unichar *)x
|
354
|
+
{
|
355
|
+
unichar hi, lo;
|
356
|
+
|
357
|
+
if (![self scanHexQuad:&hi]) {
|
358
|
+
[self addErrorWithCode:EUNICODE description: @"Missing hex quad"];
|
359
|
+
return NO;
|
360
|
+
}
|
361
|
+
|
362
|
+
if (hi >= 0xd800) { // high surrogate char?
|
363
|
+
if (hi < 0xdc00) { // yes - expect a low char
|
364
|
+
|
365
|
+
if (!(*c == '\\' && ++c && *c == 'u' && ++c && [self scanHexQuad:&lo])) {
|
366
|
+
[self addErrorWithCode:EUNICODE description: @"Missing low character in surrogate pair"];
|
367
|
+
return NO;
|
368
|
+
}
|
369
|
+
|
370
|
+
if (lo < 0xdc00 || lo >= 0xdfff) {
|
371
|
+
[self addErrorWithCode:EUNICODE description:@"Invalid low surrogate char"];
|
372
|
+
return NO;
|
373
|
+
}
|
374
|
+
|
375
|
+
hi = (hi - 0xd800) * 0x400 + (lo - 0xdc00) + 0x10000;
|
376
|
+
|
377
|
+
} else if (hi < 0xe000) {
|
378
|
+
[self addErrorWithCode:EUNICODE description:@"Invalid high character in surrogate pair"];
|
379
|
+
return NO;
|
380
|
+
}
|
381
|
+
}
|
382
|
+
|
383
|
+
*x = hi;
|
384
|
+
return YES;
|
385
|
+
}
|
386
|
+
|
387
|
+
- (BOOL)scanHexQuad:(unichar *)x
|
388
|
+
{
|
389
|
+
*x = 0;
|
390
|
+
for (int i = 0; i < 4; i++) {
|
391
|
+
unichar uc = *c;
|
392
|
+
c++;
|
393
|
+
int d = (uc >= '0' && uc <= '9')
|
394
|
+
? uc - '0' : (uc >= 'a' && uc <= 'f')
|
395
|
+
? (uc - 'a' + 10) : (uc >= 'A' && uc <= 'F')
|
396
|
+
? (uc - 'A' + 10) : -1;
|
397
|
+
if (d == -1) {
|
398
|
+
[self addErrorWithCode:EUNICODE description:@"Missing hex digit in quad"];
|
399
|
+
return NO;
|
400
|
+
}
|
401
|
+
*x *= 16;
|
402
|
+
*x += d;
|
403
|
+
}
|
404
|
+
return YES;
|
405
|
+
}
|
406
|
+
|
407
|
+
- (BOOL)scanNumber:(NSNumber **)o
|
408
|
+
{
|
409
|
+
const char *ns = c;
|
410
|
+
|
411
|
+
// The logic to test for validity of the number formatting is relicensed
|
412
|
+
// from JSON::XS with permission from its author Marc Lehmann.
|
413
|
+
// (Available at the CPAN: http://search.cpan.org/dist/JSON-XS/ .)
|
414
|
+
|
415
|
+
if ('-' == *c)
|
416
|
+
c++;
|
417
|
+
|
418
|
+
if ('0' == *c && c++) {
|
419
|
+
if (isdigit(*c)) {
|
420
|
+
[self addErrorWithCode:EPARSENUM description: @"Leading 0 disallowed in number"];
|
421
|
+
return NO;
|
422
|
+
}
|
423
|
+
|
424
|
+
} else if (!isdigit(*c) && c != ns) {
|
425
|
+
[self addErrorWithCode:EPARSENUM description: @"No digits after initial minus"];
|
426
|
+
return NO;
|
427
|
+
|
428
|
+
} else {
|
429
|
+
skipDigits(c);
|
430
|
+
}
|
431
|
+
|
432
|
+
// Fractional part
|
433
|
+
if ('.' == *c && c++) {
|
434
|
+
|
435
|
+
if (!isdigit(*c)) {
|
436
|
+
[self addErrorWithCode:EPARSENUM description: @"No digits after decimal point"];
|
437
|
+
return NO;
|
438
|
+
}
|
439
|
+
skipDigits(c);
|
440
|
+
}
|
441
|
+
|
442
|
+
// Exponential part
|
443
|
+
if ('e' == *c || 'E' == *c) {
|
444
|
+
c++;
|
445
|
+
|
446
|
+
if ('-' == *c || '+' == *c)
|
447
|
+
c++;
|
448
|
+
|
449
|
+
if (!isdigit(*c)) {
|
450
|
+
[self addErrorWithCode:EPARSENUM description: @"No digits after exponent"];
|
451
|
+
return NO;
|
452
|
+
}
|
453
|
+
skipDigits(c);
|
454
|
+
}
|
455
|
+
|
456
|
+
id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns
|
457
|
+
length:c - ns
|
458
|
+
encoding:NSUTF8StringEncoding
|
459
|
+
freeWhenDone:NO];
|
460
|
+
[str autorelease];
|
461
|
+
if (str && (*o = [NSDecimalNumber decimalNumberWithString:str]))
|
462
|
+
return YES;
|
463
|
+
|
464
|
+
[self addErrorWithCode:EPARSENUM description: @"Failed creating decimal instance"];
|
465
|
+
return NO;
|
466
|
+
}
|
467
|
+
|
468
|
+
- (BOOL)scanIsAtEnd
|
469
|
+
{
|
470
|
+
skipWhitespace(c);
|
471
|
+
return !*c;
|
472
|
+
}
|
473
|
+
|
474
|
+
|
475
|
+
@end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
/*
|
2
|
+
Copyright (C) 2009 Stig Brautaset. All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
* Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
* Neither the name of the author nor the names of its contributors may be used
|
15
|
+
to endorse or promote products derived from this software without specific
|
16
|
+
prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
19
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
20
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
*/
|
29
|
+
|
30
|
+
#import <Foundation/Foundation.h>
|
31
|
+
#import "SBJsonBase.h"
|
32
|
+
|
33
|
+
/**
|
34
|
+
@brief Options for the writer class.
|
35
|
+
|
36
|
+
This exists so the SBJSON facade can implement the options in the writer without having to re-declare them.
|
37
|
+
*/
|
38
|
+
@protocol SBJsonWriter
|
39
|
+
|
40
|
+
/**
|
41
|
+
@brief Whether we are generating human-readable (multiline) JSON.
|
42
|
+
|
43
|
+
Set whether or not to generate human-readable JSON. The default is NO, which produces
|
44
|
+
JSON without any whitespace. (Except inside strings.) If set to YES, generates human-readable
|
45
|
+
JSON with linebreaks after each array value and dictionary key/value pair, indented two
|
46
|
+
spaces per nesting level.
|
47
|
+
*/
|
48
|
+
@property BOOL humanReadable;
|
49
|
+
|
50
|
+
/**
|
51
|
+
@brief Whether or not to sort the dictionary keys in the output.
|
52
|
+
|
53
|
+
If this is set to YES, the dictionary keys in the JSON output will be in sorted order.
|
54
|
+
(This is useful if you need to compare two structures, for example.) The default is NO.
|
55
|
+
*/
|
56
|
+
@property BOOL sortKeys;
|
57
|
+
|
58
|
+
/**
|
59
|
+
@brief Return JSON representation (or fragment) for the given object.
|
60
|
+
|
61
|
+
Returns a string containing JSON representation of the passed in value, or nil on error.
|
62
|
+
If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error.
|
63
|
+
|
64
|
+
@param value any instance that can be represented as a JSON fragment
|
65
|
+
|
66
|
+
*/
|
67
|
+
- (NSString*)stringWithObject:(id)value;
|
68
|
+
|
69
|
+
@end
|
70
|
+
|
71
|
+
|
72
|
+
/**
|
73
|
+
@brief The JSON writer class.
|
74
|
+
|
75
|
+
Objective-C types are mapped to JSON types in the following way:
|
76
|
+
|
77
|
+
@li NSNull -> Null
|
78
|
+
@li NSString -> String
|
79
|
+
@li NSArray -> Array
|
80
|
+
@li NSDictionary -> Object
|
81
|
+
@li NSNumber (-initWithBool:) -> Boolean
|
82
|
+
@li NSNumber -> Number
|
83
|
+
|
84
|
+
In JSON the keys of an object must be strings. NSDictionary keys need
|
85
|
+
not be, but attempting to convert an NSDictionary with non-string keys
|
86
|
+
into JSON will throw an exception.
|
87
|
+
|
88
|
+
NSNumber instances created with the +initWithBool: method are
|
89
|
+
converted into the JSON boolean "true" and "false" values, and vice
|
90
|
+
versa. Any other NSNumber instances are converted to a JSON number the
|
91
|
+
way you would expect.
|
92
|
+
|
93
|
+
*/
|
94
|
+
@interface SBJsonWriter : SBJsonBase <SBJsonWriter> {
|
95
|
+
|
96
|
+
@private
|
97
|
+
BOOL sortKeys, humanReadable;
|
98
|
+
}
|
99
|
+
|
100
|
+
@end
|
101
|
+
|
102
|
+
// don't use - exists for backwards compatibility. Will be removed in 2.3.
|
103
|
+
@interface SBJsonWriter (Private)
|
104
|
+
- (NSString*)stringWithFragment:(id)value;
|
105
|
+
@end
|
106
|
+
|
107
|
+
/**
|
108
|
+
@brief Allows generation of JSON for otherwise unsupported classes.
|
109
|
+
|
110
|
+
If you have a custom class that you want to create a JSON representation for you can implement
|
111
|
+
this method in your class. It should return a representation of your object defined
|
112
|
+
in terms of objects that can be translated into JSON. For example, a Person
|
113
|
+
object might implement it like this:
|
114
|
+
|
115
|
+
@code
|
116
|
+
- (id)jsonProxyObject {
|
117
|
+
return [NSDictionary dictionaryWithObjectsAndKeys:
|
118
|
+
name, @"name",
|
119
|
+
phone, @"phone",
|
120
|
+
email, @"email",
|
121
|
+
nil];
|
122
|
+
}
|
123
|
+
@endcode
|
124
|
+
|
125
|
+
*/
|
126
|
+
@interface NSObject (SBProxyForJson)
|
127
|
+
- (id)proxyForJson;
|
128
|
+
@end
|
129
|
+
|