| 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 |