Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
1 /*
2 * ====================================================================
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ====================================================================
19 *
20 * This software consists of voluntary contributions made by many
21 * individuals on behalf of the Apache Software Foundation. For more
22 * information on the Apache Software Foundation, please see
23 * <http://www.apache.org/>.
24 *
25 */
27 package ch.boye.httpclientandroidlib.client.utils;
29 import java.net.URI;
30 import java.net.URISyntaxException;
31 import java.util.Stack;
33 import ch.boye.httpclientandroidlib.annotation.Immutable;
35 import ch.boye.httpclientandroidlib.HttpHost;
37 /**
38 * A collection of utilities for {@link URI URIs}, to workaround
39 * bugs within the class or for ease-of-use features.
40 *
41 * @since 4.0
42 */
43 @Immutable
44 public class URIUtils {
46 /**
47 * Constructs a {@link URI} using all the parameters. This should be
48 * used instead of
49 * {@link URI#URI(String, String, String, int, String, String, String)}
50 * or any of the other URI multi-argument URI constructors.
51 *
52 * @param scheme
53 * Scheme name
54 * @param host
55 * Host name
56 * @param port
57 * Port number
58 * @param path
59 * Path
60 * @param query
61 * Query
62 * @param fragment
63 * Fragment
64 *
65 * @throws URISyntaxException
66 * If both a scheme and a path are given but the path is
67 * relative, if the URI string constructed from the given
68 * components violates RFC 2396, or if the authority
69 * component of the string is present but cannot be parsed
70 * as a server-based authority
71 */
72 public static URI createURI(
73 final String scheme,
74 final String host,
75 int port,
76 final String path,
77 final String query,
78 final String fragment) throws URISyntaxException {
80 StringBuilder buffer = new StringBuilder();
81 if (host != null) {
82 if (scheme != null) {
83 buffer.append(scheme);
84 buffer.append("://");
85 }
86 buffer.append(host);
87 if (port > 0) {
88 buffer.append(':');
89 buffer.append(port);
90 }
91 }
92 if (path == null || !path.startsWith("/")) {
93 buffer.append('/');
94 }
95 if (path != null) {
96 buffer.append(path);
97 }
98 if (query != null) {
99 buffer.append('?');
100 buffer.append(query);
101 }
102 if (fragment != null) {
103 buffer.append('#');
104 buffer.append(fragment);
105 }
106 return new URI(buffer.toString());
107 }
109 /**
110 * A convenience method for creating a new {@link URI} whose scheme, host
111 * and port are taken from the target host, but whose path, query and
112 * fragment are taken from the existing URI. The fragment is only used if
113 * dropFragment is false.
114 *
115 * @param uri
116 * Contains the path, query and fragment to use.
117 * @param target
118 * Contains the scheme, host and port to use.
119 * @param dropFragment
120 * True if the fragment should not be copied.
121 *
122 * @throws URISyntaxException
123 * If the resulting URI is invalid.
124 */
125 public static URI rewriteURI(
126 final URI uri,
127 final HttpHost target,
128 boolean dropFragment) throws URISyntaxException {
129 if (uri == null) {
130 throw new IllegalArgumentException("URI may nor be null");
131 }
132 if (target != null) {
133 return URIUtils.createURI(
134 target.getSchemeName(),
135 target.getHostName(),
136 target.getPort(),
137 normalizePath(uri.getRawPath()),
138 uri.getRawQuery(),
139 dropFragment ? null : uri.getRawFragment());
140 } else {
141 return URIUtils.createURI(
142 null,
143 null,
144 -1,
145 normalizePath(uri.getRawPath()),
146 uri.getRawQuery(),
147 dropFragment ? null : uri.getRawFragment());
148 }
149 }
151 private static String normalizePath(String path) {
152 if (path == null) {
153 return null;
154 }
155 int n = 0;
156 for (; n < path.length(); n++) {
157 if (path.charAt(n) != '/') {
158 break;
159 }
160 }
161 if (n > 1) {
162 path = path.substring(n - 1);
163 }
164 return path;
165 }
167 /**
168 * A convenience method for
169 * {@link URIUtils#rewriteURI(URI, HttpHost, boolean)} that always keeps the
170 * fragment.
171 */
172 public static URI rewriteURI(
173 final URI uri,
174 final HttpHost target) throws URISyntaxException {
175 return rewriteURI(uri, target, false);
176 }
178 /**
179 * Resolves a URI reference against a base URI. Work-around for bug in
180 * java.net.URI (<http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>)
181 *
182 * @param baseURI the base URI
183 * @param reference the URI reference
184 * @return the resulting URI
185 */
186 public static URI resolve(final URI baseURI, final String reference) {
187 return URIUtils.resolve(baseURI, URI.create(reference));
188 }
190 /**
191 * Resolves a URI reference against a base URI. Work-around for bugs in
192 * java.net.URI (e.g. <http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4708535>)
193 *
194 * @param baseURI the base URI
195 * @param reference the URI reference
196 * @return the resulting URI
197 */
198 public static URI resolve(final URI baseURI, URI reference){
199 if (baseURI == null) {
200 throw new IllegalArgumentException("Base URI may nor be null");
201 }
202 if (reference == null) {
203 throw new IllegalArgumentException("Reference URI may nor be null");
204 }
205 String s = reference.toString();
206 if (s.startsWith("?")) {
207 return resolveReferenceStartingWithQueryString(baseURI, reference);
208 }
209 boolean emptyReference = s.length() == 0;
210 if (emptyReference) {
211 reference = URI.create("#");
212 }
213 URI resolved = baseURI.resolve(reference);
214 if (emptyReference) {
215 String resolvedString = resolved.toString();
216 resolved = URI.create(resolvedString.substring(0,
217 resolvedString.indexOf('#')));
218 }
219 return removeDotSegments(resolved);
220 }
222 /**
223 * Resolves a reference starting with a query string.
224 *
225 * @param baseURI the base URI
226 * @param reference the URI reference starting with a query string
227 * @return the resulting URI
228 */
229 private static URI resolveReferenceStartingWithQueryString(
230 final URI baseURI, final URI reference) {
231 String baseUri = baseURI.toString();
232 baseUri = baseUri.indexOf('?') > -1 ?
233 baseUri.substring(0, baseUri.indexOf('?')) : baseUri;
234 return URI.create(baseUri + reference.toString());
235 }
237 /**
238 * Removes dot segments according to RFC 3986, section 5.2.4
239 *
240 * @param uri the original URI
241 * @return the URI without dot segments
242 */
243 private static URI removeDotSegments(URI uri) {
244 String path = uri.getPath();
245 if ((path == null) || (path.indexOf("/.") == -1)) {
246 // No dot segments to remove
247 return uri;
248 }
249 String[] inputSegments = path.split("/");
250 Stack<String> outputSegments = new Stack<String>();
251 for (int i = 0; i < inputSegments.length; i++) {
252 if ((inputSegments[i].length() == 0)
253 || (".".equals(inputSegments[i]))) {
254 // Do nothing
255 } else if ("..".equals(inputSegments[i])) {
256 if (!outputSegments.isEmpty()) {
257 outputSegments.pop();
258 }
259 } else {
260 outputSegments.push(inputSegments[i]);
261 }
262 }
263 StringBuilder outputBuffer = new StringBuilder();
264 for (String outputSegment : outputSegments) {
265 outputBuffer.append('/').append(outputSegment);
266 }
267 try {
268 return new URI(uri.getScheme(), uri.getAuthority(),
269 outputBuffer.toString(), uri.getQuery(), uri.getFragment());
270 } catch (URISyntaxException e) {
271 throw new IllegalArgumentException(e);
272 }
273 }
275 /**
276 * Extracts target host from the given {@link URI}.
277 *
278 * @param uri
279 * @return the target host if the URI is absolute or <code>null</null> if the URI is
280 * relative or does not contain a valid host name.
281 *
282 * @since 4.1
283 */
284 public static HttpHost extractHost(final URI uri) {
285 if (uri == null) {
286 return null;
287 }
288 HttpHost target = null;
289 if (uri.isAbsolute()) {
290 int port = uri.getPort(); // may be overridden later
291 String host = uri.getHost();
292 if (host == null) { // normal parse failed; let's do it ourselves
293 // authority does not seem to care about the valid character-set for host names
294 host = uri.getAuthority();
295 if (host != null) {
296 // Strip off any leading user credentials
297 int at = host.indexOf('@');
298 if (at >= 0) {
299 if (host.length() > at+1 ) {
300 host = host.substring(at+1);
301 } else {
302 host = null; // @ on its own
303 }
304 }
305 // Extract the port suffix, if present
306 if (host != null) {
307 int colon = host.indexOf(':');
308 if (colon >= 0) {
309 if (colon+1 < host.length()) {
310 port = Integer.parseInt(host.substring(colon+1));
311 }
312 host = host.substring(0,colon);
313 }
314 }
315 }
316 }
317 String scheme = uri.getScheme();
318 if (host != null) {
319 target = new HttpHost(host, port, scheme);
320 }
321 }
322 return target;
323 }
325 /**
326 * This class should not be instantiated.
327 */
328 private URIUtils() {
329 }
331 }