| 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 |