OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of dart.core; | 5 part of dart.core; |
6 | 6 |
7 /** | 7 /** |
8 * A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986. | 8 * A parsed URI, as specified by RFC-3986, http://tools.ietf.org/html/rfc3986. |
9 */ | 9 */ |
10 class Uri { | 10 class Uri { |
11 final String _host; | |
11 int _port; | 12 int _port; |
12 String _path; | 13 String _path; |
13 | 14 |
14 /** | 15 /** |
15 * Returns the scheme component. | 16 * Returns the scheme component. |
16 * | 17 * |
17 * Returns the empty string if there is no scheme component. | 18 * Returns the empty string if there is no scheme component. |
18 */ | 19 */ |
19 final String scheme; | 20 final String scheme; |
20 | 21 |
(...skipping 19 matching lines...) Expand all Loading... | |
40 * authority component. | 41 * authority component. |
41 */ | 42 */ |
42 final String userInfo; | 43 final String userInfo; |
43 | 44 |
44 /** | 45 /** |
45 * Returns the host part of the authority component. | 46 * Returns the host part of the authority component. |
46 * | 47 * |
47 * Returns the empty string if there is no authority component and | 48 * Returns the empty string if there is no authority component and |
48 * hence no host. | 49 * hence no host. |
49 */ | 50 */ |
50 final String host; | 51 String get host { |
52 if (_host != null && _host.startsWith('[')) { | |
53 return _host.substring(1, _host.length - 1); | |
54 } | |
55 return _host; | |
56 } | |
51 | 57 |
52 /** | 58 /** |
53 * Returns the port part of the authority component. | 59 * Returns the port part of the authority component. |
54 * | 60 * |
55 * Returns 0 if there is no port in the authority component. | 61 * Returns 0 if there is no port in the authority component. |
56 */ | 62 */ |
57 int get port => _port; | 63 int get port => _port; |
58 | 64 |
59 /** | 65 /** |
60 * Returns the path component. | 66 * Returns the path component. |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
146 * [queryParameters]. When [query] is used the provided string is | 152 * [queryParameters]. When [query] is used the provided string is |
147 * expected to be fully percent-encoded and is used in its literal | 153 * expected to be fully percent-encoded and is used in its literal |
148 * form. When [queryParameters] is used the query is built from the | 154 * form. When [queryParameters] is used the query is built from the |
149 * provided map. Each key and value in the map is percent-encoded | 155 * provided map. Each key and value in the map is percent-encoded |
150 * and joined using equal and ampersand characters. The | 156 * and joined using equal and ampersand characters. The |
151 * percent-encoding of the keys and values encodes all characters | 157 * percent-encoding of the keys and values encodes all characters |
152 * except for the unreserved characters. | 158 * except for the unreserved characters. |
153 * | 159 * |
154 * The fragment component is set through [fragment]. | 160 * The fragment component is set through [fragment]. |
155 */ | 161 */ |
156 Uri({scheme, | 162 Uri({String scheme, |
157 this.userInfo: "", | 163 this.userInfo: "", |
158 this.host: "", | 164 String host: "", |
159 port: 0, | 165 port: 0, |
160 String path, | 166 String path, |
161 Iterable<String> pathSegments, | 167 Iterable<String> pathSegments, |
162 String query, | 168 String query, |
163 Map<String, String> queryParameters, | 169 Map<String, String> queryParameters, |
164 fragment: ""}) : | 170 fragment: ""}) : |
165 scheme = _makeScheme(scheme), | 171 scheme = _makeScheme(scheme), |
172 _host = _makeHost(host), | |
166 query = _makeQuery(query, queryParameters), | 173 query = _makeQuery(query, queryParameters), |
167 fragment = _makeFragment(fragment) { | 174 fragment = _makeFragment(fragment) { |
168 // Perform scheme specific normalization. | 175 // Perform scheme specific normalization. |
169 if (scheme == "http" && port == 80) { | 176 if (scheme == "http" && port == 80) { |
170 _port = 0; | 177 _port = 0; |
171 } else if (scheme == "https" && port == 443) { | 178 } else if (scheme == "https" && port == 443) { |
172 _port = 0; | 179 _port = 0; |
173 } else { | 180 } else { |
174 _port = port; | 181 _port = port; |
175 } | 182 } |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
230 // Split off the user info. | 237 // Split off the user info. |
231 bool hasUserInfo = false; | 238 bool hasUserInfo = false; |
232 for (int i = 0; i < authority.length; i++) { | 239 for (int i = 0; i < authority.length; i++) { |
233 if (authority.codeUnitAt(i) == _AT_SIGN) { | 240 if (authority.codeUnitAt(i) == _AT_SIGN) { |
234 hasUserInfo = true; | 241 hasUserInfo = true; |
235 userInfo = authority.substring(0, i); | 242 userInfo = authority.substring(0, i); |
236 hostStart = i + 1; | 243 hostStart = i + 1; |
237 break; | 244 break; |
238 } | 245 } |
239 } | 246 } |
247 var hostEnd = hostStart; | |
248 if (hostStart < authority.length && | |
249 authority.codeUnitAt(hostStart) == _LEFT_BRACKET) { | |
250 // IPv6 host. | |
251 for (; hostEnd < authority.length; hostEnd++) { | |
252 if (authority.codeUnitAt(hostEnd) == _RIGHT_BRACKET) break; | |
253 } | |
254 if (hostEnd == authority.length) { | |
255 throw new FormatException("Invalid IPv6 host entry."); | |
256 } | |
257 parseIPv6Address(authority.substring(hostStart + 1, hostEnd)); | |
258 hostEnd++; // Skip the closing bracket. | |
259 if (hostEnd != authority.length && | |
260 authority.codeUnitAt(hostEnd) != _COLON) { | |
261 throw new FormatException("Invalid end of authority"); | |
262 } | |
263 } | |
240 // Split host and port. | 264 // Split host and port. |
241 bool hasPort = false; | 265 bool hasPort = false; |
242 for (int i = hostStart; i < authority.length; i++) { | 266 for (;hostEnd < authority.length; hostEnd++) { |
floitsch
2013/09/10 12:11:46
nit: space after ";" ?
Søren Gjesse
2013/09/10 12:18:03
Please add space after ;
Anders Johnsen
2013/09/11 12:58:31
Done.
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
243 if (authority.codeUnitAt(i) == _COLON) { | 267 if (authority.codeUnitAt(hostEnd) == _COLON) { |
244 hasPort = true; | 268 var portString = authority.substring(hostEnd + 1); |
245 host = authority.substring(hostStart, i); | 269 if (portString.isNotEmpty) port = int.parse(portString); |
floitsch
2013/09/10 12:11:46
do we allow: "foo.bar:" as host?
is this valid?
ma
Anders Johnsen
2013/09/11 12:58:31
Yes, we even test for that:
check(new Ur
| |
246 if (!host.isEmpty) { | |
247 var portString = authority.substring(i + 1); | |
248 if (portString.isNotEmpty) port = int.parse(portString); | |
249 } | |
250 break; | 270 break; |
251 } | 271 } |
252 } | 272 } |
253 if (!hasPort) { | 273 host = authority.substring(hostStart, hostEnd); |
254 host = hasUserInfo ? authority.substring(hostStart) : authority; | |
255 } | |
256 | 274 |
257 return new Uri(scheme: scheme, | 275 return new Uri(scheme: scheme, |
258 userInfo: userInfo, | 276 userInfo: userInfo, |
259 host: host, | 277 host: host, |
260 port: port, | 278 port: port, |
261 pathSegments: unencodedPath.split("/"), | 279 pathSegments: unencodedPath.split("/"), |
262 queryParameters: queryParameters); | 280 queryParameters: queryParameters); |
263 } | 281 } |
264 | 282 |
265 /** | 283 /** |
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
467 * The returned map is unmodifiable and will throw [UnsupportedError] on any | 485 * The returned map is unmodifiable and will throw [UnsupportedError] on any |
468 * calls that would mutate it. | 486 * calls that would mutate it. |
469 */ | 487 */ |
470 Map<String, String> get queryParameters { | 488 Map<String, String> get queryParameters { |
471 if (_queryParameters == null) { | 489 if (_queryParameters == null) { |
472 _queryParameters = new _UnmodifiableMap(splitQueryString(query)); | 490 _queryParameters = new _UnmodifiableMap(splitQueryString(query)); |
473 } | 491 } |
474 return _queryParameters; | 492 return _queryParameters; |
475 } | 493 } |
476 | 494 |
495 static String _makeHost(String host) { | |
496 if (host == null || host.isEmpty) return host; | |
497 if (host.codeUnitAt(0) == _LEFT_BRACKET) { | |
498 if (host.codeUnitAt(host.length - 1) != _RIGHT_BRACKET) { | |
499 throw new FormatException('Missing end `]` to match `[` in host'); | |
500 } | |
501 parseIPv6Address(host.substring(1, host.length - 1)); | |
502 return host; | |
503 } | |
504 for (int i = 0; i < host.length; i++) { | |
505 if (host.codeUnitAt(i) == _COLON) { | |
506 parseIPv6Address(host); | |
507 var buffer = new StringBuffer(); | |
508 buffer.writeCharCode(_LEFT_BRACKET); | |
floitsch
2013/09/10 12:11:46
just write "[$host]".
This will use a string-buffe
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
509 buffer.write(host); | |
510 buffer.writeCharCode(_RIGHT_BRACKET); | |
511 return buffer.toString(); | |
512 } | |
513 } | |
514 return host; | |
515 } | |
516 | |
477 static String _makeScheme(String scheme) { | 517 static String _makeScheme(String scheme) { |
478 bool isSchemeLowerCharacter(int ch) { | 518 bool isSchemeLowerCharacter(int ch) { |
479 return ch < 128 && | 519 return ch < 128 && |
480 ((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | 520 ((_schemeLowerTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); |
481 } | 521 } |
482 | 522 |
483 bool isSchemeCharacter(int ch) { | 523 bool isSchemeCharacter(int ch) { |
484 return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); | 524 return ch < 128 && ((_schemeTable[ch >> 4] & (1 << (ch & 0x0f))) != 0); |
485 } | 525 } |
486 | 526 |
(...skipping 334 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
821 | 861 |
822 /** | 862 /** |
823 * Returns the origin of the URI in the form scheme://host:port for the | 863 * Returns the origin of the URI in the form scheme://host:port for the |
824 * schemes http and https. | 864 * schemes http and https. |
825 * | 865 * |
826 * It is an error if the scheme is not "http" or "https". | 866 * It is an error if the scheme is not "http" or "https". |
827 * | 867 * |
828 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin | 868 * See: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin |
829 */ | 869 */ |
830 String get origin { | 870 String get origin { |
831 if (scheme == "" || host == null || host == "") { | 871 if (scheme == "" || _host == null || _host == "") { |
832 throw new StateError("Cannot use origin without a scheme: $this"); | 872 throw new StateError("Cannot use origin without a scheme: $this"); |
833 } | 873 } |
834 if (scheme != "http" && scheme != "https") { | 874 if (scheme != "http" && scheme != "https") { |
835 throw new StateError( | 875 throw new StateError( |
836 "Origin is only applicable schemes http and https: $this"); | 876 "Origin is only applicable schemes http and https: $this"); |
837 } | 877 } |
838 if (port == 0) return "$scheme://$host"; | 878 if (port == 0) return "$scheme://$_host"; |
839 return "$scheme://$host:$port"; | 879 return "$scheme://$_host:$port"; |
840 } | 880 } |
841 | 881 |
842 /** | 882 /** |
843 * Returns the file path from a file URI. | 883 * Returns the file path from a file URI. |
844 * | 884 * |
845 * The returned path has either Windows or non-Windows | 885 * The returned path has either Windows or non-Windows |
846 * semantics. | 886 * semantics. |
847 * | 887 * |
848 * For non-Windows semantics the slash ("/") is used to separate | 888 * For non-Windows semantics the slash ("/") is used to separate |
849 * path segments. | 889 * path segments. |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
955 result.write(host); | 995 result.write(host); |
956 result.write("\\"); | 996 result.write("\\"); |
957 } | 997 } |
958 result.writeAll(segments, "\\"); | 998 result.writeAll(segments, "\\"); |
959 if (hasDriveLetter && segments.length == 1) result.write("\\"); | 999 if (hasDriveLetter && segments.length == 1) result.write("\\"); |
960 return result.toString(); | 1000 return result.toString(); |
961 } | 1001 } |
962 | 1002 |
963 void _writeAuthority(StringSink ss) { | 1003 void _writeAuthority(StringSink ss) { |
964 _addIfNonEmpty(ss, userInfo, userInfo, "@"); | 1004 _addIfNonEmpty(ss, userInfo, userInfo, "@"); |
965 ss.write(host == null ? "null" : | 1005 ss.write(_host == null ? "null" : _host); |
966 host.contains(':') ? '[$host]' : host); | |
967 if (port != 0) { | 1006 if (port != 0) { |
968 ss.write(":"); | 1007 ss.write(":"); |
969 ss.write(port.toString()); | 1008 ss.write(port.toString()); |
970 } | 1009 } |
971 } | 1010 } |
972 | 1011 |
973 String toString() { | 1012 String toString() { |
974 StringBuffer sb = new StringBuffer(); | 1013 StringBuffer sb = new StringBuffer(); |
975 _addIfNonEmpty(sb, scheme, scheme, ':'); | 1014 _addIfNonEmpty(sb, scheme, scheme, ':'); |
976 if (hasAuthority || (scheme == "file")) { | 1015 if (hasAuthority || (scheme == "file")) { |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1141 } else if (index != 0) { | 1180 } else if (index != 0) { |
1142 var key = element.substring(0, index); | 1181 var key = element.substring(0, index); |
1143 var value = element.substring(index + 1); | 1182 var value = element.substring(index + 1); |
1144 map[Uri.decodeQueryComponent(key, decode: decode)] = | 1183 map[Uri.decodeQueryComponent(key, decode: decode)] = |
1145 decodeQueryComponent(value, decode: decode); | 1184 decodeQueryComponent(value, decode: decode); |
1146 } | 1185 } |
1147 return map; | 1186 return map; |
1148 }); | 1187 }); |
1149 } | 1188 } |
1150 | 1189 |
1190 /** | |
1191 * Parse the [host] as a IPv4 address, returning the address as a list of | |
floitsch
2013/09/10 12:11:46
as an IPv4
Søren Gjesse
2013/09/10 12:18:03
In socket we always write "IP version 4 (IPv4)".
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1192 * 4 bytes in network byte order (big endian). | |
1193 * | |
1194 * If it's unable to parse the address as IPv4, a `FormatException` will be | |
floitsch
2013/09/10 12:11:46
If [host] is not a valid IPv4 address representati
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1195 * thrown. | |
1196 */ | |
1197 static List<int> parseIPv4Address(String host) { | |
1198 var bytes = host.split('.'); | |
1199 if (bytes.length != 4) { | |
1200 throw new FormatException( | |
Søren Gjesse
2013/09/10 12:18:03
How about making all these FormatExceptions start
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1201 'A IPv4 address should contain exactly 4 parts'); | |
1202 } | |
1203 return bytes | |
1204 .map((byteString) { | |
1205 int byte = int.parse(byteString); | |
1206 if (byte < 0 || byte > 255) { | |
1207 throw new FormatException( | |
1208 'Each part must be in the range of `0...255`'); | |
Søren Gjesse
2013/09/10 12:18:03
Only two dots in range.
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1209 } | |
1210 return byte; | |
1211 }) | |
Søren Gjesse
2013/09/10 12:18:03
Maybe this should be a Uint8List.
floitsch
2013/09/10 12:23:32
Currently not possible. Afaik IE still doesn't hav
| |
1212 .toList(); | |
1213 } | |
1214 | |
1215 /** | |
1216 * Parse the [host] as a IPv6 address, returning the address as a list of | |
floitsch
2013/09/10 12:11:46
an IPv6
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1217 * 16 bytes in network byte order (big endian). | |
1218 * | |
1219 * If it's unable to parse the address as IPv6, a `FormatException` will be | |
floitsch
2013/09/10 12:11:46
ditto.
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1220 * thrown. | |
1221 */ | |
floitsch
2013/09/10 12:11:46
Provide examples.
This would make it also easier t
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1222 static List<int> parseIPv6Address(String host) { | |
1223 int parseHex(int start, int end) { | |
1224 if (end - start > 4) { | |
1225 throw new FormatException( | |
1226 'An IPv6 part can only contain a maximum of 4 hex digits'); | |
1227 } | |
1228 int value = int.parse(host.substring(start, end), radix: 16); | |
1229 if (value < 0 || value > (1 << 16) - 1) { | |
1230 throw new FormatException( | |
1231 'Each part must be in the range of `0x0...0xFFFF`'); | |
Søren Gjesse
2013/09/10 12:18:03
Only two dots in range.
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1232 } | |
1233 return value; | |
1234 } | |
1235 if (host.length < 2) throw new FormatException('IPv6 addres is too short'); | |
1236 List<int> segments = []; | |
1237 bool wildcardSeen = false; | |
1238 int partStart = 0; | |
1239 for (int i = 0; i < host.length; i++) { | |
floitsch
2013/09/10 12:11:46
Add comment that this will parse all segments exce
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1240 if (host.codeUnitAt(i) == _COLON) { | |
1241 if (i == 0) { | |
1242 // If we see a `:` in the beginning, expect wildcard. | |
1243 i++; | |
1244 if (host.codeUnitAt(i) != _COLON) { | |
1245 throw new FormatException('Invalid start colon.'); | |
1246 } | |
1247 partStart = i; | |
1248 } | |
1249 if (i == partStart) { | |
1250 // Wildcard. We only allow one. | |
1251 if (wildcardSeen) { | |
1252 throw new FormatException('Only one IPv6 wildcart `::` is allowed'); | |
floitsch
2013/09/10 12:11:46
wildcard
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1253 } | |
1254 wildcardSeen = true; | |
1255 segments.add(-1); | |
1256 } else { | |
1257 // Found a single colon. Parse [partStart..i] as a hex entry. | |
1258 segments.add(parseHex(partStart, i)); | |
1259 } | |
1260 partStart = i + 1; | |
1261 } | |
1262 } | |
1263 if (segments.length == 0) throw new FormatException('Too few IPv6 parts'); | |
1264 if (!(segments.last == -1 && partStart == host.length)) { | |
floitsch
2013/09/10 12:11:46
Do the check explicitly:
if (partStart == host.len
Anders Johnsen
2013/09/11 12:58:31
Done.
| |
1265 try { | |
1266 segments.add(parseHex(partStart, host.length)); | |
1267 } catch (e) { | |
1268 // Failed to parse the last chunk as hex. Try IPv4. | |
1269 try { | |
1270 List<int> last = parseIPv4Address(host.substring(partStart)); | |
1271 segments.add(last[0] << 8 | last[1]); | |
1272 segments.add(last[2] << 8 | last[3]); | |
1273 } catch (e) { | |
1274 throw new FormatException('Invalid end of IPv6 address.'); | |
1275 } | |
1276 } | |
1277 if (segments.length > 8 || | |
1278 (wildcardSeen && segments.length > 7)) { | |
1279 | |
1280 throw new FormatException( | |
1281 'An IPv6 address can only contain maximum 8 parts'); | |
1282 } | |
1283 } | |
1284 return segments | |
Søren Gjesse
2013/09/10 12:18:03
Wouldn't it be simpler to allocate a list of lengt
Anders Johnsen
2013/09/11 12:58:31
IMO this is easier, than using indexes into a list
| |
1285 .expand((value) { | |
1286 if (value == -1) { | |
1287 var list = []; | |
1288 for (int i = 0; i < 9 - segments.length; i++) { | |
1289 list.addAll(const [0, 0]); | |
1290 } | |
1291 return list; | |
1292 } else { | |
1293 return [(value >> 8) & 0xFF, value & 0xFF]; | |
1294 } | |
1295 }) | |
1296 .toList(); | |
1297 } | |
1298 | |
1151 // Frequently used character codes. | 1299 // Frequently used character codes. |
1152 static const int _DOUBLE_QUOTE = 0x22; | 1300 static const int _DOUBLE_QUOTE = 0x22; |
1153 static const int _PERCENT = 0x25; | 1301 static const int _PERCENT = 0x25; |
1154 static const int _ASTERISK = 0x2A; | 1302 static const int _ASTERISK = 0x2A; |
1155 static const int _PLUS = 0x2B; | 1303 static const int _PLUS = 0x2B; |
1156 static const int _SLASH = 0x2F; | 1304 static const int _SLASH = 0x2F; |
1157 static const int _ZERO = 0x30; | 1305 static const int _ZERO = 0x30; |
1158 static const int _NINE = 0x39; | 1306 static const int _NINE = 0x39; |
1159 static const int _COLON = 0x3A; | 1307 static const int _COLON = 0x3A; |
1160 static const int _LESS = 0x3C; | 1308 static const int _LESS = 0x3C; |
1161 static const int _GREATER = 0x3E; | 1309 static const int _GREATER = 0x3E; |
1162 static const int _QUESTION = 0x3F; | 1310 static const int _QUESTION = 0x3F; |
1163 static const int _AT_SIGN = 0x40; | 1311 static const int _AT_SIGN = 0x40; |
1164 static const int _UPPER_CASE_A = 0x41; | 1312 static const int _UPPER_CASE_A = 0x41; |
1165 static const int _UPPER_CASE_F = 0x46; | 1313 static const int _UPPER_CASE_F = 0x46; |
1166 static const int _UPPER_CASE_Z = 0x5A; | 1314 static const int _UPPER_CASE_Z = 0x5A; |
1315 static const int _LEFT_BRACKET = 0x5B; | |
1167 static const int _BACKSLASH = 0x5C; | 1316 static const int _BACKSLASH = 0x5C; |
1317 static const int _RIGHT_BRACKET = 0x5D; | |
1168 static const int _LOWER_CASE_A = 0x61; | 1318 static const int _LOWER_CASE_A = 0x61; |
1169 static const int _LOWER_CASE_F = 0x66; | 1319 static const int _LOWER_CASE_F = 0x66; |
1170 static const int _LOWER_CASE_Z = 0x7A; | 1320 static const int _LOWER_CASE_Z = 0x7A; |
1171 static const int _BAR = 0x7C; | 1321 static const int _BAR = 0x7C; |
1172 | 1322 |
1173 /** | 1323 /** |
1174 * This is the internal implementation of JavaScript's encodeURI function. | 1324 * This is the internal implementation of JavaScript's encodeURI function. |
1175 * It encodes all characters in the string [text] except for those | 1325 * It encodes all characters in the string [text] except for those |
1176 * that appear in [canonicalTable], and returns the escaped string. | 1326 * that appear in [canonicalTable], and returns the escaped string. |
1177 */ | 1327 */ |
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1465 void clear() { | 1615 void clear() { |
1466 throw new UnsupportedError("Cannot modify an unmodifiable map"); | 1616 throw new UnsupportedError("Cannot modify an unmodifiable map"); |
1467 } | 1617 } |
1468 void forEach(void f(K key, V value)) => _map.forEach(f); | 1618 void forEach(void f(K key, V value)) => _map.forEach(f); |
1469 Iterable<K> get keys => _map.keys; | 1619 Iterable<K> get keys => _map.keys; |
1470 Iterable<V> get values => _map.values; | 1620 Iterable<V> get values => _map.values; |
1471 int get length => _map.length; | 1621 int get length => _map.length; |
1472 bool get isEmpty => _map.isEmpty; | 1622 bool get isEmpty => _map.isEmpty; |
1473 bool get isNotEmpty => _map.isNotEmpty; | 1623 bool get isNotEmpty => _map.isNotEmpty; |
1474 } | 1624 } |
OLD | NEW |