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

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

Issue 1398293002: Move the wire protocol support into the public API (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Add missed files Created 5 years, 2 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
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library protocol;
6
7 import 'dart:collection';
8 import 'dart:convert';
9
10 part 'generated_protocol.dart';
11
12 final Map<String, RefactoringKind> REQUEST_ID_REFACTORING_KINDS =
13 new HashMap<String, RefactoringKind>();
14
15 /**
16 * Translate the input [map], applying [keyCallback] to all its keys, and
17 * [valueCallback] to all its values.
18 */
19 mapMap(Map map, {dynamic keyCallback(key), dynamic valueCallback(value)}) {
20 Map result = {};
21 map.forEach((key, value) {
22 if (keyCallback != null) {
23 key = keyCallback(key);
24 }
25 if (valueCallback != null) {
26 value = valueCallback(value);
27 }
28 result[key] = value;
29 });
30 return result;
31 }
32
33 /**
34 * Adds the given [sourceEdits] to the list in [sourceFileEdit].
35 */
36 void _addAllEditsForSource(
37 SourceFileEdit sourceFileEdit, Iterable<SourceEdit> edits) {
38 edits.forEach(sourceFileEdit.add);
39 }
40
41 /**
42 * Adds the given [sourceEdit] to the list in [sourceFileEdit].
43 */
44 void _addEditForSource(SourceFileEdit sourceFileEdit, SourceEdit sourceEdit) {
45 List<SourceEdit> edits = sourceFileEdit.edits;
46 int index = 0;
47 while (index < edits.length && edits[index].offset > sourceEdit.offset) {
48 index++;
49 }
50 edits.insert(index, sourceEdit);
51 }
52
53 /**
54 * Adds [edit] to the [FileEdit] for the given [file].
55 */
56 void _addEditToSourceChange(
57 SourceChange change, String file, int fileStamp, SourceEdit edit) {
58 SourceFileEdit fileEdit = change.getFileEdit(file);
59 if (fileEdit == null) {
60 fileEdit = new SourceFileEdit(file, fileStamp);
61 change.addFileEdit(fileEdit);
62 }
63 fileEdit.add(edit);
64 }
65
66 /**
67 * Get the result of applying the edit to the given [code]. Access via
68 * SourceEdit.apply().
69 */
70 String _applyEdit(String code, SourceEdit edit) {
71 if (edit.length < 0) {
72 throw new RangeError('length is negative');
73 }
74 return code.replaceRange(edit.offset, edit.end, edit.replacement);
75 }
76
77 /**
78 * Get the result of applying a set of [edits] to the given [code]. Edits
79 * are applied in the order they appear in [edits]. Access via
80 * SourceEdit.applySequence().
81 */
82 String _applySequence(String code, Iterable<SourceEdit> edits) {
83 edits.forEach((SourceEdit edit) {
84 code = edit.apply(code);
85 });
86 return code;
87 }
88
89 /**
90 * Returns the [FileEdit] for the given [file], maybe `null`.
91 */
92 SourceFileEdit _getChangeFileEdit(SourceChange change, String file) {
93 for (SourceFileEdit fileEdit in change.edits) {
94 if (fileEdit.file == file) {
95 return fileEdit;
96 }
97 }
98 return null;
99 }
100
101 /**
102 * Compare the lists [listA] and [listB], using [itemEqual] to compare
103 * list elements.
104 */
105 bool _listEqual(List listA, List listB, bool itemEqual(a, b)) {
106 if (listA == null) {
107 return listB == null;
108 }
109 if (listB == null) {
110 return false;
111 }
112 if (listA.length != listB.length) {
113 return false;
114 }
115 for (int i = 0; i < listA.length; i++) {
116 if (!itemEqual(listA[i], listB[i])) {
117 return false;
118 }
119 }
120 return true;
121 }
122
123 /**
124 * Compare the maps [mapA] and [mapB], using [valueEqual] to compare map
125 * values.
126 */
127 bool _mapEqual(Map mapA, Map mapB, bool valueEqual(a, b)) {
128 if (mapA == null) {
129 return mapB == null;
130 }
131 if (mapB == null) {
132 return false;
133 }
134 if (mapA.length != mapB.length) {
135 return false;
136 }
137 for (var key in mapA.keys) {
138 if (!mapB.containsKey(key)) {
139 return false;
140 }
141 if (!valueEqual(mapA[key], mapB[key])) {
142 return false;
143 }
144 }
145 return true;
146 }
147
148 RefactoringProblemSeverity _maxRefactoringProblemSeverity(
149 RefactoringProblemSeverity a, RefactoringProblemSeverity b) {
150 if (b == null) {
151 return a;
152 }
153 if (a == null) {
154 return b;
155 } else if (a == RefactoringProblemSeverity.INFO) {
156 return b;
157 } else if (a == RefactoringProblemSeverity.WARNING) {
158 if (b == RefactoringProblemSeverity.ERROR ||
159 b == RefactoringProblemSeverity.FATAL) {
160 return b;
161 }
162 } else if (a == RefactoringProblemSeverity.ERROR) {
163 if (b == RefactoringProblemSeverity.FATAL) {
164 return b;
165 }
166 }
167 return a;
168 }
169
170 /**
171 * Create a [RefactoringFeedback] corresponding the given [kind].
172 */
173 RefactoringFeedback _refactoringFeedbackFromJson(
174 JsonDecoder jsonDecoder, String jsonPath, Object json, Map feedbackJson) {
175 RefactoringKind kind = jsonDecoder.refactoringKind;
176 if (kind == RefactoringKind.EXTRACT_LOCAL_VARIABLE) {
177 return new ExtractLocalVariableFeedback.fromJson(
178 jsonDecoder, jsonPath, json);
179 }
180 if (kind == RefactoringKind.EXTRACT_METHOD) {
181 return new ExtractMethodFeedback.fromJson(jsonDecoder, jsonPath, json);
182 }
183 if (kind == RefactoringKind.INLINE_LOCAL_VARIABLE) {
184 return new InlineLocalVariableFeedback.fromJson(
185 jsonDecoder, jsonPath, json);
186 }
187 if (kind == RefactoringKind.INLINE_METHOD) {
188 return new InlineMethodFeedback.fromJson(jsonDecoder, jsonPath, json);
189 }
190 if (kind == RefactoringKind.RENAME) {
191 return new RenameFeedback.fromJson(jsonDecoder, jsonPath, json);
192 }
193 return null;
194 }
195
196 /**
197 * Create a [RefactoringOptions] corresponding the given [kind].
198 */
199 RefactoringOptions _refactoringOptionsFromJson(JsonDecoder jsonDecoder,
200 String jsonPath, Object json, RefactoringKind kind) {
201 if (kind == RefactoringKind.EXTRACT_LOCAL_VARIABLE) {
202 return new ExtractLocalVariableOptions.fromJson(
203 jsonDecoder, jsonPath, json);
204 }
205 if (kind == RefactoringKind.EXTRACT_METHOD) {
206 return new ExtractMethodOptions.fromJson(jsonDecoder, jsonPath, json);
207 }
208 if (kind == RefactoringKind.INLINE_METHOD) {
209 return new InlineMethodOptions.fromJson(jsonDecoder, jsonPath, json);
210 }
211 if (kind == RefactoringKind.MOVE_FILE) {
212 return new MoveFileOptions.fromJson(jsonDecoder, jsonPath, json);
213 }
214 if (kind == RefactoringKind.RENAME) {
215 return new RenameOptions.fromJson(jsonDecoder, jsonPath, json);
216 }
217 return null;
218 }
219
220 /**
221 * Type of callbacks used to decode parts of JSON objects. [jsonPath] is a
222 * string describing the part of the JSON object being decoded, and [value] is
223 * the part to decode.
224 */
225 typedef Object JsonDecoderCallback(String jsonPath, Object value);
226
227 /**
228 * Instances of the class [DomainHandler] implement a [RequestHandler] and
229 * also startup and shutdown methods.
230 */
231 abstract class DomainHandler extends RequestHandler {
232 /**
233 * Perform any operations associated with the shutdown of the domain. It is
234 * not guaranteed that this method will be called. If it is, it will be
235 * called after the last [Request] has been made.
236 */
237 void shutdown() {}
238
239 /**
240 * Perform any operations associated with the startup of the domain. This
241 * will be called before the first [Request].
242 */
243 void startup() {}
244 }
245
246 /**
247 * Classes implementing [Enum] represent enumerated types in the protocol.
248 */
249 abstract class Enum {
250 /**
251 * The name of the enumerated value. This should match the name of the
252 * static getter which provides access to this enumerated value.
253 */
254 String get name;
255 }
256
257 /**
258 * Instances of the class [HasToJson] implement [toJson] method that returns
259 * a JSON presentation.
260 */
261 abstract class HasToJson {
262 /**
263 * Returns a JSON presentation of the object.
264 */
265 Map<String, Object> toJson();
266 }
267
268 /**
269 * Base class for decoding JSON objects. The derived class must implement
270 * error reporting logic.
271 */
272 abstract class JsonDecoder {
273 /**
274 * Retrieve the RefactoringKind that should be assumed when decoding
275 * refactoring feedback objects, or null if no refactoring feedback object is
276 * expected to be encountered.
277 */
278 RefactoringKind get refactoringKind;
279
280 /**
281 * Create an exception to throw if the JSON object at [jsonPath] fails to
282 * match the API definition of [expected].
283 */
284 dynamic mismatch(String jsonPath, String expected, [Object actual]);
285
286 /**
287 * Create an exception to throw if the JSON object at [jsonPath] is missing
288 * the key [key].
289 */
290 dynamic missingKey(String jsonPath, String key);
291
292 /**
293 * Decode a JSON object that is expected to be a boolean. The strings "true"
294 * and "false" are also accepted.
295 */
296 bool _decodeBool(String jsonPath, Object json) {
297 if (json is bool) {
298 return json;
299 } else if (json == 'true') {
300 return true;
301 } else if (json == 'false') {
302 return false;
303 }
304 throw mismatch(jsonPath, 'bool', json);
305 }
306
307 /**
308 * Decode a JSON object that is expected to be an integer. A string
309 * representation of an integer is also accepted.
310 */
311 int _decodeInt(String jsonPath, Object json) {
312 if (json is int) {
313 return json;
314 } else if (json is String) {
315 return int.parse(json, onError: (String value) {
316 throw mismatch(jsonPath, 'int', json);
317 });
318 }
319 throw mismatch(jsonPath, 'int', json);
320 }
321
322 /**
323 * Decode a JSON object that is expected to be a List. [decoder] is used to
324 * decode the items in the list.
325 */
326 List _decodeList(String jsonPath, Object json,
327 [JsonDecoderCallback decoder]) {
328 if (json == null) {
329 return [];
330 } else if (json is List) {
331 List result = [];
332 for (int i = 0; i < json.length; i++) {
333 result.add(decoder('$jsonPath[$i]', json[i]));
334 }
335 return result;
336 } else {
337 throw mismatch(jsonPath, 'List', json);
338 }
339 }
340
341 /**
342 * Decode a JSON object that is expected to be a Map. [keyDecoder] is used
343 * to decode the keys, and [valueDecoder] is used to decode the values.
344 */
345 Map _decodeMap(String jsonPath, Object json,
346 {JsonDecoderCallback keyDecoder, JsonDecoderCallback valueDecoder}) {
347 if (json == null) {
348 return {};
349 } else if (json is Map) {
350 Map result = {};
351 json.forEach((String key, value) {
352 Object decodedKey;
353 if (keyDecoder != null) {
354 decodedKey = keyDecoder('$jsonPath.key', key);
355 } else {
356 decodedKey = key;
357 }
358 if (valueDecoder != null) {
359 value = valueDecoder('$jsonPath[${JSON.encode(key)}]', value);
360 }
361 result[decodedKey] = value;
362 });
363 return result;
364 } else {
365 throw mismatch(jsonPath, 'Map', json);
366 }
367 }
368
369 /**
370 * Decode a JSON object that is expected to be a string.
371 */
372 String _decodeString(String jsonPath, Object json) {
373 if (json is String) {
374 return json;
375 } else {
376 throw mismatch(jsonPath, 'String', json);
377 }
378 }
379
380 /**
381 * Decode a JSON object that is expected to be one of several choices,
382 * where the choices are disambiguated by the contents of the field [field].
383 * [decoders] is a map from each possible string in the field to the decoder
384 * that should be used to decode the JSON object.
385 */
386 Object _decodeUnion(String jsonPath, Map json, String field,
387 Map<String, JsonDecoderCallback> decoders) {
388 if (json is Map) {
389 if (!json.containsKey(field)) {
390 throw missingKey(jsonPath, field);
391 }
392 var disambiguatorPath = '$jsonPath[${JSON.encode(field)}]';
393 String disambiguator = _decodeString(disambiguatorPath, json[field]);
394 if (!decoders.containsKey(disambiguator)) {
395 throw mismatch(
396 disambiguatorPath, 'One of: ${decoders.keys.toList()}', json);
397 }
398 return decoders[disambiguator](jsonPath, json);
399 } else {
400 throw mismatch(jsonPath, 'Map', json);
401 }
402 }
403 }
404
405 /**
406 * Instances of the class [Notification] represent a notification from the
407 * server about an event that occurred.
408 */
409 class Notification {
410 /**
411 * The name of the JSON attribute containing the name of the event that
412 * triggered the notification.
413 */
414 static const String EVENT = 'event';
415
416 /**
417 * The name of the JSON attribute containing the result values.
418 */
419 static const String PARAMS = 'params';
420
421 /**
422 * The name of the event that triggered the notification.
423 */
424 final String event;
425
426 /**
427 * A table mapping the names of notification parameters to their values, or
428 * null if there are no notification parameters.
429 */
430 Map<String, Object> _params;
431
432 /**
433 * Initialize a newly created [Notification] to have the given [event] name.
434 * If [_params] is provided, it will be used as the params; otherwise no
435 * params will be used.
436 */
437 Notification(this.event, [this._params]);
438
439 /**
440 * Initialize a newly created instance based upon the given JSON data
441 */
442 factory Notification.fromJson(Map<String, Object> json) {
443 return new Notification(
444 json[Notification.EVENT], json[Notification.PARAMS]);
445 }
446
447 /**
448 * Return a table representing the structure of the Json object that will be
449 * sent to the client to represent this response.
450 */
451 Map<String, Object> toJson() {
452 Map<String, Object> jsonObject = {};
453 jsonObject[EVENT] = event;
454 if (_params != null) {
455 jsonObject[PARAMS] = _params;
456 }
457 return jsonObject;
458 }
459 }
460
461 /**
462 * Instances of the class [Request] represent a request that was received.
463 */
464 class Request {
465 /**
466 * The name of the JSON attribute containing the id of the request.
467 */
468 static const String ID = 'id';
469
470 /**
471 * The name of the JSON attribute containing the name of the request.
472 */
473 static const String METHOD = 'method';
474
475 /**
476 * The name of the JSON attribute containing the request parameters.
477 */
478 static const String PARAMS = 'params';
479
480 /**
481 * The name of the optional JSON attribute indicating the time
482 * (milliseconds since epoch) at which the client made the request.
483 */
484 static const String CLIENT_REQUEST_TIME = 'clientRequestTime';
485
486 /**
487 * The unique identifier used to identify this request.
488 */
489 final String id;
490
491 /**
492 * The method being requested.
493 */
494 final String method;
495
496 /**
497 * A table mapping the names of request parameters to their values.
498 */
499 final Map<String, Object> _params;
500
501 /**
502 * The time (milliseconds since epoch) at which the client made the request
503 * or `null` if this information is not provided by the client.
504 */
505 final int clientRequestTime;
506
507 /**
508 * Initialize a newly created [Request] to have the given [id] and [method]
509 * name. If [params] is supplied, it is used as the "params" map for the
510 * request. Otherwise an empty "params" map is allocated.
511 */
512 Request(this.id, this.method,
513 [Map<String, Object> params, this.clientRequestTime])
514 : _params = params != null ? params : new HashMap<String, Object>();
515
516 /**
517 * Return a request parsed from the given json, or `null` if the [data] is
518 * not a valid json representation of a request. The [data] is expected to
519 * have the following format:
520 *
521 * {
522 * 'clientRequestTime': millisecondsSinceEpoch
523 * 'id': String,
524 * 'method': methodName,
525 * 'params': {
526 * paramter_name: value
527 * }
528 * }
529 *
530 * where both the parameters and clientRequestTime are optional.
531 * The parameters can contain any number of name/value pairs.
532 * The clientRequestTime must be an int representing the time at which
533 * the client issued the request (milliseconds since epoch).
534 */
535 factory Request.fromJson(Map<String, dynamic> result) {
536 var id = result[Request.ID];
537 var method = result[Request.METHOD];
538 if (id is! String || method is! String) {
539 return null;
540 }
541 var time = result[Request.CLIENT_REQUEST_TIME];
542 if (time != null && time is! int) {
543 return null;
544 }
545 var params = result[Request.PARAMS];
546 if (params is Map || params == null) {
547 return new Request(id, method, params, time);
548 } else {
549 return null;
550 }
551 }
552
553 /**
554 * Return a request parsed from the given [data], or `null` if the [data] is
555 * not a valid json representation of a request. The [data] is expected to
556 * have the following format:
557 *
558 * {
559 * 'clientRequestTime': millisecondsSinceEpoch
560 * 'id': String,
561 * 'method': methodName,
562 * 'params': {
563 * paramter_name: value
564 * }
565 * }
566 *
567 * where both the parameters and clientRequestTime are optional.
568 * The parameters can contain any number of name/value pairs.
569 * The clientRequestTime must be an int representing the time at which
570 * the client issued the request (milliseconds since epoch).
571 */
572 factory Request.fromString(String data) {
573 try {
574 var result = JSON.decode(data);
575 if (result is Map) {
576 return new Request.fromJson(result);
577 }
578 return null;
579 } catch (exception) {
580 return null;
581 }
582 }
583
584 /**
585 * Return a table representing the structure of the Json object that will be
586 * sent to the client to represent this response.
587 */
588 Map<String, Object> toJson() {
589 Map<String, Object> jsonObject = new HashMap<String, Object>();
590 jsonObject[ID] = id;
591 jsonObject[METHOD] = method;
592 if (_params.isNotEmpty) {
593 jsonObject[PARAMS] = _params;
594 }
595 if (clientRequestTime != null) {
596 jsonObject[CLIENT_REQUEST_TIME] = clientRequestTime;
597 }
598 return jsonObject;
599 }
600 }
601
602 /**
603 * JsonDecoder for decoding requests. Errors are reporting by throwing a
604 * [RequestFailure].
605 */
606 class RequestDecoder extends JsonDecoder {
607 /**
608 * The request being deserialized.
609 */
610 final Request _request;
611
612 RequestDecoder(this._request);
613
614 RefactoringKind get refactoringKind {
615 // Refactoring feedback objects should never appear in requests.
616 return null;
617 }
618
619 @override
620 dynamic mismatch(String jsonPath, String expected, [Object actual]) {
621 StringBuffer buffer = new StringBuffer();
622 buffer.write('Expected to be ');
623 buffer.write(expected);
624 if (actual != null) {
625 buffer.write('; found "');
626 buffer.write(JSON.encode(actual));
627 buffer.write('"');
628 }
629 return new RequestFailure(
630 new Response.invalidParameter(_request, jsonPath, buffer.toString()));
631 }
632
633 @override
634 dynamic missingKey(String jsonPath, String key) {
635 return new RequestFailure(new Response.invalidParameter(
636 _request, jsonPath, 'Expected to contain key ${JSON.encode(key)}'));
637 }
638 }
639
640 /**
641 * Instances of the class [RequestFailure] represent an exception that occurred
642 * during the handling of a request that requires that an error be returned to
643 * the client.
644 */
645 class RequestFailure implements Exception {
646 /**
647 * The response to be returned as a result of the failure.
648 */
649 final Response response;
650
651 /**
652 * Initialize a newly created exception to return the given reponse.
653 */
654 RequestFailure(this.response);
655 }
656
657 /**
658 * Instances of the class [RequestHandler] implement a handler that can handle
659 * requests and produce responses for them.
660 */
661 abstract class RequestHandler {
662 /**
663 * Attempt to handle the given [request]. If the request is not recognized by
664 * this handler, return `null` so that other handlers will be given a chance
665 * to handle it. Otherwise, return the response that should be passed back to
666 * the client.
667 */
668 Response handleRequest(Request request);
669 }
670
671 /**
672 * Instances of the class [Response] represent a response to a request.
673 */
674 class Response {
675 /**
676 * The [Response] instance that is returned when a real [Response] cannot
677 * be provided at the moment.
678 */
679 static final Response DELAYED_RESPONSE = new Response('DELAYED_RESPONSE');
680
681 /**
682 * The name of the JSON attribute containing the id of the request for which
683 * this is a response.
684 */
685 static const String ID = 'id';
686
687 /**
688 * The name of the JSON attribute containing the error message.
689 */
690 static const String ERROR = 'error';
691
692 /**
693 * The name of the JSON attribute containing the result values.
694 */
695 static const String RESULT = 'result';
696
697 /**
698 * The unique identifier used to identify the request that this response is
699 * associated with.
700 */
701 final String id;
702
703 /**
704 * The error that was caused by attempting to handle the request, or `null` if
705 * there was no error.
706 */
707 final RequestError error;
708
709 /**
710 * A table mapping the names of result fields to their values. Should be
711 * null if there is no result to send.
712 */
713 Map<String, Object> _result;
714
715 /**
716 * Initialize a newly created instance to represent a response to a request
717 * with the given [id]. If [_result] is provided, it will be used as the
718 * result; otherwise an empty result will be used. If an [error] is provided
719 * then the response will represent an error condition.
720 */
721 Response(this.id, {Map<String, Object> result, this.error})
722 : _result = result;
723
724 /**
725 * Initialize a newly created instance to represent the
726 * FILE_NOT_ANALYZED error condition.
727 */
728 Response.fileNotAnalyzed(Request request, String file)
729 : this(request.id,
730 error: new RequestError(RequestErrorCode.FILE_NOT_ANALYZED,
731 'File is not analyzed: $file.'));
732
733 /**
734 * Initialize a newly created instance to represent the FORMAT_INVALID_FILE
735 * error condition.
736 */
737 Response.formatInvalidFile(Request request)
738 : this(request.id,
739 error: new RequestError(RequestErrorCode.FORMAT_INVALID_FILE,
740 'Error during `edit.format`: invalid file.'));
741
742 /**
743 * Initialize a newly created instance to represent the FORMAT_WITH_ERROR
744 * error condition.
745 */
746 Response.formatWithErrors(Request request)
747 : this(request.id,
748 error: new RequestError(RequestErrorCode.FORMAT_WITH_ERRORS,
749 'Error during `edit.format`: source contains syntax errors.'));
750
751 /**
752 * Initialize a newly created instance based upon the given JSON data
753 */
754 factory Response.fromJson(Map<String, Object> json) {
755 try {
756 Object id = json[Response.ID];
757 if (id is! String) {
758 return null;
759 }
760 Object error = json[Response.ERROR];
761 RequestError decodedError;
762 if (error is Map) {
763 decodedError = new RequestError.fromJson(
764 new ResponseDecoder(null), '.error', error);
765 }
766 Object result = json[Response.RESULT];
767 Map<String, Object> decodedResult;
768 if (result is Map) {
769 decodedResult = result;
770 }
771 return new Response(id, error: decodedError, result: decodedResult);
772 } catch (exception) {
773 return null;
774 }
775 }
776
777 /**
778 * Initialize a newly created instance to represent the
779 * GET_ERRORS_INVALID_FILE error condition.
780 */
781 Response.getErrorsInvalidFile(Request request)
782 : this(request.id,
783 error: new RequestError(RequestErrorCode.GET_ERRORS_INVALID_FILE,
784 'Error during `analysis.getErrors`: invalid file.'));
785
786 /**
787 * Initialize a newly created instance to represent the
788 * GET_NAVIGATION_INVALID_FILE error condition.
789 */
790 Response.getNavigationInvalidFile(Request request)
791 : this(request.id,
792 error: new RequestError(
793 RequestErrorCode.GET_NAVIGATION_INVALID_FILE,
794 'Error during `analysis.getNavigation`: invalid file.'));
795
796 /**
797 * Initialize a newly created instance to represent an error condition caused
798 * by an analysis.reanalyze [request] that specifies an analysis root that is
799 * not in the current list of analysis roots.
800 */
801 Response.invalidAnalysisRoot(Request request, String rootPath)
802 : this(request.id,
803 error: new RequestError(RequestErrorCode.INVALID_ANALYSIS_ROOT,
804 "Invalid analysis root: $rootPath"));
805
806 /**
807 * Initialize a newly created instance to represent an error condition caused
808 * by a [request] that specifies an execution context whose context root does
809 * not exist.
810 */
811 Response.invalidExecutionContext(Request request, String contextId)
812 : this(request.id,
813 error: new RequestError(RequestErrorCode.INVALID_EXECUTION_CONTEXT,
814 "Invalid execution context: $contextId"));
815
816 /**
817 * Initialize a newly created instance to represent an error condition caused
818 * by a [request] that had invalid parameter. [path] is the path to the
819 * invalid parameter, in Javascript notation (e.g. "foo.bar" means that the
820 * parameter "foo" contained a key "bar" whose value was the wrong type).
821 * [expectation] is a description of the type of data that was expected.
822 */
823 Response.invalidParameter(Request request, String path, String expectation)
824 : this(request.id,
825 error: new RequestError(RequestErrorCode.INVALID_PARAMETER,
826 "Invalid parameter '$path'. $expectation."));
827
828 /**
829 * Initialize a newly created instance to represent an error condition caused
830 * by a malformed request.
831 */
832 Response.invalidRequestFormat()
833 : this('',
834 error: new RequestError(
835 RequestErrorCode.INVALID_REQUEST, 'Invalid request'));
836
837 /**
838 * Initialize a newly created instance to represent an error condition caused
839 * by a request that requires an index, but indexing is disabled.
840 */
841 Response.noIndexGenerated(Request request)
842 : this(request.id,
843 error: new RequestError(
844 RequestErrorCode.NO_INDEX_GENERATED, 'Indexing is disabled'));
845
846 /**
847 * Initialize a newly created instance to represent the
848 * ORGANIZE_DIRECTIVES_ERROR error condition.
849 */
850 Response.organizeDirectivesError(Request request, String message)
851 : this(request.id,
852 error: new RequestError(
853 RequestErrorCode.ORGANIZE_DIRECTIVES_ERROR, message));
854
855 /**
856 * Initialize a newly created instance to represent the
857 * REFACTORING_REQUEST_CANCELLED error condition.
858 */
859 Response.refactoringRequestCancelled(Request request)
860 : this(request.id,
861 error: new RequestError(
862 RequestErrorCode.REFACTORING_REQUEST_CANCELLED,
863 'The `edit.getRefactoring` request was cancelled.'));
864
865 /**
866 * Initialize a newly created instance to represent the SERVER_ERROR error
867 * condition.
868 */
869 factory Response.serverError(Request request, exception, stackTrace) {
870 RequestError error =
871 new RequestError(RequestErrorCode.SERVER_ERROR, exception.toString());
872 if (stackTrace != null) {
873 error.stackTrace = stackTrace.toString();
874 }
875 return new Response(request.id, error: error);
876 }
877
878 /**
879 * Initialize a newly created instance to represent the
880 * SORT_MEMBERS_INVALID_FILE error condition.
881 */
882 Response.sortMembersInvalidFile(Request request)
883 : this(request.id,
884 error: new RequestError(RequestErrorCode.SORT_MEMBERS_INVALID_FILE,
885 'Error during `edit.sortMembers`: invalid file.'));
886
887 /**
888 * Initialize a newly created instance to represent the
889 * SORT_MEMBERS_PARSE_ERRORS error condition.
890 */
891 Response.sortMembersParseErrors(Request request, int numErrors)
892 : this(request.id,
893 error: new RequestError(RequestErrorCode.SORT_MEMBERS_PARSE_ERRORS,
894 'Error during `edit.sortMembers`: file has $numErrors scan/parse errors.'));
895
896 /**
897 * Initialize a newly created instance to represent an error condition caused
898 * by a `analysis.setPriorityFiles` [request] that includes one or more files
899 * that are not being analyzed.
900 */
901 Response.unanalyzedPriorityFiles(String requestId, String fileNames)
902 : this(requestId,
903 error: new RequestError(RequestErrorCode.UNANALYZED_PRIORITY_FILES,
904 "Unanalyzed files cannot be a priority: '$fileNames'"));
905
906 /**
907 * Initialize a newly created instance to represent an error condition caused
908 * by a [request] that cannot be handled by any known handlers.
909 */
910 Response.unknownRequest(Request request)
911 : this(request.id,
912 error: new RequestError(
913 RequestErrorCode.UNKNOWN_REQUEST, 'Unknown request'));
914
915 /**
916 * Initialize a newly created instance to represent an error condition caused
917 * by a [request] referencing a source that does not exist.
918 */
919 Response.unknownSource(Request request)
920 : this(request.id,
921 error: new RequestError(
922 RequestErrorCode.UNKNOWN_SOURCE, 'Unknown source'));
923
924 Response.unsupportedFeature(String requestId, String message)
925 : this(requestId,
926 error: new RequestError(
927 RequestErrorCode.UNSUPPORTED_FEATURE, message));
928
929 /**
930 * Return a table representing the structure of the Json object that will be
931 * sent to the client to represent this response.
932 */
933 Map<String, Object> toJson() {
934 Map<String, Object> jsonObject = new HashMap<String, Object>();
935 jsonObject[ID] = id;
936 if (error != null) {
937 jsonObject[ERROR] = error.toJson();
938 }
939 if (_result != null) {
940 jsonObject[RESULT] = _result;
941 }
942 return jsonObject;
943 }
944 }
945
946 /**
947 * JsonDecoder for decoding responses from the server. This is intended to be
948 * used only for testing. Errors are reported using bare [Exception] objects.
949 */
950 class ResponseDecoder extends JsonDecoder {
951 final RefactoringKind refactoringKind;
952
953 ResponseDecoder(this.refactoringKind);
954
955 @override
956 dynamic mismatch(String jsonPath, String expected, [Object actual]) {
957 StringBuffer buffer = new StringBuffer();
958 buffer.write('Expected ');
959 buffer.write(expected);
960 if (actual != null) {
961 buffer.write(' found "');
962 buffer.write(JSON.encode(actual));
963 buffer.write('"');
964 }
965 buffer.write(' at ');
966 buffer.write(jsonPath);
967 return new Exception(buffer.toString());
968 }
969
970 @override
971 dynamic missingKey(String jsonPath, String key) {
972 return new Exception('Missing key $key at $jsonPath');
973 }
974 }
975
976 /**
977 * Jenkins hash function, optimized for small integers. Borrowed from
978 * sdk/lib/math/jenkins_smi_hash.dart.
979 *
980 * TODO(paulberry): Move to somewhere that can be shared with other code.
981 */
982 class _JenkinsSmiHash {
983 static int combine(int hash, int value) {
984 hash = 0x1fffffff & (hash + value);
985 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
986 return hash ^ (hash >> 6);
987 }
988
989 static int finish(int hash) {
990 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
991 hash = hash ^ (hash >> 11);
992 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
993 }
994
995 static int hash2(a, b) => finish(combine(combine(0, a), b));
996
997 static int hash4(a, b, c, d) =>
998 finish(combine(combine(combine(combine(0, a), b), c), d));
999 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698