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