OLD | NEW |
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 /** | 5 /** |
6 * Support for client code that needs to interact with the requests, responses | 6 * Support for client code that needs to interact with the requests, responses |
7 * and notifications that are part of the analysis server's wire protocol. | 7 * and notifications that are part of the analysis server's wire protocol. |
8 */ | 8 */ |
9 library analysis_server.plugin.protocol.protocol; | |
10 | |
11 import 'dart:collection'; | |
12 import 'dart:convert' hide JsonDecoder; | 9 import 'dart:convert' hide JsonDecoder; |
13 | 10 |
14 import 'package:analysis_server/protocol/protocol_generated.dart'; | 11 import 'package:analysis_server/protocol/protocol_generated.dart'; |
15 import 'package:analysis_server/src/protocol/protocol_internal.dart'; | 12 import 'package:analysis_server/src/protocol/protocol_internal.dart'; |
16 | 13 |
| 14 export 'package:analyzer_plugin/protocol/protocol.dart' show Enum; |
| 15 |
17 /** | 16 /** |
18 * A [RequestHandler] that supports [startup] and [shutdown] methods. | 17 * A [RequestHandler] that supports [startup] and [shutdown] methods. |
19 * | 18 * |
20 * Clients may not extend, implement or mix-in this class. | 19 * Clients may not extend, implement or mix-in this class. |
21 */ | 20 */ |
22 abstract class DomainHandler implements RequestHandler { | 21 abstract class DomainHandler implements RequestHandler { |
23 /** | 22 /** |
24 * Perform any operations associated with the shutdown of the domain. It is | 23 * Perform any operations associated with the shutdown of the domain. It is |
25 * not guaranteed that this method will be called. If it is, it will be | 24 * not guaranteed that this method will be called. If it is, it will be |
26 * called after the last [Request] has been made. | 25 * called after the last [Request] has been made. |
27 */ | 26 */ |
28 void shutdown() {} | 27 void shutdown() {} |
29 | 28 |
30 /** | 29 /** |
31 * Perform any operations associated with the startup of the domain. This | 30 * Perform any operations associated with the startup of the domain. This |
32 * will be called before the first [Request]. | 31 * will be called before the first [Request]. |
33 */ | 32 */ |
34 void startup() {} | 33 void startup() {} |
35 } | 34 } |
36 | 35 |
37 /** | 36 /** |
38 * An interface for enumerated types in the protocol. | 37 * A notification that can be sent from the server about an event that occurred. |
39 * | 38 * |
40 * Clients may not extend, implement or mix-in this class. | 39 * Clients may not extend, implement or mix-in this class. |
41 */ | 40 */ |
42 abstract class Enum { | |
43 /** | |
44 * The name of the enumerated value. This should match the name of the | |
45 * static getter which provides access to this enumerated value. | |
46 */ | |
47 String get name; | |
48 } | |
49 | |
50 /** | |
51 * A notification from the server about an event that occurred. | |
52 * | |
53 * Clients may not extend, implement or mix-in this class. | |
54 */ | |
55 class Notification { | 41 class Notification { |
56 /** | 42 /** |
57 * The name of the JSON attribute containing the name of the event that | 43 * The name of the JSON attribute containing the name of the event that |
58 * triggered the notification. | 44 * triggered the notification. |
59 */ | 45 */ |
60 static const String EVENT = 'event'; | 46 static const String EVENT = 'event'; |
61 | 47 |
62 /** | 48 /** |
63 * The name of the JSON attribute containing the result values. | 49 * The name of the JSON attribute containing the result values. |
64 */ | 50 */ |
65 static const String PARAMS = 'params'; | 51 static const String PARAMS = 'params'; |
66 | 52 |
67 /** | 53 /** |
68 * The name of the event that triggered the notification. | 54 * The name of the event that triggered the notification. |
69 */ | 55 */ |
70 final String event; | 56 final String event; |
71 | 57 |
72 /** | 58 /** |
73 * A table mapping the names of notification parameters to their values, or | 59 * A table mapping the names of notification parameters to their values, or |
74 * `null` if there are no notification parameters. | 60 * `null` if there are no notification parameters. |
75 */ | 61 */ |
76 final Map<String, Object> params; | 62 final Map<String, Object> params; |
77 | 63 |
78 /** | 64 /** |
79 * Initialize a newly created [Notification] to have the given [event] name. | 65 * Initialize a newly created [Notification] to have the given [event] name. |
80 * If [_params] is provided, it will be used as the params; otherwise no | 66 * If [params] is provided, it will be used as the params; otherwise no |
81 * params will be used. | 67 * params will be used. |
82 */ | 68 */ |
83 Notification(this.event, [this.params]); | 69 Notification(this.event, [this.params]); |
84 | 70 |
85 /** | 71 /** |
86 * Initialize a newly created instance based on the given JSON data. | 72 * Initialize a newly created instance based on the given JSON data. |
87 */ | 73 */ |
88 factory Notification.fromJson(Map json) { | 74 factory Notification.fromJson(Map json) { |
89 return new Notification(json[Notification.EVENT], | 75 return new Notification(json[Notification.EVENT], |
90 json[Notification.PARAMS] as Map<String, Object>); | 76 json[Notification.PARAMS] as Map<String, Object>); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
147 final Map<String, Object> params; | 133 final Map<String, Object> params; |
148 | 134 |
149 /** | 135 /** |
150 * The time (milliseconds since epoch) at which the client made the request | 136 * The time (milliseconds since epoch) at which the client made the request |
151 * or `null` if this information is not provided by the client. | 137 * or `null` if this information is not provided by the client. |
152 */ | 138 */ |
153 final int clientRequestTime; | 139 final int clientRequestTime; |
154 | 140 |
155 /** | 141 /** |
156 * Initialize a newly created [Request] to have the given [id] and [method] | 142 * Initialize a newly created [Request] to have the given [id] and [method] |
157 * name. If [params] is supplied, it is used as the "params" map for the | 143 * name. If [params] is supplied, it is used as the "params" map for the |
158 * request. Otherwise an empty "params" map is allocated. | 144 * request. Otherwise an empty "params" map is allocated. |
159 */ | 145 */ |
160 Request(this.id, this.method, | 146 Request(this.id, this.method, |
161 [Map<String, Object> params, this.clientRequestTime]) | 147 [Map<String, Object> params, this.clientRequestTime]) |
162 : params = params ?? new HashMap<String, Object>(); | 148 : params = params ?? <String, Object>{}; |
163 | 149 |
164 /** | 150 /** |
165 * Return a request parsed from the given json, or `null` if the [data] is | 151 * Return a request parsed from the given json, or `null` if the [data] is |
166 * not a valid json representation of a request. The [data] is expected to | 152 * not a valid json representation of a request. The [data] is expected to |
167 * have the following format: | 153 * have the following format: |
168 * | 154 * |
169 * { | 155 * { |
170 * 'clientRequestTime': millisecondsSinceEpoch | 156 * 'clientRequestTime': millisecondsSinceEpoch |
171 * 'id': String, | 157 * 'id': String, |
172 * 'method': methodName, | 158 * 'method': methodName, |
173 * 'params': { | 159 * 'params': { |
174 * paramter_name: value | 160 * paramter_name: value |
175 * } | 161 * } |
176 * } | 162 * } |
177 * | 163 * |
178 * where both the parameters and clientRequestTime are optional. | 164 * where both the parameters and clientRequestTime are optional. |
179 * | 165 * |
180 * The parameters can contain any number of name/value pairs. The | 166 * The parameters can contain any number of name/value pairs. The |
181 * clientRequestTime must be an int representing the time at which the client | 167 * clientRequestTime must be an int representing the time at which the client |
182 * issued the request (milliseconds since epoch). | 168 * issued the request (milliseconds since epoch). |
183 */ | 169 */ |
184 factory Request.fromJson(Map<String, dynamic> result) { | 170 factory Request.fromJson(Map<String, Object> result) { |
185 var id = result[Request.ID]; | 171 var id = result[Request.ID]; |
186 var method = result[Request.METHOD]; | 172 var method = result[Request.METHOD]; |
187 if (id is! String || method is! String) { | 173 if (id is! String || method is! String) { |
188 return null; | 174 return null; |
189 } | 175 } |
190 var time = result[Request.CLIENT_REQUEST_TIME]; | 176 var time = result[Request.CLIENT_REQUEST_TIME]; |
191 if (time != null && time is! int) { | 177 if (time != null && time is! int) { |
192 return null; | 178 return null; |
193 } | 179 } |
194 var params = result[Request.PARAMS]; | 180 var params = result[Request.PARAMS]; |
(...skipping 29 matching lines...) Expand all Loading... |
224 var result = JSON.decode(data); | 210 var result = JSON.decode(data); |
225 if (result is Map) { | 211 if (result is Map) { |
226 return new Request.fromJson(result as Map<String, dynamic>); | 212 return new Request.fromJson(result as Map<String, dynamic>); |
227 } | 213 } |
228 return null; | 214 return null; |
229 } catch (exception) { | 215 } catch (exception) { |
230 return null; | 216 return null; |
231 } | 217 } |
232 } | 218 } |
233 | 219 |
| 220 @override |
| 221 int get hashCode { |
| 222 return id.hashCode; |
| 223 } |
| 224 |
| 225 @override |
| 226 bool operator ==(Object other) { |
| 227 return other is Request && |
| 228 id == other.id && |
| 229 method == other.method && |
| 230 clientRequestTime == other.clientRequestTime && |
| 231 _equalMaps(params, other.params); |
| 232 } |
| 233 |
234 /** | 234 /** |
235 * Return a table representing the structure of the Json object that will be | 235 * Return a table representing the structure of the Json object that will be |
236 * sent to the client to represent this response. | 236 * sent to the client to represent this response. |
237 */ | 237 */ |
238 Map<String, Object> toJson() { | 238 Map<String, Object> toJson() { |
239 Map<String, Object> jsonObject = new HashMap<String, Object>(); | 239 Map<String, Object> jsonObject = <String, Object>{}; |
240 jsonObject[ID] = id; | 240 jsonObject[ID] = id; |
241 jsonObject[METHOD] = method; | 241 jsonObject[METHOD] = method; |
242 if (params.isNotEmpty) { | 242 if (params.isNotEmpty) { |
243 jsonObject[PARAMS] = params; | 243 jsonObject[PARAMS] = params; |
244 } | 244 } |
245 if (clientRequestTime != null) { | 245 if (clientRequestTime != null) { |
246 jsonObject[CLIENT_REQUEST_TIME] = clientRequestTime; | 246 jsonObject[CLIENT_REQUEST_TIME] = clientRequestTime; |
247 } | 247 } |
248 return jsonObject; | 248 return jsonObject; |
249 } | 249 } |
| 250 |
| 251 bool _equalLists(List first, List second) { |
| 252 if (first == null) { |
| 253 return second == null; |
| 254 } |
| 255 if (second == null) { |
| 256 return false; |
| 257 } |
| 258 int length = first.length; |
| 259 if (length != second.length) { |
| 260 return false; |
| 261 } |
| 262 for (int i = 0; i < length; i++) { |
| 263 if (!_equalObjects(first[i], second[i])) { |
| 264 return false; |
| 265 } |
| 266 } |
| 267 return true; |
| 268 } |
| 269 |
| 270 bool _equalMaps(Map first, Map second) { |
| 271 if (first == null) { |
| 272 return second == null; |
| 273 } |
| 274 if (second == null) { |
| 275 return false; |
| 276 } |
| 277 if (first.length != second.length) { |
| 278 return false; |
| 279 } |
| 280 for (var key in first.keys) { |
| 281 if (!second.containsKey(key)) { |
| 282 return false; |
| 283 } |
| 284 if (!_equalObjects(first[key], second[key])) { |
| 285 return false; |
| 286 } |
| 287 } |
| 288 return true; |
| 289 } |
| 290 |
| 291 bool _equalObjects(Object first, Object second) { |
| 292 if (first == null) { |
| 293 return second == null; |
| 294 } |
| 295 if (second == null) { |
| 296 return false; |
| 297 } |
| 298 if (first is Map) { |
| 299 if (second is Map) { |
| 300 return _equalMaps(first, second); |
| 301 } |
| 302 return false; |
| 303 } |
| 304 if (first is List) { |
| 305 if (second is List) { |
| 306 return _equalLists(first, second); |
| 307 } |
| 308 return false; |
| 309 } |
| 310 return first == second; |
| 311 } |
250 } | 312 } |
251 | 313 |
252 /** | 314 /** |
253 * An exception that occurred during the handling of a request that requires | 315 * An exception that occurred during the handling of a request that requires |
254 * that an error be returned to the client. | 316 * that an error be returned to the client. |
255 * | 317 * |
256 * Clients may not extend, implement or mix-in this class. | 318 * Clients may not extend, implement or mix-in this class. |
257 */ | 319 */ |
258 class RequestFailure implements Exception { | 320 class RequestFailure implements Exception { |
259 /** | 321 /** |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
319 /** | 381 /** |
320 * The error that was caused by attempting to handle the request, or `null` if | 382 * The error that was caused by attempting to handle the request, or `null` if |
321 * there was no error. | 383 * there was no error. |
322 */ | 384 */ |
323 final RequestError error; | 385 final RequestError error; |
324 | 386 |
325 /** | 387 /** |
326 * A table mapping the names of result fields to their values. Should be | 388 * A table mapping the names of result fields to their values. Should be |
327 * `null` if there is no result to send. | 389 * `null` if there is no result to send. |
328 */ | 390 */ |
329 Map<String, Object> _result; | 391 Map<String, Object> result; |
330 | 392 |
331 /** | 393 /** |
332 * Initialize a newly created instance to represent a response to a request | 394 * Initialize a newly created instance to represent a response to a request |
333 * with the given [id]. If [_result] is provided, it will be used as the | 395 * with the given [id]. If [_result] is provided, it will be used as the |
334 * result; otherwise an empty result will be used. If an [error] is provided | 396 * result; otherwise an empty result will be used. If an [error] is provided |
335 * then the response will represent an error condition. | 397 * then the response will represent an error condition. |
336 */ | 398 */ |
337 Response(this.id, {Map<String, Object> result, this.error}) | 399 Response(this.id, {Map<String, Object> result, this.error}) : result = result; |
338 : _result = result; | |
339 | 400 |
340 /** | 401 /** |
341 * Create and return the `DEBUG_PORT_COULD_NOT_BE_OPENED` error response. | 402 * Create and return the `DEBUG_PORT_COULD_NOT_BE_OPENED` error response. |
342 */ | 403 */ |
343 Response.debugPortCouldNotBeOpened(Request request, dynamic error) | 404 Response.debugPortCouldNotBeOpened(Request request, dynamic error) |
344 : this(request.id, | 405 : this(request.id, |
345 error: new RequestError( | 406 error: new RequestError( |
346 RequestErrorCode.DEBUG_PORT_COULD_NOT_BE_OPENED, '$error')); | 407 RequestErrorCode.DEBUG_PORT_COULD_NOT_BE_OPENED, '$error')); |
347 | 408 |
348 /** | 409 /** |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
558 /** | 619 /** |
559 * Initialize a newly created instance to represent an error condition caused | 620 * Initialize a newly created instance to represent an error condition caused |
560 * by a [request] for a service that is not supported. | 621 * by a [request] for a service that is not supported. |
561 */ | 622 */ |
562 Response.unsupportedFeature(String requestId, String message) | 623 Response.unsupportedFeature(String requestId, String message) |
563 : this(requestId, | 624 : this(requestId, |
564 error: new RequestError( | 625 error: new RequestError( |
565 RequestErrorCode.UNSUPPORTED_FEATURE, message)); | 626 RequestErrorCode.UNSUPPORTED_FEATURE, message)); |
566 | 627 |
567 /** | 628 /** |
568 * Return a table mapping the names of result fields to their values. Should | |
569 * be `null` if there is no result to send. | |
570 */ | |
571 Map<String, Object> get result => _result; | |
572 | |
573 /** | |
574 * Return a table representing the structure of the Json object that will be | 629 * Return a table representing the structure of the Json object that will be |
575 * sent to the client to represent this response. | 630 * sent to the client to represent this response. |
576 */ | 631 */ |
577 Map<String, Object> toJson() { | 632 Map<String, Object> toJson() { |
578 Map<String, Object> jsonObject = new HashMap<String, Object>(); | 633 Map<String, Object> jsonObject = <String, Object>{}; |
579 jsonObject[ID] = id; | 634 jsonObject[ID] = id; |
580 if (error != null) { | 635 if (error != null) { |
581 jsonObject[ERROR] = error.toJson(); | 636 jsonObject[ERROR] = error.toJson(); |
582 } | 637 } |
583 if (_result != null) { | 638 if (result != null) { |
584 jsonObject[RESULT] = _result; | 639 jsonObject[RESULT] = result; |
585 } | 640 } |
586 return jsonObject; | 641 return jsonObject; |
587 } | 642 } |
588 } | 643 } |
OLD | NEW |