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 |