OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2017, 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 import 'dart:collection'; |
| 6 |
| 7 import 'package:analyzer_plugin/protocol/generated_protocol.dart'; |
| 8 import 'package:analyzer_plugin/src/protocol/protocol_internal.dart'; |
| 9 |
| 10 /** |
| 11 * An interface for enumerated types in the protocol. |
| 12 * |
| 13 * Clients may not extend, implement or mix-in this class. |
| 14 */ |
| 15 abstract class Enum { |
| 16 /** |
| 17 * The name of the enumerated value. This should match the name of the static |
| 18 * getter which provides access to this enumerated value. |
| 19 */ |
| 20 String get name; |
| 21 } |
| 22 |
| 23 /** |
| 24 * A notification that can be sent to the server about an event that occurred. |
| 25 * |
| 26 * Clients may not extend, implement or mix-in this class. |
| 27 */ |
| 28 class Notification { |
| 29 /** |
| 30 * The name of the JSON attribute containing the name of the event that |
| 31 * triggered the notification. |
| 32 */ |
| 33 static const String EVENT = 'event'; |
| 34 |
| 35 /** |
| 36 * The name of the JSON attribute containing the result values. |
| 37 */ |
| 38 static const String PARAMS = 'params'; |
| 39 |
| 40 /** |
| 41 * The name of the event that triggered the notification. |
| 42 */ |
| 43 final String event; |
| 44 |
| 45 /** |
| 46 * A table mapping the names of notification parameters to their values, or |
| 47 * `null` if there are no notification parameters. |
| 48 */ |
| 49 Map<String, Object> params; |
| 50 |
| 51 /** |
| 52 * Initialize a newly created [Notification] to have the given [event] name. |
| 53 * If [params] is provided, it will be used as the params; otherwise no |
| 54 * params will be used. |
| 55 */ |
| 56 Notification(this.event, [this.params]); |
| 57 |
| 58 /** |
| 59 * Initialize a newly created instance based on the given JSON data. |
| 60 */ |
| 61 factory Notification.fromJson(Map json) { |
| 62 return new Notification(json[Notification.EVENT], |
| 63 json[Notification.PARAMS] as Map<String, Object>); |
| 64 } |
| 65 |
| 66 /** |
| 67 * Return a table representing the structure of the Json object that will be |
| 68 * sent to the client to represent this response. |
| 69 */ |
| 70 Map<String, Object> toJson() { |
| 71 Map<String, Object> jsonObject = {}; |
| 72 jsonObject[EVENT] = event; |
| 73 if (params != null) { |
| 74 jsonObject[PARAMS] = params; |
| 75 } |
| 76 return jsonObject; |
| 77 } |
| 78 } |
| 79 |
| 80 /** |
| 81 * A request that was received from the server. |
| 82 * |
| 83 * Clients may not extend, implement or mix-in this class. |
| 84 */ |
| 85 class Request { |
| 86 /** |
| 87 * The name of the JSON attribute containing the id of the request. |
| 88 */ |
| 89 static const String ID = 'id'; |
| 90 |
| 91 /** |
| 92 * The name of the JSON attribute containing the name of the request. |
| 93 */ |
| 94 static const String METHOD = 'method'; |
| 95 |
| 96 /** |
| 97 * The name of the JSON attribute containing the request parameters. |
| 98 */ |
| 99 static const String PARAMS = 'params'; |
| 100 |
| 101 /** |
| 102 * The name of the optional JSON attribute indicating the time (milliseconds |
| 103 * since epoch) at which the server made the request. |
| 104 */ |
| 105 static const String SERVER_REQUEST_TIME = 'serverRequestTime'; |
| 106 |
| 107 /** |
| 108 * The unique identifier used to identify this request. |
| 109 */ |
| 110 final String id; |
| 111 |
| 112 /** |
| 113 * The method being requested. |
| 114 */ |
| 115 final String method; |
| 116 |
| 117 /** |
| 118 * A table mapping the names of request parameters to their values. |
| 119 */ |
| 120 final Map<String, Object> params; |
| 121 |
| 122 /** |
| 123 * The time (milliseconds since epoch) at which the server made the request, |
| 124 * or `null` if this information is not provided by the server. |
| 125 */ |
| 126 final int serverRequestTime; |
| 127 |
| 128 /** |
| 129 * Initialize a newly created [Request] to have the given [id] and [method] |
| 130 * name. If [params] is supplied, it is used as the "params" map for the |
| 131 * request. Otherwise an empty "params" map is allocated. |
| 132 */ |
| 133 Request(this.id, this.method, |
| 134 [Map<String, Object> params, this.serverRequestTime]) |
| 135 : params = params ?? <String, Object>{}; |
| 136 |
| 137 /** |
| 138 * Return a request parsed from the given json, or `null` if the [data] is |
| 139 * not a valid json representation of a request. The [data] is expected to |
| 140 * have the following format: |
| 141 * |
| 142 * { |
| 143 * 'clientRequestTime': millisecondsSinceEpoch |
| 144 * 'id': String, |
| 145 * 'method': methodName, |
| 146 * 'params': { |
| 147 * paramter_name: value |
| 148 * } |
| 149 * } |
| 150 * |
| 151 * where both the parameters and clientRequestTime are optional. |
| 152 * |
| 153 * The parameters can contain any number of name/value pairs. The |
| 154 * clientRequestTime must be an int representing the time at which the client |
| 155 * issued the request (milliseconds since epoch). |
| 156 */ |
| 157 factory Request.fromJson(Map<String, Object> result) { |
| 158 var id = result[Request.ID]; |
| 159 var method = result[Request.METHOD]; |
| 160 if (id is! String || method is! String) { |
| 161 return null; |
| 162 } |
| 163 var time = result[Request.SERVER_REQUEST_TIME]; |
| 164 if (time != null && time is! int) { |
| 165 return null; |
| 166 } |
| 167 var params = result[Request.PARAMS]; |
| 168 if (params is Map || params == null) { |
| 169 return new Request(id, method, params as Map<String, Object>, time); |
| 170 } else { |
| 171 return null; |
| 172 } |
| 173 } |
| 174 |
| 175 @override |
| 176 int get hashCode { |
| 177 return id.hashCode; |
| 178 } |
| 179 |
| 180 @override |
| 181 bool operator ==(Object other) { |
| 182 return other is Request && |
| 183 id == other.id && |
| 184 method == other.method && |
| 185 serverRequestTime == other.serverRequestTime && |
| 186 _equalMaps(params, other.params); |
| 187 } |
| 188 |
| 189 /** |
| 190 * Return a table representing the structure of the Json object that will be |
| 191 * sent to the server to represent this response. |
| 192 */ |
| 193 Map<String, Object> toJson() { |
| 194 Map<String, Object> jsonObject = {}; |
| 195 jsonObject[ID] = id; |
| 196 jsonObject[METHOD] = method; |
| 197 if (params.isNotEmpty) { |
| 198 jsonObject[PARAMS] = params; |
| 199 } |
| 200 if (serverRequestTime != null) { |
| 201 jsonObject[SERVER_REQUEST_TIME] = serverRequestTime; |
| 202 } |
| 203 return jsonObject; |
| 204 } |
| 205 |
| 206 bool _equalLists(List first, List second) { |
| 207 if (first == null) { |
| 208 return second == null; |
| 209 } |
| 210 if (second == null) { |
| 211 return false; |
| 212 } |
| 213 int length = first.length; |
| 214 if (length != second.length) { |
| 215 return false; |
| 216 } |
| 217 for (int i = 0; i < length; i++) { |
| 218 if (!_equalObjects(first[i], second[i])) { |
| 219 return false; |
| 220 } |
| 221 } |
| 222 return true; |
| 223 } |
| 224 |
| 225 bool _equalMaps(Map first, Map second) { |
| 226 if (first == null) { |
| 227 return second == null; |
| 228 } |
| 229 if (second == null) { |
| 230 return false; |
| 231 } |
| 232 if (first.length != second.length) { |
| 233 return false; |
| 234 } |
| 235 for (var key in first.keys) { |
| 236 if (!second.containsKey(key)) { |
| 237 return false; |
| 238 } |
| 239 if (!_equalObjects(first[key], second[key])) { |
| 240 return false; |
| 241 } |
| 242 } |
| 243 return true; |
| 244 } |
| 245 |
| 246 bool _equalObjects(Object first, Object second) { |
| 247 if (first == null) { |
| 248 return second == null; |
| 249 } |
| 250 if (second == null) { |
| 251 return false; |
| 252 } |
| 253 if (first is Map) { |
| 254 if (second is Map) { |
| 255 return _equalMaps(first, second); |
| 256 } |
| 257 return false; |
| 258 } |
| 259 if (first is List) { |
| 260 if (second is List) { |
| 261 return _equalLists(first, second); |
| 262 } |
| 263 return false; |
| 264 } |
| 265 return first == second; |
| 266 } |
| 267 } |
| 268 |
| 269 /** |
| 270 * A collection of utility methods that create instances of the generated class |
| 271 * [RequestError]. |
| 272 */ |
| 273 class RequestErrorFactory { |
| 274 // /** |
| 275 // * Initialize a newly created instance to represent the |
| 276 // * GET_ERRORS_INVALID_FILE error condition. |
| 277 // */ |
| 278 // Response.getErrorsInvalidFile(Request request) |
| 279 // : this(request.id, |
| 280 // error: new RequestError(RequestErrorCode.GET_ERRORS_INVALID_FILE, |
| 281 // 'Error during `analysis.getErrors`: invalid file.')); |
| 282 // |
| 283 // /** |
| 284 // * Initialize a newly created instance to represent the |
| 285 // * GET_NAVIGATION_INVALID_FILE error condition. |
| 286 // */ |
| 287 // Response.getNavigationInvalidFile(Request request) |
| 288 // : this(request.id, |
| 289 // error: new RequestError( |
| 290 // RequestErrorCode.GET_NAVIGATION_INVALID_FILE, |
| 291 // 'Error during `analysis.getNavigation`: invalid file.')); |
| 292 // |
| 293 // /** |
| 294 // * Initialize a newly created instance to represent the |
| 295 // * GET_REACHABLE_SOURCES_INVALID_FILE error condition. |
| 296 // */ |
| 297 // Response.getReachableSourcesInvalidFile(Request request) |
| 298 // : this(request.id, |
| 299 // error: new RequestError( |
| 300 // RequestErrorCode.GET_REACHABLE_SOURCES_INVALID_FILE, |
| 301 // 'Error during `analysis.getReachableSources`: invalid file.')); |
| 302 // |
| 303 // /** |
| 304 // * Initialize a newly created instance to represent an error condition cause
d |
| 305 // * by an analysis.reanalyze [request] that specifies an analysis root that i
s |
| 306 // * not in the current list of analysis roots. |
| 307 // */ |
| 308 // Response.invalidAnalysisRoot(Request request, String rootPath) |
| 309 // : this(request.id, |
| 310 // error: new RequestError(RequestErrorCode.INVALID_ANALYSIS_ROOT, |
| 311 // "Invalid analysis root: $rootPath")); |
| 312 // |
| 313 // /** |
| 314 // * Initialize a newly created instance to represent an error condition cause
d |
| 315 // * by a [request] that specifies an execution context whose context root doe
s |
| 316 // * not exist. |
| 317 // */ |
| 318 // Response.invalidExecutionContext(Request request, String contextId) |
| 319 // : this(request.id, |
| 320 // error: new RequestError(RequestErrorCode.INVALID_EXECUTION_CONTEXT, |
| 321 // "Invalid execution context: $contextId")); |
| 322 // |
| 323 // /** |
| 324 // * Initialize a newly created instance to represent the |
| 325 // * INVALID_FILE_PATH_FORMAT error condition. |
| 326 // */ |
| 327 // Response.invalidFilePathFormat(Request request, path) |
| 328 // : this(request.id, |
| 329 // error: new RequestError(RequestErrorCode.INVALID_FILE_PATH_FORMAT, |
| 330 // 'Invalid file path format: $path')); |
| 331 |
| 332 /** |
| 333 * Return a request error representing an error condition caused by a |
| 334 * [request] that had invalid parameter. [path] is the path to the |
| 335 * invalid parameter, in Javascript notation (e.g. "foo.bar" means that the |
| 336 * parameter "foo" contained a key "bar" whose value was the wrong type). |
| 337 * [expectation] is a description of the type of data that was expected. |
| 338 */ |
| 339 static RequestError invalidParameter( |
| 340 Request request, String path, String expectation) => |
| 341 new RequestError(RequestErrorCode.INVALID_PARAMETER, |
| 342 "Invalid parameter '$path'. $expectation."); |
| 343 |
| 344 // /** |
| 345 // * Initialize a newly created instance to represent an error condition cause
d |
| 346 // * by a malformed request. |
| 347 // */ |
| 348 // Response.invalidRequestFormat() |
| 349 // : this('', |
| 350 // error: new RequestError( |
| 351 // RequestErrorCode.INVALID_REQUEST, 'Invalid request')); |
| 352 // |
| 353 // /** |
| 354 // * Initialize a newly created instance to represent an error condition cause
d |
| 355 // * by a request that requires an index, but indexing is disabled. |
| 356 // */ |
| 357 // Response.noIndexGenerated(Request request) |
| 358 // : this(request.id, |
| 359 // error: new RequestError( |
| 360 // RequestErrorCode.NO_INDEX_GENERATED, 'Indexing is disabled')); |
| 361 // |
| 362 // /** |
| 363 // * Initialize a newly created instance to represent the |
| 364 // * ORGANIZE_DIRECTIVES_ERROR error condition. |
| 365 // */ |
| 366 // Response.organizeDirectivesError(Request request, String message) |
| 367 // : this(request.id, |
| 368 // error: new RequestError( |
| 369 // RequestErrorCode.ORGANIZE_DIRECTIVES_ERROR, message)); |
| 370 // |
| 371 // /** |
| 372 // * Initialize a newly created instance to represent the |
| 373 // * REFACTORING_REQUEST_CANCELLED error condition. |
| 374 // */ |
| 375 // Response.refactoringRequestCancelled(Request request) |
| 376 // : this(request.id, |
| 377 // error: new RequestError( |
| 378 // RequestErrorCode.REFACTORING_REQUEST_CANCELLED, |
| 379 // 'The `edit.getRefactoring` request was cancelled.')); |
| 380 |
| 381 /** |
| 382 * Return a request error representing an error that occurred in the plugin. |
| 383 */ |
| 384 static RequestError pluginError(Request request, exception, stackTrace) => |
| 385 new RequestError(RequestErrorCode.PLUGIN_ERROR, exception.toString(), |
| 386 stackTrace: stackTrace); |
| 387 |
| 388 // /** |
| 389 // * Initialize a newly created instance to represent the |
| 390 // * SORT_MEMBERS_INVALID_FILE error condition. |
| 391 // */ |
| 392 // Response.sortMembersInvalidFile(Request request) |
| 393 // : this(request.id, |
| 394 // error: new RequestError(RequestErrorCode.SORT_MEMBERS_INVALID_FILE, |
| 395 // 'Error during `edit.sortMembers`: invalid file.')); |
| 396 // |
| 397 // /** |
| 398 // * Initialize a newly created instance to represent the |
| 399 // * SORT_MEMBERS_PARSE_ERRORS error condition. |
| 400 // */ |
| 401 // Response.sortMembersParseErrors(Request request, int numErrors) |
| 402 // : this(request.id, |
| 403 // error: new RequestError(RequestErrorCode.SORT_MEMBERS_PARSE_ERRORS, |
| 404 // 'Error during `edit.sortMembers`: file has $numErrors scan/parse err
ors.')); |
| 405 // |
| 406 // /** |
| 407 // * Initialize a newly created instance to represent an error condition cause
d |
| 408 // * by a `analysis.setPriorityFiles` [request] that includes one or more file
s |
| 409 // * that are not being analyzed. |
| 410 // */ |
| 411 // Response.unanalyzedPriorityFiles(String requestId, String fileNames) |
| 412 // : this(requestId, |
| 413 // error: new RequestError(RequestErrorCode.UNANALYZED_PRIORITY_FILES, |
| 414 // "Unanalyzed files cannot be a priority: '$fileNames'")); |
| 415 |
| 416 /** |
| 417 * Return a request error representing an error condition caused by a |
| 418 * [request] that cannot be handled by any known handlers. |
| 419 */ |
| 420 static RequestError unknownRequest(Request request) => |
| 421 new RequestError(RequestErrorCode.UNKNOWN_REQUEST, 'Unknown request'); |
| 422 |
| 423 // /** |
| 424 // * Initialize a newly created instance to represent an error condition cause
d |
| 425 // * by a [request] referencing a source that does not exist. |
| 426 // */ |
| 427 // Response.unknownSource(Request request) |
| 428 // : this(request.id, |
| 429 // error: new RequestError( |
| 430 // RequestErrorCode.UNKNOWN_SOURCE, 'Unknown source')); |
| 431 // |
| 432 // /** |
| 433 // * Initialize a newly created instance to represent an error condition cause
d |
| 434 // * by a [request] for a service that is not supported. |
| 435 // */ |
| 436 // Response.unsupportedFeature(String requestId, String message) |
| 437 // : this(requestId, |
| 438 // error: new RequestError( |
| 439 // RequestErrorCode.UNSUPPORTED_FEATURE, message)); |
| 440 } |
| 441 |
| 442 /** |
| 443 * An exception that occurred during the handling of a request that requires |
| 444 * that an error be returned to the server. |
| 445 * |
| 446 * Clients may not extend, implement or mix-in this class. |
| 447 */ |
| 448 class RequestFailure implements Exception { |
| 449 /** |
| 450 * A description of the error that was encountered. |
| 451 */ |
| 452 final RequestError error; |
| 453 |
| 454 /** |
| 455 * Initialize a newly created exception to return a response with the given |
| 456 * [error]. |
| 457 */ |
| 458 RequestFailure(this.error); |
| 459 } |
| 460 |
| 461 /** |
| 462 * A response to the server. |
| 463 * |
| 464 * Clients may not extend, implement or mix-in this class. |
| 465 */ |
| 466 class Response { |
| 467 /** |
| 468 * The [Response] instance that is returned when a real [Response] cannot |
| 469 * be provided at the moment. |
| 470 */ |
| 471 static final Response DELAYED_RESPONSE = new Response('DELAYED_RESPONSE'); |
| 472 |
| 473 /** |
| 474 * The name of the JSON attribute containing the id of the request for which |
| 475 * this is a response. |
| 476 */ |
| 477 static const String ID = 'id'; |
| 478 |
| 479 /** |
| 480 * The name of the JSON attribute containing the error message. |
| 481 */ |
| 482 static const String ERROR = 'error'; |
| 483 |
| 484 /** |
| 485 * The name of the JSON attribute containing the result values. |
| 486 */ |
| 487 static const String RESULT = 'result'; |
| 488 |
| 489 /** |
| 490 * The unique identifier used to identify the request that this response is |
| 491 * associated with. |
| 492 */ |
| 493 final String id; |
| 494 |
| 495 /** |
| 496 * The error that was caused by attempting to handle the request, or `null` if |
| 497 * there was no error. |
| 498 */ |
| 499 final RequestError error; |
| 500 |
| 501 /** |
| 502 * A table mapping the names of result fields to their values. Should be |
| 503 * `null` if there is no result to send. |
| 504 */ |
| 505 Map<String, Object> result; |
| 506 |
| 507 /** |
| 508 * Initialize a newly created instance to represent a response to a request |
| 509 * with the given [id]. If [_result] is provided, it will be used as the |
| 510 * result; otherwise an empty result will be used. If an [error] is provided |
| 511 * then the response will represent an error condition. |
| 512 */ |
| 513 Response(this.id, {Map<String, Object> result, this.error}) : result = result; |
| 514 |
| 515 // /** |
| 516 // * Initialize a newly created instance to represent the FILE_NOT_ANALYZED |
| 517 // * error condition. |
| 518 // */ |
| 519 // Response.fileNotAnalyzed(Request request, String file) |
| 520 // : this(request.id, |
| 521 // error: new RequestError(RequestErrorCode.FILE_NOT_ANALYZED, |
| 522 // 'File is not analyzed: $file.')); |
| 523 // |
| 524 // /** |
| 525 // * Initialize a newly created instance to represent the FORMAT_INVALID_FILE |
| 526 // * error condition. |
| 527 // */ |
| 528 // Response.formatInvalidFile(Request request) |
| 529 // : this(request.id, |
| 530 // error: new RequestError(RequestErrorCode.FORMAT_INVALID_FILE, |
| 531 // 'Error during `edit.format`: invalid file.')); |
| 532 // |
| 533 // /** |
| 534 // * Initialize a newly created instance to represent the FORMAT_WITH_ERROR |
| 535 // * error condition. |
| 536 // */ |
| 537 // Response.formatWithErrors(Request request) |
| 538 // : this(request.id, |
| 539 // error: new RequestError(RequestErrorCode.FORMAT_WITH_ERRORS, |
| 540 // 'Error during `edit.format`: source contains syntax errors.')); |
| 541 |
| 542 /** |
| 543 * Initialize a newly created instance based on the given JSON data. |
| 544 */ |
| 545 factory Response.fromJson(Map json) { |
| 546 try { |
| 547 Object id = json[Response.ID]; |
| 548 if (id is! String) { |
| 549 return null; |
| 550 } |
| 551 Object error = json[Response.ERROR]; |
| 552 RequestError decodedError; |
| 553 if (error is Map) { |
| 554 decodedError = new RequestError.fromJson( |
| 555 new ResponseDecoder(null), '.error', error); |
| 556 } |
| 557 Object result = json[Response.RESULT]; |
| 558 Map<String, Object> decodedResult; |
| 559 if (result is Map) { |
| 560 decodedResult = result as Map<String, Object>; |
| 561 } |
| 562 return new Response(id, error: decodedError, result: decodedResult); |
| 563 } catch (exception) { |
| 564 return null; |
| 565 } |
| 566 } |
| 567 |
| 568 /** |
| 569 * Return a table representing the structure of the Json object that will be |
| 570 * sent to the client to represent this response. |
| 571 */ |
| 572 Map<String, Object> toJson() { |
| 573 Map<String, Object> jsonObject = new HashMap<String, Object>(); |
| 574 jsonObject[ID] = id; |
| 575 if (error != null) { |
| 576 jsonObject[ERROR] = error.toJson(); |
| 577 } |
| 578 if (result != null) { |
| 579 jsonObject[RESULT] = result; |
| 580 } |
| 581 return jsonObject; |
| 582 } |
| 583 } |
OLD | NEW |