OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library servicec.plugins.cc; | |
6 | |
7 import 'dart:core' hide Type; | |
8 import 'dart:io' show Platform, File; | |
9 | |
10 import 'package:path/path.dart' show basenameWithoutExtension, join, dirname; | |
11 | |
12 import 'shared.dart'; | |
13 | |
14 import '../emitter.dart'; | |
15 import '../primitives.dart' as primitives; | |
16 import '../struct_layout.dart'; | |
17 | |
18 const List<String> RESOURCES = const [ | |
19 "ImmiBase.h", | |
20 ]; | |
21 | |
22 const COPYRIGHT = """ | |
23 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
24 // for details. All rights reserved. Use of this source code is governed by a | |
25 // BSD-style license that can be found in the LICENSE.md file. | |
26 """; | |
27 | |
28 const Map<String, String> _TYPES = const { | |
29 'void' : 'void', | |
30 'bool' : 'bool', | |
31 | |
32 'uint8' : 'uint8_t', | |
33 'uint16' : 'uint16_t', | |
34 | |
35 'int8' : 'int8_t', | |
36 'int16' : 'int16_t', | |
37 'int32' : 'int32_t', | |
38 'int64' : 'int64_t', | |
39 | |
40 'float32' : 'float', | |
41 'float64' : 'double', | |
42 | |
43 'String' : 'NSString*', | |
44 }; | |
45 | |
46 String getTypePointer(Type type) { | |
47 if (type.isNode) return 'Node*'; | |
48 if (type.resolved != null) { | |
49 return "${type.identifier}Node*"; | |
50 } | |
51 return _TYPES[type.identifier]; | |
52 } | |
53 | |
54 String getTypeName(Type type) { | |
55 if (type.isNode) return 'Node'; | |
56 if (type.resolved != null) { | |
57 return "${type.identifier}Node"; | |
58 } | |
59 return _TYPES[type.identifier]; | |
60 } | |
61 | |
62 void generate(String path, Map units, String outputDirectory) { | |
63 String directory = join(outputDirectory, "objc"); | |
64 _generateHeaderFile(path, units, directory); | |
65 _generateImplementationFile(path, units, directory); | |
66 | |
67 String resourcesDirectory = join(dirname(Platform.script.path), | |
68 '..', 'lib', 'src', 'resources', 'objc'); | |
69 for (String resource in RESOURCES) { | |
70 String resourcePath = join(resourcesDirectory, resource); | |
71 File file = new File(resourcePath); | |
72 String contents = file.readAsStringSync(); | |
73 writeToFile(directory, resource, contents); | |
74 } | |
75 } | |
76 | |
77 void _generateHeaderFile(String path, Map units, String directory) { | |
78 _HeaderVisitor visitor = new _HeaderVisitor(path); | |
79 visitor.visitUnits(units); | |
80 String contents = visitor.buffer.toString(); | |
81 String file = visitor.immiImplFile; | |
82 writeToFile(directory, file, contents, extension: 'h'); | |
83 } | |
84 | |
85 void _generateImplementationFile(String path, Map units, String directory) { | |
86 _ImplementationVisitor visitor = new _ImplementationVisitor(path); | |
87 visitor.visitUnits(units); | |
88 String contents = visitor.buffer.toString(); | |
89 String file = visitor.immiImplFile; | |
90 writeToFile(directory, file, contents, extension: 'mm'); | |
91 } | |
92 | |
93 String getName(node) { | |
94 if (node is Struct) return node.name; | |
95 if (node is String) return node; | |
96 throw 'Invalid arg'; | |
97 } | |
98 | |
99 String applyToMethodName(node) { | |
100 return 'applyTo'; | |
101 } | |
102 | |
103 String presentMethodName(node) { | |
104 String name = getName(node); | |
105 return 'present${name}'; | |
106 } | |
107 | |
108 String patchMethodName(node) { | |
109 String name = getName(node); | |
110 return 'patch${name}'; | |
111 } | |
112 | |
113 String applyToMethodSignature(node) { | |
114 String name = getName(node); | |
115 return '- (void)${applyToMethodName(name)}:(id <${name}Presenter>)presenter'
; | |
116 } | |
117 | |
118 String presentMethodSignature(node) { | |
119 String name = getName(node); | |
120 String type = name == 'Node' ? name : '${name}Node'; | |
121 return '- (void)${presentMethodName(name)}:(${type}*)node'; | |
122 } | |
123 | |
124 String patchMethodSignature(node) { | |
125 String name = getName(node); | |
126 return '- (void)${patchMethodName(name)}:(${name}Patch*)patch'; | |
127 } | |
128 | |
129 String applyToMethodDeclaration(Struct node) { | |
130 return applyToMethodSignature(node) + ';'; | |
131 } | |
132 | |
133 String presentMethodDeclaration(Struct node) { | |
134 return presentMethodSignature(node) + ';'; | |
135 } | |
136 | |
137 String patchMethodDeclaration(Struct node) { | |
138 return patchMethodSignature(node) + ';'; | |
139 } | |
140 | |
141 | |
142 abstract class _ObjCVisitor extends CodeGenerationVisitor { | |
143 _ObjCVisitor(String path) : super(path); | |
144 | |
145 String immiBaseFile = 'ImmiBase.h'; | |
146 String immiImplFile = 'Immi.h'; | |
147 } | |
148 | |
149 class _HeaderVisitor extends _ObjCVisitor { | |
150 _HeaderVisitor(String path) : super(path); | |
151 | |
152 List nodes = []; | |
153 | |
154 visitUnits(Map units) { | |
155 units.values.forEach(collectMethodSignatures); | |
156 units.values.forEach((unit) { nodes.addAll(unit.structs); }); | |
157 _writeHeader(); | |
158 nodes.forEach((node) { writeln('@class ${node.name}Node;'); }); | |
159 writeln(); | |
160 nodes.forEach((node) { writeln('@class ${node.name}Patch;'); }); | |
161 writeln(); | |
162 _writeActions(); | |
163 units.values.forEach(visit); | |
164 } | |
165 | |
166 visitUnit(Unit unit) { | |
167 unit.structs.forEach(visit); | |
168 } | |
169 | |
170 visitStruct(Struct node) { | |
171 StructLayout layout = node.layout; | |
172 String nodeName = "${node.name}Node"; | |
173 String nodeNameData = "${nodeName}Data"; | |
174 String patchName = "${node.name}Patch"; | |
175 String patchNameData = "${nodeName}PatchData"; | |
176 String presenterName = "${node.name}Presenter"; | |
177 writeln('@protocol $presenterName'); | |
178 writeln(presentMethodDeclaration(node)); | |
179 writeln(patchMethodDeclaration(node)); | |
180 writeln('@end'); | |
181 writeln(); | |
182 writeln('@interface $nodeName : NSObject <Node>'); | |
183 forEachSlot(node, null, (Type slotType, String slotName) { | |
184 write('@property (readonly) '); | |
185 _writeNSType(slotType); | |
186 writeln(' $slotName;'); | |
187 }); | |
188 for (var method in node.methods) { | |
189 List<Type> formalTypes = method.arguments.map((formal) => formal.type); | |
190 String actionBlock = 'Action${actionTypeSuffix(formalTypes)}Block'; | |
191 writeln('@property (readonly) $actionBlock ${method.name};'); | |
192 } | |
193 writeln('@end'); | |
194 writeln(); | |
195 writeln('@interface $patchName : NSObject <NodePatch>'); | |
196 writeln('@property (readonly) bool changed;'); | |
197 writeln('@property (readonly) $nodeName* previous;'); | |
198 writeln('@property (readonly) $nodeName* current;'); | |
199 forEachSlot(node, null, (Type slotType, String slotName) { | |
200 writeln('@property (readonly) ${patchType(slotType)}* $slotName;'); | |
201 }); | |
202 for (var method in node.methods) { | |
203 String actionPatch = actionPatchType(method); | |
204 writeln('@property (readonly) $actionPatch* ${method.name};'); | |
205 } | |
206 writeln(applyToMethodDeclaration(node)); | |
207 writeln('@end'); | |
208 writeln(); | |
209 } | |
210 | |
211 void _writeFormalWithKeyword(String keyword, Formal formal) { | |
212 write('$keyword:(${getTypeName(formal.type)})${formal.name}'); | |
213 } | |
214 | |
215 _writeActions() { | |
216 for (List<Type> formals in methodSignatures.values) { | |
217 String actionName = 'Action${actionTypeSuffix(formals)}'; | |
218 String actionBlock = '${actionName}Block'; | |
219 String actionPatch = '${actionName}Patch'; | |
220 write('typedef void (^$actionBlock)(${actionTypeFormals(formals)});'); | |
221 writeln(); | |
222 writeln('@interface $actionPatch : NSObject <Patch>'); | |
223 writeln('@property (readonly) $actionBlock current;'); | |
224 writeln('@end'); | |
225 writeln(); | |
226 } | |
227 } | |
228 | |
229 void _writeNSType(Type type) { | |
230 if (type.isList) { | |
231 write('NSArray*'); | |
232 } else { | |
233 write(getTypePointer(type)); | |
234 } | |
235 } | |
236 | |
237 void _writeHeader() { | |
238 writeln(COPYRIGHT); | |
239 writeln('// Generated file. Do not edit.'); | |
240 writeln(); | |
241 writeln('#import "$immiBaseFile"'); | |
242 writeln(); | |
243 } | |
244 | |
245 String patchType(Type type) { | |
246 if (type.isList) return 'ListPatch'; | |
247 return '${camelize(type.identifier)}Patch'; | |
248 } | |
249 | |
250 String actionTypeSuffix(List<Type> types) { | |
251 if (types.isEmpty) return 'Void'; | |
252 return types.map((Type type) => camelize(type.identifier)).join(); | |
253 } | |
254 | |
255 String actionTypeFormals(List<Type> types) { | |
256 return types.map((Type type) => getTypeName(type)).join(', '); | |
257 } | |
258 | |
259 String actionPatchType(Method method) { | |
260 List<Type> types = method.arguments.map((formal) => formal.type); | |
261 return 'Action${actionTypeSuffix(types)}Patch'; | |
262 } | |
263 | |
264 String actionBlockType(Method method) { | |
265 List<Type> types = method.arguments.map((formal) => formal.type); | |
266 return 'Action${actionTypeSuffix(types)}Block'; | |
267 } | |
268 } | |
269 | |
270 class _ImplementationVisitor extends _ObjCVisitor { | |
271 _ImplementationVisitor(String path) : super(path); | |
272 | |
273 List<Struct> nodes = []; | |
274 | |
275 visitUnits(Map units) { | |
276 units.values.forEach(collectMethodSignatures); | |
277 units.values.forEach((unit) { nodes.addAll(unit.structs); }); | |
278 _writeHeader(); | |
279 | |
280 _writeNodeBaseExtendedInterface(); | |
281 _writePatchBaseExtendedInterface(); | |
282 _writePatchPrimitivesExtendedInterface(); | |
283 _writeActionsExtendedInterface(); | |
284 units.values.forEach((unit) { | |
285 unit.structs.forEach(_writeNodeExtendedInterface); | |
286 }); | |
287 | |
288 _writeImmiServiceExtendedInterface(); | |
289 _writeImmiRootExtendedInterface(); | |
290 | |
291 _writeEventUtils(); | |
292 _writeStringUtils(); | |
293 _writeListUtils(); | |
294 | |
295 _writeNodeBaseImplementation(); | |
296 _writePatchBaseImplementation(); | |
297 _writePatchPrimitivesImplementation(); | |
298 _writeActionsImplementation(); | |
299 units.values.forEach((unit) { | |
300 unit.structs.forEach(_writeNodeImplementation); | |
301 }); | |
302 | |
303 _writeImmiServiceImplementation(); | |
304 _writeImmiRootImplementation(); | |
305 } | |
306 | |
307 visitUnit(Unit unit) { | |
308 // Everything is done in visitUnits. | |
309 } | |
310 | |
311 _writeImmiServiceExtendedInterface() { | |
312 writeln('@interface ImmiService ()'); | |
313 writeln('@property NSMutableArray* storyboards;'); | |
314 writeln('@property NSMutableDictionary* roots;'); | |
315 writeln('@end'); | |
316 writeln(); | |
317 } | |
318 | |
319 _writeImmiServiceImplementation() { | |
320 writeln('@implementation ImmiService'); | |
321 writeln(); | |
322 writeln('- (id)init {'); | |
323 writeln(' self = [super init];'); | |
324 writeln(' _storyboards = [NSMutableArray array];'); | |
325 writeln(' _roots = [NSMutableDictionary dictionary];'); | |
326 writeln(' return self;'); | |
327 writeln('}'); | |
328 writeln(); | |
329 writeln('- (ImmiRoot*)registerPresenter:(id <NodePresenter>)presenter'); | |
330 writeln(' forName:(NSString*)name {'); | |
331 writeln(' assert(self.roots[name] == nil);'); | |
332 writeln(' int length = name.length;'); | |
333 writeln(' int size = 48 + PresenterDataBuilder::kSize + length;'); | |
334 writeln(' MessageBuilder message(size);'); | |
335 writeln(' PresenterDataBuilder builder ='); | |
336 writeln(' message.initRoot<PresenterDataBuilder>();'); | |
337 writeln(' List<unichar> chars = builder.initNameData(length);'); | |
338 writeln(' [name getCharacters:chars.data()'); | |
339 writeln(' range:NSMakeRange(0, length)];'); | |
340 writeln(' uint16_t pid = ImmiServiceLayer::getPresenter(builder);'); | |
341 writeln(' ImmiRoot* root ='); | |
342 writeln(' [[ImmiRoot alloc] init:pid presenter:presenter];'); | |
343 writeln(' self.roots[name] = root;'); | |
344 writeln(' return root;'); | |
345 writeln('}'); | |
346 writeln(); | |
347 writeln('- (void)registerStoryboard:(UIStoryboard*)storyboard {'); | |
348 writeln(' [self.storyboards addObject:storyboard];'); | |
349 writeln('}'); | |
350 writeln(); | |
351 writeln('- (ImmiRoot*)getRootByName:(NSString*)name {'); | |
352 writeln(' ImmiRoot* root = self.roots[name];'); | |
353 writeln(' if (root != nil) return root;'); | |
354 writeln(' id <NodePresenter> presenter = nil;'); | |
355 writeln(' for (int i = 0; i < self.storyboards.count; ++i) {'); | |
356 writeln(' @try {'); | |
357 writeln(' presenter = [self.storyboards[i]'); | |
358 writeln(' instantiateViewControllerWithIdentifier:name];'); | |
359 writeln(' break;'); | |
360 writeln(' }'); | |
361 writeln(' @catch (NSException* e) {'); | |
362 writeln(' if (e.name != NSInvalidArgumentException) {'); | |
363 writeln(' @throw e;'); | |
364 writeln(' }'); | |
365 writeln(' }'); | |
366 writeln(' }'); | |
367 writeln(' if (presenter == nil) abort();'); | |
368 writeln(' return [self registerPresenter:presenter forName:name];'); | |
369 writeln('}'); | |
370 writeln(); | |
371 writeln('- (id <NodePresenter>)getPresenterByName:(NSString*)name {'); | |
372 writeln(' return [[self getRootByName:name] presenter];'); | |
373 writeln('}'); | |
374 writeln(); | |
375 writeln('@end'); | |
376 writeln(); | |
377 } | |
378 | |
379 _writeImmiRootExtendedInterface() { | |
380 writeln('@interface ImmiRoot ()'); | |
381 writeln('@property (readonly) uint16_t pid;'); | |
382 writeln('@property (readonly) id <NodePresenter> presenter;'); | |
383 writeln('@property Node* previous;'); | |
384 writeln('@property bool refreshPending;'); | |
385 writeln('@property bool refreshRequired;'); | |
386 writeln('@property (nonatomic) dispatch_queue_t refreshQueue;'); | |
387 writeln('- (id)init:(uint16_t)pid presenter:(id <NodePresenter>)presenter;')
; | |
388 writeln('@end'); | |
389 writeln(); | |
390 } | |
391 | |
392 _writeImmiRootImplementation() { | |
393 writeln('typedef void (^ImmiRefreshCallback)(const PatchData&);'); | |
394 writeln('void ImmiRefresh(PatchData patchData, void* callbackData) {'); | |
395 writeln(' @autoreleasepool {'); | |
396 writeln(' ImmiRefreshCallback block ='); | |
397 writeln(' (__bridge_transfer ImmiRefreshCallback)callbackData;'); | |
398 writeln(' block(patchData);'); | |
399 writeln(' patchData.Delete();'); | |
400 writeln(' }'); | |
401 writeln('}'); | |
402 writeln('@implementation ImmiRoot'); | |
403 writeln(); | |
404 writeln('- (id)init:(uint16_t)pid'); | |
405 writeln(' presenter:(id <NodePresenter>)presenter {'); | |
406 writeln(' self = [super init];'); | |
407 writeln(' assert(pid > 0);'); | |
408 writeln(' _pid = pid;'); | |
409 writeln(' _presenter = presenter;'); | |
410 writeln(' _refreshPending = false;'); | |
411 writeln(' _refreshRequired = false;'); | |
412 writeln(' _refreshQueue = dispatch_queue_create('); | |
413 writeln(' "com.google.immi.refreshQueue", DISPATCH_QUEUE_SERIAL);'); | |
414 writeln(' return self;'); | |
415 writeln('}'); | |
416 writeln(); | |
417 writeln('- (void)refresh {'); | |
418 writeln(' ImmiRefreshCallback doApply = ^(const PatchData& patchData) {'); | |
419 writeln(' if (patchData.isNode()) {'); | |
420 writeln(' NodePatch* patch = [[NodePatch alloc]'); | |
421 writeln(' initWith:patchData.getNode()'); | |
422 writeln(' previous:self.previous'); | |
423 writeln(' inGraph:self];'); | |
424 writeln(' self.previous = patch.current;'); | |
425 writeln(' [patch applyTo:self.presenter];'); | |
426 writeln(' }'); | |
427 writeln(' dispatch_async(self.refreshQueue, ^{'); | |
428 writeln(' if (self.refreshRequired) {'); | |
429 writeln(' self.refreshRequired = false;'); | |
430 writeln(' [self refresh];'); | |
431 writeln(' } else {'); | |
432 writeln(' self.refreshPending = false;'); | |
433 writeln(' }'); | |
434 writeln(' });'); | |
435 writeln(' };'); | |
436 writeln(' ${serviceName}::refreshAsync('); | |
437 writeln(' self.pid,'); | |
438 writeln(' ImmiRefresh,'); | |
439 writeln(' (__bridge_retained void*)[doApply copy]);'); | |
440 writeln('}'); | |
441 writeln(); | |
442 writeln('- (void)reset {'); | |
443 writeln(' ${serviceName}::reset(self.pid);'); | |
444 writeln('}'); | |
445 writeln(); | |
446 writeln('- (void)dispatch:(ImmiDispatchBlock)block {'); | |
447 writeln(' block();'); | |
448 writeln(' [self requestRefresh];'); | |
449 writeln('}'); | |
450 writeln(); | |
451 writeln('- (void)requestRefresh {'); | |
452 writeln(' dispatch_async(self.refreshQueue, ^{'); | |
453 writeln(' if (self.refreshPending) {'); | |
454 writeln(' self.refreshRequired = true;'); | |
455 writeln(' } else {'); | |
456 writeln(' self.refreshPending = true;'); | |
457 writeln(' [self refresh];'); | |
458 writeln(' }'); | |
459 writeln(' });'); | |
460 writeln('}'); | |
461 writeln(); | |
462 writeln('@end'); | |
463 writeln(); | |
464 } | |
465 | |
466 _writeNodeExtendedInterface(Struct node) { | |
467 String name = node.name; | |
468 String nodeName = "${name}Node"; | |
469 String patchName = "${name}Patch"; | |
470 String nodeDataName = "${nodeName}Data"; | |
471 String patchDataName = "${patchName}Data"; | |
472 writeln('@interface $nodeName ()'); | |
473 if (node.methods.isNotEmpty) { | |
474 writeln('@property (weak) ImmiRoot* root;'); | |
475 } | |
476 writeln('- (id)initWith:(const $nodeDataName&)data'); | |
477 writeln(' inGraph:(ImmiRoot*)root;'); | |
478 writeln('- (id)initWithPatch:($patchName*)patch;'); | |
479 writeln('@end'); | |
480 writeln(); | |
481 writeln('@interface $patchName ()'); | |
482 writeln('- (id)initIdentityPatch:($nodeName*)previous;'); | |
483 writeln('- (id)initWith:(const $patchDataName&)data'); | |
484 writeln(' previous:($nodeName*)previous'); | |
485 writeln(' inGraph:(ImmiRoot*)root;'); | |
486 writeln('@end'); | |
487 writeln(); | |
488 } | |
489 | |
490 _writeNodeImplementation(Struct node) { | |
491 String name = node.name; | |
492 String nodeName = "${node.name}Node"; | |
493 String patchName = "${node.name}Patch"; | |
494 String nodeDataName = "${nodeName}Data"; | |
495 String patchDataName = "${patchName}Data"; | |
496 String updateDataName = "${node.name}UpdateData"; | |
497 writeln('@implementation $nodeName'); | |
498 writeln('- (id)initWith:(const $nodeDataName&)data'); | |
499 writeln(' inGraph:(ImmiRoot*)root {'); | |
500 writeln(' self = [super init];'); | |
501 forEachSlot(node, null, (Type slotType, String slotName) { | |
502 String camelName = camelize(slotName); | |
503 write(' _$slotName = '); | |
504 if (slotType.isList) { | |
505 String slotTypeName = getTypeName(slotType.elementType.isNode ? | |
506 slotType.elementType : | |
507 slotType); | |
508 String slotTypeData = "${slotTypeName}Data"; | |
509 writeln('ListUtils<$slotTypeData>::decodeList('); | |
510 writeln(' data.get${camelName}(), create$slotTypeName, root);'); | |
511 } else if (slotType.isString) { | |
512 writeln('decodeString(data.get${camelName}Data());'); | |
513 } else if (slotType.isNode || slotType.resolved != null) { | |
514 String slotTypeName = getTypeName(slotType); | |
515 writeln('[[$slotTypeName alloc] initWith:data.get${camelName}()'); | |
516 writeln(' inGraph:(ImmiRoot*)root];'); | |
517 } else { | |
518 writeln('data.get${camelName}();'); | |
519 } | |
520 }); | |
521 for (var method in node.methods) { | |
522 String actionId = '${method.name}Id'; | |
523 writeln(' uint16_t $actionId = data.get${camelize(method.name)}();'); | |
524 write(' _${method.name} = '); | |
525 List<Type> formals = method.arguments.map((formal) => formal.type); | |
526 _writeActionBlockImplementation(actionId, formals); | |
527 writeln(';'); | |
528 } | |
529 writeln(' return self;'); | |
530 writeln('}'); | |
531 | |
532 writeln('- (id)initWithPatch:($patchName*)patch {'); | |
533 writeln(' self = [super init];'); | |
534 forEachSlot(node, null, (Type slotType, String slotName) { | |
535 writeln(' _$slotName = patch.$slotName.current;'); | |
536 }); | |
537 for (Method method in node.methods) { | |
538 String name = method.name; | |
539 writeln(' _$name = patch.$name.current;'); | |
540 } | |
541 writeln(' return self;'); | |
542 writeln('}'); | |
543 writeln('@end'); | |
544 writeln(); | |
545 writeln('@implementation $patchName {'); | |
546 writeln(' NodePatchType _type;'); | |
547 writeln('}'); | |
548 writeln('- (id)initIdentityPatch:($nodeName*)previous {'); | |
549 writeln(' self = [super init];'); | |
550 writeln(' _type = kIdentityNodePatch;'); | |
551 writeln(' _previous = previous;'); | |
552 writeln(' _current = previous;'); | |
553 writeln(' return self;'); | |
554 writeln('}'); | |
555 writeln('- (id)initWith:(const $patchDataName&)data'); | |
556 writeln(' previous:($nodeName*)previous'); | |
557 writeln(' inGraph:(ImmiRoot*)root {'); | |
558 writeln(' self = [super init];'); | |
559 writeln(' _previous = previous;'); | |
560 if (node.layout.slots.isNotEmpty || node.methods.isNotEmpty) { | |
561 // The updates list is ordered consistently with the struct fields. | |
562 writeln(' if (data.isUpdates()) {'); | |
563 writeln(' List<$updateDataName> updates = data.getUpdates();'); | |
564 writeln(' int length = updates.length();'); | |
565 writeln(' int next = 0;'); | |
566 forEachSlotAndMethod(node, null, (field, String name) { | |
567 String camelName = camelize(name); | |
568 String fieldPatchType = | |
569 field is Method ? actionPatchType(field) : patchType(field.type); | |
570 writeln(' if (next < length && updates[next].is${camelName}()) {'); | |
571 writeln(' _$name = [[$fieldPatchType alloc]'); | |
572 write(' '); | |
573 String dataGetter = 'updates[next++].get${camelName}'; | |
574 if (field is Formal && !field.type.isList && field.type.isString) { | |
575 writeln('initWith:decodeString(${dataGetter}Data())'); | |
576 } else { | |
577 writeln('initWith:$dataGetter()'); | |
578 } | |
579 writeln(' previous:previous.$name'); | |
580 writeln(' inGraph:root];'); | |
581 writeln(' } else {'); | |
582 writeln(' _$name = [[$fieldPatchType alloc]'); | |
583 writeln(' initIdentityPatch:previous.$name];'); | |
584 writeln(' }'); | |
585 }); | |
586 writeln(' assert(next == length);'); | |
587 writeln(' _type = kUpdateNodePatch;'); | |
588 writeln(' _current = [[$nodeName alloc] initWithPatch:self];'); | |
589 writeln(' return self;'); | |
590 writeln(' }'); | |
591 } | |
592 writeln(' assert(data.isReplace());'); | |
593 writeln(' _type = kReplaceNodePatch;'); | |
594 writeln(' _current = [[$nodeName alloc] initWith:data.getReplace()'); | |
595 writeln(' inGraph:root];'); | |
596 // For replace patches we leave fields and methods as default initialized. | |
597 writeln(' return self;'); | |
598 writeln('}'); | |
599 | |
600 writeln('- (bool)changed { return _type != kIdentityNodePatch; }'); | |
601 writeln('- (bool)replaced { return _type == kReplaceNodePatch; }'); | |
602 writeln('- (bool)updated { return _type == kUpdateNodePatch; }'); | |
603 write(applyToMethodSignature(node)); | |
604 writeln(' {'); | |
605 writeln(' if (!self.changed) return;'); | |
606 writeln(' if (self.replaced) {'); | |
607 writeln(' [presenter present${node.name}:self.current];'); | |
608 writeln(' } else {'); | |
609 writeln(' [presenter patch${node.name}:self];'); | |
610 writeln(' }'); | |
611 writeln('}'); | |
612 writeln('@end'); | |
613 writeln(); | |
614 } | |
615 | |
616 void _writeNodeBaseExtendedInterface() { | |
617 writeln('@interface Node ()'); | |
618 writeln('@property (readonly) id <Node> node;'); | |
619 writeln('- (id)init:(id <Node>)node;'); | |
620 writeln('- (id)initWith:(const NodeData&)data'); | |
621 writeln(' inGraph:(ImmiRoot*)root;'); | |
622 writeln('+ (id <Node>)createNode:(const NodeData&)data'); | |
623 writeln(' inGraph:(ImmiRoot*)root;'); | |
624 writeln('@end'); | |
625 writeln(); | |
626 } | |
627 | |
628 void _writeNodeBaseImplementation() { | |
629 writeln('@implementation Node'); | |
630 writeln('- (bool)is:(Class)klass {'); | |
631 writeln(' return [self.node isMemberOfClass:klass];'); | |
632 writeln('}'); | |
633 writeln('- (id)as:(Class)klass {'); | |
634 writeln(' assert([self is:klass]);'); | |
635 writeln(' return self.node;'); | |
636 writeln('}'); | |
637 writeln('- (id)init:(id <Node>)node {'); | |
638 writeln(' self = [super init];'); | |
639 writeln(' _node = node;'); | |
640 writeln(' return self;'); | |
641 writeln('}'); | |
642 writeln('- (id)initWith:(const NodeData&)data'); | |
643 writeln(' inGraph:(ImmiRoot*)root {'); | |
644 writeln(' return [self init:[Node createNode:data inGraph:root]];'); | |
645 writeln('}'); | |
646 writeln('+ (id <Node>)createNode:(const NodeData&)data'); | |
647 writeln(' inGraph:(ImmiRoot*)root {'); | |
648 nodes.forEach((node) { | |
649 writeln(' if (data.is${node.name}()) {'); | |
650 writeln(' return [[${node.name}Node alloc]'); | |
651 writeln(' initWith:data.get${node.name}()'); | |
652 writeln(' inGraph:root];'); | |
653 writeln(' }'); | |
654 }); | |
655 writeln(' abort();'); | |
656 writeln('}'); | |
657 writeln('@end'); | |
658 writeln(); | |
659 } | |
660 | |
661 void _writePatchBaseExtendedInterface() { | |
662 writeln('typedef enum { kIdentityNodePatch, kReplaceNodePatch, kUpdateNodePa
tch } NodePatchType;'); | |
663 writeln('@interface NodePatch ()'); | |
664 writeln('@property (readonly) id <NodePatch> patch;'); | |
665 writeln('@property (readonly) Node* node;'); | |
666 writeln('- (id)initIdentityPatch:(Node*)previous;'); | |
667 writeln('- (id)initWith:(const NodePatchData&)data'); | |
668 writeln(' previous:(Node*)previous'); | |
669 writeln(' inGraph:(ImmiRoot*)root;'); | |
670 writeln('+ (id <NodePatch>)createPatch:(const NodePatchData&)data'); | |
671 writeln(' previous:(id <Node>)previous'); | |
672 writeln(' inGraph:(ImmiRoot*)root;'); | |
673 writeln('@end'); | |
674 writeln(); | |
675 } | |
676 | |
677 void _writePatchBaseImplementation() { | |
678 writeln('@implementation NodePatch {'); | |
679 writeln(' NodePatchType _type;'); | |
680 writeln('}'); | |
681 writeln('+ (id <NodePatch>)createPatch:(const NodePatchData&)data'); | |
682 writeln(' previous:(id <Node>)previous'); | |
683 writeln(' inGraph:(ImmiRoot*)root {'); | |
684 nodes.forEach((node) { | |
685 String name = node.name; | |
686 String nodeName = '${name}Node'; | |
687 String patchName = '${name}Patch'; | |
688 writeln(' if (data.is$name()) {'); | |
689 writeln(' $nodeName* previousNode ='); | |
690 writeln(' [previous isMemberOfClass:$nodeName.class] ?'); | |
691 writeln(' previous :'); | |
692 writeln(' nil;'); | |
693 writeln(' return [[$patchName alloc]'); | |
694 writeln(' initWith:data.get$name()'); | |
695 writeln(' previous:previousNode'); | |
696 writeln(' inGraph:root];'); | |
697 writeln(' }'); | |
698 }); | |
699 writeln(' abort();'); | |
700 writeln('}'); | |
701 writeln('- (id)initIdentityPatch:(Node*)previous {'); | |
702 writeln(' self = [super init];'); | |
703 writeln(' _type = kIdentityNodePatch;'); | |
704 writeln(' _previous = previous;'); | |
705 writeln(' _current = previous;'); | |
706 writeln(' return self;'); | |
707 writeln('}'); | |
708 writeln('- (id)initWith:(const NodePatchData&)data'); | |
709 writeln(' previous:(Node*)previous'); | |
710 writeln(' inGraph:(ImmiRoot*)root {'); | |
711 writeln(' self = [super init];'); | |
712 writeln(' _previous = previous;'); | |
713 writeln(' _patch = [NodePatch'); | |
714 writeln(' createPatch:data'); | |
715 writeln(' previous:previous.node'); | |
716 writeln(' inGraph:root];'); | |
717 writeln(' _type = _patch.replaced ? kReplaceNodePatch : kUpdateNodePatch;')
; | |
718 writeln(' _current = [[Node alloc] init:_patch.current];'); | |
719 writeln(' return self;'); | |
720 writeln('}'); | |
721 writeln(); | |
722 writeln('- (bool)changed { return _type != kIdentityNodePatch; }'); | |
723 writeln('- (bool)replaced { return _type == kReplaceNodePatch; }'); | |
724 writeln('- (bool)updated { return _type == kUpdateNodePatch; }'); | |
725 writeln(); | |
726 write(applyToMethodSignature('Node')); | |
727 writeln(' {'); | |
728 writeln(' if (!self.changed) return;'); | |
729 writeln(' if (self.replaced) {'); | |
730 writeln(' [presenter presentNode:self.current];'); | |
731 writeln(' } else {'); | |
732 writeln(' [presenter patchNode:self];'); | |
733 writeln(' }'); | |
734 writeln('}'); | |
735 writeln('- (bool)is:(Class)klass {'); | |
736 writeln(' return [self.patch isMemberOfClass:klass];'); | |
737 writeln('}'); | |
738 writeln('- (id)as:(Class)klass {'); | |
739 writeln(' assert([self is:klass]);'); | |
740 writeln(' return self.patch;'); | |
741 writeln('}'); | |
742 writeln('@end'); | |
743 writeln(); | |
744 } | |
745 | |
746 void _writePatchPrimitivesExtendedInterface() { | |
747 _TYPES.forEach((String idlType, String objcType) { | |
748 if (idlType == 'void') return; | |
749 String patchTypeName = '${camelize(idlType)}Patch'; | |
750 String patchDataName = objcType; | |
751 writeln('@interface $patchTypeName ()'); | |
752 writeln('- (id)initIdentityPatch:($objcType)previous;'); | |
753 writeln('- (id)initWith:($patchDataName)data'); | |
754 writeln(' previous:($objcType)previous'); | |
755 writeln(' inGraph:(ImmiRoot*)root;'); | |
756 writeln('@end'); | |
757 writeln(); | |
758 }); | |
759 | |
760 writeln('typedef enum { kAnyNode, kSpecificNode } ListPatchType;'); | |
761 writeln(); | |
762 writeln('@interface ListRegionPatch ()'); | |
763 writeln('@property (readonly) int countDelta;'); | |
764 writeln('- (int)applyTo:(NSMutableArray*)outArray'); | |
765 writeln(' with:(NSArray*)inArray;'); | |
766 writeln('+ (ListRegionPatch*)regionPatch:(const ListRegionData&)data'); | |
767 writeln(' type:(ListPatchType)type'); | |
768 writeln(' previous:(NSArray*)previous'); | |
769 writeln(' inGraph:(ImmiRoot*)root;'); | |
770 writeln('@end'); | |
771 writeln('@interface ListRegionRemovePatch ()'); | |
772 writeln('- (id)initWith:(const ListRegionData&)data'); | |
773 writeln(' type:(ListPatchType)type'); | |
774 writeln(' inGraph:(ImmiRoot*)root;'); | |
775 writeln('@end'); | |
776 writeln('@interface ListRegionInsertPatch ()'); | |
777 writeln('- (id)initWith:(const ListRegionData&)data'); | |
778 writeln(' type:(ListPatchType)type'); | |
779 writeln(' inGraph:(ImmiRoot*)root;'); | |
780 writeln('@end'); | |
781 writeln('@interface ListRegionUpdatePatch ()'); | |
782 writeln('- (id)initWith:(const ListRegionData&)data'); | |
783 writeln(' type:(ListPatchType)type'); | |
784 writeln(' previous:(NSArray*)previous'); | |
785 writeln(' inGraph:(ImmiRoot*)root;'); | |
786 writeln('@end'); | |
787 | |
788 writeln('@interface ListPatch ()'); | |
789 writeln('- (id)initIdentityPatch:(NSArray*)previous;'); | |
790 writeln('- (id)initWith:(const ListPatchData&)data'); | |
791 writeln(' previous:(NSArray*)previous'); | |
792 writeln(' inGraph:(ImmiRoot*)root;'); | |
793 writeln('@end'); | |
794 writeln(); | |
795 } | |
796 | |
797 void _writePatchPrimitivesImplementation() { | |
798 _TYPES.forEach((String idlType, String objcType) { | |
799 if (idlType == 'void') return; | |
800 String patchTypeName = '${camelize(idlType)}Patch'; | |
801 String patchDataName = objcType; | |
802 writeln('@implementation $patchTypeName'); | |
803 writeln('- (id)initIdentityPatch:($objcType)previous {'); | |
804 writeln(' self = [super init];'); | |
805 writeln(' _previous = previous;'); | |
806 writeln(' _current = previous;'); | |
807 writeln(' return self;'); | |
808 writeln('}'); | |
809 writeln('- (id)initWith:($patchDataName)data'); | |
810 writeln(' previous:($objcType)previous'); | |
811 writeln(' inGraph:(ImmiRoot*)root {'); | |
812 writeln(' self = [super init];'); | |
813 writeln(' _previous = previous;'); | |
814 writeln(' _current = data;'); | |
815 writeln(' return self;'); | |
816 writeln('}'); | |
817 writeln('- (bool)changed {'); | |
818 writeln(' return _previous != _current;'); | |
819 writeln('}'); | |
820 writeln('@end'); | |
821 writeln(); | |
822 }); | |
823 | |
824 writeln('@implementation ListRegionPatch'); | |
825 writeln('+ (ListRegionPatch*)regionPatch:(const ListRegionData&)data'); | |
826 writeln(' type:(ListPatchType)type'); | |
827 writeln(' previous:(NSArray*)previous'); | |
828 writeln(' inGraph:(ImmiRoot*)root {'); | |
829 writeln(' if (data.isRemove()) {'); | |
830 writeln(' return [[ListRegionRemovePatch alloc] initWith:data'); | |
831 writeln(' type:type'); | |
832 writeln(' inGraph:root];'); | |
833 writeln(' }'); | |
834 writeln(' if (data.isInsert()) {'); | |
835 writeln(' return [[ListRegionInsertPatch alloc] initWith:data'); | |
836 writeln(' type:type'); | |
837 writeln(' inGraph:root];'); | |
838 writeln(' }'); | |
839 writeln(' NSAssert(data.isUpdate(), @"Invalid list patch for region");'); | |
840 writeln(' return [[ListRegionUpdatePatch alloc] initWith:data'); | |
841 writeln(' type:type'); | |
842 writeln(' previous:previous'); | |
843 writeln(' inGraph:root];'); | |
844 writeln('}'); | |
845 writeln('- (id)init:(int)index {'); | |
846 writeln(' self = [super init];'); | |
847 writeln(' _index = index;'); | |
848 writeln(' return self;'); | |
849 writeln('}'); | |
850 writeln('- (bool)isRemove { return false; }'); | |
851 writeln('- (bool)isInsert { return false; }'); | |
852 writeln('- (bool)isUpdate { return false; }'); | |
853 writeln('- (int)applyTo:(NSMutableArray*)outArray'); | |
854 writeln(' with:(NSArray*)inArray {'); | |
855 writeln(' @throw [NSException'); | |
856 writeln(' exceptionWithName:NSInternalInconsistencyException'); | |
857 writeln(' reason:@"-applyTo:with: base is abstract"'); | |
858 writeln(' userInfo:nil];'); | |
859 writeln('}'); | |
860 writeln('@end'); | |
861 writeln(); | |
862 writeln('@implementation ListRegionRemovePatch'); | |
863 writeln('- (id)initWith:(const ListRegionData&)data'); | |
864 writeln(' type:(ListPatchType)type'); | |
865 writeln(' inGraph:(ImmiRoot*)root {'); | |
866 writeln(' self = [super init:data.getIndex()];'); | |
867 writeln(' _count = data.getRemove();'); | |
868 writeln(' return self;'); | |
869 writeln('}'); | |
870 writeln('- (bool)isRemove { return true; }'); | |
871 writeln('- (int)countDelta { return -self.count; }'); | |
872 writeln('- (int)applyTo:(NSMutableArray*)outArray'); | |
873 writeln(' with:(NSArray*)inArray {'); | |
874 writeln(' return self.count;'); | |
875 writeln('}'); | |
876 writeln('@end'); | |
877 writeln(); | |
878 writeln('@implementation ListRegionInsertPatch'); | |
879 writeln('- (id)initWith:(const ListRegionData&)data'); | |
880 writeln(' type:(ListPatchType)type'); | |
881 writeln(' inGraph:(ImmiRoot*)root {'); | |
882 writeln(' self = [super init:data.getIndex()];'); | |
883 writeln(' const List<NodeData>& insertData = data.getInsert();'); | |
884 writeln(' NSMutableArray* nodes ='); | |
885 writeln(' [NSMutableArray arrayWithCapacity:insertData.length()];'); | |
886 writeln(' int length = insertData.length();'); | |
887 writeln(' if (type == kAnyNode) {'); | |
888 writeln(' for (int i = 0; i < length; ++i) {'); | |
889 writeln(' nodes[i] = [[Node alloc] initWith:insertData[i]'); | |
890 writeln(' inGraph:root];'); | |
891 writeln(' }'); | |
892 writeln(' } else {'); | |
893 writeln(' assert(type == kSpecificNode);'); | |
894 writeln(' for (int i = 0; i < length; ++i) {'); | |
895 writeln(' nodes[i] = [Node createNode:insertData[i] inGraph:root];'); | |
896 writeln(' }'); | |
897 writeln(' }'); | |
898 writeln(' _nodes = nodes;'); | |
899 writeln(' return self;'); | |
900 writeln('}'); | |
901 writeln('- (bool)isInsert { return true; }'); | |
902 writeln('- (int)countDelta { return self.nodes.count; }'); | |
903 writeln('- (int)applyTo:(NSMutableArray*)outArray'); | |
904 writeln(' with:(NSArray*)inArray {'); | |
905 writeln(' [outArray addObjectsFromArray:self.nodes];'); | |
906 writeln(' return 0;'); | |
907 writeln('}'); | |
908 writeln('@end'); | |
909 writeln(); | |
910 writeln('@implementation ListRegionUpdatePatch'); | |
911 writeln('- (id)initWith:(const ListRegionData&)data'); | |
912 writeln(' type:(ListPatchType)type'); | |
913 writeln(' previous:(NSArray*)previous'); | |
914 writeln(' inGraph:(ImmiRoot*)root {'); | |
915 writeln(' self = [super init:data.getIndex()];'); | |
916 writeln(' const List<NodePatchData>& updateData = data.getUpdate();'); | |
917 writeln(' int length = updateData.length();'); | |
918 writeln(' NSMutableArray* updates ='); | |
919 writeln(' [NSMutableArray arrayWithCapacity:length];'); | |
920 writeln(' if (type == kAnyNode) {'); | |
921 writeln(' for (int i = 0; i < length; ++i) {'); | |
922 writeln(' updates[i] = [[NodePatch alloc]'); | |
923 writeln(' initWith:updateData[i]'); | |
924 writeln(' previous:previous[self.index + i]'); | |
925 writeln(' inGraph:root];'); | |
926 writeln(' }'); | |
927 writeln(' } else {'); | |
928 writeln(' assert(type == kSpecificNode);'); | |
929 writeln(' for (int i = 0; i < length; ++i) {'); | |
930 writeln(' updates[i] = [NodePatch'); | |
931 writeln(' createPatch:updateData[i]'); | |
932 writeln(' previous:previous[self.index + i]'); | |
933 writeln(' inGraph:root];'); | |
934 writeln(' }'); | |
935 writeln(' }'); | |
936 writeln(' _updates = updates;'); | |
937 writeln(' return self;'); | |
938 writeln('}'); | |
939 writeln('- (bool)isUpdate { return true; }'); | |
940 writeln('- (int)countDelta { return 0; }'); | |
941 writeln('- (int)applyTo:(NSMutableArray*)outArray'); | |
942 writeln(' with:(NSArray*)inArray {'); | |
943 writeln(' int length = self.updates.count;'); | |
944 writeln(' for (int i = 0; i < length; ++i) {'); | |
945 writeln(' id <NodePatch> patch = self.updates[i];'); | |
946 writeln(' [outArray addObject:patch.current];'); | |
947 writeln(' }'); | |
948 writeln(' return length;'); | |
949 writeln('}'); | |
950 writeln('@end'); | |
951 writeln(); | |
952 writeln('@implementation ListPatch {'); | |
953 writeln(' NSMutableArray* _regions;'); | |
954 writeln('}'); | |
955 writeln('- (id)initIdentityPatch:(NSArray*)previous {'); | |
956 writeln(' self = [super init];'); | |
957 writeln(' _changed = false;'); | |
958 writeln(' _previous = previous;'); | |
959 writeln(' _current = previous;'); | |
960 writeln(' return self;'); | |
961 writeln('}'); | |
962 writeln('- (id)initWith:(const ListPatchData&)data'); | |
963 writeln(' previous:(NSArray*)previous'); | |
964 writeln(' inGraph:(ImmiRoot*)root {'); | |
965 writeln(' self = [super init];'); | |
966 writeln(' _changed = true;'); | |
967 writeln(' _previous = previous;'); | |
968 writeln(' ListPatchType type = (ListPatchType)data.getType();'); | |
969 writeln(' const List<ListRegionData>& regions = data.getRegions();'); | |
970 writeln(' NSMutableArray* patches ='); | |
971 writeln(' [NSMutableArray arrayWithCapacity:regions.length()];'); | |
972 writeln(' for (int i = 0; i < regions.length(); ++i) {'); | |
973 writeln(' patches[i] ='); | |
974 writeln(' [ListRegionPatch regionPatch:regions[i]'); | |
975 writeln(' type:type'); | |
976 writeln(' previous:previous'); | |
977 writeln(' inGraph:root];'); | |
978 writeln(' }'); | |
979 writeln(' _regions = patches;'); | |
980 writeln(' _current = [self applyWith:previous];'); | |
981 writeln(' return self;'); | |
982 writeln('}'); | |
983 writeln('- (NSArray*)applyWith:(NSArray*)array {'); | |
984 writeln(' int newCount = array.count;'); | |
985 writeln(' for (int i = 0; i < self.regions.count; ++i) {'); | |
986 writeln(' ListRegionPatch* patch = self.regions[i];'); | |
987 writeln(' newCount += patch.countDelta;'); | |
988 writeln(' }'); | |
989 writeln(' int sourceIndex = 0;'); | |
990 writeln(' NSMutableArray* newArray ='); | |
991 writeln(' [NSMutableArray arrayWithCapacity:newCount];'); | |
992 writeln(' for (int i = 0; i < self.regions.count; ++i) {'); | |
993 writeln(' ListRegionPatch* patch = self.regions[i];'); | |
994 writeln(' while (sourceIndex < patch.index) {'); | |
995 writeln(' [newArray addObject:array[sourceIndex++]];'); | |
996 writeln(' }'); | |
997 writeln(' sourceIndex += [patch applyTo:newArray with:array];'); | |
998 writeln(' }'); | |
999 writeln(' while (sourceIndex < array.count) {'); | |
1000 writeln(' [newArray addObject:array[sourceIndex++]];'); | |
1001 writeln(' }'); | |
1002 writeln(' return newArray;'); | |
1003 writeln('}'); | |
1004 writeln('@end'); | |
1005 writeln(); | |
1006 } | |
1007 | |
1008 void _writeActionsExtendedInterface() { | |
1009 for (List<Type> formals in methodSignatures.values) { | |
1010 String actionName = 'Action${actionTypeSuffix(formals)}'; | |
1011 String actionBlock = '${actionName}Block'; | |
1012 String actionPatch = '${actionName}Patch'; | |
1013 writeln('@interface $actionPatch ()'); | |
1014 writeln('- (id)initIdentityPatch:($actionBlock)previous;'); | |
1015 writeln('- (id)initWith:(uint16_t)actionId'); | |
1016 writeln(' previous:($actionBlock)previous'); | |
1017 writeln(' inGraph:(ImmiRoot*)root;'); | |
1018 writeln('@end'); | |
1019 writeln(); | |
1020 } | |
1021 } | |
1022 | |
1023 void _writeActionsImplementation() { | |
1024 for (List<Type> formals in methodSignatures.values) { | |
1025 String suffix = actionTypeSuffix(formals); | |
1026 String actionName = 'Action$suffix'; | |
1027 String actionBlock = '${actionName}Block'; | |
1028 String actionPatch = '${actionName}Patch'; | |
1029 | |
1030 writeln('@implementation $actionPatch {'); | |
1031 writeln(' NodePatchType _type;'); | |
1032 writeln('}'); | |
1033 writeln('- (id)initIdentityPatch:($actionBlock)previous {'); | |
1034 writeln(' self = [super init];'); | |
1035 writeln(' _type = kIdentityNodePatch;'); | |
1036 writeln(' _current = previous;'); | |
1037 writeln(' return self;'); | |
1038 writeln('}'); | |
1039 writeln('- (id)initWith:(uint16_t)actionId'); | |
1040 writeln(' previous:($actionBlock)previous'); | |
1041 writeln(' inGraph:(ImmiRoot*)root {'); | |
1042 writeln(' self = [super init];'); | |
1043 writeln(' _type = kReplaceNodePatch;'); | |
1044 write(' _current = '); | |
1045 _writeActionBlockImplementation('actionId', formals); | |
1046 writeln(';'); | |
1047 writeln(' return self;'); | |
1048 writeln('}'); | |
1049 writeln('- (bool)changed { return _type != kIdentityNodePatch; }'); | |
1050 writeln('@end'); | |
1051 writeln(); | |
1052 } | |
1053 } | |
1054 | |
1055 void _writeActionBlockImplementation(String actionId, List<Type> formals) { | |
1056 String suffix = actionTypeSuffix(formals); | |
1057 bool boxedArguments = formals.any((t) => t.isString); | |
1058 String actionFormals = ''; | |
1059 if (formals.isNotEmpty) { | |
1060 var typedFormals = | |
1061 mapWithIndex(formals, (i, f) => '${getTypeName(f)} arg$i').join(', '); | |
1062 actionFormals = '(${typedFormals})'; | |
1063 } | |
1064 writeln('^$actionFormals{'); | |
1065 writeln(' [root dispatch:^{'); | |
1066 if (boxedArguments) { | |
1067 writeln(' int size = 48 + Action${suffix}ArgsBuilder::kSize;'); | |
1068 int i = 0; | |
1069 for (Type formal in formals) { | |
1070 if (formal.isString) { | |
1071 writeln(' size += arg$i.length;'); | |
1072 } | |
1073 i++; | |
1074 } | |
1075 writeln(' MessageBuilder message(size);'); | |
1076 writeln(' Action${suffix}ArgsBuilder args ='); | |
1077 writeln(' message.initRoot<Action${suffix}ArgsBuilder>();'); | |
1078 writeln(' args.setId($actionId);'); | |
1079 i = 0; | |
1080 for (Type formal in formals) { | |
1081 if (formal.isString) { | |
1082 String charBuffer = 'args.initArg${i}Data(arg$i.length).data()'; | |
1083 writeln(' [arg$i'); | |
1084 writeln(' getCharacters:$charBuffer'); | |
1085 writeln(' range:NSMakeRange(0, arg$i.length)];'); | |
1086 } else { | |
1087 writeln(' args.setArg$i(arg$i);'); | |
1088 } | |
1089 i++; | |
1090 } | |
1091 } | |
1092 writeln(' ${serviceName}::dispatch${suffix}Async('); | |
1093 if (boxedArguments) { | |
1094 writeln(' args,'); | |
1095 } else { | |
1096 writeln(' $actionId,'); | |
1097 for (int i = 0; i < formals.length; ++i) { | |
1098 writeln(' arg$i,'); | |
1099 } | |
1100 } | |
1101 writeln(' noopVoidEventCallback,'); | |
1102 writeln(' NULL);'); | |
1103 writeln(' }];'); | |
1104 write(' }'); | |
1105 } | |
1106 | |
1107 void _writeListUtils() { | |
1108 nodes.forEach((Struct node) { | |
1109 String name = node.name; | |
1110 String nodeName = "${node.name}Node"; | |
1111 String patchName = "${node.name}Patch"; | |
1112 String nodeDataName = "${nodeName}Data"; | |
1113 String patchDataName = "${patchName}Data"; | |
1114 writeln('id create$nodeName(const $nodeDataName& data, ImmiRoot* root) {')
; | |
1115 writeln(' return [[$nodeName alloc] initWith:data inGraph:root];'); | |
1116 writeln('}'); | |
1117 writeln(); | |
1118 }); | |
1119 // TODO(zerny): Support lists of primitive types. | |
1120 writeln(""" | |
1121 Node* createNode(const NodeData& data, ImmiRoot* root) { | |
1122 return [[Node alloc] initWith:data inGraph:root]; | |
1123 } | |
1124 | |
1125 template<typename T> | |
1126 class ListUtils { | |
1127 public: | |
1128 typedef id (*DecodeElementFunction)(const T&, ImmiRoot*); | |
1129 | |
1130 static NSMutableArray* decodeList(const List<T>& list, | |
1131 DecodeElementFunction decodeElement, | |
1132 ImmiRoot* root) { | |
1133 NSMutableArray* array = [NSMutableArray arrayWithCapacity:list.length()]; | |
1134 for (int i = 0; i < list.length(); ++i) { | |
1135 [array addObject:decodeElement(list[i], root)]; | |
1136 } | |
1137 return array; | |
1138 } | |
1139 }; | |
1140 """); | |
1141 } | |
1142 | |
1143 void _writeStringUtils() { | |
1144 write(""" | |
1145 NSString* decodeString(const List<unichar>& chars) { | |
1146 List<unichar>& tmp = const_cast<List<unichar>&>(chars); | |
1147 return [[NSString alloc] initWithCharacters:tmp.data() | |
1148 length:tmp.length()]; | |
1149 } | |
1150 | |
1151 void encodeString(NSString* string, List<unichar> chars) { | |
1152 assert(string.length == chars.length()); | |
1153 [string getCharacters:chars.data() | |
1154 range:NSMakeRange(0, string.length)]; | |
1155 } | |
1156 | |
1157 """); | |
1158 } | |
1159 | |
1160 void _writeEventUtils() { | |
1161 writeln('typedef uint16_t EventID;'); | |
1162 writeln('void noopVoidEventCallback(void*) {}'); | |
1163 writeln(); | |
1164 } | |
1165 | |
1166 void _writeHeader() { | |
1167 String fileName = basenameWithoutExtension(path); | |
1168 writeln(COPYRIGHT); | |
1169 writeln('// Generated file. Do not edit.'); | |
1170 writeln(); | |
1171 writeln('#import "$immiImplFile"'); | |
1172 writeln(); | |
1173 } | |
1174 | |
1175 void _writeFormalWithKeyword(String keyword, Formal formal) { | |
1176 write('$keyword:(${getTypeName(formal.type)})${formal.name}'); | |
1177 } | |
1178 | |
1179 String patchType(Type type) { | |
1180 if (type.isList) return 'ListPatch'; | |
1181 return '${camelize(type.identifier)}Patch'; | |
1182 } | |
1183 | |
1184 void _writeNSType(Type type) { | |
1185 if (type.isList) { | |
1186 write('NSArray*'); | |
1187 } else { | |
1188 write(getTypePointer(type)); | |
1189 } | |
1190 } | |
1191 | |
1192 String actionFormalTypes(List<Type> types) { | |
1193 return types.map((Type type) => getTypeName(type)).join(', '); | |
1194 } | |
1195 | |
1196 String actionTypedArguments(List<Formal> types) { | |
1197 return types.map((Formal formal) { | |
1198 return '${getTypeName(formal.type)} ${formal.name}'; | |
1199 }).join(', '); | |
1200 } | |
1201 | |
1202 String actionArguments(List<Formal> types) { | |
1203 return types.map((Formal formal) { | |
1204 return '${formal.name}'; | |
1205 }).join(', '); | |
1206 } | |
1207 | |
1208 String actionPatchType(Method method) { | |
1209 List<Type> types = method.arguments.map((formal) => formal.type); | |
1210 return 'Action${actionTypeSuffix(types)}Patch'; | |
1211 } | |
1212 | |
1213 String actionBlockType(Method method) { | |
1214 List<Type> types = method.arguments.map((formal) => formal.type); | |
1215 return 'Action${actionTypeSuffix(types)}Block'; | |
1216 } | |
1217 } | |
OLD | NEW |