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