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

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

Issue 725143004: Format and sort analyzer and analysis_server packages. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library codegen.protocol; 5 library codegen.protocol;
6 6
7 import 'dart:convert'; 7 import 'dart:convert';
8 8
9 import 'package:html5lib/dom.dart' as dom;
10
9 import 'api.dart'; 11 import 'api.dart';
10 import 'codegen_dart.dart'; 12 import 'codegen_dart.dart';
11 import 'codegen_tools.dart'; 13 import 'codegen_tools.dart';
12 import 'from_html.dart'; 14 import 'from_html.dart';
13 import 'implied_types.dart'; 15 import 'implied_types.dart';
14 import 'to_html.dart'; 16 import 'to_html.dart';
15 17
16 import 'package:html5lib/dom.dart' as dom;
17
18 /**
19 * Container for code that can be used to translate a data type from JSON.
20 */
21 abstract class FromJsonCode {
22 /**
23 * True if the data type is already in JSON form, so the translation is the
24 * identity function.
25 */
26 bool get isIdentity;
27
28 /**
29 * Get the translation code in the form of a closure.
30 */
31 String get asClosure;
32
33 /**
34 * Get the translation code in the form of a code snippet, where [jsonPath]
35 * is the variable holding the JSON path, and [json] is the variable holding
36 * the raw JSON.
37 */
38 String asSnippet(String jsonPath, String json);
39 }
40
41 /**
42 * Representation of FromJsonCode for a function defined elsewhere.
43 */
44 class FromJsonFunction extends FromJsonCode {
45 final String asClosure;
46
47 FromJsonFunction(this.asClosure);
48
49 @override
50 bool get isIdentity => false;
51
52 @override
53 String asSnippet(String jsonPath, String json) =>
54 '$asClosure($jsonPath, $json)';
55 }
56
57 typedef String FromJsonSnippetCallback(String jsonPath, String json);
58
59 /**
60 * Representation of FromJsonCode for a snippet of inline code.
61 */
62 class FromJsonSnippet extends FromJsonCode {
63 /**
64 * Callback that can be used to generate the code snippet, once the names
65 * of the [jsonPath] and [json] variables are known.
66 */
67 final FromJsonSnippetCallback callback;
68
69 FromJsonSnippet(this.callback);
70
71 @override
72 bool get isIdentity => false;
73
74 @override
75 String get asClosure =>
76 '(String jsonPath, Object json) => ${callback('jsonPath', 'json')}';
77
78 @override
79 String asSnippet(String jsonPath, String json) => callback(jsonPath, json);
80 }
81
82 /**
83 * Representation of FromJsonCode for the identity transformation.
84 */
85 class FromJsonIdentity extends FromJsonSnippet {
86 FromJsonIdentity() : super((String jsonPath, String json) => json);
87
88 @override
89 bool get isIdentity => true;
90 }
91
92 /**
93 * Container for code that can be used to translate a data type to JSON.
94 */
95 abstract class ToJsonCode {
96 /**
97 * True if the data type is already in JSON form, so the translation is the
98 * identity function.
99 */
100 bool get isIdentity;
101
102 /**
103 * Get the translation code in the form of a closure.
104 */
105 String get asClosure;
106
107 /**
108 * Get the translation code in the form of a code snippet, where [value]
109 * is the variable holding the object to be translated.
110 */
111 String asSnippet(String value);
112 }
113
114 /**
115 * Representation of ToJsonCode for a function defined elsewhere.
116 */
117 class ToJsonFunction extends ToJsonCode {
118 final String asClosure;
119
120 ToJsonFunction(this.asClosure);
121
122 @override
123 bool get isIdentity => false;
124
125 @override
126 String asSnippet(String value) => '$asClosure($value)';
127 }
128
129 typedef String ToJsonSnippetCallback(String value);
130
131 /**
132 * Representation of ToJsonCode for a snippet of inline code.
133 */
134 class ToJsonSnippet extends ToJsonCode {
135 /**
136 * Callback that can be used to generate the code snippet, once the name
137 * of the [value] variable is known.
138 */
139 final ToJsonSnippetCallback callback;
140
141 /**
142 * Dart type of the [value] variable.
143 */
144 final String type;
145
146 ToJsonSnippet(this.type, this.callback);
147
148 @override
149 bool get isIdentity => false;
150
151 @override
152 String get asClosure => '($type value) => ${callback('value')}';
153
154 @override
155 String asSnippet(String value) => callback(value);
156 }
157
158 /**
159 * Representation of FromJsonCode for the identity transformation.
160 */
161 class ToJsonIdentity extends ToJsonSnippet {
162 ToJsonIdentity(String type) : super(type, (String value) => value);
163
164 @override
165 bool get isIdentity => true;
166 }
167
168 /** 18 /**
169 * Special flags that need to be inserted into the declaration of the Element 19 * Special flags that need to be inserted into the declaration of the Element
170 * class. 20 * class.
171 */ 21 */
172 const Map<String, String> specialElementFlags = const { 22 const Map<String, String> specialElementFlags = const {
173 'abstract': '0x01', 23 'abstract': '0x01',
174 'const': '0x02', 24 'const': '0x02',
175 'final': '0x04', 25 'final': '0x04',
176 'static': '0x08', 26 'static': '0x08',
177 'private': '0x10', 27 'private': '0x10',
178 'deprecated': '0x20' 28 'deprecated': '0x20'
179 }; 29 };
180 30
31 final GeneratedFile target =
32 new GeneratedFile('../../lib/src/generated_protocol.dart', () {
33 CodegenProtocolVisitor visitor = new CodegenProtocolVisitor(readApi());
34 return visitor.collectCode(visitor.visitApi);
35 });
36
37 /**
38 * Translate spec_input.html into protocol_matchers.dart.
39 */
40 main() {
41 target.generate();
42 }
43
181 /** 44 /**
182 * Callback type used to represent arbitrary code generation. 45 * Callback type used to represent arbitrary code generation.
183 */ 46 */
184 typedef void CodegenCallback(); 47 typedef void CodegenCallback();
185 48
49 typedef String FromJsonSnippetCallback(String jsonPath, String json);
50
51 typedef String ToJsonSnippetCallback(String value);
52
186 /** 53 /**
187 * Visitor which produces Dart code representing the API. 54 * Visitor which produces Dart code representing the API.
188 */ 55 */
189 class CodegenProtocolVisitor extends DartCodegenVisitor with CodeGenerator { 56 class CodegenProtocolVisitor extends DartCodegenVisitor with CodeGenerator {
190 /** 57 /**
191 * Class members for which the constructor argument should be optional, even 58 * Class members for which the constructor argument should be optional, even
192 * if the member is not an optional part of the protocol. For list types, 59 * if the member is not an optional part of the protocol. For list types,
193 * the constructor will default the member to the empty list. 60 * the constructor will default the member to the empty list.
194 */ 61 */
195 static const Map<String, List<String>> _optionalConstructorArguments = const { 62 static const Map<String, List<String>> _optionalConstructorArguments = const {
(...skipping 13 matching lines...) Expand all
209 * API as well as those implied by the definitions of requests, responses, 76 * API as well as those implied by the definitions of requests, responses,
210 * notifications, etc. 77 * notifications, etc.
211 */ 78 */
212 final Map<String, ImpliedType> impliedTypes; 79 final Map<String, ImpliedType> impliedTypes;
213 80
214 CodegenProtocolVisitor(Api api) 81 CodegenProtocolVisitor(Api api)
215 : super(api), 82 : super(api),
216 toHtmlVisitor = new ToHtmlVisitor(api), 83 toHtmlVisitor = new ToHtmlVisitor(api),
217 impliedTypes = computeImpliedTypes(api); 84 impliedTypes = computeImpliedTypes(api);
218 85
219 @override 86 /**
220 visitApi() { 87 * Compute the code necessary to compare two objects for equality.
221 outputHeader(); 88 */
222 writeln(); 89 String compareEqualsCode(TypeDecl type, String thisVar, String otherVar) {
223 writeln('part of protocol;'); 90 TypeDecl resolvedType = resolveTypeReferenceChain(type);
224 emitClasses(); 91 if (resolvedType is TypeReference ||
92 resolvedType is TypeEnum ||
93 resolvedType is TypeObject ||
94 resolvedType is TypeUnion) {
95 return '$thisVar == $otherVar';
96 } else if (resolvedType is TypeList) {
97 String itemTypeName = dartType(resolvedType.itemType);
98 String subComparison = compareEqualsCode(resolvedType.itemType, 'a', 'b');
99 String closure = '($itemTypeName a, $itemTypeName b) => $subComparison';
100 return '_listEqual($thisVar, $otherVar, $closure)';
101 } else if (resolvedType is TypeMap) {
102 String valueTypeName = dartType(resolvedType.valueType);
103 String subComparison =
104 compareEqualsCode(resolvedType.valueType, 'a', 'b');
105 String closure = '($valueTypeName a, $valueTypeName b) => $subComparison';
106 return '_mapEqual($thisVar, $otherVar, $closure)';
107 }
108 throw new Exception(
109 "Don't know how to compare for equality: $resolvedType");
225 } 110 }
226 111
227 /** 112 /**
228 * Translate each type implied by the API to a class. 113 * Translate each type implied by the API to a class.
229 */ 114 */
230 void emitClasses() { 115 void emitClasses() {
231 for (ImpliedType impliedType in impliedTypes.values) { 116 for (ImpliedType impliedType in impliedTypes.values) {
232 TypeDecl type = impliedType.type; 117 TypeDecl type = impliedType.type;
233 String dartTypeName = capitalize(impliedType.camelName); 118 String dartTypeName = capitalize(impliedType.camelName);
234 if (type == null) { 119 if (type == null) {
235 emitEmptyObjectClass(dartTypeName, impliedType); 120 emitEmptyObjectClass(dartTypeName, impliedType);
236 } else if (type is TypeObject || type == null) { 121 } else if (type is TypeObject || type == null) {
237 writeln(); 122 writeln();
238 emitObjectClass(dartTypeName, type, impliedType); 123 emitObjectClass(dartTypeName, type, impliedType);
239 } else if (type is TypeEnum) { 124 } else if (type is TypeEnum) {
240 writeln(); 125 writeln();
241 emitEnumClass(dartTypeName, type, impliedType); 126 emitEnumClass(dartTypeName, type, impliedType);
242 } 127 }
243 } 128 }
244 } 129 }
245 130
246 /** 131 /**
132 * Emit a convenience constructor for decoding a piece of protocol, if
133 * appropriate. Return true if a constructor was emitted.
134 */
135 bool emitConvenienceConstructor(String className, ImpliedType impliedType) {
136 // The type of object from which this piece of protocol should be decoded.
137 String inputType;
138 // The name of the input object.
139 String inputName;
140 // The field within the input object to decode.
141 String fieldName;
142 // Constructor call to create the JsonDecoder object.
143 String makeDecoder;
144 // Name of the constructor to create.
145 String constructorName;
146 // Extra arguments for the constructor.
147 List<String> extraArgs = <String>[];
148 switch (impliedType.kind) {
149 case 'requestParams':
150 inputType = 'Request';
151 inputName = 'request';
152 fieldName = '_params';
153 makeDecoder = 'new RequestDecoder(request)';
154 constructorName = 'fromRequest';
155 break;
156 case 'requestResult':
157 inputType = 'Response';
158 inputName = 'response';
159 fieldName = '_result';
160 makeDecoder =
161 'new ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id ))';
162 constructorName = 'fromResponse';
163 break;
164 case 'notificationParams':
165 inputType = 'Notification';
166 inputName = 'notification';
167 fieldName = '_params';
168 makeDecoder = 'new ResponseDecoder(null)';
169 constructorName = 'fromNotification';
170 break;
171 case 'refactoringOptions':
172 inputType = 'EditGetRefactoringParams';
173 inputName = 'refactoringParams';
174 fieldName = 'options';
175 makeDecoder = 'new RequestDecoder(request)';
176 constructorName = 'fromRefactoringParams';
177 extraArgs.add('Request request');
178 break;
179 default:
180 return false;
181 }
182 List<String> args = ['$inputType $inputName'];
183 args.addAll(extraArgs);
184 writeln('factory $className.$constructorName(${args.join(', ')}) {');
185 indent(() {
186 String fieldNameString =
187 literalString(fieldName.replaceFirst(new RegExp('^_'), ''));
188 if (className == 'EditGetRefactoringParams') {
189 writeln('var params = new $className.fromJson(');
190 writeln(' $makeDecoder, $fieldNameString, $inputName.$fieldName);');
191 writeln('REQUEST_ID_REFACTORING_KINDS[request.id] = params.kind;');
192 writeln('return params;');
193 } else {
194 writeln('return new $className.fromJson(');
195 writeln(' $makeDecoder, $fieldNameString, $inputName.$fieldName);');
196 }
197 });
198 writeln('}');
199 return true;
200 }
201
202 /**
247 * Emit a class representing an data structure that doesn't exist in the 203 * Emit a class representing an data structure that doesn't exist in the
248 * protocol because it is empty (e.g. the "params" object for a request that 204 * protocol because it is empty (e.g. the "params" object for a request that
249 * doesn't have any parameters). 205 * doesn't have any parameters).
250 */ 206 */
251 void emitEmptyObjectClass(String className, ImpliedType impliedType) { 207 void emitEmptyObjectClass(String className, ImpliedType impliedType) {
252 docComment(toHtmlVisitor.collectHtml(() { 208 docComment(toHtmlVisitor.collectHtml(() {
253 toHtmlVisitor.p(() { 209 toHtmlVisitor.p(() {
254 toHtmlVisitor.write(impliedType.humanReadableName); 210 toHtmlVisitor.write(impliedType.humanReadableName);
255 }); 211 });
256 })); 212 }));
257 writeln('class $className {'); 213 writeln('class $className {');
258 indent(() { 214 indent(() {
259 if (emitToRequestMember(impliedType)) { 215 if (emitToRequestMember(impliedType)) {
260 writeln(); 216 writeln();
261 } 217 }
262 if (emitToResponseMember(impliedType)) { 218 if (emitToResponseMember(impliedType)) {
263 writeln(); 219 writeln();
264 } 220 }
265 if (emitToNotificationMember(impliedType)) { 221 if (emitToNotificationMember(impliedType)) {
266 writeln(); 222 writeln();
267 } 223 }
268 emitObjectEqualsMember(null, className); 224 emitObjectEqualsMember(null, className);
269 writeln(); 225 writeln();
270 emitObjectHashCode(null, className); 226 emitObjectHashCode(null, className);
271 }); 227 });
272 writeln('}'); 228 writeln('}');
273 } 229 }
274 230
275 /** 231 /**
232 * Emit a class to encapsulate an enum.
233 */
234 void emitEnumClass(String className, TypeEnum type, ImpliedType impliedType) {
235 docComment(toHtmlVisitor.collectHtml(() {
236 toHtmlVisitor.p(() {
237 toHtmlVisitor.write(impliedType.humanReadableName);
238 });
239 if (impliedType.type != null) {
240 toHtmlVisitor.showType(null, impliedType.type);
241 }
242 }));
243 writeln('class $className implements Enum {');
244 indent(() {
245 if (emitSpecialStaticMembers(className)) {
246 writeln();
247 }
248 for (TypeEnumValue value in type.values) {
249 docComment(toHtmlVisitor.collectHtml(() {
250 toHtmlVisitor.translateHtml(value.html);
251 }));
252 String valueString = literalString(value.value);
253 writeln(
254 'static const ${value.value} = const $className._($valueString);');
255 writeln();
256 }
257 writeln('final String name;');
258 writeln();
259 writeln('const $className._(this.name);');
260 writeln();
261 emitEnumClassConstructor(className, type);
262 writeln();
263 emitEnumFromJsonConstructor(className, type, impliedType);
264 writeln();
265 if (emitSpecialConstructors(className)) {
266 writeln();
267 }
268 if (emitSpecialGetters(className)) {
269 writeln();
270 }
271 if (emitSpecialMethods(className)) {
272 writeln();
273 }
274 writeln('@override');
275 writeln('String toString() => "$className.\$name";');
276 writeln();
277 writeln('String toJson() => name;');
278 });
279 writeln('}');
280 }
281
282 /**
283 * Emit the constructor for an enum class.
284 */
285 void emitEnumClassConstructor(String className, TypeEnum type) {
286 writeln('factory $className(String name) {');
287 indent(() {
288 writeln('switch (name) {');
289 indent(() {
290 for (TypeEnumValue value in type.values) {
291 String valueString = literalString(value.value);
292 writeln('case $valueString:');
293 indent(() {
294 writeln('return ${value.value};');
295 });
296 }
297 });
298 writeln('}');
299 writeln(r"throw new Exception('Illegal enum value: $name');");
300 });
301 writeln('}');
302 }
303
304 /**
305 * Emit the method for decoding an enum from JSON.
306 */
307 void emitEnumFromJsonConstructor(String className, TypeEnum type,
308 ImpliedType impliedType) {
309 writeln(
310 'factory $className.fromJson(JsonDecoder jsonDecoder, String jsonPath, O bject json) {');
311 indent(() {
312 writeln('if (json is String) {');
313 indent(() {
314 writeln('try {');
315 indent(() {
316 writeln('return new $className(json);');
317 });
318 writeln('} catch(_) {');
319 indent(() {
320 writeln('// Fall through');
321 });
322 writeln('}');
323 });
324 writeln('}');
325 String humanReadableNameString =
326 literalString(impliedType.humanReadableName);
327 writeln(
328 'throw jsonDecoder.mismatch(jsonPath, $humanReadableNameString);');
329 });
330 writeln('}');
331 }
332
333 /**
276 * Emit the class to encapsulate an object type. 334 * Emit the class to encapsulate an object type.
277 */ 335 */
278 void emitObjectClass(String className, TypeObject type, 336 void emitObjectClass(String className, TypeObject type,
279 ImpliedType impliedType) { 337 ImpliedType impliedType) {
280 docComment(toHtmlVisitor.collectHtml(() { 338 docComment(toHtmlVisitor.collectHtml(() {
281 toHtmlVisitor.p(() { 339 toHtmlVisitor.p(() {
282 toHtmlVisitor.write(impliedType.humanReadableName); 340 toHtmlVisitor.write(impliedType.humanReadableName);
283 }); 341 });
284 if (impliedType.type != null) { 342 if (impliedType.type != null) {
285 toHtmlVisitor.showType(null, impliedType.type); 343 toHtmlVisitor.showType(null, impliedType.type);
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
338 writeln('String toString() => JSON.encode(toJson());'); 396 writeln('String toString() => JSON.encode(toJson());');
339 writeln(); 397 writeln();
340 emitObjectEqualsMember(type, className); 398 emitObjectEqualsMember(type, className);
341 writeln(); 399 writeln();
342 emitObjectHashCode(type, className); 400 emitObjectHashCode(type, className);
343 }); 401 });
344 writeln('}'); 402 writeln('}');
345 } 403 }
346 404
347 /** 405 /**
348 * If the class named [className] requires special static members, emit them 406 * Emit the constructor for an object class.
349 * and return true. 407 */
350 */ 408 void emitObjectConstructor(TypeObject type, String className) {
351 bool emitSpecialStaticMembers(String className) { 409 List<String> args = <String>[];
352 switch (className) { 410 List<String> optionalArgs = <String>[];
353 case 'Element': 411 List<CodegenCallback> extraInitCode = <CodegenCallback>[];
354 List<String> makeFlagsArgs = <String>[]; 412 for (TypeObjectField field in type.fields) {
355 List<String> makeFlagsStatements = <String>[]; 413 if (field.value != null) {
356 specialElementFlags.forEach((String name, String value) { 414 continue;
357 String flag = 'FLAG_${name.toUpperCase()}'; 415 }
358 String camelName = camelJoin(['is', name]); 416 String arg = 'this.${field.name}';
359 writeln('static const int $flag = $value;'); 417 if (isOptionalConstructorArg(className, field)) {
360 makeFlagsArgs.add('$camelName: false'); 418 optionalArgs.add(arg);
361 makeFlagsStatements.add('if ($camelName) flags |= $flag;'); 419 if (!field.optional) {
362 }); 420 // Optional constructor arg, but non-optional field. If no arg is
363 writeln(); 421 // given, the constructor should populate with the empty list.
364 writeln('static int makeFlags({${makeFlagsArgs.join(', ')}}) {'); 422 TypeDecl fieldType = field.type;
365 indent(() { 423 if (fieldType is TypeList) {
366 writeln('int flags = 0;'); 424 extraInitCode.add(() {
367 for (String statement in makeFlagsStatements) { 425 writeln('if (${field.name} == null) {');
368 writeln(statement); 426 indent(() {
369 } 427 writeln('${field.name} = <${dartType(fieldType.itemType)}>[];');
370 writeln('return flags;'); 428 });
371 }); 429 writeln('}');
372 writeln('}'); 430 });
373 return true; 431 } else {
374 case 'SourceEdit': 432 throw new Exception(
375 docComment( 433 "Don't know how to create default field value.");
376 [ 434 }
377 new dom.Text( 435 }
378 'Get the result of applying a set of ' + 436 } else {
379 '[edits] to the given [code]. Edits are applied in the order ' + 437 args.add(arg);
380 'they appear in [edits].')]); 438 }
439 }
440 if (optionalArgs.isNotEmpty) {
441 args.add('{${optionalArgs.join(', ')}}');
442 }
443 write('$className(${args.join(', ')})');
444 if (extraInitCode.isEmpty) {
445 writeln(';');
446 } else {
447 writeln(' {');
448 indent(() {
449 for (CodegenCallback callback in extraInitCode) {
450 callback();
451 }
452 });
453 writeln('}');
454 }
455 }
456
457 /**
458 * Emit the operator== code for an object class.
459 */
460 void emitObjectEqualsMember(TypeObject type, String className) {
461 writeln('@override');
462 writeln('bool operator==(other) {');
463 indent(() {
464 writeln('if (other is $className) {');
465 indent(() {
466 var comparisons = <String>[];
467 if (type != null) {
468 for (TypeObjectField field in type.fields) {
469 if (field.value != null) {
470 continue;
471 }
472 comparisons.add(
473 compareEqualsCode(field.type, field.name, 'other.${field.name}') );
474 }
475 }
476 if (comparisons.isEmpty) {
477 writeln('return true;');
478 } else {
479 String concatenated = comparisons.join(' &&\n ');
480 writeln('return $concatenated;');
481 }
482 });
483 writeln('}');
484 writeln('return false;');
485 });
486 writeln('}');
487 }
488
489 /**
490 * Emit the method for decoding an object from JSON.
491 */
492 void emitObjectFromJsonConstructor(String className, TypeObject type,
493 ImpliedType impliedType) {
494 String humanReadableNameString =
495 literalString(impliedType.humanReadableName);
496 if (className == 'RefactoringFeedback') {
497 writeln(
498 'factory RefactoringFeedback.fromJson(JsonDecoder jsonDecoder, '
499 'String jsonPath, Object json, Map responseJson) {');
500 indent(() {
381 writeln( 501 writeln(
382 'static String applySequence(String code, Iterable<SourceEdit> edits ) =>'); 502 'return _refactoringFeedbackFromJson(jsonDecoder, jsonPath, '
383 writeln(' _applySequence(code, edits);'); 503 'json, responseJson);');
384 return true; 504 });
385 default: 505 writeln('}');
386 return false; 506 return;
387 } 507 }
388 } 508 if (className == 'RefactoringOptions') {
389 509 writeln(
390 /** 510 'factory RefactoringOptions.fromJson(JsonDecoder jsonDecoder, '
511 'String jsonPath, Object json, RefactoringKind kind) {');
512 indent(() {
513 writeln(
514 'return _refactoringOptionsFromJson(jsonDecoder, jsonPath, ' 'json, kind);');
515 });
516 writeln('}');
517 return;
518 }
519 writeln(
520 'factory $className.fromJson(JsonDecoder jsonDecoder, String jsonPath, O bject json) {');
521 indent(() {
522 writeln('if (json == null) {');
523 indent(() {
524 writeln('json = {};');
525 });
526 writeln('}');
527 writeln('if (json is Map) {');
528 indent(() {
529 List<String> args = <String>[];
530 List<String> optionalArgs = <String>[];
531 for (TypeObjectField field in type.fields) {
532 String fieldNameString = literalString(field.name);
533 String fieldAccessor = 'json[$fieldNameString]';
534 String jsonPath = 'jsonPath + ${literalString('.${field.name}')}';
535 if (field.value != null) {
536 String valueString = literalString(field.value);
537 writeln('if ($fieldAccessor != $valueString) {');
538 indent(() {
539 writeln(
540 'throw jsonDecoder.mismatch(jsonPath, "equal " + $valueString) ;');
541 });
542 writeln('}');
543 continue;
544 }
545 if (isOptionalConstructorArg(className, field)) {
546 optionalArgs.add('${field.name}: ${field.name}');
547 } else {
548 args.add(field.name);
549 }
550 TypeDecl fieldType = field.type;
551 String fieldDartType = dartType(fieldType);
552 writeln('$fieldDartType ${field.name};');
553 writeln('if (json.containsKey($fieldNameString)) {');
554 indent(() {
555 String fromJson =
556 fromJsonCode(fieldType).asSnippet(jsonPath, fieldAccessor);
557 writeln('${field.name} = $fromJson;');
558 });
559 write('}');
560 if (!field.optional) {
561 writeln(' else {');
562 indent(() {
563 writeln(
564 "throw jsonDecoder.missingKey(jsonPath, $fieldNameString);");
565 });
566 writeln('}');
567 } else {
568 writeln();
569 }
570 }
571 args.addAll(optionalArgs);
572 writeln('return new $className(${args.join(', ')});');
573 });
574 writeln('} else {');
575 indent(() {
576 writeln(
577 'throw jsonDecoder.mismatch(jsonPath, $humanReadableNameString);');
578 });
579 writeln('}');
580 });
581 writeln('}');
582 }
583
584 /**
585 * Emit the hashCode getter for an object class.
586 */
587 void emitObjectHashCode(TypeObject type, String className) {
588 writeln('@override');
589 writeln('int get hashCode {');
590 indent(() {
591 if (type == null) {
592 writeln('return ${className.hashCode};');
593 } else {
594 writeln('int hash = 0;');
595 for (TypeObjectField field in type.fields) {
596 String valueToCombine;
597 if (field.value != null) {
598 valueToCombine = field.value.hashCode.toString();
599 } else {
600 valueToCombine = '${field.name}.hashCode';
601 }
602 writeln('hash = _JenkinsSmiHash.combine(hash, $valueToCombine);');
603 }
604 writeln('return _JenkinsSmiHash.finish(hash);');
605 }
606 });
607 writeln('}');
608 }
609
610 /**
391 * If the class named [className] requires special constructors, emit them 611 * If the class named [className] requires special constructors, emit them
392 * and return true. 612 * and return true.
393 */ 613 */
394 bool emitSpecialConstructors(String className) { 614 bool emitSpecialConstructors(String className) {
395 switch (className) { 615 switch (className) {
396 case 'LinkedEditGroup': 616 case 'LinkedEditGroup':
397 docComment([new dom.Text('Construct an empty LinkedEditGroup.')]); 617 docComment([new dom.Text('Construct an empty LinkedEditGroup.')]);
398 writeln( 618 writeln(
399 'LinkedEditGroup.empty() : this(<Position>[], 0, <LinkedEditSuggesti on>[]);'); 619 'LinkedEditGroup.empty() : this(<Position>[], 0, <LinkedEditSuggesti on>[]);');
400 return true; 620 return true;
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 docComment([new dom.Text('Adds the given [Edit]s.')]); 713 docComment([new dom.Text('Adds the given [Edit]s.')]);
494 writeln('void addAll(Iterable<SourceEdit> edits) =>'); 714 writeln('void addAll(Iterable<SourceEdit> edits) =>');
495 writeln(' _addAllEditsForSource(this, edits);'); 715 writeln(' _addAllEditsForSource(this, edits);');
496 return true; 716 return true;
497 default: 717 default:
498 return false; 718 return false;
499 } 719 }
500 } 720 }
501 721
502 /** 722 /**
503 * Emit the constructor for an object class. 723 * If the class named [className] requires special static members, emit them
724 * and return true.
504 */ 725 */
505 void emitObjectConstructor(TypeObject type, String className) { 726 bool emitSpecialStaticMembers(String className) {
506 List<String> args = <String>[]; 727 switch (className) {
507 List<String> optionalArgs = <String>[]; 728 case 'Element':
508 List<CodegenCallback> extraInitCode = <CodegenCallback>[]; 729 List<String> makeFlagsArgs = <String>[];
509 for (TypeObjectField field in type.fields) { 730 List<String> makeFlagsStatements = <String>[];
510 if (field.value != null) { 731 specialElementFlags.forEach((String name, String value) {
511 continue; 732 String flag = 'FLAG_${name.toUpperCase()}';
512 } 733 String camelName = camelJoin(['is', name]);
513 String arg = 'this.${field.name}'; 734 writeln('static const int $flag = $value;');
514 if (isOptionalConstructorArg(className, field)) { 735 makeFlagsArgs.add('$camelName: false');
515 optionalArgs.add(arg); 736 makeFlagsStatements.add('if ($camelName) flags |= $flag;');
516 if (!field.optional) { 737 });
517 // Optional constructor arg, but non-optional field. If no arg is 738 writeln();
518 // given, the constructor should populate with the empty list. 739 writeln('static int makeFlags({${makeFlagsArgs.join(', ')}}) {');
519 TypeDecl fieldType = field.type; 740 indent(() {
520 if (fieldType is TypeList) { 741 writeln('int flags = 0;');
521 extraInitCode.add(() { 742 for (String statement in makeFlagsStatements) {
522 writeln('if (${field.name} == null) {'); 743 writeln(statement);
523 indent(() {
524 writeln('${field.name} = <${dartType(fieldType.itemType)}>[];');
525 });
526 writeln('}');
527 });
528 } else {
529 throw new Exception(
530 "Don't know how to create default field value.");
531 } 744 }
532 } 745 writeln('return flags;');
533 } else { 746 });
534 args.add(arg); 747 writeln('}');
535 } 748 return true;
536 } 749 case 'SourceEdit':
537 if (optionalArgs.isNotEmpty) { 750 docComment(
538 args.add('{${optionalArgs.join(', ')}}'); 751 [
539 } 752 new dom.Text(
540 write('$className(${args.join(', ')})'); 753 'Get the result of applying a set of ' +
541 if (extraInitCode.isEmpty) { 754 '[edits] to the given [code]. Edits are applied in the order ' +
542 writeln(';'); 755 'they appear in [edits].')]);
543 } else { 756 writeln(
544 writeln(' {'); 757 'static String applySequence(String code, Iterable<SourceEdit> edits ) =>');
545 indent(() { 758 writeln(' _applySequence(code, edits);');
546 for (CodegenCallback callback in extraInitCode) { 759 return true;
547 callback(); 760 default:
548 } 761 return false;
549 });
550 writeln('}');
551 } 762 }
552 } 763 }
553 764
554 /** 765 /**
555 * True if the constructor argument for the given field should be optional.
556 */
557 bool isOptionalConstructorArg(String className, TypeObjectField field) {
558 if (field.optional) {
559 return true;
560 }
561 List<String> forceOptional = _optionalConstructorArguments[className];
562 if (forceOptional != null && forceOptional.contains(field.name)) {
563 return true;
564 }
565 return false;
566 }
567
568 /**
569 * Emit the toJson() code for an object class. 766 * Emit the toJson() code for an object class.
570 */ 767 */
571 void emitToJsonMember(TypeObject type) { 768 void emitToJsonMember(TypeObject type) {
572 writeln('Map<String, dynamic> toJson() {'); 769 writeln('Map<String, dynamic> toJson() {');
573 indent(() { 770 indent(() {
574 writeln('Map<String, dynamic> result = {};'); 771 writeln('Map<String, dynamic> result = {};');
575 for (TypeObjectField field in type.fields) { 772 for (TypeObjectField field in type.fields) {
576 String fieldNameString = literalString(field.name); 773 String fieldNameString = literalString(field.name);
577 if (field.value != null) { 774 if (field.value != null) {
578 writeln('result[$fieldNameString] = ${literalString(field.value)};'); 775 writeln('result[$fieldNameString] = ${literalString(field.value)};');
(...skipping 10 matching lines...) Expand all
589 } else { 786 } else {
590 writeln(populateField); 787 writeln(populateField);
591 } 788 }
592 } 789 }
593 writeln('return result;'); 790 writeln('return result;');
594 }); 791 });
595 writeln('}'); 792 writeln('}');
596 } 793 }
597 794
598 /** 795 /**
796 * Emit the toNotification() code for a class, if appropriate. Returns true
797 * if code was emitted.
798 */
799 bool emitToNotificationMember(ImpliedType impliedType) {
800 if (impliedType.kind == 'notificationParams') {
801 writeln('Notification toNotification() {');
802 indent(() {
803 String eventString =
804 literalString((impliedType.apiNode as Notification).longEvent);
805 String jsonPart = impliedType.type != null ? 'toJson()' : 'null';
806 writeln('return new Notification($eventString, $jsonPart);');
807 });
808 writeln('}');
809 return true;
810 }
811 return false;
812 }
813
814 /**
599 * Emit the toRequest() code for a class, if appropriate. Returns true if 815 * Emit the toRequest() code for a class, if appropriate. Returns true if
600 * code was emitted. 816 * code was emitted.
601 */ 817 */
602 bool emitToRequestMember(ImpliedType impliedType) { 818 bool emitToRequestMember(ImpliedType impliedType) {
603 if (impliedType.kind == 'requestParams') { 819 if (impliedType.kind == 'requestParams') {
604 writeln('Request toRequest(String id) {'); 820 writeln('Request toRequest(String id) {');
605 indent(() { 821 indent(() {
606 String methodString = 822 String methodString =
607 literalString((impliedType.apiNode as Request).longMethod); 823 literalString((impliedType.apiNode as Request).longMethod);
608 String jsonPart = impliedType.type != null ? 'toJson()' : 'null'; 824 String jsonPart = impliedType.type != null ? 'toJson()' : 'null';
(...skipping 16 matching lines...) Expand all
625 String jsonPart = impliedType.type != null ? 'toJson()' : 'null'; 841 String jsonPart = impliedType.type != null ? 'toJson()' : 'null';
626 writeln('return new Response(id, result: $jsonPart);'); 842 writeln('return new Response(id, result: $jsonPart);');
627 }); 843 });
628 writeln('}'); 844 writeln('}');
629 return true; 845 return true;
630 } 846 }
631 return false; 847 return false;
632 } 848 }
633 849
634 /** 850 /**
635 * Emit the toNotification() code for a class, if appropriate. Returns true 851 * Compute the code necessary to translate [type] from JSON.
636 * if code was emitted.
637 */ 852 */
638 bool emitToNotificationMember(ImpliedType impliedType) { 853 FromJsonCode fromJsonCode(TypeDecl type) {
639 if (impliedType.kind == 'notificationParams') { 854 if (type is TypeReference) {
640 writeln('Notification toNotification() {'); 855 TypeDefinition referencedDefinition = api.types[type.typeName];
641 indent(() { 856 if (referencedDefinition != null) {
642 String eventString = 857 TypeDecl referencedType = referencedDefinition.type;
643 literalString((impliedType.apiNode as Notification).longEvent); 858 if (referencedType is TypeObject || referencedType is TypeEnum) {
644 String jsonPart = impliedType.type != null ? 'toJson()' : 'null'; 859 return new FromJsonSnippet((String jsonPath, String json) {
645 writeln('return new Notification($eventString, $jsonPart);'); 860 String typeName = dartType(type);
646 }); 861 if (typeName == 'RefactoringFeedback') {
647 writeln('}'); 862 return
863 'new $typeName.fromJson(jsonDecoder, $jsonPath, $json, json)';
864 } else if (typeName == 'RefactoringOptions') {
865 return
866 'new $typeName.fromJson(jsonDecoder, $jsonPath, $json, kind)';
867 } else {
868 return 'new $typeName.fromJson(jsonDecoder, $jsonPath, $json)';
869 }
870 });
871 } else {
872 return fromJsonCode(referencedType);
873 }
874 } else {
875 switch (type.typeName) {
876 case 'String':
877 return new FromJsonFunction('jsonDecoder._decodeString');
878 case 'bool':
879 return new FromJsonFunction('jsonDecoder._decodeBool');
880 case 'int':
881 case 'long':
882 return new FromJsonFunction('jsonDecoder._decodeInt');
883 case 'object':
884 return new FromJsonIdentity();
885 default:
886 throw new Exception('Unexpected type name ${type.typeName}');
887 }
888 }
889 } else if (type is TypeMap) {
890 FromJsonCode keyCode;
891 if (dartType(type.keyType) != 'String') {
892 keyCode = fromJsonCode(type.keyType);
893 } else {
894 keyCode = new FromJsonIdentity();
895 }
896 FromJsonCode valueCode = fromJsonCode(type.valueType);
897 if (keyCode.isIdentity && valueCode.isIdentity) {
898 return new FromJsonFunction('jsonDecoder._decodeMap');
899 } else {
900 return new FromJsonSnippet((String jsonPath, String json) {
901 StringBuffer result = new StringBuffer();
902 result.write('jsonDecoder._decodeMap($jsonPath, $json');
903 if (!keyCode.isIdentity) {
904 result.write(', keyDecoder: ${keyCode.asClosure}');
905 }
906 if (!valueCode.isIdentity) {
907 result.write(', valueDecoder: ${valueCode.asClosure}');
908 }
909 result.write(')');
910 return result.toString();
911 });
912 }
913 } else if (type is TypeList) {
914 FromJsonCode itemCode = fromJsonCode(type.itemType);
915 if (itemCode.isIdentity) {
916 return new FromJsonFunction('jsonDecoder._decodeList');
917 } else {
918 return new FromJsonSnippet(
919 (String jsonPath, String json) =>
920 'jsonDecoder._decodeList($jsonPath, $json, ${itemCode.asClosure} )');
921 }
922 } else if (type is TypeUnion) {
923 List<String> decoders = <String>[];
924 for (TypeDecl choice in type.choices) {
925 TypeDecl resolvedChoice = resolveTypeReferenceChain(choice);
926 if (resolvedChoice is TypeObject) {
927 TypeObjectField field = resolvedChoice.getField(type.field);
928 if (field == null) {
929 throw new Exception(
930 'Each choice in the union needs a field named ${type.field}');
931 }
932 if (field.value == null) {
933 throw new Exception(
934 'Each choice in the union needs a constant value for the field $ {type.field}');
935 }
936 String closure = fromJsonCode(choice).asClosure;
937 decoders.add('${literalString(field.value)}: $closure');
938 } else {
939 throw new Exception('Union types must be unions of objects.');
940 }
941 }
942 return new FromJsonSnippet(
943 (String jsonPath, String json) =>
944 'jsonDecoder._decodeUnion($jsonPath, $json, ${literalString(type.f ield)}, {${decoders.join(', ')}})');
945 } else {
946 throw new Exception("Can't convert $type from JSON");
947 }
948 }
949
950 /**
951 * True if the constructor argument for the given field should be optional.
952 */
953 bool isOptionalConstructorArg(String className, TypeObjectField field) {
954 if (field.optional) {
955 return true;
956 }
957 List<String> forceOptional = _optionalConstructorArguments[className];
958 if (forceOptional != null && forceOptional.contains(field.name)) {
648 return true; 959 return true;
649 } 960 }
650 return false; 961 return false;
651 } 962 }
652 963
653 /** 964 /**
654 * Emit the operator== code for an object class. 965 * Create a string literal that evaluates to [s].
655 */ 966 */
656 void emitObjectEqualsMember(TypeObject type, String className) { 967 String literalString(String s) {
657 writeln('@override'); 968 return JSON.encode(s);
658 writeln('bool operator==(other) {');
659 indent(() {
660 writeln('if (other is $className) {');
661 indent(() {
662 var comparisons = <String>[];
663 if (type != null) {
664 for (TypeObjectField field in type.fields) {
665 if (field.value != null) {
666 continue;
667 }
668 comparisons.add(
669 compareEqualsCode(field.type, field.name, 'other.${field.name}') );
670 }
671 }
672 if (comparisons.isEmpty) {
673 writeln('return true;');
674 } else {
675 String concatenated = comparisons.join(' &&\n ');
676 writeln('return $concatenated;');
677 }
678 });
679 writeln('}');
680 writeln('return false;');
681 });
682 writeln('}');
683 }
684
685 /**
686 * Emit the hashCode getter for an object class.
687 */
688 void emitObjectHashCode(TypeObject type, String className) {
689 writeln('@override');
690 writeln('int get hashCode {');
691 indent(() {
692 if (type == null) {
693 writeln('return ${className.hashCode};');
694 } else {
695 writeln('int hash = 0;');
696 for (TypeObjectField field in type.fields) {
697 String valueToCombine;
698 if (field.value != null) {
699 valueToCombine = field.value.hashCode.toString();
700 } else {
701 valueToCombine = '${field.name}.hashCode';
702 }
703 writeln('hash = _JenkinsSmiHash.combine(hash, $valueToCombine);');
704 }
705 writeln('return _JenkinsSmiHash.finish(hash);');
706 }
707 });
708 writeln('}');
709 }
710
711 /**
712 * Emit a class to encapsulate an enum.
713 */
714 void emitEnumClass(String className, TypeEnum type, ImpliedType impliedType) {
715 docComment(toHtmlVisitor.collectHtml(() {
716 toHtmlVisitor.p(() {
717 toHtmlVisitor.write(impliedType.humanReadableName);
718 });
719 if (impliedType.type != null) {
720 toHtmlVisitor.showType(null, impliedType.type);
721 }
722 }));
723 writeln('class $className implements Enum {');
724 indent(() {
725 if (emitSpecialStaticMembers(className)) {
726 writeln();
727 }
728 for (TypeEnumValue value in type.values) {
729 docComment(toHtmlVisitor.collectHtml(() {
730 toHtmlVisitor.translateHtml(value.html);
731 }));
732 String valueString = literalString(value.value);
733 writeln(
734 'static const ${value.value} = const $className._($valueString);');
735 writeln();
736 }
737 writeln('final String name;');
738 writeln();
739 writeln('const $className._(this.name);');
740 writeln();
741 emitEnumClassConstructor(className, type);
742 writeln();
743 emitEnumFromJsonConstructor(className, type, impliedType);
744 writeln();
745 if (emitSpecialConstructors(className)) {
746 writeln();
747 }
748 if (emitSpecialGetters(className)) {
749 writeln();
750 }
751 if (emitSpecialMethods(className)) {
752 writeln();
753 }
754 writeln('@override');
755 writeln('String toString() => "$className.\$name";');
756 writeln();
757 writeln('String toJson() => name;');
758 });
759 writeln('}');
760 }
761
762 /**
763 * Emit the constructor for an enum class.
764 */
765 void emitEnumClassConstructor(String className, TypeEnum type) {
766 writeln('factory $className(String name) {');
767 indent(() {
768 writeln('switch (name) {');
769 indent(() {
770 for (TypeEnumValue value in type.values) {
771 String valueString = literalString(value.value);
772 writeln('case $valueString:');
773 indent(() {
774 writeln('return ${value.value};');
775 });
776 }
777 });
778 writeln('}');
779 writeln(r"throw new Exception('Illegal enum value: $name');");
780 });
781 writeln('}');
782 } 969 }
783 970
784 /** 971 /**
785 * Compute the code necessary to convert [type] to JSON. 972 * Compute the code necessary to convert [type] to JSON.
786 */ 973 */
787 ToJsonCode toJsonCode(TypeDecl type) { 974 ToJsonCode toJsonCode(TypeDecl type) {
788 TypeDecl resolvedType = resolveTypeReferenceChain(type); 975 TypeDecl resolvedType = resolveTypeReferenceChain(type);
789 if (resolvedType is TypeReference) { 976 if (resolvedType is TypeReference) {
790 return new ToJsonIdentity(dartType(type)); 977 return new ToJsonIdentity(dartType(type));
791 } else if (resolvedType is TypeList) { 978 } else if (resolvedType is TypeList) {
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
832 (String value) => '$value.toJson()'); 1019 (String value) => '$value.toJson()');
833 } else if (resolvedType is TypeObject || resolvedType is TypeEnum) { 1020 } else if (resolvedType is TypeObject || resolvedType is TypeEnum) {
834 return new ToJsonSnippet( 1021 return new ToJsonSnippet(
835 dartType(type), 1022 dartType(type),
836 (String value) => '$value.toJson()'); 1023 (String value) => '$value.toJson()');
837 } else { 1024 } else {
838 throw new Exception("Can't convert $resolvedType from JSON"); 1025 throw new Exception("Can't convert $resolvedType from JSON");
839 } 1026 }
840 } 1027 }
841 1028
842 /** 1029 @override
843 * Compute the code necessary to compare two objects for equality. 1030 visitApi() {
844 */ 1031 outputHeader();
845 String compareEqualsCode(TypeDecl type, String thisVar, String otherVar) { 1032 writeln();
846 TypeDecl resolvedType = resolveTypeReferenceChain(type); 1033 writeln('part of protocol;');
847 if (resolvedType is TypeReference || 1034 emitClasses();
848 resolvedType is TypeEnum ||
849 resolvedType is TypeObject ||
850 resolvedType is TypeUnion) {
851 return '$thisVar == $otherVar';
852 } else if (resolvedType is TypeList) {
853 String itemTypeName = dartType(resolvedType.itemType);
854 String subComparison = compareEqualsCode(resolvedType.itemType, 'a', 'b');
855 String closure = '($itemTypeName a, $itemTypeName b) => $subComparison';
856 return '_listEqual($thisVar, $otherVar, $closure)';
857 } else if (resolvedType is TypeMap) {
858 String valueTypeName = dartType(resolvedType.valueType);
859 String subComparison =
860 compareEqualsCode(resolvedType.valueType, 'a', 'b');
861 String closure = '($valueTypeName a, $valueTypeName b) => $subComparison';
862 return '_mapEqual($thisVar, $otherVar, $closure)';
863 }
864 throw new Exception(
865 "Don't know how to compare for equality: $resolvedType");
866 }
867
868 /**
869 * Emit the method for decoding an object from JSON.
870 */
871 void emitObjectFromJsonConstructor(String className, TypeObject type,
872 ImpliedType impliedType) {
873 String humanReadableNameString =
874 literalString(impliedType.humanReadableName);
875 if (className == 'RefactoringFeedback') {
876 writeln(
877 'factory RefactoringFeedback.fromJson(JsonDecoder jsonDecoder, '
878 'String jsonPath, Object json, Map responseJson) {');
879 indent(() {
880 writeln(
881 'return _refactoringFeedbackFromJson(jsonDecoder, jsonPath, '
882 'json, responseJson);');
883 });
884 writeln('}');
885 return;
886 }
887 if (className == 'RefactoringOptions') {
888 writeln(
889 'factory RefactoringOptions.fromJson(JsonDecoder jsonDecoder, '
890 'String jsonPath, Object json, RefactoringKind kind) {');
891 indent(() {
892 writeln(
893 'return _refactoringOptionsFromJson(jsonDecoder, jsonPath, ' 'json, kind);');
894 });
895 writeln('}');
896 return;
897 }
898 writeln(
899 'factory $className.fromJson(JsonDecoder jsonDecoder, String jsonPath, O bject json) {');
900 indent(() {
901 writeln('if (json == null) {');
902 indent(() {
903 writeln('json = {};');
904 });
905 writeln('}');
906 writeln('if (json is Map) {');
907 indent(() {
908 List<String> args = <String>[];
909 List<String> optionalArgs = <String>[];
910 for (TypeObjectField field in type.fields) {
911 String fieldNameString = literalString(field.name);
912 String fieldAccessor = 'json[$fieldNameString]';
913 String jsonPath = 'jsonPath + ${literalString('.${field.name}')}';
914 if (field.value != null) {
915 String valueString = literalString(field.value);
916 writeln('if ($fieldAccessor != $valueString) {');
917 indent(() {
918 writeln(
919 'throw jsonDecoder.mismatch(jsonPath, "equal " + $valueString) ;');
920 });
921 writeln('}');
922 continue;
923 }
924 if (isOptionalConstructorArg(className, field)) {
925 optionalArgs.add('${field.name}: ${field.name}');
926 } else {
927 args.add(field.name);
928 }
929 TypeDecl fieldType = field.type;
930 String fieldDartType = dartType(fieldType);
931 writeln('$fieldDartType ${field.name};');
932 writeln('if (json.containsKey($fieldNameString)) {');
933 indent(() {
934 String fromJson =
935 fromJsonCode(fieldType).asSnippet(jsonPath, fieldAccessor);
936 writeln('${field.name} = $fromJson;');
937 });
938 write('}');
939 if (!field.optional) {
940 writeln(' else {');
941 indent(() {
942 writeln(
943 "throw jsonDecoder.missingKey(jsonPath, $fieldNameString);");
944 });
945 writeln('}');
946 } else {
947 writeln();
948 }
949 }
950 args.addAll(optionalArgs);
951 writeln('return new $className(${args.join(', ')});');
952 });
953 writeln('} else {');
954 indent(() {
955 writeln(
956 'throw jsonDecoder.mismatch(jsonPath, $humanReadableNameString);');
957 });
958 writeln('}');
959 });
960 writeln('}');
961 }
962
963 /**
964 * Emit the method for decoding an enum from JSON.
965 */
966 void emitEnumFromJsonConstructor(String className, TypeEnum type,
967 ImpliedType impliedType) {
968 writeln(
969 'factory $className.fromJson(JsonDecoder jsonDecoder, String jsonPath, O bject json) {');
970 indent(() {
971 writeln('if (json is String) {');
972 indent(() {
973 writeln('try {');
974 indent(() {
975 writeln('return new $className(json);');
976 });
977 writeln('} catch(_) {');
978 indent(() {
979 writeln('// Fall through');
980 });
981 writeln('}');
982 });
983 writeln('}');
984 String humanReadableNameString =
985 literalString(impliedType.humanReadableName);
986 writeln(
987 'throw jsonDecoder.mismatch(jsonPath, $humanReadableNameString);');
988 });
989 writeln('}');
990 }
991
992 /**
993 * Compute the code necessary to translate [type] from JSON.
994 */
995 FromJsonCode fromJsonCode(TypeDecl type) {
996 if (type is TypeReference) {
997 TypeDefinition referencedDefinition = api.types[type.typeName];
998 if (referencedDefinition != null) {
999 TypeDecl referencedType = referencedDefinition.type;
1000 if (referencedType is TypeObject || referencedType is TypeEnum) {
1001 return new FromJsonSnippet((String jsonPath, String json) {
1002 String typeName = dartType(type);
1003 if (typeName == 'RefactoringFeedback') {
1004 return
1005 'new $typeName.fromJson(jsonDecoder, $jsonPath, $json, json)';
1006 } else if (typeName == 'RefactoringOptions') {
1007 return
1008 'new $typeName.fromJson(jsonDecoder, $jsonPath, $json, kind)';
1009 } else {
1010 return 'new $typeName.fromJson(jsonDecoder, $jsonPath, $json)';
1011 }
1012 });
1013 } else {
1014 return fromJsonCode(referencedType);
1015 }
1016 } else {
1017 switch (type.typeName) {
1018 case 'String':
1019 return new FromJsonFunction('jsonDecoder._decodeString');
1020 case 'bool':
1021 return new FromJsonFunction('jsonDecoder._decodeBool');
1022 case 'int':
1023 case 'long':
1024 return new FromJsonFunction('jsonDecoder._decodeInt');
1025 case 'object':
1026 return new FromJsonIdentity();
1027 default:
1028 throw new Exception('Unexpected type name ${type.typeName}');
1029 }
1030 }
1031 } else if (type is TypeMap) {
1032 FromJsonCode keyCode;
1033 if (dartType(type.keyType) != 'String') {
1034 keyCode = fromJsonCode(type.keyType);
1035 } else {
1036 keyCode = new FromJsonIdentity();
1037 }
1038 FromJsonCode valueCode = fromJsonCode(type.valueType);
1039 if (keyCode.isIdentity && valueCode.isIdentity) {
1040 return new FromJsonFunction('jsonDecoder._decodeMap');
1041 } else {
1042 return new FromJsonSnippet((String jsonPath, String json) {
1043 StringBuffer result = new StringBuffer();
1044 result.write('jsonDecoder._decodeMap($jsonPath, $json');
1045 if (!keyCode.isIdentity) {
1046 result.write(', keyDecoder: ${keyCode.asClosure}');
1047 }
1048 if (!valueCode.isIdentity) {
1049 result.write(', valueDecoder: ${valueCode.asClosure}');
1050 }
1051 result.write(')');
1052 return result.toString();
1053 });
1054 }
1055 } else if (type is TypeList) {
1056 FromJsonCode itemCode = fromJsonCode(type.itemType);
1057 if (itemCode.isIdentity) {
1058 return new FromJsonFunction('jsonDecoder._decodeList');
1059 } else {
1060 return new FromJsonSnippet(
1061 (String jsonPath, String json) =>
1062 'jsonDecoder._decodeList($jsonPath, $json, ${itemCode.asClosure} )');
1063 }
1064 } else if (type is TypeUnion) {
1065 List<String> decoders = <String>[];
1066 for (TypeDecl choice in type.choices) {
1067 TypeDecl resolvedChoice = resolveTypeReferenceChain(choice);
1068 if (resolvedChoice is TypeObject) {
1069 TypeObjectField field = resolvedChoice.getField(type.field);
1070 if (field == null) {
1071 throw new Exception(
1072 'Each choice in the union needs a field named ${type.field}');
1073 }
1074 if (field.value == null) {
1075 throw new Exception(
1076 'Each choice in the union needs a constant value for the field $ {type.field}');
1077 }
1078 String closure = fromJsonCode(choice).asClosure;
1079 decoders.add('${literalString(field.value)}: $closure');
1080 } else {
1081 throw new Exception('Union types must be unions of objects.');
1082 }
1083 }
1084 return new FromJsonSnippet(
1085 (String jsonPath, String json) =>
1086 'jsonDecoder._decodeUnion($jsonPath, $json, ${literalString(type.f ield)}, {${decoders.join(', ')}})');
1087 } else {
1088 throw new Exception("Can't convert $type from JSON");
1089 }
1090 }
1091
1092 /**
1093 * Emit a convenience constructor for decoding a piece of protocol, if
1094 * appropriate. Return true if a constructor was emitted.
1095 */
1096 bool emitConvenienceConstructor(String className, ImpliedType impliedType) {
1097 // The type of object from which this piece of protocol should be decoded.
1098 String inputType;
1099 // The name of the input object.
1100 String inputName;
1101 // The field within the input object to decode.
1102 String fieldName;
1103 // Constructor call to create the JsonDecoder object.
1104 String makeDecoder;
1105 // Name of the constructor to create.
1106 String constructorName;
1107 // Extra arguments for the constructor.
1108 List<String> extraArgs = <String>[];
1109 switch (impliedType.kind) {
1110 case 'requestParams':
1111 inputType = 'Request';
1112 inputName = 'request';
1113 fieldName = '_params';
1114 makeDecoder = 'new RequestDecoder(request)';
1115 constructorName = 'fromRequest';
1116 break;
1117 case 'requestResult':
1118 inputType = 'Response';
1119 inputName = 'response';
1120 fieldName = '_result';
1121 makeDecoder =
1122 'new ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id ))';
1123 constructorName = 'fromResponse';
1124 break;
1125 case 'notificationParams':
1126 inputType = 'Notification';
1127 inputName = 'notification';
1128 fieldName = '_params';
1129 makeDecoder = 'new ResponseDecoder(null)';
1130 constructorName = 'fromNotification';
1131 break;
1132 case 'refactoringOptions':
1133 inputType = 'EditGetRefactoringParams';
1134 inputName = 'refactoringParams';
1135 fieldName = 'options';
1136 makeDecoder = 'new RequestDecoder(request)';
1137 constructorName = 'fromRefactoringParams';
1138 extraArgs.add('Request request');
1139 break;
1140 default:
1141 return false;
1142 }
1143 List<String> args = ['$inputType $inputName'];
1144 args.addAll(extraArgs);
1145 writeln('factory $className.$constructorName(${args.join(', ')}) {');
1146 indent(() {
1147 String fieldNameString =
1148 literalString(fieldName.replaceFirst(new RegExp('^_'), ''));
1149 if (className == 'EditGetRefactoringParams') {
1150 writeln('var params = new $className.fromJson(');
1151 writeln(' $makeDecoder, $fieldNameString, $inputName.$fieldName);');
1152 writeln('REQUEST_ID_REFACTORING_KINDS[request.id] = params.kind;');
1153 writeln('return params;');
1154 } else {
1155 writeln('return new $className.fromJson(');
1156 writeln(' $makeDecoder, $fieldNameString, $inputName.$fieldName);');
1157 }
1158 });
1159 writeln('}');
1160 return true;
1161 }
1162
1163 /**
1164 * Create a string literal that evaluates to [s].
1165 */
1166 String literalString(String s) {
1167 return JSON.encode(s);
1168 } 1035 }
1169 } 1036 }
1170 1037
1171 final GeneratedFile target = 1038 /**
1172 new GeneratedFile('../../lib/src/generated_protocol.dart', () { 1039 * Container for code that can be used to translate a data type from JSON.
1173 CodegenProtocolVisitor visitor = new CodegenProtocolVisitor(readApi()); 1040 */
1174 return visitor.collectCode(visitor.visitApi); 1041 abstract class FromJsonCode {
1175 }); 1042 /**
1043 * Get the translation code in the form of a closure.
1044 */
1045 String get asClosure;
1046
1047 /**
1048 * True if the data type is already in JSON form, so the translation is the
1049 * identity function.
1050 */
1051 bool get isIdentity;
1052
1053 /**
1054 * Get the translation code in the form of a code snippet, where [jsonPath]
1055 * is the variable holding the JSON path, and [json] is the variable holding
1056 * the raw JSON.
1057 */
1058 String asSnippet(String jsonPath, String json);
1059 }
1176 1060
1177 /** 1061 /**
1178 * Translate spec_input.html into protocol_matchers.dart. 1062 * Representation of FromJsonCode for a function defined elsewhere.
1179 */ 1063 */
1180 main() { 1064 class FromJsonFunction extends FromJsonCode {
1181 target.generate(); 1065 final String asClosure;
1066
1067 FromJsonFunction(this.asClosure);
1068
1069 @override
1070 bool get isIdentity => false;
1071
1072 @override
1073 String asSnippet(String jsonPath, String json) =>
1074 '$asClosure($jsonPath, $json)';
1182 } 1075 }
1076
1077 /**
1078 * Representation of FromJsonCode for the identity transformation.
1079 */
1080 class FromJsonIdentity extends FromJsonSnippet {
1081 FromJsonIdentity() : super((String jsonPath, String json) => json);
1082
1083 @override
1084 bool get isIdentity => true;
1085 }
1086
1087 /**
1088 * Representation of FromJsonCode for a snippet of inline code.
1089 */
1090 class FromJsonSnippet extends FromJsonCode {
1091 /**
1092 * Callback that can be used to generate the code snippet, once the names
1093 * of the [jsonPath] and [json] variables are known.
1094 */
1095 final FromJsonSnippetCallback callback;
1096
1097 FromJsonSnippet(this.callback);
1098
1099 @override
1100 String get asClosure =>
1101 '(String jsonPath, Object json) => ${callback('jsonPath', 'json')}';
1102
1103 @override
1104 bool get isIdentity => false;
1105
1106 @override
1107 String asSnippet(String jsonPath, String json) => callback(jsonPath, json);
1108 }
1109
1110 /**
1111 * Container for code that can be used to translate a data type to JSON.
1112 */
1113 abstract class ToJsonCode {
1114 /**
1115 * Get the translation code in the form of a closure.
1116 */
1117 String get asClosure;
1118
1119 /**
1120 * True if the data type is already in JSON form, so the translation is the
1121 * identity function.
1122 */
1123 bool get isIdentity;
1124
1125 /**
1126 * Get the translation code in the form of a code snippet, where [value]
1127 * is the variable holding the object to be translated.
1128 */
1129 String asSnippet(String value);
1130 }
1131
1132 /**
1133 * Representation of ToJsonCode for a function defined elsewhere.
1134 */
1135 class ToJsonFunction extends ToJsonCode {
1136 final String asClosure;
1137
1138 ToJsonFunction(this.asClosure);
1139
1140 @override
1141 bool get isIdentity => false;
1142
1143 @override
1144 String asSnippet(String value) => '$asClosure($value)';
1145 }
1146
1147 /**
1148 * Representation of FromJsonCode for the identity transformation.
1149 */
1150 class ToJsonIdentity extends ToJsonSnippet {
1151 ToJsonIdentity(String type) : super(type, (String value) => value);
1152
1153 @override
1154 bool get isIdentity => true;
1155 }
1156
1157 /**
1158 * Representation of ToJsonCode for a snippet of inline code.
1159 */
1160 class ToJsonSnippet extends ToJsonCode {
1161 /**
1162 * Callback that can be used to generate the code snippet, once the name
1163 * of the [value] variable is known.
1164 */
1165 final ToJsonSnippetCallback callback;
1166
1167 /**
1168 * Dart type of the [value] variable.
1169 */
1170 final String type;
1171
1172 ToJsonSnippet(this.type, this.callback);
1173
1174 @override
1175 String get asClosure => '($type value) => ${callback('value')}';
1176
1177 @override
1178 bool get isIdentity => false;
1179
1180 @override
1181 String asSnippet(String value) => callback(value);
1182 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/tool/spec/codegen_dart.dart ('k') | pkg/analysis_server/tool/spec/codegen_inttest_methods.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698