Chromium Code Reviews| OLD | NEW |
|---|---|
| (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: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
| |
| 8 | |
| 9 /** | |
| 10 * Instances of the class [Request] represent a request that was received. | |
| 11 */ | |
| 12 class Request { | |
| 13 /** | |
| 14 * The name of the JSON attribute containing the id of the request. | |
| 15 */ | |
| 16 static const String ID = 'id'; | |
| 17 | |
| 18 /** | |
| 19 * The name of the JSON attribute containing the name of the request. | |
| 20 */ | |
| 21 static const String METHOD = 'method'; | |
| 22 | |
| 23 /** | |
| 24 * The name of the JSON attribute containing the request parameters. | |
| 25 */ | |
| 26 static const String PARAMS = 'params'; | |
| 27 | |
| 28 /** | |
| 29 * The unique identifier used to identify this request. | |
| 30 */ | |
| 31 final String id; | |
| 32 | |
| 33 /** | |
| 34 * The method being requested. | |
| 35 */ | |
| 36 final String method; | |
| 37 | |
| 38 /** | |
| 39 * A table mapping the names of request parameters to their values. | |
| 40 */ | |
| 41 final Map<String, Object> params = new Map<String, Object>(); | |
| 42 | |
| 43 /** | |
| 44 * A decoder that can be used to decode strings into JSON objects. | |
| 45 */ | |
| 46 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.
| |
| 47 | |
| 48 /** | |
| 49 * Initialize a newly created [Request] to have the given [id] and [method] | |
| 50 * name. | |
| 51 */ | |
| 52 Request(this.id, this.method); | |
| 53 | |
| 54 /** | |
| 55 * Return a request parsed from the given [data], or `null` if the [data] is | |
| 56 * not a valid json representation of a request. The [data] is expected to | |
| 57 * have the following format: | |
| 58 * | |
| 59 * { | |
| 60 * 'id': String, | |
| 61 * 'method': methodName, | |
| 62 * 'params': { | |
| 63 * paramter_name: value | |
| 64 * } | |
| 65 * } | |
| 66 * | |
| 67 * where the parameters are optional and can contain any number of name/value | |
| 68 * pairs. | |
| 69 */ | |
| 70 factory Request.fromString(String data) { | |
| 71 try { | |
| 72 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
| |
| 73 if (result is! Map) { | |
| 74 return null; | |
| 75 } | |
| 76 String id = result[Request.ID]; | |
| 77 String method = result[Request.METHOD]; | |
| 78 Map<String, Object> params = result[Request.PARAMS]; | |
| 79 Request request = new Request(id, method); | |
| 80 params.forEach((String key, Object value) { | |
| 81 request.setParameter(key, value); | |
| 82 }); | |
| 83 return request; | |
| 84 } catch (exception) { | |
| 85 return null; | |
| 86 } | |
| 87 } | |
| 88 | |
| 89 /** | |
| 90 * Return the value of the parameter with the given [name], or `null` if there | |
| 91 * is no such parameter associated with this request. | |
| 92 */ | |
| 93 Object getParameter(String name) => params[name]; | |
| 94 | |
| 95 /** | |
| 96 * Return the value of the parameter with the given [name], or throw a | |
| 97 * [RequestFailure] exception with an appropriate error message if there is no | |
| 98 * such parameter associated with this request. | |
| 99 */ | |
| 100 Object getRequiredParameter(String name) { | |
| 101 Object value = params[name]; | |
| 102 if (value == null) { | |
| 103 throw new RequestFailure(new Response.missingRequiredParameter(this, name) ); | |
| 104 } | |
| 105 return value; | |
| 106 } | |
| 107 | |
| 108 /** | |
| 109 * Set the value of the parameter with the given [name] to the given [value]. | |
| 110 */ | |
| 111 void setParameter(String name, Object value) { | |
| 112 params[name] = value; | |
| 113 } | |
| 114 | |
| 115 /** | |
| 116 * Convert the given [value] to a boolean, or throw a [RequestFailure] | |
| 117 * exception if the [value] could not be converted. | |
| 118 * | |
| 119 * The value is typically the result of invoking either [getParameter] or | |
| 120 * [getRequiredParameter]. | |
| 121 */ | |
| 122 bool toBool(Object value) { | |
| 123 if (value is bool) { | |
| 124 return value; | |
| 125 } else if (value is String) { | |
| 126 return value == 'true'; | |
| 127 } | |
| 128 throw new RequestFailure(new Response.expectedBoolean(this, value)); | |
| 129 } | |
| 130 | |
| 131 /** | |
| 132 * Convert the given [value] to an integer, or throw a [RequestFailure] | |
| 133 * exception if the [value] could not be converted. | |
| 134 * | |
| 135 * The value is typically the result of invoking either [getParameter] or | |
| 136 * [getRequiredParameter]. | |
| 137 */ | |
| 138 int toInt(Object value) { | |
| 139 if (value is int) { | |
| 140 return value; | |
| 141 } else if (value is String) { | |
| 142 return int.parse(value, onError: (String value) { | |
| 143 throw new RequestFailure(new Response.expectedInteger(this, value)); | |
| 144 }); | |
| 145 } | |
| 146 throw new RequestFailure(new Response.expectedInteger(this, value)); | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 /** | |
| 151 * Instances of the class [Response] represent a response to a request. | |
| 152 */ | |
| 153 class Response { | |
| 154 /** | |
| 155 * The name of the JSON attribute containing the id of the request for which | |
| 156 * this is a response. | |
| 157 */ | |
| 158 static const String ID = 'id'; | |
| 159 | |
| 160 /** | |
| 161 * The name of the JSON attribute containing the error message. | |
| 162 */ | |
| 163 static const String ERROR = 'error'; | |
| 164 | |
| 165 /** | |
| 166 * The name of the JSON attribute containing the result values. | |
| 167 */ | |
| 168 static const String RESULT = 'result'; | |
| 169 | |
| 170 /** | |
| 171 * The unique identifier used to identify the request that this response is | |
| 172 * associated with. | |
| 173 */ | |
| 174 final String id; | |
| 175 | |
| 176 /** | |
| 177 * The error that was caused by attempting to handle the request, or `null` if | |
| 178 * there was no error. | |
| 179 */ | |
| 180 final Object error; | |
| 181 | |
| 182 /** | |
| 183 * A table mapping the names of result fields to their values. The table | |
| 184 * should be empty if there was an error. | |
| 185 */ | |
| 186 final Map<String, Object> result = new Map<String, Object>(); | |
| 187 | |
| 188 /** | |
| 189 * Initialize a newly created instance to represent a response to a request | |
| 190 * with the given [id]. If an [error] is provided then the response will | |
| 191 * represent an error condition. | |
| 192 */ | |
| 193 Response(this.id, [this.error]); | |
| 194 | |
| 195 /** | |
| 196 * Initialize a newly created instance to represent an error condition caused | |
| 197 * by a [request] referencing a context that does not exist. | |
| 198 */ | |
| 199 Response.contextDoesNotExist(Request request) | |
| 200 : this(request.id, 'Context does not exist'); | |
| 201 | |
| 202 /** | |
| 203 * Initialize a newly created instance to represent an error condition caused | |
| 204 * by a [request] that was expected to have a boolean-valued parameter but was | |
| 205 * passed a non-boolean value. | |
| 206 */ | |
| 207 Response.expectedBoolean(Request request, String value) | |
| 208 : this(request.id, 'Expected a boolean value, but found "$value"'); | |
| 209 | |
| 210 /** | |
| 211 * Initialize a newly created instance to represent an error condition caused | |
| 212 * by a [request] that was expected to have a integer-valued parameter but was | |
| 213 * passed a non-integer value. | |
| 214 */ | |
| 215 Response.expectedInteger(Request request, String value) | |
| 216 : this(request.id, 'Expected an integer value, but found "$value"'); | |
| 217 | |
| 218 /** | |
| 219 * Initialize a newly created instance to represent an error condition caused | |
| 220 * by a malformed request. | |
| 221 */ | |
| 222 Response.invalidRequestFormat() | |
| 223 : this('', 'Invalid request'); | |
| 224 | |
| 225 /** | |
| 226 * Initialize a newly created instance to represent an error condition caused | |
| 227 * by a [request] that does not have a required parameter. | |
| 228 */ | |
| 229 Response.missingRequiredParameter(Request request, String parameterName) | |
| 230 : this(request.id, 'Missing required parameter: $parameterName'); | |
| 231 | |
| 232 /** | |
| 233 * Initialize a newly created instance to represent an error condition caused | |
| 234 * by a [request] that takes a set of analysis options but for which an | |
| 235 * unknown analysis option was provided. | |
| 236 */ | |
| 237 Response.unknownAnalysisOption(Request request, String optionName) | |
| 238 : this(request.id, 'Unknown analysis option: "$optionName"'); | |
| 239 | |
| 240 /** | |
| 241 * Initialize a newly created instance to represent an error condition caused | |
| 242 * by a [request] that cannot be handled by any known handlers. | |
| 243 */ | |
| 244 Response.unknownRequest(Request request) | |
| 245 : this(request.id, 'Unknown request'); | |
| 246 | |
| 247 /** | |
| 248 * Return the value of the result field with the given [name]. | |
| 249 */ | |
| 250 Object getResult(String name) { | |
| 251 return result[name]; | |
| 252 } | |
| 253 | |
| 254 /** | |
| 255 * Set the value of the result field with the given [name] to the given [value ]. | |
| 256 */ | |
| 257 void setResult(String name, Object value) { | |
| 258 result[name] = value; | |
| 259 } | |
| 260 | |
| 261 /** | |
| 262 * Return a table representing the structure of the Json object that will be | |
| 263 * sent to the client to represent this response. | |
| 264 */ | |
| 265 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.
| |
| 266 Map jsonObject = new Map(); | |
| 267 jsonObject[ID] = id; | |
| 268 jsonObject[ERROR] = error; | |
| 269 if (!result.isEmpty) { | |
| 270 jsonObject[RESULT] = result; | |
| 271 } | |
| 272 return jsonObject; | |
| 273 } | |
| 274 } | |
| 275 | |
| 276 /** | |
| 277 * Instances of the class [Notification] represent a notification from the | |
| 278 * server about an event that occurred. | |
| 279 */ | |
| 280 class Notification { | |
| 281 /** | |
| 282 * The name of the JSON attribute containing the name of the event that | |
| 283 * triggered the notification. | |
| 284 */ | |
| 285 static const String EVENT = 'event'; | |
| 286 | |
| 287 /** | |
| 288 * The name of the JSON attribute containing the result values. | |
| 289 */ | |
| 290 static const String PARAMS = 'params'; | |
| 291 | |
| 292 /** | |
| 293 * The name of the event that triggered the notification. | |
| 294 */ | |
| 295 final String event; | |
| 296 | |
| 297 /** | |
| 298 * A table mapping the names of notification parameters to their values. | |
| 299 */ | |
| 300 final Map<String, Object> params = new Map<String, Object>(); | |
| 301 | |
| 302 /** | |
| 303 * Initialize a newly created [Notification] to have the given [event] name. | |
| 304 */ | |
| 305 Notification(this.event); | |
| 306 | |
| 307 /** | |
| 308 * Return the value of the parameter with the given [name], or `null` if there | |
| 309 * is no such parameter associated with this notification. | |
| 310 */ | |
| 311 Object getParameter(String name) => params[name]; | |
| 312 | |
| 313 /** | |
| 314 * Set the value of the parameter with the given [name] to the given [value]. | |
| 315 */ | |
| 316 void setParameter(String name, Object value) { | |
| 317 params[name] = value; | |
| 318 } | |
| 319 | |
| 320 /** | |
| 321 * Return a table representing the structure of the Json object that will be | |
| 322 * sent to the client to represent this response. | |
| 323 */ | |
| 324 Map<String, Object> toJson() { | |
| 325 Map jsonObject = new Map(); | |
| 326 jsonObject[EVENT] = event; | |
| 327 if (!params.isEmpty) { | |
| 328 jsonObject[PARAMS] = params; | |
| 329 } | |
| 330 return jsonObject; | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 /** | |
| 335 * Instances of the class [RequestHandler] implement a handler that can handle | |
| 336 * requests and produce responses for them. | |
| 337 */ | |
| 338 abstract class RequestHandler { | |
| 339 /** | |
| 340 * Attempt to handle the given [request]. If the request is not recognized by | |
| 341 * this handler, return `null` so that other handlers will be given a chance | |
| 342 * to handle it. Otherwise, return the response that should be passed back to | |
| 343 * the client. | |
| 344 */ | |
| 345 Response handleRequest(Request request); | |
| 346 } | |
| 347 | |
| 348 /** | |
| 349 * Instances of the class [RequestFailure] represent an exception that occurred | |
| 350 * during the handling of a request that requires that an error be returned to | |
| 351 * the client. | |
| 352 */ | |
| 353 class RequestFailure implements Exception { | |
| 354 /** | |
| 355 * The response to be returned as a result of the failure. | |
| 356 */ | |
| 357 final Response response; | |
| 358 | |
| 359 /** | |
| 360 * Initialize a newly created exception to return the given reponse. | |
| 361 */ | |
| 362 RequestFailure(this.response); | |
| 363 } | |
| OLD | NEW |