| 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 #library('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 Uri.fromString(String uri) : this._fromMatch(_splitRe.firstMatch(uri)); | |
| 22 | |
| 23 Uri._fromMatch(Match m) : this(_emptyIfNull(m[_COMPONENT_SCHEME]), | |
| 24 _emptyIfNull(m[_COMPONENT_USER_INFO]), | |
| 25 _emptyIfNull(m[_COMPONENT_DOMAIN]), | |
| 26 _parseIntOrZero(m[_COMPONENT_PORT]), | |
| 27 _emptyIfNull(m[_COMPONENT_PATH]), | |
| 28 _emptyIfNull(m[_COMPONENT_QUERY_DATA]), | |
| 29 _emptyIfNull(m[_COMPONENT_FRAGMENT])); | |
| 30 | |
| 31 const Uri([String this.scheme = "", String this.userInfo ="", | |
| 32 String this.domain = "", int this.port = 0, | |
| 33 String this.path = "", String this.query = "", | |
| 34 String this.fragment = ""]); | |
| 35 | |
| 36 static String _emptyIfNull(String val) => val != null ? val : ''; | |
| 37 | |
| 38 static int _parseIntOrZero(String val) { | |
| 39 if (val !== null && val != '') { | |
| 40 return Math.parseInt(val); | |
| 41 } else { | |
| 42 return 0; | |
| 43 } | |
| 44 } | |
| 45 | |
| 46 // NOTE: This code was ported from: closure-library/closure/goog/uri/utils.js | |
| 47 static final RegExp _splitRe = const RegExp( | |
| 48 '^' | |
| 49 '(?:' | |
| 50 '([^:/?#.]+)' // scheme - ignore special characters | |
| 51 // used by other URL parts such as :, | |
| 52 // ?, /, #, and . | |
| 53 ':)?' | |
| 54 '(?://' | |
| 55 '(?:([^/?#]*)@)?' // userInfo | |
| 56 '([\\w\\d\\-\\u0100-\\uffff.%]*)' | |
| 57 // domain - restrict to letters, | |
| 58 // digits, dashes, dots, percent | |
| 59 // escapes, and unicode characters. | |
| 60 '(?::([0-9]+))?' // port | |
| 61 ')?' | |
| 62 '([^?#]+)?' // path | |
| 63 '(?:\\?([^#]*))?' // query | |
| 64 '(?:#(.*))?' // fragment | |
| 65 '\$'); | |
| 66 | |
| 67 static final _COMPONENT_SCHEME = 1; | |
| 68 static final _COMPONENT_USER_INFO = 2; | |
| 69 static final _COMPONENT_DOMAIN = 3; | |
| 70 static final _COMPONENT_PORT = 4; | |
| 71 static final _COMPONENT_PATH = 5; | |
| 72 static final _COMPONENT_QUERY_DATA = 6; | |
| 73 static final _COMPONENT_FRAGMENT = 7; | |
| 74 | |
| 75 /** | |
| 76 * Returns `true` if the URI is absolute. | |
| 77 */ | |
| 78 bool isAbsolute() { | |
| 79 if ("" == scheme) return false; | |
| 80 if ("" != fragment) return false; | |
| 81 return true; | |
| 82 | |
| 83 /* absolute-URI = scheme ":" hier-part [ "?" query ] | |
| 84 * hier-part = "//" authority path-abempty | |
| 85 * / path-absolute | |
| 86 * / path-rootless | |
| 87 * / path-empty | |
| 88 * | |
| 89 * path = path-abempty ; begins with "/" or is empty | |
| 90 * / path-absolute ; begins with "/" but not "//" | |
| 91 * / path-noscheme ; begins with a non-colon segment | |
| 92 * / path-rootless ; begins with a segment | |
| 93 * / path-empty ; zero characters | |
| 94 * | |
| 95 * path-abempty = *( "/" segment ) | |
| 96 * path-absolute = "/" [ segment-nz *( "/" segment ) ] | |
| 97 * path-noscheme = segment-nz-nc *( "/" segment ) | |
| 98 * path-rootless = segment-nz *( "/" segment ) | |
| 99 * path-empty = 0<pchar> | |
| 100 * segment = *pchar | |
| 101 * segment-nz = 1*pchar | |
| 102 * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) | |
| 103 * ; non-zero-length segment without any colon ":" | |
| 104 * | |
| 105 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | |
| 106 */ | |
| 107 } | |
| 108 | |
| 109 Uri resolve(String uri) { | |
| 110 return resolveUri(new Uri.fromString(uri)); | |
| 111 } | |
| 112 | |
| 113 Uri resolveUri(Uri reference) { | |
| 114 // From RFC 3986. | |
| 115 String targetScheme; | |
| 116 String targetUserInfo; | |
| 117 String targetDomain; | |
| 118 int targetPort; | |
| 119 String targetPath; | |
| 120 String targetQuery; | |
| 121 if (reference.scheme != "") { | |
| 122 targetScheme = reference.scheme; | |
| 123 targetUserInfo = reference.userInfo; | |
| 124 targetDomain = reference.domain; | |
| 125 targetPort = reference.port; | |
| 126 targetPath = removeDotSegments(reference.path); | |
| 127 targetQuery = reference.query; | |
| 128 } else { | |
| 129 if (reference.hasAuthority()) { | |
| 130 targetUserInfo = reference.userInfo; | |
| 131 targetDomain = reference.domain; | |
| 132 targetPort = reference.port; | |
| 133 targetPath = removeDotSegments(reference.path); | |
| 134 targetQuery = reference.query; | |
| 135 } else { | |
| 136 if (reference.path == "") { | |
| 137 targetPath = this.path; | |
| 138 if (reference.query != "") { | |
| 139 targetQuery = reference.query; | |
| 140 } else { | |
| 141 targetQuery = this.query; | |
| 142 } | |
| 143 } else { | |
| 144 if (reference.path.startsWith("/")) { | |
| 145 targetPath = removeDotSegments(reference.path); | |
| 146 } else { | |
| 147 targetPath = removeDotSegments(merge(this.path, reference.path)); | |
| 148 } | |
| 149 targetQuery = reference.query; | |
| 150 } | |
| 151 targetUserInfo = this.userInfo; | |
| 152 targetDomain = this.domain; | |
| 153 targetPort = this.port; | |
| 154 } | |
| 155 targetScheme = this.scheme; | |
| 156 } | |
| 157 return new Uri(targetScheme, targetUserInfo, targetDomain, targetPort, | |
| 158 targetPath, targetQuery, reference.fragment); | |
| 159 } | |
| 160 | |
| 161 bool hasAuthority() { | |
| 162 return (userInfo != "") || (domain != "") || (port != 0); | |
| 163 } | |
| 164 | |
| 165 String toString() { | |
| 166 StringBuffer sb = new StringBuffer(); | |
| 167 _addIfNonEmpty(sb, scheme, scheme, ':'); | |
| 168 if (hasAuthority() || (scheme == "file")) { | |
| 169 sb.add("//"); | |
| 170 _addIfNonEmpty(sb, userInfo, userInfo, "@"); | |
| 171 sb.add(domain === null ? "null" : domain); | |
| 172 if (port != 0) { | |
| 173 sb.add(":"); | |
| 174 sb.add(port.toString()); | |
| 175 } | |
| 176 } | |
| 177 sb.add(path === null ? "null" : path); | |
| 178 _addIfNonEmpty(sb, query, "?", query); | |
| 179 _addIfNonEmpty(sb, fragment, "#", fragment); | |
| 180 return sb.toString(); | |
| 181 } | |
| 182 | |
| 183 static void _addIfNonEmpty(StringBuffer sb, String test, | |
| 184 String first, String second) { | |
| 185 if ("" != test) { | |
| 186 sb.add(first === null ? "null" : first); | |
| 187 sb.add(second === null ? "null" : second); | |
| 188 } | |
| 189 } | |
| 190 } | |
| 191 | |
| 192 String merge(String base, String reference) { | 5 String merge(String base, String reference) { |
| 193 if (base == "") return "/$reference"; | 6 if (base == "") return "/$reference"; |
| 194 return "${base.substring(0, base.lastIndexOf("/") + 1)}$reference"; | 7 return "${base.substring(0, base.lastIndexOf("/") + 1)}$reference"; |
| 195 } | 8 } |
| 196 | 9 |
| 197 String removeDotSegments(String path) { | 10 String removeDotSegments(String path) { |
| 198 List<String> output = []; | 11 List<String> output = []; |
| 199 bool appendSlash = false; | 12 bool appendSlash = false; |
| 200 for (String segment in path.split("/")) { | 13 for (String segment in path.split("/")) { |
| 201 appendSlash = false; | 14 appendSlash = false; |
| 202 if (segment == "..") { | 15 if (segment == "..") { |
| 203 if (!output.isEmpty() && | 16 if (!output.isEmpty() && |
| 204 ((output.length != 1) || (output[0] != ""))) output.removeLast(); | 17 ((output.length != 1) || (output[0] != ""))) output.removeLast(); |
| 205 appendSlash = true; | 18 appendSlash = true; |
| 206 } else if ("." == segment) { | 19 } else if ("." == segment) { |
| 207 appendSlash = true; | 20 appendSlash = true; |
| 208 } else { | 21 } else { |
| 209 output.add(segment); | 22 output.add(segment); |
| 210 } | 23 } |
| 211 } | 24 } |
| 212 if (appendSlash) output.add(""); | 25 if (appendSlash) output.add(""); |
| 213 return Strings.join(output, "/"); | 26 return Strings.join(output, "/"); |
| 214 } | 27 } |
| OLD | NEW |