iCuke 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. data/.gitignore +2 -0
  2. data/.gitmodules +3 -0
  3. data/README.rdoc +8 -7
  4. data/Rakefile +10 -30
  5. data/VERSION +1 -1
  6. data/app/.gitignore +3 -0
  7. data/app/LICENSE +20 -0
  8. data/app/Readme.markdown +3 -0
  9. data/app/Shared/AppDelegate_Shared.h +17 -0
  10. data/app/Shared/AppDelegate_Shared.m +25 -0
  11. data/app/{images/yellowslide.png → Shared/Icon.png} +0 -0
  12. data/app/Shared/ModalViewController.h +15 -0
  13. data/app/Shared/ModalViewController.m +96 -0
  14. data/app/Shared/TestViewController_Shared.h +15 -0
  15. data/app/Shared/TestViewController_Shared.m +43 -0
  16. data/app/{Info.plist → Shared/Universal-Info.plist} +17 -4
  17. data/app/Shared/Universal_Prefix.pch +8 -0
  18. data/app/Shared/main.m +14 -0
  19. data/app/Universal.xcodeproj/project.pbxproj +301 -0
  20. data/app/iPad/AppDelegate_Pad.h +15 -0
  21. data/app/iPad/AppDelegate_Pad.m +28 -0
  22. data/app/iPad/Default-Landscape.png +0 -0
  23. data/app/iPad/Default-Portrait.png +0 -0
  24. data/app/iPad/MainWindow_Pad.xib +343 -0
  25. data/app/iPad/TestViewController_Pad.h +15 -0
  26. data/app/iPad/TestViewController_Pad.m +52 -0
  27. data/app/iPhone/AppDelegate_Phone.h +15 -0
  28. data/app/iPhone/AppDelegate_Phone.m +28 -0
  29. data/app/{images/segment_search.png → iPhone/Default.png} +0 -0
  30. data/app/iPhone/MainWindow_Phone.xib +363 -0
  31. data/app/iPhone/TestViewController_Phone.h +15 -0
  32. data/app/iPhone/TestViewController_Phone.m +36 -0
  33. data/bin/.gitignore +1 -0
  34. data/bin/icuke +10 -0
  35. data/bin/icuke-module +17 -0
  36. data/ext/.gitignore +1 -0
  37. data/ext/Rakefile +6 -0
  38. data/ext/WaxSim/Simulator.h +31 -0
  39. data/ext/WaxSim/Simulator.m +131 -0
  40. data/ext/WaxSim/WaxSim.m +110 -0
  41. data/ext/WaxSim/WaxSim.xcodeproj/project.pbxproj +238 -0
  42. data/ext/WaxSim/WaxSim_Prefix.pch +7 -0
  43. data/ext/WaxSim/iPhoneSimulatorRemoteClient/iPhoneSimulatorRemoteClient.h +126 -0
  44. data/ext/iCuke/Rakefile +14 -17
  45. data/ext/iCuke/libicuke-sdk3.1.dylib +0 -0
  46. data/ext/iCuke/libicuke-sdk4.0.dylib +0 -0
  47. data/ext/iCuke/libicuke.dylib +0 -0
  48. data/ext/iCuke/{DefaultsResponse.h → sdk3.1/DefaultsResponse.h} +0 -0
  49. data/ext/iCuke/{DefaultsResponse.m → sdk3.1/DefaultsResponse.m} +0 -0
  50. data/ext/iCuke/{EventResponse.h → sdk3.1/EventResponse.h} +0 -0
  51. data/ext/iCuke/{EventResponse.m → sdk3.1/EventResponse.m} +0 -0
  52. data/ext/iCuke/sdk3.1/ModuleResponse.h +5 -0
  53. data/ext/iCuke/sdk3.1/ModuleResponse.m +76 -0
  54. data/ext/iCuke/sdk3.1/Rakefile +20 -0
  55. data/ext/iCuke/{Recorder.h → sdk3.1/Recorder.h} +0 -0
  56. data/ext/iCuke/{Recorder.m → sdk3.1/Recorder.m} +0 -0
  57. data/ext/iCuke/{RecorderResponse.h → sdk3.1/RecorderResponse.h} +0 -0
  58. data/ext/iCuke/{RecorderResponse.m → sdk3.1/RecorderResponse.m} +0 -0
  59. data/ext/iCuke/{SynthesizeSingleton.h → sdk3.1/SynthesizeSingleton.h} +0 -0
  60. data/ext/iCuke/{ViewResponse.h → sdk3.1/ViewResponse.h} +0 -0
  61. data/ext/iCuke/{ViewResponse.m → sdk3.1/ViewResponse.m} +0 -0
  62. data/ext/iCuke/{Viewer.h → sdk3.1/Viewer.h} +0 -0
  63. data/ext/iCuke/sdk3.1/Viewer.m +203 -0
  64. data/ext/iCuke/{iCukeHTTPResponseHandler.h → sdk3.1/iCukeHTTPResponseHandler.h} +0 -0
  65. data/ext/iCuke/{iCukeHTTPResponseHandler.m → sdk3.1/iCukeHTTPResponseHandler.m} +0 -0
  66. data/ext/iCuke/{iCukeHTTPServer.h → sdk3.1/iCukeHTTPServer.h} +0 -0
  67. data/ext/iCuke/{iCukeHTTPServer.m → sdk3.1/iCukeHTTPServer.m} +0 -0
  68. data/ext/iCuke/{iCukeServer.h → sdk3.1/iCukeServer.h} +0 -2
  69. data/ext/iCuke/{iCukeServer.m → sdk3.1/iCukeServer.m} +22 -20
  70. data/ext/iCuke/{json → sdk3.1/json}/JSON.h +0 -0
  71. data/ext/iCuke/{json → sdk3.1/json}/NSObject+SBJSON.h +0 -0
  72. data/ext/iCuke/{json → sdk3.1/json}/NSObject+SBJSON.m +0 -0
  73. data/ext/iCuke/{json → sdk3.1/json}/NSString+SBJSON.h +0 -0
  74. data/ext/iCuke/{json → sdk3.1/json}/NSString+SBJSON.m +0 -0
  75. data/ext/iCuke/{json → sdk3.1/json}/SBJSON.h +0 -0
  76. data/ext/iCuke/{json → sdk3.1/json}/SBJSON.m +0 -0
  77. data/ext/iCuke/{json → sdk3.1/json}/SBJsonBase.h +0 -0
  78. data/ext/iCuke/{json → sdk3.1/json}/SBJsonBase.m +0 -0
  79. data/ext/iCuke/{json → sdk3.1/json}/SBJsonParser.h +0 -0
  80. data/ext/iCuke/{json → sdk3.1/json}/SBJsonParser.m +0 -0
  81. data/ext/iCuke/{json → sdk3.1/json}/SBJsonWriter.h +0 -0
  82. data/ext/iCuke/{json → sdk3.1/json}/SBJsonWriter.m +0 -0
  83. data/ext/iCuke/sdk4.0/DefaultsResponse.h +5 -0
  84. data/ext/iCuke/sdk4.0/DefaultsResponse.m +66 -0
  85. data/ext/iCuke/sdk4.0/EventResponse.h +5 -0
  86. data/ext/iCuke/sdk4.0/EventResponse.m +130 -0
  87. data/ext/iCuke/sdk4.0/ModuleResponse.h +5 -0
  88. data/ext/iCuke/sdk4.0/ModuleResponse.m +76 -0
  89. data/ext/iCuke/sdk4.0/Rakefile +20 -0
  90. data/ext/iCuke/sdk4.0/Recorder.h +17 -0
  91. data/ext/iCuke/sdk4.0/Recorder.m +90 -0
  92. data/ext/iCuke/sdk4.0/RecorderResponse.h +6 -0
  93. data/ext/iCuke/sdk4.0/RecorderResponse.m +68 -0
  94. data/ext/iCuke/sdk4.0/SynthesizeSingleton.h +68 -0
  95. data/ext/iCuke/sdk4.0/ViewResponse.h +5 -0
  96. data/ext/iCuke/sdk4.0/ViewResponse.m +84 -0
  97. data/ext/iCuke/sdk4.0/Viewer.h +8 -0
  98. data/ext/iCuke/{Viewer.m → sdk4.0/Viewer.m} +14 -0
  99. data/ext/iCuke/sdk4.0/iCukeHTTPResponseHandler.h +50 -0
  100. data/ext/iCuke/sdk4.0/iCukeHTTPResponseHandler.m +381 -0
  101. data/ext/iCuke/sdk4.0/iCukeHTTPServer.h +53 -0
  102. data/ext/iCuke/sdk4.0/iCukeHTTPServer.m +365 -0
  103. data/ext/iCuke/sdk4.0/iCukeServer.h +14 -0
  104. data/ext/iCuke/sdk4.0/iCukeServer.m +65 -0
  105. data/ext/iCuke/sdk4.0/json/JSON.h +50 -0
  106. data/ext/iCuke/sdk4.0/json/NSObject+SBJSON.h +68 -0
  107. data/ext/iCuke/sdk4.0/json/NSObject+SBJSON.m +53 -0
  108. data/ext/iCuke/sdk4.0/json/NSString+SBJSON.h +58 -0
  109. data/ext/iCuke/sdk4.0/json/NSString+SBJSON.m +55 -0
  110. data/ext/iCuke/sdk4.0/json/SBJSON.h +75 -0
  111. data/ext/iCuke/sdk4.0/json/SBJSON.m +212 -0
  112. data/ext/iCuke/sdk4.0/json/SBJsonBase.h +86 -0
  113. data/ext/iCuke/sdk4.0/json/SBJsonBase.m +78 -0
  114. data/ext/iCuke/sdk4.0/json/SBJsonParser.h +87 -0
  115. data/ext/iCuke/sdk4.0/json/SBJsonParser.m +475 -0
  116. data/ext/iCuke/sdk4.0/json/SBJsonWriter.h +129 -0
  117. data/ext/iCuke/sdk4.0/json/SBJsonWriter.m +228 -0
  118. data/features/iphone.feature +15 -0
  119. data/features/support/env.rb +1 -1
  120. data/iCuke.gemspec +118 -114
  121. data/lib/icuke/cucumber.rb +93 -64
  122. data/lib/icuke/screen.rb +110 -0
  123. data/lib/icuke/sdk.rb +134 -0
  124. data/lib/icuke/simulate.rb +9 -5
  125. data/lib/icuke/simulator.rb +10 -11
  126. data/lib/icuke/waxsim.rb +36 -0
  127. data/lib/icuke.rb +2 -1
  128. metadata +124 -129
  129. data/app/AlertsViewController.h +0 -59
  130. data/app/AlertsViewController.m +0 -341
  131. data/app/AppDelegate.h +0 -60
  132. data/app/AppDelegate.m +0 -101
  133. data/app/ButtonsViewController.h +0 -80
  134. data/app/ButtonsViewController.m +0 -448
  135. data/app/Constants.h +0 -58
  136. data/app/ControlsViewController.h +0 -72
  137. data/app/ControlsViewController.m +0 -379
  138. data/app/ImagesViewController.h +0 -62
  139. data/app/ImagesViewController.m +0 -137
  140. data/app/MainViewController.h +0 -57
  141. data/app/MainViewController.m +0 -262
  142. data/app/Picker/CustomPickerDataSource.h +0 -55
  143. data/app/Picker/CustomPickerDataSource.m +0 -135
  144. data/app/Picker/CustomView.h +0 -62
  145. data/app/Picker/CustomView.m +0 -119
  146. data/app/PickerViewController.h +0 -88
  147. data/app/PickerViewController.m +0 -402
  148. data/app/Prefix.pch +0 -5
  149. data/app/ReadMe.txt +0 -82
  150. data/app/SearchBarController.h +0 -58
  151. data/app/SearchBarController.m +0 -105
  152. data/app/SegmentViewController.h +0 -54
  153. data/app/SegmentViewController.m +0 -195
  154. data/app/TextFieldController.h +0 -67
  155. data/app/TextFieldController.m +0 -342
  156. data/app/TextViewController.h +0 -57
  157. data/app/TextViewController.m +0 -164
  158. data/app/ToolbarViewController.h +0 -74
  159. data/app/ToolbarViewController.m +0 -303
  160. data/app/TransitionViewController.h +0 -64
  161. data/app/TransitionViewController.m +0 -178
  162. data/app/UICatalog.xcodeproj/project.pbxproj +0 -615
  163. data/app/WebViewController.h +0 -57
  164. data/app/WebViewController.m +0 -173
  165. data/app/en.lproj/AlertsViewController.xib +0 -144
  166. data/app/en.lproj/ButtonsViewController.xib +0 -147
  167. data/app/en.lproj/ControlsViewController.xib +0 -147
  168. data/app/en.lproj/ImagesViewController.xib +0 -264
  169. data/app/en.lproj/Localizable.strings +0 -41
  170. data/app/en.lproj/MainWindow.xib +0 -306
  171. data/app/en.lproj/PickerViewController.xib +0 -415
  172. data/app/en.lproj/SearchBarController.xib +0 -142
  173. data/app/en.lproj/SegmentViewController.xib +0 -143
  174. data/app/en.lproj/TextFieldController.xib +0 -167
  175. data/app/en.lproj/TextViewController.xib +0 -149
  176. data/app/en.lproj/ToolbarViewController.xib +0 -491
  177. data/app/en.lproj/TransitionViewController.xib +0 -255
  178. data/app/en.lproj/WebViewController.xib +0 -141
  179. data/app/images/12-6AM.png +0 -0
  180. data/app/images/12-6PM.png +0 -0
  181. data/app/images/6-12AM.png +0 -0
  182. data/app/images/6-12PM.png +0 -0
  183. data/app/images/Default.png +0 -0
  184. data/app/images/Icon.png +0 -0
  185. data/app/images/UIButton_custom.png +0 -0
  186. data/app/images/blueButton.png +0 -0
  187. data/app/images/orangeslide.png +0 -0
  188. data/app/images/scene1.jpg +0 -0
  189. data/app/images/scene2.jpg +0 -0
  190. data/app/images/scene3.jpg +0 -0
  191. data/app/images/scene4.jpg +0 -0
  192. data/app/images/scene5.jpg +0 -0
  193. data/app/images/segment_check.png +0 -0
  194. data/app/images/segment_tools.png +0 -0
  195. data/app/images/slider_ball.png +0 -0
  196. data/app/images/whiteButton.png +0 -0
  197. data/app/main.m +0 -58
  198. data/features/uicatalog.feature +0 -20
  199. data/lib/icuke/com.apple.Accessibility.plist +0 -0
  200. data/lib/icuke/headless.rb +0 -54
  201. 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
+