Index: pkg/analysis_server/lib/src/protocol.dart |
diff --git a/pkg/analysis_server/lib/src/protocol.dart b/pkg/analysis_server/lib/src/protocol.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..86600eb05e74eba94544cd2e9dc072794dfdc461 |
--- /dev/null |
+++ b/pkg/analysis_server/lib/src/protocol.dart |
@@ -0,0 +1,363 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library protocol; |
+ |
+import 'dart:convert'; |
devoncarew
2014/01/20 22:35:01
Perhaps,
import 'dart:convert' show JSON;
Brian Wilkerson
2014/01/21 03:21:20
Added 'show JsonDecoder' pending resolution of the
|
+ |
+/** |
+ * Instances of the class [Request] represent a request that was received. |
+ */ |
+class Request { |
+ /** |
+ * The name of the JSON attribute containing the id of the request. |
+ */ |
+ static const String ID = 'id'; |
+ |
+ /** |
+ * The name of the JSON attribute containing the name of the request. |
+ */ |
+ static const String METHOD = 'method'; |
+ |
+ /** |
+ * The name of the JSON attribute containing the request parameters. |
+ */ |
+ static const String PARAMS = 'params'; |
+ |
+ /** |
+ * The unique identifier used to identify this request. |
+ */ |
+ final String id; |
+ |
+ /** |
+ * The method being requested. |
+ */ |
+ final String method; |
+ |
+ /** |
+ * A table mapping the names of request parameters to their values. |
+ */ |
+ final Map<String, Object> params = new Map<String, Object>(); |
+ |
+ /** |
+ * A decoder that can be used to decode strings into JSON objects. |
+ */ |
+ static const JsonDecoder DECODER = const JsonDecoder(null); |
devoncarew
2014/01/20 22:35:01
You can delete this line.
Brian Wilkerson
2014/01/21 03:21:20
Only if I make the next change.
|
+ |
+ /** |
+ * Initialize a newly created [Request] to have the given [id] and [method] |
+ * name. |
+ */ |
+ Request(this.id, this.method); |
+ |
+ /** |
+ * Return a request parsed from the given [data], or `null` if the [data] is |
+ * not a valid json representation of a request. The [data] is expected to |
+ * have the following format: |
+ * |
+ * { |
+ * 'id': String, |
+ * 'method': methodName, |
+ * 'params': { |
+ * paramter_name: value |
+ * } |
+ * } |
+ * |
+ * where the parameters are optional and can contain any number of name/value |
+ * pairs. |
+ */ |
+ factory Request.fromString(String data) { |
+ try { |
+ var result = DECODER.convert(data); |
devoncarew
2014/01/20 22:35:01
==>
var result = JSON.decode(data);
Brian Wilkerson
2014/01/21 03:21:20
I considered this, but this method creates a new d
devoncarew
2014/01/21 04:16:08
Hmm, I was not aware that a new decoder is created
|
+ if (result is! Map) { |
+ return null; |
+ } |
+ String id = result[Request.ID]; |
+ String method = result[Request.METHOD]; |
+ Map<String, Object> params = result[Request.PARAMS]; |
+ Request request = new Request(id, method); |
+ params.forEach((String key, Object value) { |
+ request.setParameter(key, value); |
+ }); |
+ return request; |
+ } catch (exception) { |
+ return null; |
+ } |
+ } |
+ |
+ /** |
+ * Return the value of the parameter with the given [name], or `null` if there |
+ * is no such parameter associated with this request. |
+ */ |
+ Object getParameter(String name) => params[name]; |
+ |
+ /** |
+ * Return the value of the parameter with the given [name], or throw a |
+ * [RequestFailure] exception with an appropriate error message if there is no |
+ * such parameter associated with this request. |
+ */ |
+ Object getRequiredParameter(String name) { |
+ Object value = params[name]; |
+ if (value == null) { |
+ throw new RequestFailure(new Response.missingRequiredParameter(this, name)); |
+ } |
+ return value; |
+ } |
+ |
+ /** |
+ * Set the value of the parameter with the given [name] to the given [value]. |
+ */ |
+ void setParameter(String name, Object value) { |
+ params[name] = value; |
+ } |
+ |
+ /** |
+ * Convert the given [value] to a boolean, or throw a [RequestFailure] |
+ * exception if the [value] could not be converted. |
+ * |
+ * The value is typically the result of invoking either [getParameter] or |
+ * [getRequiredParameter]. |
+ */ |
+ bool toBool(Object value) { |
+ if (value is bool) { |
+ return value; |
+ } else if (value is String) { |
+ return value == 'true'; |
+ } |
+ throw new RequestFailure(new Response.expectedBoolean(this, value)); |
+ } |
+ |
+ /** |
+ * Convert the given [value] to an integer, or throw a [RequestFailure] |
+ * exception if the [value] could not be converted. |
+ * |
+ * The value is typically the result of invoking either [getParameter] or |
+ * [getRequiredParameter]. |
+ */ |
+ int toInt(Object value) { |
+ if (value is int) { |
+ return value; |
+ } else if (value is String) { |
+ return int.parse(value, onError: (String value) { |
+ throw new RequestFailure(new Response.expectedInteger(this, value)); |
+ }); |
+ } |
+ throw new RequestFailure(new Response.expectedInteger(this, value)); |
+ } |
+} |
+ |
+/** |
+ * Instances of the class [Response] represent a response to a request. |
+ */ |
+class Response { |
+ /** |
+ * The name of the JSON attribute containing the id of the request for which |
+ * this is a response. |
+ */ |
+ static const String ID = 'id'; |
+ |
+ /** |
+ * The name of the JSON attribute containing the error message. |
+ */ |
+ static const String ERROR = 'error'; |
+ |
+ /** |
+ * The name of the JSON attribute containing the result values. |
+ */ |
+ static const String RESULT = 'result'; |
+ |
+ /** |
+ * The unique identifier used to identify the request that this response is |
+ * associated with. |
+ */ |
+ final String id; |
+ |
+ /** |
+ * The error that was caused by attempting to handle the request, or `null` if |
+ * there was no error. |
+ */ |
+ final Object error; |
+ |
+ /** |
+ * A table mapping the names of result fields to their values. The table |
+ * should be empty if there was an error. |
+ */ |
+ final Map<String, Object> result = new Map<String, Object>(); |
+ |
+ /** |
+ * Initialize a newly created instance to represent a response to a request |
+ * with the given [id]. If an [error] is provided then the response will |
+ * represent an error condition. |
+ */ |
+ Response(this.id, [this.error]); |
+ |
+ /** |
+ * Initialize a newly created instance to represent an error condition caused |
+ * by a [request] referencing a context that does not exist. |
+ */ |
+ Response.contextDoesNotExist(Request request) |
+ : this(request.id, 'Context does not exist'); |
+ |
+ /** |
+ * Initialize a newly created instance to represent an error condition caused |
+ * by a [request] that was expected to have a boolean-valued parameter but was |
+ * passed a non-boolean value. |
+ */ |
+ Response.expectedBoolean(Request request, String value) |
+ : this(request.id, 'Expected a boolean value, but found "$value"'); |
+ |
+ /** |
+ * Initialize a newly created instance to represent an error condition caused |
+ * by a [request] that was expected to have a integer-valued parameter but was |
+ * passed a non-integer value. |
+ */ |
+ Response.expectedInteger(Request request, String value) |
+ : this(request.id, 'Expected an integer value, but found "$value"'); |
+ |
+ /** |
+ * Initialize a newly created instance to represent an error condition caused |
+ * by a malformed request. |
+ */ |
+ Response.invalidRequestFormat() |
+ : this('', 'Invalid request'); |
+ |
+ /** |
+ * Initialize a newly created instance to represent an error condition caused |
+ * by a [request] that does not have a required parameter. |
+ */ |
+ Response.missingRequiredParameter(Request request, String parameterName) |
+ : this(request.id, 'Missing required parameter: $parameterName'); |
+ |
+ /** |
+ * Initialize a newly created instance to represent an error condition caused |
+ * by a [request] that takes a set of analysis options but for which an |
+ * unknown analysis option was provided. |
+ */ |
+ Response.unknownAnalysisOption(Request request, String optionName) |
+ : this(request.id, 'Unknown analysis option: "$optionName"'); |
+ |
+ /** |
+ * Initialize a newly created instance to represent an error condition caused |
+ * by a [request] that cannot be handled by any known handlers. |
+ */ |
+ Response.unknownRequest(Request request) |
+ : this(request.id, 'Unknown request'); |
+ |
+ /** |
+ * Return the value of the result field with the given [name]. |
+ */ |
+ Object getResult(String name) { |
+ return result[name]; |
+ } |
+ |
+ /** |
+ * Set the value of the result field with the given [name] to the given [value]. |
+ */ |
+ void setResult(String name, Object value) { |
+ result[name] = value; |
+ } |
+ |
+ /** |
+ * Return a table representing the structure of the Json object that will be |
+ * sent to the client to represent this response. |
+ */ |
+ Map<String, Object> toJson() { |
devoncarew
2014/01/20 22:35:01
I have generally seen this as:
Map<String, dynami
Brian Wilkerson
2014/01/21 03:21:20
The difference primarily has to do with warnings.
|
+ Map jsonObject = new Map(); |
+ jsonObject[ID] = id; |
+ jsonObject[ERROR] = error; |
+ if (!result.isEmpty) { |
+ jsonObject[RESULT] = result; |
+ } |
+ return jsonObject; |
+ } |
+} |
+ |
+/** |
+ * Instances of the class [Notification] represent a notification from the |
+ * server about an event that occurred. |
+ */ |
+class Notification { |
+ /** |
+ * The name of the JSON attribute containing the name of the event that |
+ * triggered the notification. |
+ */ |
+ static const String EVENT = 'event'; |
+ |
+ /** |
+ * The name of the JSON attribute containing the result values. |
+ */ |
+ static const String PARAMS = 'params'; |
+ |
+ /** |
+ * The name of the event that triggered the notification. |
+ */ |
+ final String event; |
+ |
+ /** |
+ * A table mapping the names of notification parameters to their values. |
+ */ |
+ final Map<String, Object> params = new Map<String, Object>(); |
+ |
+ /** |
+ * Initialize a newly created [Notification] to have the given [event] name. |
+ */ |
+ Notification(this.event); |
+ |
+ /** |
+ * Return the value of the parameter with the given [name], or `null` if there |
+ * is no such parameter associated with this notification. |
+ */ |
+ Object getParameter(String name) => params[name]; |
+ |
+ /** |
+ * Set the value of the parameter with the given [name] to the given [value]. |
+ */ |
+ void setParameter(String name, Object value) { |
+ params[name] = value; |
+ } |
+ |
+ /** |
+ * Return a table representing the structure of the Json object that will be |
+ * sent to the client to represent this response. |
+ */ |
+ Map<String, Object> toJson() { |
+ Map jsonObject = new Map(); |
+ jsonObject[EVENT] = event; |
+ if (!params.isEmpty) { |
+ jsonObject[PARAMS] = params; |
+ } |
+ return jsonObject; |
+ } |
+} |
+ |
+/** |
+ * Instances of the class [RequestHandler] implement a handler that can handle |
+ * requests and produce responses for them. |
+ */ |
+abstract class RequestHandler { |
+ /** |
+ * Attempt to handle the given [request]. If the request is not recognized by |
+ * this handler, return `null` so that other handlers will be given a chance |
+ * to handle it. Otherwise, return the response that should be passed back to |
+ * the client. |
+ */ |
+ Response handleRequest(Request request); |
+} |
+ |
+/** |
+ * Instances of the class [RequestFailure] represent an exception that occurred |
+ * during the handling of a request that requires that an error be returned to |
+ * the client. |
+ */ |
+class RequestFailure implements Exception { |
+ /** |
+ * The response to be returned as a result of the failure. |
+ */ |
+ final Response response; |
+ |
+ /** |
+ * Initialize a newly created exception to return the given reponse. |
+ */ |
+ RequestFailure(this.response); |
+} |