OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 part of dart.uri; |
| 6 |
| 7 /** |
| 8 * A parsed URI, inspired by Closure's [URI][] class. Implements [RFC-3986][]. |
| 9 * [uri]: http://closure-library.googlecode.com/svn/docs/class_goog_Uri.html |
| 10 * [RFC-3986]: http://tools.ietf.org/html/rfc3986#section-4.3) |
| 11 */ |
| 12 class Uri { |
| 13 final String scheme; |
| 14 final String userInfo; |
| 15 final String domain; |
| 16 final int port; |
| 17 final String path; |
| 18 final String query; |
| 19 final String fragment; |
| 20 |
| 21 /** |
| 22 * Deprecated. Please use [parse] instead. |
| 23 */ |
| 24 Uri.fromString(String uri) : this._fromMatch(_splitRe.firstMatch(uri)); |
| 25 |
| 26 static Uri parse(String uri) => new Uri._fromMatch(_splitRe.firstMatch(uri)); |
| 27 |
| 28 Uri._fromMatch(Match m) : |
| 29 this.fromComponents(scheme: _emptyIfNull(m[_COMPONENT_SCHEME]), |
| 30 userInfo: _emptyIfNull(m[_COMPONENT_USER_INFO]), |
| 31 domain: _emptyIfNull(m[_COMPONENT_DOMAIN]), |
| 32 port: _parseIntOrZero(m[_COMPONENT_PORT]), |
| 33 path: _emptyIfNull(m[_COMPONENT_PATH]), |
| 34 query: _emptyIfNull(m[_COMPONENT_QUERY_DATA]), |
| 35 fragment: _emptyIfNull(m[_COMPONENT_FRAGMENT])); |
| 36 |
| 37 const Uri.fromComponents({this.scheme: "", |
| 38 this.userInfo: "", |
| 39 this.domain: "", |
| 40 this.port: 0, |
| 41 this.path: "", |
| 42 this.query: "", |
| 43 this.fragment: ""}); |
| 44 |
| 45 Uri(String uri) : this.fromString(uri); |
| 46 |
| 47 static String _emptyIfNull(String val) => val != null ? val : ''; |
| 48 |
| 49 static int _parseIntOrZero(String val) { |
| 50 if (val != null && val != '') { |
| 51 return int.parse(val); |
| 52 } else { |
| 53 return 0; |
| 54 } |
| 55 } |
| 56 |
| 57 // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js |
| 58 static final RegExp _splitRe = new RegExp( |
| 59 '^' |
| 60 '(?:' |
| 61 '([^:/?#.]+)' // scheme - ignore special characters |
| 62 // used by other URL parts such as :, |
| 63 // ?, /, #, and . |
| 64 ':)?' |
| 65 '(?://' |
| 66 '(?:([^/?#]*)@)?' // userInfo |
| 67 '([\\w\\d\\-\\u0100-\\uffff.%]*)' |
| 68 // domain - restrict to letters, |
| 69 // digits, dashes, dots, percent |
| 70 // escapes, and unicode characters. |
| 71 '(?::([0-9]+))?' // port |
| 72 ')?' |
| 73 '([^?#]+)?' // path |
| 74 '(?:\\?([^#]*))?' // query |
| 75 '(?:#(.*))?' // fragment |
| 76 '\$'); |
| 77 |
| 78 static const _COMPONENT_SCHEME = 1; |
| 79 static const _COMPONENT_USER_INFO = 2; |
| 80 static const _COMPONENT_DOMAIN = 3; |
| 81 static const _COMPONENT_PORT = 4; |
| 82 static const _COMPONENT_PATH = 5; |
| 83 static const _COMPONENT_QUERY_DATA = 6; |
| 84 static const _COMPONENT_FRAGMENT = 7; |
| 85 |
| 86 /** |
| 87 * Returns `true` if the URI is absolute. |
| 88 */ |
| 89 bool get isAbsolute { |
| 90 if ("" == scheme) return false; |
| 91 if ("" != fragment) return false; |
| 92 return true; |
| 93 |
| 94 /* absolute-URI = scheme ":" hier-part [ "?" query ] |
| 95 * hier-part = "//" authority path-abempty |
| 96 * / path-absolute |
| 97 * / path-rootless |
| 98 * / path-empty |
| 99 * |
| 100 * path = path-abempty ; begins with "/" or is empty |
| 101 * / path-absolute ; begins with "/" but not "//" |
| 102 * / path-noscheme ; begins with a non-colon segment |
| 103 * / path-rootless ; begins with a segment |
| 104 * / path-empty ; zero characters |
| 105 * |
| 106 * path-abempty = *( "/" segment ) |
| 107 * path-absolute = "/" [ segment-nz *( "/" segment ) ] |
| 108 * path-noscheme = segment-nz-nc *( "/" segment ) |
| 109 * path-rootless = segment-nz *( "/" segment ) |
| 110 * path-empty = 0<pchar> |
| 111 * segment = *pchar |
| 112 * segment-nz = 1*pchar |
| 113 * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) |
| 114 * ; non-zero-length segment without any colon ":" |
| 115 * |
| 116 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" |
| 117 */ |
| 118 } |
| 119 |
| 120 Uri resolve(String uri) { |
| 121 return resolveUri(Uri.parse(uri)); |
| 122 } |
| 123 |
| 124 Uri resolveUri(Uri reference) { |
| 125 // From RFC 3986. |
| 126 String targetScheme; |
| 127 String targetUserInfo; |
| 128 String targetDomain; |
| 129 int targetPort; |
| 130 String targetPath; |
| 131 String targetQuery; |
| 132 if (reference.scheme != "") { |
| 133 targetScheme = reference.scheme; |
| 134 targetUserInfo = reference.userInfo; |
| 135 targetDomain = reference.domain; |
| 136 targetPort = reference.port; |
| 137 targetPath = removeDotSegments(reference.path); |
| 138 targetQuery = reference.query; |
| 139 } else { |
| 140 if (reference.hasAuthority) { |
| 141 targetUserInfo = reference.userInfo; |
| 142 targetDomain = reference.domain; |
| 143 targetPort = reference.port; |
| 144 targetPath = removeDotSegments(reference.path); |
| 145 targetQuery = reference.query; |
| 146 } else { |
| 147 if (reference.path == "") { |
| 148 targetPath = this.path; |
| 149 if (reference.query != "") { |
| 150 targetQuery = reference.query; |
| 151 } else { |
| 152 targetQuery = this.query; |
| 153 } |
| 154 } else { |
| 155 if (reference.path.startsWith("/")) { |
| 156 targetPath = removeDotSegments(reference.path); |
| 157 } else { |
| 158 targetPath = removeDotSegments(merge(this.path, reference.path)); |
| 159 } |
| 160 targetQuery = reference.query; |
| 161 } |
| 162 targetUserInfo = this.userInfo; |
| 163 targetDomain = this.domain; |
| 164 targetPort = this.port; |
| 165 } |
| 166 targetScheme = this.scheme; |
| 167 } |
| 168 return new Uri.fromComponents(scheme: targetScheme, |
| 169 userInfo: targetUserInfo, |
| 170 domain: targetDomain, |
| 171 port: targetPort, |
| 172 path: targetPath, |
| 173 query: targetQuery, |
| 174 fragment: reference.fragment); |
| 175 } |
| 176 |
| 177 bool get hasAuthority { |
| 178 return (userInfo != "") || (domain != "") || (port != 0); |
| 179 } |
| 180 |
| 181 /** |
| 182 * For http/https schemes returns URI's [origin][] - scheme://domain:port. |
| 183 * For all other schemes throws ArgumentError. |
| 184 * [origin]: http://www.w3.org/TR/2011/WD-html5-20110405/origin-0.html#origin |
| 185 */ |
| 186 String get origin { |
| 187 if (scheme == "") { |
| 188 // TODO(aprelev@gmail.com): Use StateException instead |
| 189 throw new ArgumentError("Cannot use origin without a scheme"); |
| 190 } |
| 191 if (scheme != "http" && scheme != "https") { |
| 192 // TODO(aprelev@gmail.com): Use StateException instead |
| 193 throw new ArgumentError( |
| 194 "origin is applicable to http/https schemes only. Not \'$scheme\'"); |
| 195 } |
| 196 StringBuffer sb = new StringBuffer(); |
| 197 sb.add(scheme); |
| 198 sb.add(":"); |
| 199 if (domain == null || domain == "") { |
| 200 // TODO(aprelev@gmail.com): Use StateException instead |
| 201 throw new ArgumentError("Cannot use origin without a domain"); |
| 202 } |
| 203 |
| 204 sb.add("//"); |
| 205 sb.add(domain); |
| 206 if (port != 0) { |
| 207 sb.add(":"); |
| 208 sb.add(port); |
| 209 } |
| 210 return sb.toString(); |
| 211 } |
| 212 |
| 213 String toString() { |
| 214 StringBuffer sb = new StringBuffer(); |
| 215 _addIfNonEmpty(sb, scheme, scheme, ':'); |
| 216 if (hasAuthority || (scheme == "file")) { |
| 217 sb.add("//"); |
| 218 _addIfNonEmpty(sb, userInfo, userInfo, "@"); |
| 219 sb.add(domain == null ? "null" : domain); |
| 220 if (port != 0) { |
| 221 sb.add(":"); |
| 222 sb.add(port.toString()); |
| 223 } |
| 224 } |
| 225 sb.add(path == null ? "null" : path); |
| 226 _addIfNonEmpty(sb, query, "?", query); |
| 227 _addIfNonEmpty(sb, fragment, "#", fragment); |
| 228 return sb.toString(); |
| 229 } |
| 230 |
| 231 bool operator==(other) { |
| 232 if (other is! Uri) return false; |
| 233 Uri uri = other; |
| 234 return scheme == uri.scheme && |
| 235 userInfo == uri.userInfo && |
| 236 domain == uri.domain && |
| 237 port == uri.port && |
| 238 path == uri.path && |
| 239 query == uri.query && |
| 240 fragment == uri.fragment; |
| 241 } |
| 242 |
| 243 int get hashCode { |
| 244 int combine(part, current) { |
| 245 // The sum is truncated to 30 bits to make sure it fits into a Smi. |
| 246 return (current * 31 + part.hashCode) & 0x3FFFFFFF; |
| 247 } |
| 248 return combine(scheme, combine(userInfo, combine(domain, combine(port, |
| 249 combine(path, combine(query, combine(fragment, 1))))))); |
| 250 } |
| 251 |
| 252 static void _addIfNonEmpty(StringBuffer sb, String test, |
| 253 String first, String second) { |
| 254 if ("" != test) { |
| 255 sb.add(first == null ? "null" : first); |
| 256 sb.add(second == null ? "null" : second); |
| 257 } |
| 258 } |
| 259 } |
OLD | NEW |