Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(623)

Side by Side Diff: pkg/analysis_server/tool/spec/codegen_dart_protocol.dart

Issue 479043002: Code generate Dart classes representing the analysis server API. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart 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 codegen.protocol;
6
7 import 'dart:convert';
8
9 import 'api.dart';
10 import 'codegen_tools.dart';
11 import 'from_html.dart';
12 import 'implied_types.dart';
13 import 'to_html.dart';
14
15 import 'package:html5lib/dom.dart' as dom;
16
17 /**
18 * Container for code that can be used to translate a data type from JSON.
19 */
20 abstract class FromJsonCode {
21 /**
22 * True if the data type is already in JSON form, so the translation is the
23 * identity function.
24 */
25 bool get isIdentity;
26
27 /**
28 * Get the translation code in the form of a closure.
29 */
30 String get asClosure;
31
32 /**
33 * Get the translation code in the form of a code snippet, where [jsonPath]
34 * is the variable holding the JSON path, and [json] is the variable holding
35 * the raw JSON.
36 */
37 String asSnippet(String jsonPath, String json);
38 }
39
40 /**
41 * Representation of FromJsonCode for a function defined elsewhere.
42 */
43 class FromJsonFunction extends FromJsonCode {
44 final String asClosure;
45
46 FromJsonFunction(this.asClosure);
47
48 @override
49 bool get isIdentity => false;
50
51 @override
52 String asSnippet(String jsonPath, String json) =>
53 '$asClosure($jsonPath, $json)';
54 }
55
56 typedef String FromJsonSnippetCallback(String jsonPath, String json);
57
58 /**
59 * Representation of FromJsonCode for a snippet of inline code.
60 */
61 class FromJsonSnippet extends FromJsonCode {
62 /**
63 * Callback that can be used to generate the code snippet, once the names
64 * of the [jsonPath] and [json] variables are known.
65 */
66 final FromJsonSnippetCallback callback;
67
68 FromJsonSnippet(this.callback);
69
70 @override
71 bool get isIdentity => false;
72
73 @override
74 String get asClosure =>
75 '(String jsonPath, Object json) => ${callback('jsonPath', 'json')}';
76
77 @override
78 String asSnippet(String jsonPath, String json) => callback(jsonPath, json);
79 }
80
81 /**
82 * Representation of FromJsonCode for the identity transformation.
83 */
84 class FromJsonIdentity extends FromJsonSnippet {
85 FromJsonIdentity() : super((String jsonPath, String json) => json);
86
87 @override
88 bool get isIdentity => true;
89 }
90
91 /**
92 * Container for code that can be used to translate a data type to JSON.
93 */
94 abstract class ToJsonCode {
95 /**
96 * True if the data type is already in JSON form, so the translation is the
97 * identity function.
98 */
99 bool get isIdentity;
100
101 /**
102 * Get the translation code in the form of a closure.
103 */
104 String get asClosure;
105
106 /**
107 * Get the translation code in the form of a code snippet, where [value]
108 * is the variable holding the object to be translated.
109 */
110 String asSnippet(String value);
111 }
112
113 /**
114 * Representation of ToJsonCode for a function defined elsewhere.
115 */
116 class ToJsonFunction extends ToJsonCode {
117 final String asClosure;
118
119 ToJsonFunction(this.asClosure);
120
121 @override
122 bool get isIdentity => false;
123
124 @override
125 String asSnippet(String value) => '$asClosure($value)';
126 }
127
128 typedef String ToJsonSnippetCallback(String value);
129
130 /**
131 * Representation of ToJsonCode for a snippet of inline code.
132 */
133 class ToJsonSnippet extends ToJsonCode {
134 /**
135 * Callback that can be used to generate the code snippet, once the name
136 * of the [value] variable is known.
137 */
138 final ToJsonSnippetCallback callback;
139
140 /**
141 * Dart type of the [value] variable.
142 */
143 final String type;
144
145 ToJsonSnippet(this.type, this.callback);
146
147 @override
148 bool get isIdentity => false;
149
150 @override
151 String get asClosure => '($type value) => ${callback('value')}';
152
153 @override
154 String asSnippet(String value) => callback(value);
155 }
156
157 /**
158 * Representation of FromJsonCode for the identity transformation.
159 */
160 class ToJsonIdentity extends ToJsonSnippet {
161 ToJsonIdentity(String type) : super(type, (String value) => value);
162
163 @override
164 bool get isIdentity => true;
165 }
166
167 /**
168 * Visitor which produces Dart code representing the API.
169 */
170 class CodegenProtocolVisitor extends HierarchicalApiVisitor with CodeGenerator {
171 /**
172 * Type references in the spec that are named something else in Dart.
173 */
174 static const Map<String, String> _typeRenames = const {
175 'object': 'Object',
176 };
177
178 /**
179 * Visitor used to produce doc comments.
180 */
181 final ToHtmlVisitor toHtmlVisitor;
182
183 /**
184 * Types implied by the API. This includes types explicitly named in the
185 * API as well as those implied by the definitions of requests, responses,
186 * notifications, etc.
187 */
188 final Map<String, ImpliedType> impliedTypes;
189
190 CodegenProtocolVisitor(Api api)
191 : super(api),
192 toHtmlVisitor = new ToHtmlVisitor(api),
193 impliedTypes = computeImpliedTypes(api);
194
195 @override
196 visitApi() {
197 outputHeader();
198 writeln();
199 writeln('part of protocol2;');
200 emitClasses();
201 writeln();
202 emitJsonDecoder();
203 emitConvenienceFunctions();
204 }
205
206 /**
207 * Translate each type implied by the API to a class.
208 */
209 void emitClasses() {
210 for (ImpliedType impliedType in impliedTypes.values) {
211 TypeDecl type = impliedType.type;
212 if (type != null) {
213 String dartTypeName = capitalize(impliedType.camelName);
214 if (type is TypeObject) {
215 writeln();
216 emitObjectClass(dartTypeName, type, impliedType);
217 } else if (type is TypeEnum) {
218 writeln();
219 emitEnumClass(dartTypeName, type, impliedType);
220 }
221 }
222 }
223 }
224
225 /**
226 * Emit the class to encapsulate an object type.
227 */
228 void emitObjectClass(String className, TypeObject type, ImpliedType
229 impliedType) {
230 docComment(toHtmlVisitor.collectHtml(() {
231 toHtmlVisitor.p(() {
232 toHtmlVisitor.write(impliedType.humanReadableName);
233 });
234 if (impliedType.type != null) {
235 toHtmlVisitor.showType(null, impliedType.type);
236 }
237 }));
238 writeln('class $className {');
239 indent(() {
240 for (TypeObjectField field in type.fields) {
241 if (field.value != null) {
242 continue;
243 }
244 docComment(toHtmlVisitor.collectHtml(() {
245 toHtmlVisitor.translateHtml(field.html);
246 }));
247 writeln('final ${dartType(field.type)} ${field.name};');
248 writeln();
249 }
250 emitObjectConstructor(type, className);
251 writeln();
252 emitToJsonMember(type);
253 writeln();
254 writeln('String toString() => JSON.encode(toJson());');
255 writeln();
256 emitObjectEqualsMember(type, className);
257 writeln();
258 emitObjectHashCode(type);
259 });
260 writeln('}');
261 }
262
263 /**
264 * Emit the constructor for an object class.
265 */
266 void emitObjectConstructor(TypeObject type, String className) {
267 List<String> args = <String>[];
268 List<String> optionalArgs = <String>[];
269 for (TypeObjectField field in type.fields) {
270 if (field.value != null) {
271 continue;
272 }
273 String arg = 'this.${field.name}';
274 if (field.optional) {
275 optionalArgs.add(arg);
276 } else {
277 args.add(arg);
278 }
279 }
280 if (optionalArgs.isNotEmpty) {
281 args.add('{${optionalArgs.join(', ')}}');
282 }
283 writeln('$className(${args.join(', ')});');
284 }
285
286 /**
287 * Emit the toJson() code for an object class.
288 */
289 void emitToJsonMember(TypeObject type) {
290 writeln('Map<String, dynamic> toJson() {');
291 indent(() {
292 writeln('Map<String, dynamic> result = {};');
293 for (TypeObjectField field in type.fields) {
294 String fieldNameString = literalString(field.name);
295 if (field.value != null) {
296 writeln('result[$fieldNameString] = ${literalString(field.value)};');
297 continue;
298 }
299 String fieldToJson = toJsonCode(field.type).asSnippet(field.name);
300 String populateField = 'result[$fieldNameString] = $fieldToJson;';
301 if (field.optional) {
302 writeln('if (${field.name} != null) {');
303 indent(() {
304 writeln(populateField);
305 });
306 writeln('}');
307 } else {
308 writeln(populateField);
309 }
310 }
311 writeln('return result;');
312 });
313 writeln('}');
314 }
315
316 /**
317 * Emit the operator== code for an object class.
318 */
319 void emitObjectEqualsMember(TypeObject type, String className) {
320 writeln('bool operator==(other) {');
321 indent(() {
322 writeln('if (other is $className) {');
323 indent(() {
324 var comparisons = <String>[];
325 for (TypeObjectField field in type.fields) {
326 if (field.value != null) {
327 continue;
328 }
329 comparisons.add('${field.name} == other.${field.name}');
330 }
331 if (comparisons.isEmpty) {
332 writeln('return true;');
333 } else {
334 String concatenated = comparisons.join(' &&\n ');
335 writeln('return $concatenated;');
336 }
337 });
338 writeln('}');
339 writeln('return false;');
340 });
341 writeln('}');
342 }
343
344 /**
345 * Emit the hashCode getter for an object class.
346 */
347 void emitObjectHashCode(TypeObject type) {
348 writeln('int get hashCode {');
349 indent(() {
350 writeln('int hash = 0;');
351 for (TypeObjectField field in type.fields) {
352 String valueToCombine;
353 if (field.value != null) {
354 valueToCombine = field.value.hashCode.toString();
355 } else {
356 valueToCombine = '${field.name}.hashCode';
357 }
358 writeln('hash = _JenkinsSmiHash.combine(hash, $valueToCombine);');
359 }
360 writeln('return _JenkinsSmiHash.finish(hash);');
361 });
362 writeln('}');
363 }
364
365 /**
366 * Emit a class to encapsulate an enum.
367 */
368 void emitEnumClass(String className, TypeEnum type, ImpliedType impliedType) {
369 docComment(toHtmlVisitor.collectHtml(() {
370 toHtmlVisitor.p(() {
371 toHtmlVisitor.write(impliedType.humanReadableName);
372 });
373 if (impliedType.type != null) {
374 toHtmlVisitor.showType(null, impliedType.type);
375 }
376 }));
377 writeln('class $className {');
378 indent(() {
379 for (TypeEnumValue value in type.values) {
380 docComment(toHtmlVisitor.collectHtml(() {
381 toHtmlVisitor.translateHtml(value.html);
382 }));
383 String valueString = literalString(value.value);
384 writeln(
385 'static const ${value.value} = const $className._($valueString);');
386 writeln();
387 }
388 writeln('final String name;');
389 writeln();
390 writeln('const $className._(this.name);');
391 writeln();
392 emitEnumClassConstructor(className, type);
393 writeln();
394 writeln('String toString() => "$className.\$name";');
395 writeln();
396 writeln('String toJson() => name;');
397 });
398 writeln('}');
399 }
400
401 /**
402 * Emit the constructor for an enum class.
403 */
404 void emitEnumClassConstructor(String className, TypeEnum type) {
405 writeln('factory $className(String name) {');
406 indent(() {
407 writeln('switch (name) {');
408 indent(() {
409 for (TypeEnumValue value in type.values) {
410 String valueString = literalString(value.value);
411 writeln('case $valueString:');
412 indent(() {
413 writeln('return ${value.value};');
414 });
415 }
416 });
417 writeln('}');
418 writeln(r"throw new Exception('Illegal enum value: $name');");
419 });
420 writeln('}');
421 }
422
423 /**
424 * Compute the code necessary to convert [type] to JSON.
425 */
426 ToJsonCode toJsonCode(TypeDecl type) {
427 TypeDecl resolvedType = resolveTypeReferenceChain(type);
428 if (resolvedType is TypeReference) {
429 return new ToJsonIdentity(dartType(type));
430 } else if (resolvedType is TypeList) {
431 ToJsonCode itemCode = toJsonCode(resolvedType.itemType);
432 if (itemCode.isIdentity) {
433 return new ToJsonIdentity(dartType(type));
434 } else {
435 return new ToJsonSnippet(dartType(type), (String value) =>
436 '$value.map(${itemCode.asClosure})');
437 }
438 } else if (resolvedType is TypeMap) {
439 ToJsonCode keyCode;
440 if (dartType(resolvedType.keyType) != 'String') {
441 keyCode = toJsonCode(resolvedType.keyType);
442 } else {
443 keyCode = new ToJsonIdentity(dartType(resolvedType.keyType));
444 }
445 ToJsonCode valueCode = toJsonCode(resolvedType.valueType);
446 if (keyCode.isIdentity && valueCode.isIdentity) {
447 return new ToJsonIdentity(dartType(resolvedType));
448 } else {
449 return new ToJsonSnippet(dartType(type), (String value) {
450 StringBuffer result = new StringBuffer();
451 result.write('_mapMap($value');
452 if (!keyCode.isIdentity) {
453 result.write(', keyCallback: ${keyCode.asClosure}');
454 }
455 if (!valueCode.isIdentity) {
456 result.write(', valueCallback: ${valueCode.asClosure}');
457 }
458 result.write(')');
459 return result.toString();
460 });
461 }
462 } else if (resolvedType is TypeUnion) {
463 for (TypeDecl choice in resolvedType.choices) {
464 if (resolveTypeReferenceChain(choice) is! TypeObject) {
465 throw new Exception('Union types must be unions of objects');
466 }
467 }
468 return new ToJsonSnippet(dartType(type), (String value) =>
469 '$value.toJson()');
470 } else if (resolvedType is TypeObject || resolvedType is TypeEnum) {
471 return new ToJsonSnippet(dartType(type), (String value) =>
472 '$value.toJson()');
473 } else {
474 throw new Exception("Can't convert $resolvedType from JSON");
475 }
476 }
477
478 /**
479 * Emit the JsonDecoder class, which contains code necessary to translate
480 * objects from JSON format.
481 */
482 void emitJsonDecoder() {
483 writeln('abstract class JsonDecoder extends JsonDecoderBase {');
484 indent(() {
485 for (ImpliedType impliedType in impliedTypes.values) {
486 TypeDecl type = impliedType.type;
487 if (type != null) {
488 String dartTypeName = capitalize(impliedType.camelName);
489 if (type is TypeObject) {
490 writeln();
491 emitObjectFromJson(dartTypeName, type, impliedType);
492 } else if (type is TypeEnum) {
493 writeln();
494 emitEnumFromJson(dartTypeName, type, impliedType);
495 }
496 }
497 }
498 });
499 writeln('}');
500 }
501
502 /**
503 * Emit the method for decoding an object from JSON.
504 */
505 void emitObjectFromJson(String className, TypeObject type, ImpliedType
506 impliedType) {
507 String humanReadableNameString = literalString(impliedType.humanReadableName
508 );
509 String methodName = camelJoin(['decode', className]);
510 writeln('$className $methodName(String jsonPath, Object json) {');
511 indent(() {
512 writeln('if (json is Map) {');
513 indent(() {
514 List<String> args = <String>[];
515 List<String> optionalArgs = <String>[];
516 for (TypeObjectField field in type.fields) {
517 String fieldNameString = literalString(field.name);
518 String fieldAccessor = 'json[$fieldNameString]';
519 String jsonPath = 'jsonPath + ${literalString('.${field.name}')}';
520 if (field.value != null) {
521 String valueString = literalString(field.value);
522 writeln('if ($fieldAccessor != $valueString) {');
523 indent(() {
524 writeln('throw mismatch(jsonPath, "equal " + $valueString);');
525 });
526 writeln('}');
527 continue;
528 }
529 if (field.optional) {
530 optionalArgs.add('${field.name}: ${field.name}');
531 } else {
532 args.add(field.name);
533 }
534 String fieldDartType = dartType(field.type);
535 writeln('$fieldDartType ${field.name};');
536 writeln('if (json.containsKey($fieldNameString)) {');
537 indent(() {
538 String toJson = fromJsonCode(field.type).asSnippet(jsonPath,
539 fieldAccessor);
540 writeln('${field.name} = $toJson;');
541 });
542 write('}');
543 if (!field.optional) {
544 writeln(' else {');
545 indent(() {
546 writeln("throw missingKey(jsonPath, $fieldNameString);");
547 });
548 writeln('}');
549 } else {
550 writeln();
551 }
552 }
553 args.addAll(optionalArgs);
554 writeln('return new $className(${args.join(', ')});');
555 });
556 writeln('} else {');
557 indent(() {
558 writeln('throw mismatch(jsonPath, $humanReadableNameString);');
559 });
560 writeln('}');
561 });
562 writeln('}');
563 }
564
565 /**
566 * Emit the method for decoding an enum from JSON.
567 */
568 void emitEnumFromJson(String className, TypeEnum type, ImpliedType
569 impliedType) {
570 String methodName = camelJoin(['decode', className]);
571 writeln('$className $methodName(String jsonPath, Object json) {');
572 indent(() {
573 writeln('if (json is String) {');
574 indent(() {
575 writeln('try {');
576 indent(() {
577 writeln('return new $className(json);');
578 });
579 writeln('} catch(_) {');
580 indent(() {
581 writeln('// Fall through');
582 });
583 writeln('}');
584 });
585 writeln('}');
586 String humanReadableNameString = literalString(
587 impliedType.humanReadableName);
588 writeln('throw mismatch(jsonPath, $humanReadableNameString);');
589 });
590 writeln('}');
591 }
592
593 /**
594 * Compute the code necessary to translate [type] from JSON.
595 */
596 FromJsonCode fromJsonCode(TypeDecl type) {
597 if (type is TypeReference) {
598 TypeDefinition referencedDefinition = api.types[type.typeName];
599 if (referencedDefinition != null) {
600 TypeDecl referencedType = referencedDefinition.type;
601 if (referencedType is TypeObject || referencedType is TypeEnum) {
602 String methodName = camelJoin(['decode', type.typeName]);
603 return new FromJsonFunction(methodName);
604 } else {
605 return fromJsonCode(referencedType);
606 }
607 } else {
608 switch (type.typeName) {
609 case 'String':
610 return new FromJsonFunction('_decodeString');
611 case 'bool':
612 return new FromJsonFunction('_decodeBool');
613 case 'int':
614 return new FromJsonFunction('_decodeInt');
615 case 'object':
616 return new FromJsonIdentity();
617 default:
618 throw new Exception('Unexpected type name ${type.typeName}');
619 }
620 }
621 } else if (type is TypeMap) {
622 FromJsonCode keyCode;
623 if (dartType(type.keyType) != 'String') {
624 keyCode = fromJsonCode(type.keyType);
625 } else {
626 keyCode = new FromJsonIdentity();
627 }
628 FromJsonCode valueCode = fromJsonCode(type.valueType);
629 if (keyCode.isIdentity && valueCode.isIdentity) {
630 return new FromJsonFunction('_decodeMap');
631 } else {
632 return new FromJsonSnippet((String jsonPath, String json) {
633 StringBuffer result = new StringBuffer();
634 result.write('_decodeMap($jsonPath, $json');
635 if (!keyCode.isIdentity) {
636 result.write(', keyDecoder: ${keyCode.asClosure}');
637 }
638 if (!valueCode.isIdentity) {
639 result.write(', valueDecoder: ${valueCode.asClosure}');
640 }
641 result.write(')');
642 return result.toString();
643 });
644 }
645 } else if (type is TypeList) {
646 FromJsonCode itemCode = fromJsonCode(type.itemType);
647 if (itemCode.isIdentity) {
648 return new FromJsonFunction('_decodeList');
649 } else {
650 return new FromJsonSnippet((String jsonPath, String json) =>
651 '_decodeList($jsonPath, $json, ${itemCode.asClosure})');
652 }
653 } else if (type is TypeUnion) {
654 List<String> decoders = <String>[];
655 for (TypeDecl choice in type.choices) {
656 TypeDecl resolvedChoice = resolveTypeReferenceChain(choice);
657 if (resolvedChoice is TypeObject) {
658 TypeObjectField field = resolvedChoice.getField(type.field);
659 if (field == null) {
660 throw new Exception(
661 'Each choice in the union needs a field named ${type.field}');
662 }
663 if (field.value == null) {
664 throw new Exception(
665 'Each choice in the union needs a constant value for the field $ {type.field}');
666 }
667 String closure = fromJsonCode(choice).asClosure;
668 decoders.add('${literalString(field.value)}: $closure');
669 } else {
670 throw new Exception('Union types must be unions of objects.');
671 }
672 }
673 return new FromJsonSnippet((String jsonPath, String json) =>
674 '_decodeUnion($jsonPath, $json, ${literalString(type.field)}, {${decod ers.join(', ')}})'
675 );
676 } else {
677 throw new Exception("Can't convert $type from JSON");
678 }
679 }
680
681 /**
682 * Emit convenience functions for decoding parts of the protocol (requests,
683 * responses, and notifications).
684 */
685 void emitConvenienceFunctions() {
686 for (Domain domain in api.domains) {
687 String domainName = domain.name;
688 for (Request request in domain.requests) {
689 String requestNameCamel = camelJoin([domainName, request.method]);
690 String requestLongName = request.longMethod;
691 dom.Element html = request.html;
692 if (request.params != null) {
693 emitConvenienceDecoder(requestNameCamel, requestLongName, html,
694 'request', 'params', 'Request', 'request', 'new RequestDecoder(req uest)',
695 'a request');
696 }
697 if (request.result != null) {
698 emitConvenienceDecoder(requestNameCamel, requestLongName, html,
699 'response', 'result', 'Response', 'response', 'new ResponseDecoder ()',
700 'the response to a request');
701 }
702 }
703 for (Notification notification in domain.notifications) {
704 String notificationNameCamel = camelJoin([domainName,
705 notification.event]);
706 String notificationLongName = notification.longEvent;
707 dom.Element html = notification.html;
708 if (notification.params != null) {
709 emitConvenienceDecoder(notificationNameCamel, notificationLongName,
710 html, 'notification', 'params', 'Notification', 'notification',
711 'new ResponseDecoder()', 'a notification');
712 }
713 }
714 }
715 for (Refactoring refactoring in api.refactorings) {
716 String refactoringNameCamel = camelJoin(refactoring.kind.toLowerCase(
717 ).split('_'));
718 String refactoringLongName = refactoring.kind;
719 dom.Element html = refactoring.html;
720 if (refactoring.feedback != null) {
721 emitConvenienceDecoder(refactoringNameCamel, refactoringLongName, html,
722 'feedback', 'feedback', 'EditGetRefactoringResult', 'refactoringResu lt',
723 'new ResponseDecoder()', 'refactoring feedback');
724 }
725 if (refactoring.options != null) {
726 emitConvenienceDecoder(refactoringNameCamel, refactoringLongName, html,
727 'options', 'options', 'EditGetRefactoringParams', 'refactoringParams ',
728 'new RequestDecoder(request)', 'refactoring options', extraArgs:
729 ['Request request']);
730 }
731 }
732 }
733
734 /**
735 * Emit a convenience function for decoding a piece of protocol. [camelName]
736 * is the name of the protocol piece (e.g. "analysisGetErrors"). [longName]
737 * is the long name used in the spec (e.g. "analysis.getErrors"). [html] is
738 * the HTML documentation of the protocol piece. [fieldName] is the field
739 * within the protocol piece to be decoded (e.g. 'params'). [inputType] is
740 * the type of the object to be input to the convenience function.
741 * [inputName] is the name of the parameter to the convenience function.
742 * [makeDecoder] is a code snippet for creating the JsonDecoder-derived class
743 * to do the decoding. [description] is a short description of the kind of
744 * protocol being decoded (e.g. "a notification").
745 */
746 void emitConvenienceDecoder(String camelName, String longName, dom.Element
747 html, String methodSuffix, String fieldName, String inputType, String
748 inputName, String makeDecoder, String description, {List<String> extraArgs :
749 const []}) {
750 String impliedTypeName = camelJoin([camelName, fieldName], doCapitalize:
751 true);
752 String methodName = camelJoin(['decode', camelName, methodSuffix]);
753 String paramsDecoder = camelJoin(['decode', impliedTypeName]);
754 writeln();
755 docComment(toHtmlVisitor.collectHtml(() {
756 toHtmlVisitor.p(() {
757 toHtmlVisitor.writeln('Decode $description of type $longName');
758 });
759 toHtmlVisitor.translateHtml(html);
760 }));
761 List<String> args = ['$inputType $inputName'];
762 args.addAll(extraArgs);
763 writeln('$impliedTypeName $methodName(${args.join(', ')}) {');
764 indent(() {
765 writeln('var decoder = $makeDecoder;');
766 String fieldNameString = literalString(fieldName);
767 writeln(
768 "return decoder.$paramsDecoder($fieldNameString, $inputName.$fieldName );");
769 });
770 writeln('}');
771 }
772
773 /**
774 * Create a string literal that evaluates to [s].
775 */
776 String literalString(String s) {
777 return JSON.encode(s);
778 }
779
780 /**
781 * Convert the given [TypeDecl] to a Dart type.
782 */
783 String dartType(TypeDecl type) {
784 if (type is TypeReference) {
785 String typeName = type.typeName;
786 TypeDefinition referencedDefinition = api.types[typeName];
787 if (_typeRenames.containsKey(typeName)) {
788 return _typeRenames[typeName];
789 }
790 if (referencedDefinition == null) {
791 return typeName;
792 }
793 TypeDecl referencedType = referencedDefinition.type;
794 if (referencedType is TypeObject || referencedType is TypeEnum) {
795 return typeName;
796 }
797 return dartType(referencedType);
798 } else if (type is TypeList) {
799 return 'List<${dartType(type.itemType)}>';
800 } else if (type is TypeMap) {
801 return 'Map<${dartType(type.keyType)}, ${dartType(type.valueType)}>';
802 } else if (type is TypeUnion) {
803 return 'dynamic';
804 } else {
805 throw new Exception("Can't convert to a dart type");
806 }
807 }
808 }
809
810 final GeneratedFile target = new GeneratedFile(
811 '../../lib/src/generated_protocol.dart', () {
812 CodegenProtocolVisitor visitor = new CodegenProtocolVisitor(readApi());
813 return visitor.collectCode(visitor.visitApi);
814 });
815
816 /**
817 * Translate spec_input.html into protocol_matchers.dart.
818 */
819 main() {
820 target.generate();
821 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698