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 |