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:strings/strings.dart' as strings; | |
11 import 'package:path/path.dart' show basenameWithoutExtension, join, dirname; | |
12 | |
13 import 'shared.dart'; | |
14 | |
15 import '../emitter.dart'; | |
16 import '../primitives.dart' as primitives; | |
17 import '../struct_layout.dart'; | |
18 | |
19 const List<String> RESOURCES = const [ | |
20 'Action.java', | |
21 'ActionPatch.java', | |
22 'AnyNodePresenter.java', | |
23 'ImmiRoot.java', | |
24 'ImmiService.java', | |
25 'ListPatch.java', | |
26 'Node.java', | |
27 'NodePatch.java', | |
28 'NodePresenter.java', | |
29 'Patch.java', | |
30 ]; | |
31 | |
32 const Map<String, String> _TYPES = const <String, String>{ | |
33 'void' : 'void', | |
34 'bool' : 'boolean', | |
35 | |
36 'uint8' : 'int', | |
37 'uint16' : 'int', | |
38 | |
39 'int8' : 'int', | |
40 'int16' : 'int', | |
41 'int32' : 'int', | |
42 'int64' : 'long', | |
43 | |
44 'float32' : 'float', | |
45 'float64' : 'double', | |
46 | |
47 'String' : 'String', | |
48 }; | |
49 | |
50 void generate(String path, Map units, String outputDirectory) { | |
51 String directory = join(outputDirectory, 'java', 'immi'); | |
52 _generateNodeFiles(path, units, directory); | |
53 | |
54 String resourcesDirectory = join(dirname(Platform.script.path), | |
55 '..', 'lib', 'src', 'resources', 'java', 'immi'); | |
56 for (String resource in RESOURCES) { | |
57 String resourcePath = join(resourcesDirectory, resource); | |
58 File file = new File(resourcePath); | |
59 String contents = file.readAsStringSync(); | |
60 writeToFile(directory, resource, contents); | |
61 } | |
62 } | |
63 | |
64 void _generateNodeFiles(String path, Map units, String outputDirectory) { | |
65 var actions = new _ActionsCollector(); | |
66 new _AnyNodeWriter(units).writeTo(outputDirectory); | |
67 new _AnyNodePatchWriter(units).writeTo(outputDirectory); | |
68 for (Unit unit in units.values) { | |
69 actions.collectMethodSignatures(unit); | |
70 for (Struct node in unit.structs) { | |
71 new _JavaNodeWriter(node).writeTo(outputDirectory); | |
72 new _JavaNodePatchWriter(node).writeTo(outputDirectory); | |
73 new _JavaNodePresenterWriter(node).writeTo(outputDirectory); | |
74 } | |
75 } | |
76 _TYPES.forEach((idlType, javaType) { | |
77 if (idlType == 'void') return; | |
78 new _JavaPrimitivePatchWriter(idlType, javaType).writeTo(outputDirectory); | |
79 }); | |
80 for (var types in actions.methodSignatures.values) { | |
81 _JavaActionWriter.fromTypes(types.toList()).writeTo(outputDirectory); | |
82 } | |
83 } | |
84 | |
85 class _ActionsCollector extends CodeGenerationVisitor { | |
86 _ActionsCollector() : super(null); | |
87 } | |
88 | |
89 class _JavaVisitor extends CodeGenerationVisitor { | |
90 static const COPYRIGHT = """ | |
91 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
92 // for details. All rights reserved. Use of this source code is governed by a | |
93 // BSD-style license that can be found in the LICENSE.md file. | |
94 """; | |
95 | |
96 _JavaVisitor(String path) : super(path); | |
97 } | |
98 | |
99 class _JavaWriter extends _JavaVisitor { | |
100 | |
101 final String className; | |
102 | |
103 bool shouldWriteHeader = false; | |
104 Set<String> imports = new Set<String>(); | |
105 | |
106 _JavaWriter(String className) | |
107 : super(className), | |
108 className = className; | |
109 | |
110 void writeHeader([List<String> imports]) { | |
111 shouldWriteHeader = true; | |
112 if (imports != null) imports.forEach((i) { this.imports.add(i); }); | |
113 } | |
114 | |
115 void writeTo(String directory) { | |
116 var content = buffer.toString(); | |
117 buffer.clear(); | |
118 if (shouldWriteHeader) { | |
119 writeln(_JavaVisitor.COPYRIGHT); | |
120 writeln('// Generated file. Do not edit.'); | |
121 writeln(); | |
122 writeln('package immi;'); | |
123 writeln(); | |
124 if (imports.isNotEmpty) { | |
125 var sorted = imports.toList()..sort(); | |
126 var inJava = <String>[]; | |
127 var inOther = <String>[]; | |
128 for (var import in sorted) { | |
129 if (import.startsWith('java')) { | |
130 inJava.add(import); | |
131 } else { | |
132 inOther.add(import); | |
133 } | |
134 } | |
135 inJava.forEach(writeImport); | |
136 if (inJava.isNotEmpty) writeln(); | |
137 inOther.forEach(writeImport); | |
138 if (inOther.isNotEmpty) writeln(); | |
139 } | |
140 } | |
141 buffer.write(content); | |
142 writeToFile(directory, className, buffer.toString(), extension: 'java'); | |
143 } | |
144 | |
145 writeImport(import) { | |
146 writeln('import $import;'); | |
147 } | |
148 | |
149 String instanceOrNull(String name, String type) { | |
150 return '($name instanceof $type) ? ($type)$name : null'; | |
151 } | |
152 | |
153 String getTypeName(Type type) { | |
154 if (type.isList) { | |
155 imports.add('java.util.List'); | |
156 return 'List<${getNonListTypeName(type)}>'; | |
157 } | |
158 return getNonListTypeName(type); | |
159 } | |
160 | |
161 String getNonListTypeName(Type type) { | |
162 // TODO(zerny): Make the type structure regular! | |
163 if (type.isNode || type.isList && type.elementType.isNode) return 'AnyNode'; | |
164 if (type.resolved != null) { | |
165 return "${type.identifier}Node"; | |
166 } | |
167 return _TYPES[type.identifier]; | |
168 } | |
169 | |
170 String getNodeDataTypeName(Type type) { | |
171 if (type.isList && type.elementType.isNode) return 'NodeDataList'; | |
172 if (type.isList) return '${getNonListTypeName(type)}DataList'; | |
173 if (type.isNode) return 'NodeData'; | |
174 assert(type.resolved != null); | |
175 return '${type.identifier}NodeData'; | |
176 } | |
177 | |
178 String getPatchTypeName(Type type) { | |
179 if (type.isList) return 'ListPatch<${getNonListTypeName(type)}>'; | |
180 if (type.isNode) return 'AnyNodePatch'; | |
181 if (type.resolved != null) { | |
182 return "${type.identifier}Patch"; | |
183 } | |
184 return "${camelize(type.identifier)}Patch"; | |
185 } | |
186 } | |
187 | |
188 class _JavaPrimitivePatchWriter extends _JavaWriter { | |
189 _JavaPrimitivePatchWriter(String idlType, javaType) | |
190 : super('${strings.camelize(strings.underscore(idlType))}Patch') { | |
191 writeHeader(); | |
192 writeln('public class $className implements Patch {'); | |
193 writeln(); | |
194 writeln(' // Public interface.'); | |
195 writeln(); | |
196 writeln(' @Override'); | |
197 writeln(' public boolean hasChanged() { return previous != current; }'); | |
198 writeln(); | |
199 writeln(' public $javaType getCurrent() { return current; }'); | |
200 writeln(' public $javaType getPrevious() { return previous; }'); | |
201 writeln(); | |
202 writeln(' // Package private implementation.'); | |
203 writeln(); | |
204 writeln(' $className($javaType previous) {'); | |
205 writeln(' this.previous = previous;'); | |
206 writeln(' current = previous;'); | |
207 writeln(' }'); | |
208 writeln(); | |
209 writeln(' $className($javaType data, $javaType previous, ImmiRoot root) {')
; | |
210 writeln(' this.previous = previous;'); | |
211 writeln(' current = data;'); | |
212 writeln(' }'); | |
213 writeln(); | |
214 writeln(' private $javaType previous;'); | |
215 writeln(' private $javaType current;'); | |
216 writeln('}'); | |
217 } | |
218 } | |
219 | |
220 class _JavaActionWriter extends _JavaWriter { | |
221 Method method; | |
222 List<Type> types; | |
223 List<String> arguments; | |
224 | |
225 _JavaActionWriter(this.method, this.types, this.arguments) | |
226 : super(null); | |
227 | |
228 static _JavaActionWriter fromTypes(List<Type> types) { | |
229 int i = 0; | |
230 return new _JavaActionWriter( | |
231 null, | |
232 types, | |
233 types.map((_) => 'arg${i++}').toList()); | |
234 } | |
235 | |
236 static _JavaActionWriter fromMethod(Method method) { | |
237 return new _JavaActionWriter( | |
238 method, | |
239 method.arguments.map((f) => f.type).toList(), | |
240 method.arguments.map((f) => f.name).toList()); | |
241 } | |
242 | |
243 writeTo(String outputDirectory) { | |
244 bool boxedArguments = types.any((t) => t.isString); | |
245 writeHeader([ | |
246 'dartino.ImmiServiceLayer', | |
247 ]); | |
248 writeln('public final class $className implements Action {'); | |
249 writeln(); | |
250 writeln(' // Public interface.'); | |
251 writeln(); | |
252 writeln(' public void dispatch($actionFormals) {'); | |
253 writeln(' root.dispatch(new Runnable() {'); | |
254 writeln(' @Override'); | |
255 writeln(' public void run() {'); | |
256 if (boxedArguments) { | |
257 String builder = actionArgsBuilder; | |
258 imports.add('dartino.MessageBuilder'); | |
259 imports.add('dartino.$builder'); | |
260 writeln(' int space = 48 + $builder.kSize;'); | |
261 for (int i = 0; i < types.length; ++i) { | |
262 if (types[i].isString) { | |
263 writeln(' space += ${arguments[i]}.length();'); | |
264 } | |
265 } | |
266 writeln(' MessageBuilder message = new MessageBuilder(space);'); | |
267 writeln(' $builder args = new $builder();'); | |
268 writeln(' message.initRoot(args, $builder.kSize);'); | |
269 writeln(' args.setId(id);'); | |
270 arguments.forEach((a) { | |
271 writeln(' args.set${camelize(a)}($a);'); | |
272 }); | |
273 } | |
274 write(' ImmiServiceLayer.dispatch${actionSuffix}Async('); | |
275 if (boxedArguments) { | |
276 write('args, '); | |
277 } else { | |
278 write('id, '); | |
279 arguments.forEach((a) { write('$a, '); }); | |
280 } | |
281 writeln('null);'); | |
282 writeln(' }'); | |
283 writeln(' });'); | |
284 writeln(' }'); | |
285 writeln(); | |
286 writeln(' // Package private implementation.'); | |
287 writeln(); | |
288 writeln(' $className(int id, ImmiRoot root) {'); | |
289 writeln(' this.id = id;'); | |
290 writeln(' this.root = root;'); | |
291 writeln(' }'); | |
292 writeln(); | |
293 writeln(' private int id;'); | |
294 writeln(' private ImmiRoot root;'); | |
295 writeln('}'); | |
296 super.writeTo(outputDirectory); | |
297 } | |
298 | |
299 // Override className since we can't provide it in the constructor. | |
300 String get className => actionName; | |
301 | |
302 String get actionFormals { | |
303 int i = 0; | |
304 return types.map((t) => 'final ${getTypeName(t)} arg${i++}').join(', '); | |
305 } | |
306 | |
307 String get actionName { | |
308 return 'Action${actionSuffix}'; | |
309 } | |
310 | |
311 String get actionPatchName { | |
312 return 'ActionPatch<$actionName>'; | |
313 } | |
314 | |
315 String get actionSuffix { | |
316 if (types.isEmpty) return 'Void'; | |
317 return types.map((t) => camelize(t.identifier)).join(); | |
318 } | |
319 | |
320 String get actionArgsBuilder { | |
321 return '${actionName}ArgsBuilder'; | |
322 } | |
323 } | |
324 | |
325 class _JavaNodeBaseWriter extends _JavaWriter { | |
326 final String name; | |
327 final String nodeName; | |
328 final String patchName; | |
329 final String presenterName; | |
330 | |
331 _JavaNodeBaseWriter(String name, String fileName) | |
332 : super(fileName), | |
333 name = name, | |
334 nodeName = '${name}Node', | |
335 patchName = '${name}Patch', | |
336 presenterName = '${name}Presenter'; | |
337 } | |
338 | |
339 class _JavaNodePresenterWriter extends _JavaNodeBaseWriter { | |
340 | |
341 _JavaNodePresenterWriter(Struct node) | |
342 : super(node.name, '${node.name}Presenter') { | |
343 visitStruct(node); | |
344 } | |
345 | |
346 visitStruct(Struct node) { | |
347 writeHeader(); | |
348 write('public interface $presenterName'); | |
349 writeln(' extends NodePresenter<$nodeName, $patchName> {}'); | |
350 } | |
351 } | |
352 | |
353 class _JavaNodeWriter extends _JavaNodeBaseWriter { | |
354 Struct node; | |
355 Iterable<_JavaNodeWriter> actions; | |
356 | |
357 _JavaNodeWriter(Struct node) | |
358 : super(node.name, '${node.name}Node') { | |
359 visitStruct(node); | |
360 } | |
361 | |
362 visitStruct(Struct node) { | |
363 this.node = node; | |
364 actions = node.methods.map(_JavaActionWriter.fromMethod); | |
365 writeHeader([ | |
366 'dartino.${nodeName}Data', | |
367 ]); | |
368 writeln('public final class $nodeName implements Node {'); | |
369 writeln(); | |
370 writeln(' // Public interface.'); | |
371 writeln(); | |
372 forEachSlot(node, null, writeFieldGetter); | |
373 actions.forEach(writeActionGetter); | |
374 writeln(); | |
375 writeln(' // Package private implementation.'); | |
376 writeln(); | |
377 writeConstructorFromData(); | |
378 writeln(); | |
379 writeConstructorFromPatch(); | |
380 writeln(); | |
381 forEachSlot(node, null, writeFieldBacking); | |
382 actions.forEach(writeActionBacking); | |
383 writeln('}'); | |
384 } | |
385 | |
386 writeConstructorFromData() { | |
387 writeln(' $nodeName(${nodeName}Data data, ImmiRoot root) {'); | |
388 forEachSlot(node, null, writeFieldInitializationFromData); | |
389 actions.forEach(writeActionInitializationFromData); | |
390 writeln(' }'); | |
391 } | |
392 | |
393 writeConstructorFromPatch() { | |
394 writeln(' $nodeName($patchName patch) {'); | |
395 forEachSlot(node, null, writeFieldInitializationFromPatch); | |
396 actions.forEach(writeActionInitializationFromPatch); | |
397 writeln(' }'); | |
398 } | |
399 | |
400 writeFieldGetter(Type type, String name) { | |
401 String typeName = getTypeName(type); | |
402 writeln(' public $typeName get${camelize(name)}() { return $name; }'); | |
403 } | |
404 | |
405 writeFieldBacking(Type type, String name) { | |
406 String typeName = getTypeName(type); | |
407 writeln(' private $typeName $name;'); | |
408 } | |
409 | |
410 writeFieldInitializationFromData(Type type, String name) { | |
411 String camelName = camelize(name); | |
412 String typeName = getTypeName(type); | |
413 if (type.isList) { | |
414 String typeName = getNonListTypeName(type); | |
415 String dataName = getNodeDataTypeName(type); | |
416 imports.add('dartino.$dataName'); | |
417 imports.add('java.util.Collections'); | |
418 imports.add('java.util.ArrayList'); | |
419 writeln(' {'); | |
420 writeln(' $dataName dataList = data.get$camelName();'); | |
421 writeln(' int length = dataList.size();'); | |
422 writeln(' List<$typeName> list = new ArrayList<$typeName>(length);'); | |
423 writeln(' for (int i = 0; i < length; ++i) {'); | |
424 writeln(' list.add(new $typeName(dataList.get(i), root));'); | |
425 writeln(' }'); | |
426 writeln(' $name = Collections.unmodifiableList(list);'); | |
427 writeln(' }'); | |
428 } else if (type.isNode || type.resolved != null) { | |
429 writeln(' $name = new $typeName(data.get$camelName(), root);'); | |
430 } else { | |
431 writeln(' $name = data.get$camelName();'); | |
432 } | |
433 } | |
434 | |
435 writeFieldInitializationFromPatch(Type type, String name) { | |
436 String camelName = camelize(name); | |
437 writeln(' $name = patch.get$camelName().getCurrent();'); | |
438 } | |
439 | |
440 writeActionBacking(_JavaActionWriter action) { | |
441 String name = action.method.name; | |
442 writeln(' private ${action.actionName} $name;'); | |
443 } | |
444 | |
445 writeActionGetter(_JavaActionWriter action) { | |
446 String name = action.method.name; | |
447 String camelName = camelize(name); | |
448 writeln(' public ${action.actionName} get$camelName() { return $name; }'); | |
449 } | |
450 | |
451 writeActionInitializationFromData(_JavaActionWriter action) { | |
452 String name = action.method.name; | |
453 Strin camelName = camelize(name); | |
454 writeln(' $name = new ${action.actionName}(data.get$camelName(), root);')
; | |
455 } | |
456 | |
457 writeActionInitializationFromPatch(_JavaActionWriter action) { | |
458 String name = action.method.name; | |
459 Strin camelName = camelize(name); | |
460 writeln(' $name = patch.get$camelName().getCurrent();'); | |
461 } | |
462 } | |
463 | |
464 class _JavaNodePatchWriter extends _JavaNodeBaseWriter { | |
465 Struct node; | |
466 Iterable<_JavaNodeWriter> actions; | |
467 | |
468 _JavaNodePatchWriter(Struct node) | |
469 : super(node.name, '${node.name}Patch') { | |
470 visitStruct(node); | |
471 } | |
472 | |
473 visitStruct(Struct node) { | |
474 this.node = node; | |
475 actions = node.methods.map(_JavaActionWriter.fromMethod); | |
476 writeHeader([ | |
477 'dartino.${patchName}Data', | |
478 'dartino.${node.name}UpdateData', | |
479 'dartino.${node.name}UpdateDataList', | |
480 'java.util.List', | |
481 ]); | |
482 write('public final class $patchName'); | |
483 writeln(' implements NodePatch<$nodeName, $presenterName> {'); | |
484 writeln(); | |
485 writeln(' // Public interface.'); | |
486 writeln(); | |
487 writeln(' @Override'); | |
488 write(' public boolean hasChanged()'); | |
489 writeln(' { return type != PatchType.IdentityNodePatch; }'); | |
490 writeln(' @Override'); | |
491 write(' public boolean wasReplaced()'); | |
492 writeln(' { return type == PatchType.ReplaceNodePatch; }'); | |
493 writeln(' @Override'); | |
494 write(' public boolean wasUpdated()'); | |
495 writeln(' { return type == PatchType.UpdateNodePatch; }'); | |
496 writeln(); | |
497 writeln(' @Override'); | |
498 writeln(' public $nodeName getCurrent() { return current; }'); | |
499 writeln(' @Override'); | |
500 writeln(' public $nodeName getPrevious() { return previous; }'); | |
501 writeln(); | |
502 forEachSlot(node, null, writeFieldGetter); | |
503 actions.forEach(writeActionGetter); | |
504 writeln(); | |
505 writeln(' @Override'); | |
506 writeln(' public void applyTo($presenterName presenter) {'); | |
507 writeln(' if (!hasChanged()) return;'); | |
508 writeln(' if (wasReplaced()) {'); | |
509 writeln(' presenter.present(current);'); | |
510 writeln(' } else {'); | |
511 writeln(' assert wasUpdated();'); | |
512 writeln(' presenter.patch(this);'); | |
513 writeln(' }'); | |
514 writeln(' }'); | |
515 writeln(); | |
516 writeln(' // Package private implementation.'); | |
517 writeln(); | |
518 writeConstructorIdentity(); | |
519 writeln(); | |
520 writeConstructorFromData(); | |
521 writeln(); | |
522 writeln(' private PatchType type;'); | |
523 writeln(' private $nodeName current;'); | |
524 writeln(' private $nodeName previous;'); | |
525 forEachSlot(node, null, writeFieldBacking); | |
526 actions.forEach(writeActionBacking); | |
527 writeln('}'); | |
528 } | |
529 | |
530 writeConstructorIdentity() { | |
531 writeln(' $patchName($nodeName previous) {'); | |
532 writeln(' this.previous = previous;'); | |
533 writeln(' current = previous;'); | |
534 writeln(' type = PatchType.IdentityNodePatch;'); | |
535 writeln(' }'); | |
536 } | |
537 | |
538 writeConstructorFromData() { | |
539 writeln(' $patchName(${patchName}Data data, $nodeName previous, ImmiRoot ro
ot) {'); | |
540 writeln(' this.previous = previous;'); | |
541 if (node.layout.slots.isNotEmpty || node.methods.isNotEmpty) { | |
542 // The updates list is ordered consistently with the struct members. | |
543 writeln(' if (data.isUpdates()) {'); | |
544 writeln(' ${name}UpdateDataList updates = data.getUpdates();'); | |
545 writeln(' int length = updates.size();'); | |
546 writeln(' int next = 0;'); | |
547 forEachSlot(node, null, writeFieldInitializationFromData); | |
548 actions.forEach(writeActionInitializationFromData); | |
549 // TODO(zerny): Implement actions. | |
550 writeln(' assert next == length;'); | |
551 writeln(' type = PatchType.UpdateNodePatch;'); | |
552 writeln(' current = new $nodeName(this);'); | |
553 writeln(' return;'); | |
554 writeln(' }'); | |
555 } | |
556 writeln(' assert data.isReplace();'); | |
557 writeln(' type = PatchType.ReplaceNodePatch;'); | |
558 writeln(' current = new $nodeName(data.getReplace(), root);'); | |
559 writeln(' }'); | |
560 } | |
561 | |
562 writeFieldGetter(Type type, String name) { | |
563 String camelName = camelize(name); | |
564 String patchTypeName = getPatchTypeName(type); | |
565 writeln(' public $patchTypeName get$camelName() { return $name; }'); | |
566 } | |
567 | |
568 writeFieldBacking(Type type, String name) { | |
569 String patchTypeName = getPatchTypeName(type); | |
570 writeln(' private $patchTypeName $name;'); | |
571 } | |
572 | |
573 writeFieldInitializationFromData(Type type, String name) { | |
574 String camelName = camelize(name); | |
575 String patchTypeName = getPatchTypeName(type); | |
576 String dataGetter = 'updates.get(next++).get$camelName'; | |
577 writeln(' if (next < length && updates.get(next).is$camelName()) {'); | |
578 writeln(' $name = new $patchTypeName('); | |
579 writeln(' $dataGetter(), previous.get$camelName(), root);'); | |
580 writeln(' } else {'); | |
581 writeln(' $name = new $patchTypeName(previous.get$camelName());'); | |
582 writeln(' }'); | |
583 } | |
584 | |
585 writeActionGetter(_JavaActionWriter action) { | |
586 String name = action.method.name; | |
587 String camelName = camelize(name); | |
588 writeln(' public ${action.actionPatchName} get$camelName() {'); | |
589 writeln(' return $name;'); | |
590 writeln(' }'); | |
591 } | |
592 | |
593 writeActionBacking(_JavaActionWriter action) { | |
594 String name = action.method.name; | |
595 writeln(' private ${action.actionPatchName} $name;'); | |
596 } | |
597 | |
598 writeActionInitializationFromData(_JavaActionWriter action) { | |
599 String name = action.method.name; | |
600 String camelName = camelize(name); | |
601 String actionName = action.actionName; | |
602 String patchTypeName = action.actionPatchName; | |
603 String dataGetter = 'updates.get(next++).get$camelName'; | |
604 writeln(' if (next < length && updates.get(next).is$camelName()) {'); | |
605 writeln(' $name = new $patchTypeName('); | |
606 writeln(' new $actionName($dataGetter(), root),'); | |
607 writeln(' previous.get$camelName(),'); | |
608 writeln(' root);'); | |
609 writeln(' } else {'); | |
610 writeln(' $name = new $patchTypeName(previous.get$camelName());'); | |
611 writeln(' }'); | |
612 } | |
613 } | |
614 | |
615 class _AnyNodeWriter extends _JavaNodeBaseWriter { | |
616 final String nodeName = 'AnyNode'; | |
617 | |
618 _AnyNodeWriter(Map units) | |
619 : super('AnyNode', 'AnyNode') { | |
620 writeHeader([ | |
621 'dartino.NodeData', | |
622 ]); | |
623 writeln('public final class $nodeName implements Node {'); | |
624 writeln(); | |
625 writeln(' // Public interface.'); | |
626 writeln(); | |
627 writeln(' public boolean is(java.lang.Class clazz) {'); | |
628 writeln(' return clazz.isInstance(node);'); | |
629 writeln(' }'); | |
630 writeln(' public <T> T as(java.lang.Class<T> clazz) {'); | |
631 writeln(' return clazz.cast(node);'); | |
632 writeln(' }'); | |
633 writeln(); | |
634 writeln(' // Package private implementation.'); | |
635 writeln(); | |
636 writeln(' $nodeName(Node node) {'); | |
637 writeln(' this.node = node;'); | |
638 writeln(' }'); | |
639 writeln(); | |
640 writeln(' $nodeName(NodeData data, ImmiRoot root) {'); | |
641 writeln(' node = fromData(data, root);'); | |
642 writeln(' }'); | |
643 writeln(); | |
644 writeln(' static Node fromData(NodeData data, ImmiRoot root) {'); | |
645 units.values.forEach(visit); | |
646 writeln(' throw new RuntimeException("Invalid node-type tag");'); | |
647 writeln(' }'); | |
648 writeln(); | |
649 writeln(' Node getNode() { return node; }'); | |
650 writeln(); | |
651 writeln(' private Node node;'); | |
652 writeln('}'); | |
653 } | |
654 | |
655 visitUnit(Unit unit) { | |
656 unit.structs.forEach(visit); | |
657 } | |
658 | |
659 visitStruct(Struct node) { | |
660 write(' if (data.is${node.name}())'); | |
661 writeln(' return new ${node.name}Node(data.get${node.name}(), root);'); | |
662 } | |
663 } | |
664 | |
665 class _AnyNodePatchWriter extends _JavaNodeBaseWriter { | |
666 final String nodeName = 'AnyNode'; | |
667 | |
668 _AnyNodePatchWriter(Map units) | |
669 : super('AnyNode', 'AnyNodePatch') { | |
670 writeHeader([ | |
671 'dartino.NodePatchData', | |
672 ]); | |
673 write('public final class $patchName'); | |
674 writeln(' implements NodePatch<$nodeName, $presenterName> {'); | |
675 writeln(); | |
676 writeln(' // Public interface.'); | |
677 writeln(); | |
678 writeln(' @Override'); | |
679 write(' public boolean hasChanged()'); | |
680 writeln(' { return patch != null; }'); | |
681 writeln(' @Override'); | |
682 write(' public boolean wasReplaced()'); | |
683 writeln(' { return patch != null && patch.wasReplaced(); }'); | |
684 writeln(' @Override'); | |
685 write(' public boolean wasUpdated()'); | |
686 writeln(' { return patch != null && patch.wasUpdated(); }'); | |
687 writeln(); | |
688 writeln(' @Override'); | |
689 writeln(' public $nodeName getCurrent() { return current; }'); | |
690 writeln(' @Override'); | |
691 writeln(' public $nodeName getPrevious() { return previous; }'); | |
692 writeln(); | |
693 writeln(' @Override'); | |
694 writeln(' public void applyTo($presenterName presenter) {'); | |
695 writeln(' if (!hasChanged()) return;'); | |
696 writeln(' if (wasReplaced()) {'); | |
697 writeln(' presenter.present(current);'); | |
698 writeln(' } else {'); | |
699 writeln(' assert wasUpdated();'); | |
700 writeln(' presenter.patch(this);'); | |
701 writeln(' }'); | |
702 writeln(' }'); | |
703 writeln(); | |
704 writeln(' public boolean is(java.lang.Class clazz) {'); | |
705 writeln(' return clazz.isInstance(patch);'); | |
706 writeln(' }'); | |
707 writeln(' public <T> T as(java.lang.Class<T> clazz) {'); | |
708 writeln(' return clazz.cast(patch);'); | |
709 writeln(' }'); | |
710 writeln(); | |
711 writeln(' // Package private implementation.'); | |
712 writeln(); | |
713 writeln(' $patchName($nodeName previous) {'); | |
714 writeln(' this.previous = previous;'); | |
715 writeln(' current = previous;'); | |
716 writeln(' }'); | |
717 writeln(); | |
718 writeln(' $patchName('); | |
719 writeln(' NodePatchData data,'); | |
720 writeln(' $nodeName previous,'); | |
721 writeln(' ImmiRoot root) {'); | |
722 writeln(' Node node = previous == null ? null : previous.getNode();'); | |
723 writeln(' patch = fromData(data, node, root);'); | |
724 writeln(' current = new AnyNode(patch.getCurrent());'); | |
725 writeln(' this.previous = previous;'); | |
726 writeln(' }'); | |
727 writeln(); | |
728 writeln(' static NodePatch fromData('); | |
729 writeln(' NodePatchData data,'); | |
730 writeln(' Node previous,'); | |
731 writeln(' ImmiRoot root) {'); | |
732 // Create the patch based on the concrete type-tag. | |
733 units.values.forEach(visit); | |
734 writeln(' throw new RuntimeException("Unknown node-patch tag");'); | |
735 writeln(' }'); | |
736 writeln(); | |
737 writeln(' private NodePatch patch;'); | |
738 writeln(' private $nodeName current;'); | |
739 writeln(' private $nodeName previous;'); | |
740 writeln('}'); | |
741 } | |
742 | |
743 visitUnit(Unit unit) { | |
744 unit.structs.forEach(visit); | |
745 } | |
746 | |
747 visitStruct(Struct node) { | |
748 String name = node.name; | |
749 String patchName = '${name}Patch'; | |
750 String nodeName = '${name}Node'; | |
751 writeln(' if (data.is$name()) {'); | |
752 writeln(' $nodeName typedPrevious = null;'); | |
753 writeln(' if (previous instanceof $nodeName) {'); | |
754 writeln(' typedPrevious = ($nodeName)previous;'); | |
755 writeln(' }'); | |
756 writeln(' return new $patchName('); | |
757 writeln(' data.get$name(), typedPrevious, root);'); | |
758 writeln(' }'); | |
759 } | |
760 } | |
OLD | NEW |