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 protocol2; | |
6 | |
7 import 'dart:convert'; | |
8 | |
9 import 'protocol.dart'; | |
10 | |
11 part 'generated_protocol.dart'; | |
12 | |
13 /** | |
14 * Translate the input [map], applying [keyCallback] to all its keys, and | |
15 * [valueCallback] to all its values. | |
16 */ | |
17 _mapMap(Map map, {dynamic keyCallback(key), dynamic valueCallback(value)}) { | |
18 Map result = {}; | |
19 map.forEach((key, value) { | |
20 if (keyCallback != null) { | |
21 key = keyCallback(key); | |
22 } | |
23 if (valueCallback != null) { | |
24 value = valueCallback(value); | |
25 } | |
26 result[key] = value; | |
27 }); | |
28 return result; | |
29 } | |
30 | |
31 /** | |
32 * Jenkins hash function, optimized for small integers. Borrowed from | |
33 * sdk/lib/math/jenkins_smi_hash.dart. | |
34 * | |
35 * TODO(paulberry): Move to somewhere that can be shared with other code. | |
36 */ | |
37 class _JenkinsSmiHash { | |
38 static int combine(int hash, int value) { | |
39 hash = 0x1fffffff & (hash + value); | |
40 hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); | |
41 return hash ^ (hash >> 6); | |
42 } | |
43 | |
44 static int finish(int hash) { | |
45 hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); | |
46 hash = hash ^ (hash >> 11); | |
47 return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); | |
48 } | |
49 | |
50 static int hash2(a, b) => finish(combine(combine(0, a), b)); | |
51 | |
52 static int hash4(a, b, c, d) => | |
53 finish(combine(combine(combine(combine(0, a), b), c), d)); | |
54 } | |
55 | |
56 | |
57 /** | |
58 * Type of callbacks used to decode parts of JSON objects. [jsonPath] is a | |
59 * string describing the part of the JSON object being decoded, and [value] is | |
60 * the part to decode. | |
61 */ | |
62 typedef Object JsonDecoderCallback(String jsonPath, Object value); | |
63 | |
64 /** | |
65 * Base class for decoding JSON objects. The derived class must implement | |
66 * error reporting logic. | |
67 */ | |
68 abstract class JsonDecoderBase { | |
69 /** | |
70 * Create an exception to throw if the JSON object at [jsonPath] is missing | |
71 * the key [key]. | |
72 */ | |
73 dynamic missingKey(String jsonPath, String key); | |
74 | |
75 /** | |
76 * Create an exception to throw if the JSON object at [jsonPath] fails to | |
77 * match the API definition of [expected]. | |
78 */ | |
79 dynamic mismatch(String jsonPath, String expected); | |
80 | |
81 /** | |
82 * Decode a JSON object that is expected to be a Map. [keyDecoder] is used | |
83 * to decode the keys, and [valueDecoder] is used to decode the values. | |
84 */ | |
85 Map _decodeMap(String jsonPath, Object json, {JsonDecoderCallback keyDecoder, | |
86 JsonDecoderCallback valueDecoder}) { | |
87 if (json == null) { | |
88 return {}; | |
89 } else if (json is Map) { | |
90 Map result = {}; | |
91 json.forEach((String key, value) { | |
92 Object decodedKey; | |
93 if (keyDecoder != null) { | |
94 decodedKey = keyDecoder('$jsonPath.key', key); | |
95 } else { | |
96 decodedKey = key; | |
97 } | |
98 if (valueDecoder != null) { | |
99 value = valueDecoder('$jsonPath[${JSON.encode(key)}]', value); | |
100 } | |
101 result[decodedKey] = value; | |
102 }); | |
103 return result; | |
104 } else { | |
105 throw mismatch(jsonPath, 'Map'); | |
106 } | |
107 } | |
108 | |
109 /** | |
110 * Decode a JSON object that is expected to be a List. [decoder] is used to | |
111 * decode the items in the list. | |
112 */ | |
113 List _decodeList(String jsonPath, Object json, [JsonDecoderCallback decoder]) { | |
114 if (json == null) { | |
115 return []; | |
116 } else if (json is List) { | |
117 List result = []; | |
118 for (int i = 0; i < json.length; i++) { | |
119 result.add(decoder('$jsonPath[$i]', json[i])); | |
120 } | |
121 return result; | |
122 } else { | |
123 throw mismatch(jsonPath, 'List'); | |
124 } | |
125 } | |
126 | |
127 /** | |
128 * Decode a JSON object that is expected to be a string. | |
129 */ | |
130 String _decodeString(String jsonPath, Object json) { | |
131 if (json is String) { | |
132 return json; | |
133 } else { | |
134 throw mismatch(jsonPath, 'String'); | |
135 } | |
136 } | |
137 | |
138 /** | |
139 * Decode a JSON object that is expected to be a boolean. The strings "true" | |
140 * and "false" are also accepted. | |
141 */ | |
142 bool _decodeBool(String jsonPath, Object json) { | |
danrubel
2014/08/17 17:10:21
Do you want to sort the class memebers via cmd-1 -
Paul Berry
2014/08/18 21:58:03
Done.
| |
143 if (json is bool) { | |
144 return json; | |
145 } else if (json == 'true') { | |
146 return true; | |
147 } else if (json == 'false') { | |
148 return false; | |
149 } | |
150 throw mismatch(jsonPath, 'bool'); | |
151 } | |
152 | |
153 /** | |
154 * Decode a JSON object that is expected to be an integer. A string | |
155 * representation of an integer is also accepted. | |
156 */ | |
157 int _decodeInt(String jsonPath, Object json) { | |
158 if (json is int) { | |
159 return json; | |
160 } else if (json is String) { | |
161 return int.parse(json, onError: (String value) { | |
162 throw mismatch(jsonPath, 'int'); | |
163 }); | |
164 } | |
165 throw mismatch(jsonPath, 'int'); | |
166 } | |
167 | |
168 /** | |
169 * Decode a JSON object that is expected to be one of several choices, | |
170 * where the choices are disambiguated by the contents of the field [field]. | |
171 * [decoders] is a map from each possible string in the field to the decoder | |
172 * that should be used to decode the JSON object. | |
173 */ | |
174 Object _decodeUnion(String jsonPath, Map json, String field, | |
175 Map<String, JsonDecoderCallback> decoders) { | |
176 if (json is Map) { | |
177 if (!json.containsKey(field)) { | |
178 throw missingKey(jsonPath, field); | |
179 } | |
180 var disambiguatorPath = '$jsonPath[${JSON.encode(field)}]'; | |
181 String disambiguator = _decodeString(disambiguatorPath, | |
182 json[field]); | |
183 if (!decoders.containsKey(disambiguator)) { | |
184 throw mismatch(disambiguatorPath, 'One of: ${decoders.keys.toList()}'); | |
185 } | |
186 return decoders[disambiguator](jsonPath, json); | |
187 } else { | |
188 throw mismatch(jsonPath, 'Map'); | |
189 } | |
190 } | |
191 } | |
192 | |
193 /** | |
194 * JsonDecoder for decoding requests. Errors are reporting by throwing a | |
195 * [RequestFailure]. | |
196 */ | |
197 class RequestDecoder extends JsonDecoder { | |
198 /** | |
199 * The request being deserialized. | |
200 */ | |
201 final Request _request; | |
202 | |
203 RequestDecoder(this._request); | |
204 | |
205 @override | |
206 dynamic mismatch(String jsonPath, String expected) { | |
207 return new RequestFailure(new Response.invalidParameter(_request, jsonPath, | |
208 'be $expected')); | |
209 } | |
210 | |
211 @override | |
212 dynamic missingKey(String jsonPath, String key) { | |
213 return new RequestFailure(new Response.invalidParameter(_request, jsonPath, | |
214 'contain key ${JSON.encode(key)}')); | |
215 } | |
216 } | |
217 | |
218 /** | |
219 * JsonDecoder for decoding responses from the server. This is intended to be | |
220 * used only for testing. Errors are reported using bare [Exception] objects. | |
221 */ | |
222 class ResponseDecoder extends JsonDecoder { | |
223 @override | |
224 dynamic mismatch(String jsonPath, String expected) { | |
225 return new Exception('Expected $expected at $jsonPath'); | |
226 } | |
227 | |
228 @override | |
229 dynamic missingKey(String jsonPath, String key) { | |
230 return new Exception('Missing key $key at $jsonPath'); | |
231 } | |
232 } | |
OLD | NEW |