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

Side by Side Diff: pkg/analysis_server/lib/src/protocol.dart

Issue 506433002: Finish modifying analysis server to make use of code generation. (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
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 protocol; 5 library protocol;
6 6
7 import 'dart:collection'; 7 import 'dart:collection';
8 import 'dart:convert' show JsonDecoder; 8 import 'dart:convert';
9 9
10 import 'package:analysis_server/src/services/json.dart'; 10 import 'package:analysis_server/src/computer/element.dart' show
11 elementFromEngine;
12 import 'package:analysis_server/src/search/search_result.dart' show
13 searchResultFromMatch;
14 import 'package:analysis_server/src/services/correction/fix.dart' show Fix;
15 import 'package:analysis_server/src/services/search/search_engine.dart' as
16 engine;
17 import 'package:analyzer/src/generated/ast.dart' as engine;
18 import 'package:analyzer/src/generated/element.dart' as engine;
19 import 'package:analyzer/src/generated/engine.dart' as engine;
20 import 'package:analyzer/src/generated/error.dart' as engine;
21 import 'package:analyzer/src/generated/source.dart' as engine;
22
23 part 'generated_protocol.dart';
24
25 /**
26 * Translate the input [map], applying [keyCallback] to all its keys, and
27 * [valueCallback] to all its values.
28 */
29 mapMap(Map map, {dynamic keyCallback(key), dynamic valueCallback(value)}) {
30 Map result = {};
31 map.forEach((key, value) {
32 if (keyCallback != null) {
33 key = keyCallback(key);
34 }
35 if (valueCallback != null) {
36 value = valueCallback(value);
37 }
38 result[key] = value;
39 });
40 return result;
41 }
42
43 /**
44 * Adds the given [sourceEdits] to the list in [sourceFileEdit].
45 */
46 void _addAllEditsForSource(SourceFileEdit sourceFileEdit,
47 Iterable<SourceEdit> edits) {
48 edits.forEach(sourceFileEdit.add);
49 }
50
51 /**
52 * Adds the given [sourceEdit] to the list in [sourceFileEdit].
53 */
54 void _addEditForSource(SourceFileEdit sourceFileEdit, SourceEdit sourceEdit) {
55 List<SourceEdit> edits = sourceFileEdit.edits;
56 int index = 0;
57 while (index < edits.length && edits[index].offset > sourceEdit.offset) {
58 index++;
59 }
60 edits.insert(index, sourceEdit);
61 }
62
63 /**
64 * Adds [edit] to the [FileEdit] for the given [file].
65 */
66 void _addEditToSourceChange(SourceChange change, String file, SourceEdit edit) {
67 SourceFileEdit fileEdit = change.getFileEdit(file);
68 if (fileEdit == null) {
69 fileEdit = new SourceFileEdit(file);
70 change.addFileEdit(fileEdit);
71 }
72 fileEdit.add(edit);
73 }
74
75 /**
76 * Create an AnalysisError based on error information from the analyzer
77 * engine. Access via AnalysisError.fromEngine().
78 */
79 AnalysisError _analysisErrorFromEngine(engine.LineInfo lineInfo,
80 engine.AnalysisError error) {
81 engine.ErrorCode errorCode = error.errorCode;
82 // prepare location
83 Location location;
84 {
85 String file = error.source.fullName;
86 int offset = error.offset;
87 int length = error.length;
88 int startLine = -1;
89 int startColumn = -1;
90 if (lineInfo != null) {
91 engine.LineInfo_Location lineLocation = lineInfo.getLocation(offset);
92 if (lineLocation != null) {
93 startLine = lineLocation.lineNumber;
94 startColumn = lineLocation.columnNumber;
95 }
96 }
97 location = new Location(file, offset, length, startLine, startColumn);
98 }
99 // done
100 var severity = new ErrorSeverity(errorCode.errorSeverity.name);
101 var type = new ErrorType(errorCode.type.name);
102 String message = error.message;
103 String correction = error.correction;
104 return new AnalysisError(
105 severity,
106 type,
107 location,
108 message,
109 correction: correction);
110 }
111
112 /**
113 * Returns a list of AnalysisErrors correponding to the given list of Engine
114 * errors. Access via AnalysisError.listFromEngine().
115 */
116 List<AnalysisError> _analysisErrorListFromEngine(engine.LineInfo lineInfo,
117 List<engine.AnalysisError> errors) {
118 return errors.map((engine.AnalysisError error) {
119 return new AnalysisError.fromEngine(lineInfo, error);
120 }).toList();
121 }
122
123 /**
124 * Get the result of applying the edit to the given [code]. Access via
125 * SourceEdit.apply().
126 */
127 String _applyEdit(String code, SourceEdit edit) {
128 return code.substring(0, edit.offset) +
129 edit.replacement +
130 code.substring(edit.end);
131 }
132
133 /**
134 * Get the result of applying a set of [edits] to the given [code]. Edits
135 * are applied in the order they appear in [edits]. Access via
136 * SourceEdit.applySequence().
137 */
138 String _applySequence(String code, Iterable<SourceEdit> edits) {
139 edits.forEach((SourceEdit edit) {
140 code = edit.apply(code);
141 });
142 return code;
143 }
144
145 /**
146 * Map an element kind from the analyzer engine to a [CompletionSuggestionKind].
147 */
148 CompletionSuggestionKind _completionSuggestionKindFromElementKind(engine.Element Kind kind) {
149 // ElementKind.ANGULAR_FORMATTER,
150 // ElementKind.ANGULAR_COMPONENT,
151 // ElementKind.ANGULAR_CONTROLLER,
152 // ElementKind.ANGULAR_DIRECTIVE,
153 // ElementKind.ANGULAR_PROPERTY,
154 // ElementKind.ANGULAR_SCOPE_PROPERTY,
155 // ElementKind.ANGULAR_SELECTOR,
156 // ElementKind.ANGULAR_VIEW,
157 if (kind == engine.ElementKind.CLASS) return CompletionSuggestionKind.CLASS;
158 // ElementKind.COMPILATION_UNIT,
159 if (kind == engine.ElementKind.CONSTRUCTOR) return CompletionSuggestionKind.CO NSTRUCTOR;
160 // ElementKind.DYNAMIC,
161 // ElementKind.EMBEDDED_HTML_SCRIPT,
162 // ElementKind.ERROR,
163 // ElementKind.EXPORT,
164 // ElementKind.EXTERNAL_HTML_SCRIPT,
165 if (kind == engine.ElementKind.FIELD) return CompletionSuggestionKind.FIELD;
166 if (kind == engine.ElementKind.FUNCTION) return CompletionSuggestionKind.FUNCT ION;
167 if (kind == engine.ElementKind.FUNCTION_TYPE_ALIAS) return CompletionSuggestio nKind.FUNCTION_TYPE_ALIAS;
168 if (kind == engine.ElementKind.GETTER) return CompletionSuggestionKind.GETTER;
169 // ElementKind.HTML,
170 if (kind == engine.ElementKind.IMPORT) return CompletionSuggestionKind.IMPORT;
171 // ElementKind.LABEL,
172 // ElementKind.LIBRARY,
173 if (kind == engine.ElementKind.LOCAL_VARIABLE) return CompletionSuggestionKind .LOCAL_VARIABLE;
174 if (kind == engine.ElementKind.METHOD) return CompletionSuggestionKind.METHOD;
175 // ElementKind.NAME,
176 if (kind == engine.ElementKind.PARAMETER) return CompletionSuggestionKind.PARA METER;
177 // ElementKind.POLYMER_ATTRIBUTE,
178 // ElementKind.POLYMER_TAG_DART,
179 // ElementKind.POLYMER_TAG_HTML,
180 // ElementKind.PREFIX,
181 if (kind == engine.ElementKind.SETTER) return CompletionSuggestionKind.SETTER;
182 if (kind == engine.ElementKind.TOP_LEVEL_VARIABLE) return CompletionSuggestion Kind.TOP_LEVEL_VARIABLE;
183 // ElementKind.TYPE_PARAMETER,
184 // ElementKind.UNIVERSE
185 throw new ArgumentError('Unknown CompletionSuggestionKind for: $kind');
186 }
187
188 /**
189 * Create an ElementKind based on a value from the analyzer engine. Access
190 * this function via new ElementKind.fromEngine().
191 */
192 ElementKind _elementKindFromEngine(engine.ElementKind kind) {
193 if (kind == engine.ElementKind.CLASS) {
194 return ElementKind.CLASS;
195 }
196 if (kind == engine.ElementKind.COMPILATION_UNIT) {
197 return ElementKind.COMPILATION_UNIT;
198 }
199 if (kind == engine.ElementKind.CONSTRUCTOR) {
200 return ElementKind.CONSTRUCTOR;
201 }
202 if (kind == engine.ElementKind.FIELD) {
203 return ElementKind.FIELD;
204 }
205 if (kind == engine.ElementKind.FUNCTION) {
206 return ElementKind.FUNCTION;
207 }
208 if (kind == engine.ElementKind.FUNCTION_TYPE_ALIAS) {
209 return ElementKind.FUNCTION_TYPE_ALIAS;
210 }
211 if (kind == engine.ElementKind.GETTER) {
212 return ElementKind.GETTER;
213 }
214 if (kind == engine.ElementKind.LIBRARY) {
215 return ElementKind.LIBRARY;
216 }
217 if (kind == engine.ElementKind.LOCAL_VARIABLE) {
218 return ElementKind.LOCAL_VARIABLE;
219 }
220 if (kind == engine.ElementKind.METHOD) {
221 return ElementKind.METHOD;
222 }
223 if (kind == engine.ElementKind.PARAMETER) {
224 return ElementKind.PARAMETER;
225 }
226 if (kind == engine.ElementKind.SETTER) {
227 return ElementKind.SETTER;
228 }
229 if (kind == engine.ElementKind.TOP_LEVEL_VARIABLE) {
230 return ElementKind.TOP_LEVEL_VARIABLE;
231 }
232 if (kind == engine.ElementKind.TYPE_PARAMETER) {
233 return ElementKind.TYPE_PARAMETER;
234 }
235 return ElementKind.UNKNOWN;
236 }
237
238 /**
239 * Returns the [FileEdit] for the given [file], maybe `null`.
240 */
241 SourceFileEdit _getChangeFileEdit(SourceChange change, String file) {
242 for (SourceFileEdit fileEdit in change.edits) {
243 if (fileEdit.file == file) {
244 return fileEdit;
245 }
246 }
247 return null;
248 }
249
250 /**
251 * Compare the lists [listA] and [listB], using [itemEqual] to compare
252 * list elements.
253 */
254 bool _listEqual(List listA, List listB, bool itemEqual(a, b)) {
255 if (listA.length != listB.length) {
256 return false;
257 }
258 for (int i = 0; i < listA.length; i++) {
259 if (!itemEqual(listA[i], listB[i])) {
260 return false;
261 }
262 }
263 return true;
264 }
265
266 /**
267 * Creates a new [Location].
268 */
269 Location _locationForArgs(engine.AnalysisContext context, engine.Source source,
270 engine.SourceRange range) {
271 int startLine = 0;
272 int startColumn = 0;
273 {
274 engine.LineInfo lineInfo = context.getLineInfo(source);
275 if (lineInfo != null) {
276 engine.LineInfo_Location offsetLocation =
277 lineInfo.getLocation(range.offset);
278 startLine = offsetLocation.lineNumber;
279 startColumn = offsetLocation.columnNumber;
280 }
281 }
282 return new Location(
283 source.fullName,
284 range.offset,
285 range.length,
286 startLine,
287 startColumn);
288 }
289
290 /**
291 * Creates a new [Location] for the given [engine.Element].
292 */
293 Location _locationFromElement(engine.Element element) {
294 engine.AnalysisContext context = element.context;
295 engine.Source source = element.source;
296 String name = element.displayName;
297 int offset = element.nameOffset;
298 int length = name != null ? name.length : 0;
299 if (element is engine.CompilationUnitElement) {
300 offset = 0;
301 length = 0;
302 }
303 engine.SourceRange range = new engine.SourceRange(offset, length);
304 return _locationForArgs(context, source, range);
305 }
306
307 /**
308 * Creates a new [Location] for the given [engine.SearchMatch].
309 */
310 Location _locationFromMatch(engine.SearchMatch match) {
311 engine.Element enclosingElement = match.element;
312 return _locationForArgs(
313 enclosingElement.context,
314 enclosingElement.source,
315 match.sourceRange);
316 }
317
318 /**
319 * Creates a new [Location] for the given [engine.AstNode].
320 */
321 Location _locationFromNode(engine.AstNode node) {
322 engine.CompilationUnit unit =
323 node.getAncestor((node) => node is engine.CompilationUnit);
324 engine.CompilationUnitElement unitElement = unit.element;
325 engine.AnalysisContext context = unitElement.context;
326 engine.Source source = unitElement.source;
327 engine.SourceRange range = new engine.SourceRange(node.offset, node.length);
328 return _locationForArgs(context, source, range);
329 }
330
331 /**
332 * Creates a new [Location] for the given [engine.CompilationUnit].
333 */
334 Location _locationFromUnit(engine.CompilationUnit unit,
335 engine.SourceRange range) {
336 engine.CompilationUnitElement unitElement = unit.element;
337 engine.AnalysisContext context = unitElement.context;
338 engine.Source source = unitElement.source;
339 return _locationForArgs(context, source, range);
340 }
341
342 /**
343 * Compare the maps [mapA] and [mapB], using [valueEqual] to compare map
344 * values.
345 */
346 bool _mapEqual(Map mapA, Map mapB, bool valueEqual(a, b)) {
347 if (mapA.length != mapB.length) {
348 return false;
349 }
350 for (var key in mapA.keys) {
351 if (!mapB.containsKey(key)) {
352 return false;
353 }
354 if (!valueEqual(mapA[key], mapB[key])) {
355 return false;
356 }
357 }
358 return true;
359 }
360
361 RefactoringProblemSeverity
362 _maxRefactoringProblemSeverity(RefactoringProblemSeverity a,
363 RefactoringProblemSeverity b) {
364 if (b == null) {
365 return a;
366 }
367 if (a == null) {
368 return b;
369 } else if (a == RefactoringProblemSeverity.INFO) {
370 return b;
371 } else if (a == RefactoringProblemSeverity.WARNING) {
372 if (b == RefactoringProblemSeverity.ERROR ||
373 b == RefactoringProblemSeverity.FATAL) {
374 return b;
375 }
376 } else if (a == RefactoringProblemSeverity.ERROR) {
377 if (b == RefactoringProblemSeverity.FATAL) {
378 return b;
379 }
380 }
381 return a;
382 }
383
384 /**
385 * Create an OverriddenMember based on an element from the analyzer engine.
386 */
387 OverriddenMember _overriddenMemberFromEngine(engine.Element member) {
388 Element element = elementFromEngine(member);
389 String className = member.enclosingElement.displayName;
390 return new OverriddenMember(element, className);
391 }
392
393
394 /**
395 * Create a SearchResultKind based on a value from the search engine.
396 */
397 SearchResultKind _searchResultKindFromEngine(engine.MatchKind kind) {
398 if (kind == engine.MatchKind.DECLARATION) {
399 return SearchResultKind.DECLARATION;
400 }
401 if (kind == engine.MatchKind.READ) {
402 return SearchResultKind.READ;
403 }
404 if (kind == engine.MatchKind.READ_WRITE) {
405 return SearchResultKind.READ_WRITE;
406 }
407 if (kind == engine.MatchKind.WRITE) {
408 return SearchResultKind.WRITE;
409 }
410 if (kind == engine.MatchKind.INVOCATION) {
411 return SearchResultKind.INVOCATION;
412 }
413 if (kind == engine.MatchKind.REFERENCE) {
414 return SearchResultKind.REFERENCE;
415 }
416 return SearchResultKind.UNKNOWN;
417 }
418
419
420 /**
421 * Type of callbacks used to decode parts of JSON objects. [jsonPath] is a
422 * string describing the part of the JSON object being decoded, and [value] is
423 * the part to decode.
424 */
425 typedef Object JsonDecoderCallback(String jsonPath, Object value);
426
427 /**
428 * Base class for decoding JSON objects. The derived class must implement
429 * error reporting logic.
430 */
431 abstract class JsonDecoder {
432 /**
433 * Create an exception to throw if the JSON object at [jsonPath] fails to
434 * match the API definition of [expected].
435 */
436 dynamic mismatch(String jsonPath, String expected);
437
438 /**
439 * Create an exception to throw if the JSON object at [jsonPath] is missing
440 * the key [key].
441 */
442 dynamic missingKey(String jsonPath, String key);
443
444 /**
445 * Decode a JSON object that is expected to be a boolean. The strings "true"
446 * and "false" are also accepted.
447 */
448 bool _decodeBool(String jsonPath, Object json) {
449 if (json is bool) {
450 return json;
451 } else if (json == 'true') {
452 return true;
453 } else if (json == 'false') {
454 return false;
455 }
456 throw mismatch(jsonPath, 'bool');
457 }
458
459 /**
460 * Decode a JSON object that is expected to be an integer. A string
461 * representation of an integer is also accepted.
462 */
463 int _decodeInt(String jsonPath, Object json) {
464 if (json is int) {
465 return json;
466 } else if (json is String) {
467 return int.parse(json, onError: (String value) {
468 throw mismatch(jsonPath, 'int');
469 });
470 }
471 throw mismatch(jsonPath, 'int');
472 }
473
474 /**
475 * Decode a JSON object that is expected to be a List. [decoder] is used to
476 * decode the items in the list.
477 */
478 List _decodeList(String jsonPath, Object json,
479 [JsonDecoderCallback decoder]) {
480 if (json == null) {
481 return [];
482 } else if (json is List) {
483 List result = [];
484 for (int i = 0; i < json.length; i++) {
485 result.add(decoder('$jsonPath[$i]', json[i]));
486 }
487 return result;
488 } else {
489 throw mismatch(jsonPath, 'List');
490 }
491 }
492
493 /**
494 * Decode a JSON object that is expected to be a Map. [keyDecoder] is used
495 * to decode the keys, and [valueDecoder] is used to decode the values.
496 */
497 Map _decodeMap(String jsonPath, Object json, {JsonDecoderCallback keyDecoder,
498 JsonDecoderCallback valueDecoder}) {
499 if (json == null) {
500 return {};
501 } else if (json is Map) {
502 Map result = {};
503 json.forEach((String key, value) {
504 Object decodedKey;
505 if (keyDecoder != null) {
506 decodedKey = keyDecoder('$jsonPath.key', key);
507 } else {
508 decodedKey = key;
509 }
510 if (valueDecoder != null) {
511 value = valueDecoder('$jsonPath[${JSON.encode(key)}]', value);
512 }
513 result[decodedKey] = value;
514 });
515 return result;
516 } else {
517 throw mismatch(jsonPath, 'Map');
518 }
519 }
520
521 /**
522 * Decode a JSON object that is expected to be a string.
523 */
524 String _decodeString(String jsonPath, Object json) {
525 if (json is String) {
526 return json;
527 } else {
528 throw mismatch(jsonPath, 'String');
529 }
530 }
531
532 /**
533 * Decode a JSON object that is expected to be one of several choices,
534 * where the choices are disambiguated by the contents of the field [field].
535 * [decoders] is a map from each possible string in the field to the decoder
536 * that should be used to decode the JSON object.
537 */
538 Object _decodeUnion(String jsonPath, Map json, String field, Map<String,
539 JsonDecoderCallback> decoders) {
540 if (json is Map) {
541 if (!json.containsKey(field)) {
542 throw missingKey(jsonPath, field);
543 }
544 var disambiguatorPath = '$jsonPath[${JSON.encode(field)}]';
545 String disambiguator = _decodeString(disambiguatorPath, json[field]);
546 if (!decoders.containsKey(disambiguator)) {
547 throw mismatch(disambiguatorPath, 'One of: ${decoders.keys.toList()}');
548 }
549 return decoders[disambiguator](jsonPath, json);
550 } else {
551 throw mismatch(jsonPath, 'Map');
552 }
553 }
554 }
555
556
557 /**
558 * Instances of the class [Notification] represent a notification from the
559 * server about an event that occurred.
560 */
561 class Notification {
562 /**
563 * The name of the JSON attribute containing the name of the event that
564 * triggered the notification.
565 */
566 static const String EVENT = 'event';
567
568 /**
569 * The name of the JSON attribute containing the result values.
570 */
571 static const String PARAMS = 'params';
572
573 /**
574 * The name of the event that triggered the notification.
575 */
576 final String event;
577
578 /**
579 * A table mapping the names of notification parameters to their values, or
580 * null if there are no notification parameters.
581 */
582 Map<String, Object> _params;
583
584 /**
585 * Initialize a newly created [Notification] to have the given [event] name.
586 * If [_params] is provided, it will be used as the params; otherwise no
587 * params will be used.
588 */
589 Notification(this.event, [this._params]);
590
591 /**
592 * Initialize a newly created instance based upon the given JSON data
593 */
594 factory Notification.fromJson(Map<String, Object> json) {
595 return new Notification(json[Notification.EVENT],
596 json[Notification.PARAMS]);
597 }
598
599 /**
600 * Return a table representing the structure of the Json object that will be
601 * sent to the client to represent this response.
602 */
603 Map<String, Object> toJson() {
604 Map<String, Object> jsonObject = {};
605 jsonObject[EVENT] = event;
606 if (_params != null) {
607 jsonObject[PARAMS] = _params;
608 }
609 return jsonObject;
610 }
611 }
612
11 613
12 /** 614 /**
13 * Instances of the class [Request] represent a request that was received. 615 * Instances of the class [Request] represent a request that was received.
14 */ 616 */
15 class Request { 617 class Request {
16 /** 618 /**
17 * The name of the JSON attribute containing the id of the request. 619 * The name of the JSON attribute containing the id of the request.
18 */ 620 */
19 static const String ID = 'id'; 621 static const String ID = 'id';
20 622
(...skipping 13 matching lines...) Expand all
34 final String id; 636 final String id;
35 637
36 /** 638 /**
37 * The method being requested. 639 * The method being requested.
38 */ 640 */
39 final String method; 641 final String method;
40 642
41 /** 643 /**
42 * A table mapping the names of request parameters to their values. 644 * A table mapping the names of request parameters to their values.
43 */ 645 */
44 final Map<String, Object> params; 646 final Map<String, Object> _params;
45
46 /**
47 * A decoder that can be used to decode strings into JSON objects.
48 */
49 static const JsonDecoder DECODER = const JsonDecoder(null);
50 647
51 /** 648 /**
52 * Initialize a newly created [Request] to have the given [id] and [method] 649 * Initialize a newly created [Request] to have the given [id] and [method]
53 * name. If [params] is supplied, it is used as the "params" map for the 650 * name. If [params] is supplied, it is used as the "params" map for the
54 * request. Otherwise an empty "params" map is allocated. 651 * request. Otherwise an empty "params" map is allocated.
55 */ 652 */
56 Request(this.id, this.method, [Map<String, Object> params]) 653 Request(this.id, this.method, [Map<String, Object> params])
57 : params = params != null ? params : new HashMap<String, Object>(); 654 : _params = params != null ? params : new HashMap<String, Object>();
58 655
59 /** 656 /**
60 * Return a request parsed from the given [data], or `null` if the [data] is 657 * Return a request parsed from the given [data], or `null` if the [data] is
61 * not a valid json representation of a request. The [data] is expected to 658 * not a valid json representation of a request. The [data] is expected to
62 * have the following format: 659 * have the following format:
63 * 660 *
64 * { 661 * {
65 * 'id': String, 662 * 'id': String,
66 * 'method': methodName, 663 * 'method': methodName,
67 * 'params': { 664 * 'params': {
68 * paramter_name: value 665 * paramter_name: value
69 * } 666 * }
70 * } 667 * }
71 * 668 *
72 * where the parameters are optional and can contain any number of name/value 669 * where the parameters are optional and can contain any number of name/value
73 * pairs. 670 * pairs.
74 */ 671 */
75 factory Request.fromString(String data) { 672 factory Request.fromString(String data) {
76 try { 673 try {
77 var result = DECODER.convert(data); 674 var result = JSON.decode(data);
78 if (result is! Map) { 675 if (result is! Map) {
79 return null; 676 return null;
80 } 677 }
81 var id = result[Request.ID]; 678 var id = result[Request.ID];
82 var method = result[Request.METHOD]; 679 var method = result[Request.METHOD];
83 if (id is! String || method is! String) { 680 if (id is! String || method is! String) {
84 return null; 681 return null;
85 } 682 }
86 var params = result[Request.PARAMS]; 683 var params = result[Request.PARAMS];
87 if (params is Map || params == null) { 684 if (params is Map || params == null) {
88 return new Request(id, method, params); 685 return new Request(id, method, params);
89 } else { 686 } else {
90 return null; 687 return null;
91 } 688 }
92 } catch (exception) { 689 } catch (exception) {
93 return null; 690 return null;
94 } 691 }
95 } 692 }
96 693
97 /** 694 /**
98 * Return a table representing the structure of the Json object that will be 695 * Return a table representing the structure of the Json object that will be
99 * sent to the client to represent this response. 696 * sent to the client to represent this response.
100 */ 697 */
101 Map<String, Object> toJson() { 698 Map<String, Object> toJson() {
102 Map<String, Object> jsonObject = new HashMap<String, Object>(); 699 Map<String, Object> jsonObject = new HashMap<String, Object>();
103 jsonObject[ID] = id; 700 jsonObject[ID] = id;
104 jsonObject[METHOD] = method; 701 jsonObject[METHOD] = method;
105 if (params.isNotEmpty) { 702 if (_params.isNotEmpty) {
106 jsonObject[PARAMS] = params; 703 jsonObject[PARAMS] = _params;
107 } 704 }
108 return jsonObject; 705 return jsonObject;
109 } 706 }
110 } 707 }
111 708
112 /** 709 /**
113 * Instances of the class [Response] represent a response to a request. 710 * JsonDecoder for decoding requests. Errors are reporting by throwing a
711 * [RequestFailure].
114 */ 712 */
115 class Response { 713 class RequestDecoder extends JsonDecoder {
116 /** 714 /**
117 * The [Response] instance that is returned when a real [Response] cannot 715 * The request being deserialized.
118 * be provided at the moment.
119 */ 716 */
120 static final Response DELAYED_RESPONSE = new Response('DELAYED_RESPONSE'); 717 final Request _request;
121 718
122 /** 719 RequestDecoder(this._request);
123 * The name of the JSON attribute containing the id of the request for which
124 * this is a response.
125 */
126 static const String ID = 'id';
127 720
128 /** 721 @override
129 * The name of the JSON attribute containing the error message. 722 dynamic mismatch(String jsonPath, String expected) {
130 */ 723 return new RequestFailure(
131 static const String ERROR = 'error'; 724 new Response.invalidParameter(_request, jsonPath, 'be $expected'));
132
133 /**
134 * The name of the JSON attribute containing the result values.
135 */
136 static const String RESULT = 'result';
137
138 /**
139 * The unique identifier used to identify the request that this response is
140 * associated with.
141 */
142 final String id;
143
144 /**
145 * The error that was caused by attempting to handle the request, or `null` if
146 * there was no error.
147 */
148 final RequestError error;
149
150 /**
151 * A table mapping the names of result fields to their values. Should be
152 * null if there is no result to send.
153 */
154 Map<String, Object> result;
155
156 /**
157 * Initialize a newly created instance to represent a response to a request
158 * with the given [id]. If [result] is provided, it will be used as the
159 * result; otherwise an empty result will be used. If an [error] is provided
160 * then the response will represent an error condition.
161 */
162 Response(this.id, {this.result, this.error});
163
164 /**
165 * Initialize a newly created instance to represent an error condition caused
166 * by a [request] referencing a context that does not exist.
167 */
168 Response.contextDoesNotExist(Request request)
169 : this(request.id, error: new RequestError('NONEXISTENT_CONTEXT', 'Context d oes not exist'));
170
171 /**
172 * Initialize a newly created instance to represent an error condition caused
173 * by a [request] that had invalid parameter. [path] is the path to the
174 * invalid parameter, in Javascript notation (e.g. "foo.bar" means that the
175 * parameter "foo" contained a key "bar" whose value was the wrong type).
176 * [expectation] is a description of the type of data that was expected.
177 */
178 Response.invalidParameter(Request request, String path, String expectation)
179 : this(request.id, error: new RequestError('INVALID_PARAMETER',
180 "Expected parameter $path to $expectation"));
181
182 /**
183 * Initialize a newly created instance to represent an error condition caused
184 * by a malformed request.
185 */
186 Response.invalidRequestFormat()
187 : this('', error: new RequestError('INVALID_REQUEST', 'Invalid request'));
188
189 /**
190 * Initialize a newly created instance to represent an error condition caused
191 * by a [request] that does not have a required parameter.
192 */
193 Response.missingRequiredParameter(Request request, String parameterName)
194 : this(request.id, error: new RequestError('MISSING_PARAMETER', 'Missing req uired parameter: $parameterName'));
195
196 /**
197 * Initialize a newly created instance to represent an error condition caused
198 * by a [request] that takes a set of analysis options but for which an
199 * unknown analysis option was provided.
200 */
201 Response.unknownAnalysisOption(Request request, String optionName)
202 : this(request.id, error: new RequestError('UNKNOWN_ANALYSIS_OPTION', 'Unkno wn analysis option: "$optionName"'));
203
204 /**
205 * Initialize a newly created instance to represent an error condition caused
206 * by a [request] that cannot be handled by any known handlers.
207 */
208 Response.unknownRequest(Request request)
209 : this(request.id, error: new RequestError('UNKNOWN_REQUEST', 'Unknown reque st'));
210
211 Response.contextAlreadyExists(Request request)
212 : this(request.id, error: new RequestError('CONTENT_ALREADY_EXISTS', 'Contex t already exists'));
213
214 Response.unsupportedFeature(String requestId, String message)
215 : this(requestId, error: new RequestError('UNSUPPORTED_FEATURE', message));
216
217 /**
218 * Initialize a newly created instance to represent an error condition caused
219 * by a `analysis.setSubscriptions` [request] that includes an unknown
220 * analysis service name.
221 */
222 Response.unknownAnalysisService(Request request, String name)
223 : this(request.id, error: new RequestError('UNKNOWN_ANALYSIS_SERVICE', 'Unkn own analysis service: "$name"'));
224
225 /**
226 * Initialize a newly created instance to represent an error condition caused
227 * by a `analysis.setPriorityFiles` [request] that includes one or more files
228 * that are not being analyzed.
229 */
230 Response.unanalyzedPriorityFiles(Request request, String fileNames)
231 : this(request.id, error: new RequestError('UNANALYZED_PRIORITY_FILES', "Una nalyzed files cannot be a priority: '$fileNames'"));
232
233 /**
234 * Initialize a newly created instance to represent an error condition caused
235 * by a `analysis.updateOptions` [request] that includes an unknown analysis
236 * option.
237 */
238 Response.unknownOptionName(Request request, String optionName)
239 : this(request.id, error: new RequestError('UNKNOWN_OPTION_NAME', 'Unknown a nalysis option: "$optionName"'));
240
241 /**
242 * Initialize a newly created instance to represent an error condition caused
243 * by an error during `analysis.getErrors`.
244 */
245 Response.getErrorsError(Request request, String message,
246 Map<String, Object> result)
247 : this(
248 request.id,
249 error: new RequestError('GET_ERRORS_ERROR', 'Error during `analysis.getE rrors`: $message.'),
250 result: result);
251
252 /**
253 * Initialize a newly created instance based upon the given JSON data
254 */
255 factory Response.fromJson(Map<String, Object> json) {
256 try {
257 Object id = json[Response.ID];
258 if (id is! String) {
259 return null;
260 }
261 Object error = json[Response.ERROR];
262 RequestError decodedError;
263 if (error is Map) {
264 decodedError = new RequestError.fromJson(error);
265 }
266 Object result = json[Response.RESULT];
267 Map<String, Object> decodedResult;
268 if (result is Map) {
269 decodedResult = result;
270 }
271 return new Response(id, error: decodedError,
272 result: decodedResult);
273 } catch (exception) {
274 return null;
275 }
276 } 725 }
277 726
278 /** 727 @override
279 * Return a table representing the structure of the Json object that will be 728 dynamic missingKey(String jsonPath, String key) {
280 * sent to the client to represent this response. 729 return new RequestFailure(
281 */ 730 new Response.invalidParameter(
282 Map<String, Object> toJson() { 731 _request,
283 Map<String, Object> jsonObject = new HashMap<String, Object>(); 732 jsonPath,
284 jsonObject[ID] = id; 733 'contain key ${JSON.encode(key)}'));
285 if (error != null) {
286 jsonObject[ERROR] = error.toJson();
287 }
288 if (result != null) {
289 jsonObject[RESULT] = result;
290 }
291 return jsonObject;
292 } 734 }
293 } 735 }
294 736
295 /** 737 /**
296 * Instances of the class [RequestError] represent information about an error 738 * Instances of the class [RequestError] represent information about an error
297 * that occurred while attempting to respond to a [Request]. 739 * that occurred while attempting to respond to a [Request].
298 */ 740 */
299 class RequestError { 741 class RequestError {
300 /** 742 /**
301 * The name of the JSON attribute containing the code that uniquely identifies 743 * The name of the JSON attribute containing the code that uniquely identifies
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
372 * A table mapping the names of notification parameters to their values. 814 * A table mapping the names of notification parameters to their values.
373 */ 815 */
374 final Map<String, Object> data = new HashMap<String, Object>(); 816 final Map<String, Object> data = new HashMap<String, Object>();
375 817
376 /** 818 /**
377 * Initialize a newly created [Error] to have the given [code] and [message]. 819 * Initialize a newly created [Error] to have the given [code] and [message].
378 */ 820 */
379 RequestError(this.code, this.message); 821 RequestError(this.code, this.message);
380 822
381 /** 823 /**
382 * Initialize a newly created [Error] to indicate a parse error. Invalid JSON 824 * Initialize a newly created [Error] from the given JSON.
383 * was received by the server. An error occurred on the server while parsing
384 * the JSON text.
385 */ 825 */
386 RequestError.parseError() : this(CODE_PARSE_ERROR, "Parse error"); 826 factory RequestError.fromJson(Map<String, Object> json) {
827 try {
828 String code = json[RequestError.CODE];
829 String message = json[RequestError.MESSAGE];
830 Map<String, Object> data = json[RequestError.DATA];
831 RequestError requestError = new RequestError(code, message);
832 if (data != null) {
833 data.forEach((String key, Object value) {
834 requestError.setData(key, value);
835 });
836 }
837 return requestError;
838 } catch (exception) {
839 return null;
840 }
841 }
387 842
388 /** 843 /**
389 * Initialize a newly created [Error] to indicate that the analysis server 844 * Initialize a newly created [Error] to indicate an internal error.
390 * has already been started (and hence won't accept new connections).
391 */ 845 */
392 RequestError.serverAlreadyStarted() 846 RequestError.internalError() : this(CODE_INTERNAL_ERROR, "Internal error");
393 : this(CODE_SERVER_ALREADY_STARTED, "Server already started"); 847
848 /**
849 * Initialize a newly created [Error] to indicate one or more invalid
850 * parameters.
851 */
852 RequestError.invalidParameters() : this(CODE_INVALID_PARAMS, "Invalid paramete rs");
394 853
395 /** 854 /**
396 * Initialize a newly created [Error] to indicate an invalid request. The 855 * Initialize a newly created [Error] to indicate an invalid request. The
397 * JSON sent is not a valid [Request] object. 856 * JSON sent is not a valid [Request] object.
398 */ 857 */
399 RequestError.invalidRequest() : this(CODE_INVALID_REQUEST, "Invalid request"); 858 RequestError.invalidRequest() : this(CODE_INVALID_REQUEST, "Invalid request");
400 859
401 /** 860 /**
402 * Initialize a newly created [Error] to indicate that a method was not found. 861 * Initialize a newly created [Error] to indicate that a method was not found.
403 * Either the method does not exist or is not currently available. 862 * Either the method does not exist or is not currently available.
404 */ 863 */
405 RequestError.methodNotFound() : this(CODE_METHOD_NOT_FOUND, "Method not found" ); 864 RequestError.methodNotFound() : this(CODE_METHOD_NOT_FOUND, "Method not found" );
406 865
407 /** 866 /**
408 * Initialize a newly created [Error] to indicate one or more invalid 867 * Initialize a newly created [Error] to indicate a parse error. Invalid JSON
409 * parameters. 868 * was received by the server. An error occurred on the server while parsing
869 * the JSON text.
410 */ 870 */
411 RequestError.invalidParameters() : this(CODE_INVALID_PARAMS, "Invalid paramete rs"); 871 RequestError.parseError() : this(CODE_PARSE_ERROR, "Parse error");
412 872
413 /** 873 /**
414 * Initialize a newly created [Error] to indicate an internal error. 874 * Initialize a newly created [Error] to indicate that the analysis server
875 * has already been started (and hence won't accept new connections).
415 */ 876 */
416 RequestError.internalError() : this(CODE_INTERNAL_ERROR, "Internal error"); 877 RequestError.serverAlreadyStarted()
417 878 : this(CODE_SERVER_ALREADY_STARTED, "Server already started");
418 /**
419 * Initialize a newly created [Error] from the given JSON.
420 */
421 factory RequestError.fromJson(Map<String, Object> json) {
422 try {
423 String code = json[RequestError.CODE];
424 String message = json[RequestError.MESSAGE];
425 Map<String, Object> data = json[RequestError.DATA];
426 RequestError requestError = new RequestError(code, message);
427 if (data != null) {
428 data.forEach((String key, Object value) {
429 requestError.setData(key, value);
430 });
431 }
432 return requestError;
433 } catch (exception) {
434 return null;
435 }
436 }
437 879
438 /** 880 /**
439 * Return the value of the data with the given [name], or `null` if there is 881 * Return the value of the data with the given [name], or `null` if there is
440 * no such data associated with this error. 882 * no such data associated with this error.
441 */ 883 */
442 Object getData(String name) => data[name]; 884 Object getData(String name) => data[name];
443 885
444 /** 886 /**
445 * Set the value of the data with the given [name] to the given [value]. 887 * Set the value of the data with the given [name] to the given [value].
446 */ 888 */
(...skipping 12 matching lines...) Expand all
459 if (!data.isEmpty) { 901 if (!data.isEmpty) {
460 jsonObject[DATA] = data; 902 jsonObject[DATA] = data;
461 } 903 }
462 return jsonObject; 904 return jsonObject;
463 } 905 }
464 906
465 @override 907 @override
466 String toString() => toJson().toString(); 908 String toString() => toJson().toString();
467 } 909 }
468 910
469 /** 911
470 * Instances of the class [Notification] represent a notification from the 912 /**
471 * server about an event that occurred. 913 * Instances of the class [RequestFailure] represent an exception that occurred
472 */ 914 * during the handling of a request that requires that an error be returned to
473 class Notification { 915 * the client.
474 /** 916 */
475 * The name of the JSON attribute containing the name of the event that 917 class RequestFailure implements Exception {
476 * triggered the notification. 918 /**
477 */ 919 * The response to be returned as a result of the failure.
478 static const String EVENT = 'event'; 920 */
479 921 final Response response;
480 /** 922
481 * The name of the JSON attribute containing the result values. 923 /**
482 */ 924 * Initialize a newly created exception to return the given reponse.
483 static const String PARAMS = 'params'; 925 */
484 926 RequestFailure(this.response);
485 /**
486 * The name of the event that triggered the notification.
487 */
488 final String event;
489
490 /**
491 * A table mapping the names of notification parameters to their values, or
492 * null if there are no notification parameters.
493 */
494 Map<String, Object> params;
495
496 /**
497 * Initialize a newly created [Notification] to have the given [event] name.
498 * If [params] is provided, it will be used as the params; otherwise no
499 * params will be used.
500 */
501 Notification(this.event, [this.params]);
502
503 /**
504 * Initialize a newly created instance based upon the given JSON data
505 */
506 factory Notification.fromJson(Map<String, Object> json) {
507 try {
508 String event = json[Notification.EVENT];
509 Object params = json[Notification.PARAMS];
510 Notification notification = new Notification(event);
511 if (params is Map) {
512 params.forEach((String key, Object value) {
513 notification.setParameter(key, value);
514 });
515 }
516 return notification;
517 } catch (exception) {
518 return null;
519 }
520 }
521
522 /**
523 * Set the value of the parameter with the given [name] to the given [value].
524 */
525 void setParameter(String name, Object value) {
526 if (params == null) {
527 params = new HashMap<String, Object>();
528 }
529 params[name] = _toJson(value);
530 }
531
532 /**
533 * Return a table representing the structure of the Json object that will be
534 * sent to the client to represent this response.
535 */
536 Map<String, Object> toJson() {
537 Map<String, Object> jsonObject = {};
538 jsonObject[EVENT] = event;
539 if (params != null) {
540 jsonObject[PARAMS] = params;
541 }
542 return jsonObject;
543 }
544 } 927 }
545 928
546 /** 929 /**
547 * Instances of the class [RequestHandler] implement a handler that can handle 930 * Instances of the class [RequestHandler] implement a handler that can handle
548 * requests and produce responses for them. 931 * requests and produce responses for them.
549 */ 932 */
550 abstract class RequestHandler { 933 abstract class RequestHandler {
551 /** 934 /**
552 * Attempt to handle the given [request]. If the request is not recognized by 935 * Attempt to handle the given [request]. If the request is not recognized by
553 * this handler, return `null` so that other handlers will be given a chance 936 * this handler, return `null` so that other handlers will be given a chance
554 * to handle it. Otherwise, return the response that should be passed back to 937 * to handle it. Otherwise, return the response that should be passed back to
555 * the client. 938 * the client.
556 */ 939 */
557 Response handleRequest(Request request); 940 Response handleRequest(Request request);
558 } 941 }
559 942
560 /** 943 /**
561 * Instances of the class [RequestFailure] represent an exception that occurred 944 * Instances of the class [Response] represent a response to a request.
562 * during the handling of a request that requires that an error be returned to 945 */
563 * the client. 946 class Response {
564 */ 947 /**
565 class RequestFailure implements Exception { 948 * The [Response] instance that is returned when a real [Response] cannot
566 /** 949 * be provided at the moment.
567 * The response to be returned as a result of the failure. 950 */
568 */ 951 static final Response DELAYED_RESPONSE = new Response('DELAYED_RESPONSE');
569 final Response response; 952
570 953 /**
571 /** 954 * The name of the JSON attribute containing the id of the request for which
572 * Initialize a newly created exception to return the given reponse. 955 * this is a response.
573 */ 956 */
574 RequestFailure(this.response); 957 static const String ID = 'id';
575 } 958
576 959 /**
577 /** 960 * The name of the JSON attribute containing the error message.
578 * Returns a JSON presention of [value]. 961 */
579 */ 962 static const String ERROR = 'error';
580 _toJson(Object value) { 963
581 if (value is HasToJson) { 964 /**
582 return value.toJson(); 965 * The name of the JSON attribute containing the result values.
583 } 966 */
584 if (value is Iterable) { 967 static const String RESULT = 'result';
585 return value.map((item) => _toJson(item)).toList(); 968
586 } 969 /**
587 return value; 970 * The unique identifier used to identify the request that this response is
588 } 971 * associated with.
972 */
973 final String id;
974
975 /**
976 * The error that was caused by attempting to handle the request, or `null` if
977 * there was no error.
978 */
979 final RequestError error;
980
981 /**
982 * A table mapping the names of result fields to their values. Should be
983 * null if there is no result to send.
984 */
985 Map<String, Object> _result;
986
987 /**
988 * Initialize a newly created instance to represent a response to a request
989 * with the given [id]. If [_result] is provided, it will be used as the
990 * result; otherwise an empty result will be used. If an [error] is provided
991 * then the response will represent an error condition.
992 */
993 Response(this.id, {Map<String, Object> result, this.error})
994 : _result = result;
995
996 Response.contextAlreadyExists(Request request)
997 : this(request.id, error: new RequestError('CONTENT_ALREADY_EXISTS', 'Contex t already exists'));
998
999 /**
1000 * Initialize a newly created instance to represent an error condition caused
1001 * by a [request] referencing a context that does not exist.
1002 */
1003 Response.contextDoesNotExist(Request request)
1004 : this(request.id, error: new RequestError('NONEXISTENT_CONTEXT', 'Context d oes not exist'));
1005
1006 /**
1007 * Initialize a newly created instance based upon the given JSON data
1008 */
1009 factory Response.fromJson(Map<String, Object> json) {
1010 try {
1011 Object id = json[Response.ID];
1012 if (id is! String) {
1013 return null;
1014 }
1015 Object error = json[Response.ERROR];
1016 RequestError decodedError;
1017 if (error is Map) {
1018 decodedError = new RequestError.fromJson(error);
1019 }
1020 Object result = json[Response.RESULT];
1021 Map<String, Object> decodedResult;
1022 if (result is Map) {
1023 decodedResult = result;
1024 }
1025 return new Response(id, error: decodedError,
1026 result: decodedResult);
1027 } catch (exception) {
1028 return null;
1029 }
1030 }
1031
1032 /**
1033 * Initialize a newly created instance to represent an error condition caused
1034 * by an error during `analysis.getErrors`.
1035 */
1036 Response.getErrorsError(Request request, String message,
1037 Map<String, Object> result)
1038 : this(
1039 request.id,
1040 error: new RequestError('GET_ERRORS_ERROR', 'Error during `analysis.getE rrors`: $message.'),
1041 result: result);
1042
1043 /**
1044 * Initialize a newly created instance to represent an error condition caused
1045 * by a [request] that had invalid parameter. [path] is the path to the
1046 * invalid parameter, in Javascript notation (e.g. "foo.bar" means that the
1047 * parameter "foo" contained a key "bar" whose value was the wrong type).
1048 * [expectation] is a description of the type of data that was expected.
1049 */
1050 Response.invalidParameter(Request request, String path, String expectation)
1051 : this(request.id, error: new RequestError('INVALID_PARAMETER',
1052 "Expected parameter $path to $expectation"));
1053
1054 /**
1055 * Initialize a newly created instance to represent an error condition caused
1056 * by a malformed request.
1057 */
1058 Response.invalidRequestFormat()
1059 : this('', error: new RequestError('INVALID_REQUEST', 'Invalid request'));
1060
1061 /**
1062 * Initialize a newly created instance to represent an error condition caused
1063 * by a [request] that does not have a required parameter.
1064 */
1065 Response.missingRequiredParameter(Request request, String parameterName)
1066 : this(request.id, error: new RequestError('MISSING_PARAMETER', 'Missing req uired parameter: $parameterName'));
1067
1068 /**
1069 * Initialize a newly created instance to represent an error condition caused
1070 * by a `analysis.setPriorityFiles` [request] that includes one or more files
1071 * that are not being analyzed.
1072 */
1073 Response.unanalyzedPriorityFiles(Request request, String fileNames)
1074 : this(request.id, error: new RequestError('UNANALYZED_PRIORITY_FILES', "Una nalyzed files cannot be a priority: '$fileNames'"));
1075
1076 /**
1077 * Initialize a newly created instance to represent an error condition caused
1078 * by a [request] that takes a set of analysis options but for which an
1079 * unknown analysis option was provided.
1080 */
1081 Response.unknownAnalysisOption(Request request, String optionName)
1082 : this(request.id, error: new RequestError('UNKNOWN_ANALYSIS_OPTION', 'Unkno wn analysis option: "$optionName"'));
1083
1084 /**
1085 * Initialize a newly created instance to represent an error condition caused
1086 * by a `analysis.setSubscriptions` [request] that includes an unknown
1087 * analysis service name.
1088 */
1089 Response.unknownAnalysisService(Request request, String name)
1090 : this(request.id, error: new RequestError('UNKNOWN_ANALYSIS_SERVICE', 'Unkn own analysis service: "$name"'));
1091
1092 /**
1093 * Initialize a newly created instance to represent an error condition caused
1094 * by a `analysis.updateOptions` [request] that includes an unknown analysis
1095 * option.
1096 */
1097 Response.unknownOptionName(Request request, String optionName)
1098 : this(request.id, error: new RequestError('UNKNOWN_OPTION_NAME', 'Unknown a nalysis option: "$optionName"'));
1099
1100 /**
1101 * Initialize a newly created instance to represent an error condition caused
1102 * by a [request] that cannot be handled by any known handlers.
1103 */
1104 Response.unknownRequest(Request request)
1105 : this(request.id, error: new RequestError('UNKNOWN_REQUEST', 'Unknown reque st'));
1106
1107 Response.unsupportedFeature(String requestId, String message)
1108 : this(requestId, error: new RequestError('UNSUPPORTED_FEATURE', message));
1109
1110 /**
1111 * Return a table representing the structure of the Json object that will be
1112 * sent to the client to represent this response.
1113 */
1114 Map<String, Object> toJson() {
1115 Map<String, Object> jsonObject = new HashMap<String, Object>();
1116 jsonObject[ID] = id;
1117 if (error != null) {
1118 jsonObject[ERROR] = error.toJson();
1119 }
1120 if (_result != null) {
1121 jsonObject[RESULT] = _result;
1122 }
1123 return jsonObject;
1124 }
1125 }
1126
1127 /**
1128 * JsonDecoder for decoding responses from the server. This is intended to be
1129 * used only for testing. Errors are reported using bare [Exception] objects.
1130 */
1131 class ResponseDecoder extends JsonDecoder {
1132 @override
1133 dynamic mismatch(String jsonPath, String expected) {
1134 return new Exception('Expected $expected at $jsonPath');
1135 }
1136
1137 @override
1138 dynamic missingKey(String jsonPath, String key) {
1139 return new Exception('Missing key $key at $jsonPath');
1140 }
1141 }
1142
1143 /**
1144 * Jenkins hash function, optimized for small integers. Borrowed from
1145 * sdk/lib/math/jenkins_smi_hash.dart.
1146 *
1147 * TODO(paulberry): Move to somewhere that can be shared with other code.
1148 */
1149 class _JenkinsSmiHash {
1150 static int combine(int hash, int value) {
1151 hash = 0x1fffffff & (hash + value);
1152 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
1153 return hash ^ (hash >> 6);
1154 }
1155
1156 static int finish(int hash) {
1157 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
1158 hash = hash ^ (hash >> 11);
1159 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
1160 }
1161
1162 static int hash2(a, b) => finish(combine(combine(0, a), b));
1163
1164 static int hash4(a, b, c, d) =>
1165 finish(combine(combine(combine(combine(0, a), b), c), d));
1166 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/lib/src/operation/operation_analysis.dart ('k') | pkg/analysis_server/lib/src/protocol2.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698