GlueGen v2.6.0-rc-20250712
GlueGen, Native Binding Generator for Java™ (public API).
Uri.java
Go to the documentation of this file.
1/**
2 * Copyright 2014 JogAmp Community. All rights reserved.
3 * Copyright 2006, 2010 The Apache Software Foundation.
4 *
5 * This code is derived from the Apache Harmony project's {@code class java.net.URI.Helper},
6 * and has been heavily modified for GlueGen/JogAmp.
7 *
8 * Licensed to the Apache Software Foundation (ASF) under one or more
9 * contributor license agreements. See the LICENSE.txt file distributed with
10 * this work for additional information regarding copyright ownership.
11 * The ASF licenses this file to You under the Apache License, Version 2.0
12 * (the "License"); you may not use this file except in compliance with
13 * the License. You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23package com.jogamp.common.net;
24
25import java.io.File;
26import java.io.UnsupportedEncodingException;
27import java.net.MalformedURLException;
28import java.net.URISyntaxException;
29import java.util.StringTokenizer;
30import java.util.regex.Pattern;
31
32import jogamp.common.Debug;
33
34import com.jogamp.common.util.IOUtil;
35import com.jogamp.common.util.PropertyAccess;
36
37/**
38 * This class implements an immutable Uri as defined by <a href="https://tools.ietf.org/html/rfc2396">RFC 2396</a>.
39 * <p>
40 * Character encoding is employed as defined by <a href="https://tools.ietf.org/html/rfc3986">RFC 3986</a>,
41 * see <a href="https://tools.ietf.org/html/rfc3986#section-2.1">RFC 3986 section 2.1</a>,
42 * while multibyte unicode characters are preserved in encoded parts.
43 * </p>
44 *
45 * <pre>
46 1 [scheme:]scheme-specific-part[#fragment]
47 2 [scheme:][//authority]path[?query][#fragment]
48 3 [scheme:][//[user-info@]host[:port]]path[?query][#fragment]
49
50 scheme-specific-part: [//authority]path[?query]
51 authority: [user-info@]host[:port]
52 * </pre>
53 * <p>
54 * <a href="https://tools.ietf.org/html/rfc3986#section-2.2">RFC 3986 section 2.2</a> <i>Reserved Characters</i> (January 2005)
55 * <table border="1">
56 <tr>
57 <td><code>!</code></td>
58 <td><code>*</code></td>
59 <td><code>'</code></td>
60 <td><code>(</code></td>
61 <td><code>)</code></td>
62 <td><code>;</code></td>
63 <td><code>:</code></td>
64 <td><code>@</code></td>
65 <td><code>&amp;</code></td>
66 <td><code>=</code></td>
67 <td><code>+</code></td>
68 <td><code>$</code></td>
69 <td><code>,</code></td>
70 <td><code>/</code></td>
71 <td><code>?</code></td>
72 <td><code>#</code></td>
73 <td><code>[</code></td>
74 <td><code>]</code></td>
75 </tr>
76 * </table>
77 * </p>
78 * <p>
79 * <a href="https://tools.ietf.org/html/rfc3986#section-2.3">RFC 3986 section 2.3</a> <i>Unreserved Characters</i> (January 2005)
80 * <table border="1">
81 <tr>
82 <td><code>A</code></td>
83 <td><code>B</code></td>
84 <td><code>C</code></td>
85 <td><code>D</code></td>
86 <td><code>E</code></td>
87 <td><code>F</code></td>
88 <td><code>G</code></td>
89 <td><code>H</code></td>
90 <td><code>I</code></td>
91 <td><code>J</code></td>
92 <td><code>K</code></td>
93 <td><code>L</code></td>
94 <td><code>M</code></td>
95 <td><code>N</code></td>
96 <td><code>O</code></td>
97 <td><code>P</code></td>
98 <td><code>Q</code></td>
99 <td><code>R</code></td>
100 <td><code>S</code></td>
101 <td><code>T</code></td>
102 <td><code>U</code></td>
103 <td><code>V</code></td>
104 <td><code>W</code></td>
105 <td><code>X</code></td>
106 <td><code>Y</code></td>
107 <td><code>Z</code></td>
108 </tr>
109 <tr>
110 <td><code>a</code></td>
111 <td><code>b</code></td>
112 <td><code>c</code></td>
113 <td><code>d</code></td>
114 <td><code>e</code></td>
115 <td><code>f</code></td>
116 <td><code>g</code></td>
117 <td><code>h</code></td>
118 <td><code>i</code></td>
119 <td><code>j</code></td>
120 <td><code>k</code></td>
121 <td><code>l</code></td>
122 <td><code>m</code></td>
123 <td><code>n</code></td>
124 <td><code>o</code></td>
125 <td><code>p</code></td>
126 <td><code>q</code></td>
127 <td><code>r</code></td>
128 <td><code>s</code></td>
129 <td><code>t</code></td>
130 <td><code>u</code></td>
131 <td><code>v</code></td>
132 <td><code>w</code></td>
133 <td><code>x</code></td>
134 <td><code>y</code></td>
135 <td><code>z</code></td>
136 </tr>
137 <tr>
138 <td><code>0</code></td>
139 <td><code>1</code></td>
140 <td><code>2</code></td>
141 <td><code>3</code></td>
142 <td><code>4</code></td>
143 <td><code>5</code></td>
144 <td><code>6</code></td>
145 <td><code>7</code></td>
146 <td><code>8</code></td>
147 <td><code>9</code></td>
148 <td><code>-</code></td>
149 <td><code>_</code></td>
150 <td><code>.</code></td>
151 <td><code>~</code></td>
152 </tr>
153 * </table>
154 * </p>
155 * <p>
156 * Other characters in a Uri must be percent encoded.
157 * </p>
158 * @since 2.2.1
159 */
160public class Uri {
161 private static final boolean DEBUG;
162 private static final boolean DEBUG_SHOWFIX;
163
164 static {
165 Debug.initSingleton();
166 DEBUG = IOUtil.DEBUG || Debug.debug("Uri");
167 DEBUG_SHOWFIX = PropertyAccess.isPropertyDefined("jogamp.debug.Uri.ShowFix", true);
168 }
169
170 /**
171 * Usually used to fix a path from a previously contained and opaque Uri,
172 * i.e. {@link #getContainedUri()}.
173 * <p>
174 * Such an opaque Uri w/ erroneous encoding may have been injected via
175 * {@link #valueOf(URI)} and {@link #valueOf(URL)} where the given URL or URI was opaque!
176 * </p>
177 * <p>
178 * This remedies issues when dealing w/ java URI/URL opaque sources,
179 * which do not comply to the spec, i.e. containe un-encoded chars, e.g. ':', '$', ..
180 * </p>
181 */
182 private static final int PARSE_HINT_FIX_PATH = 1 << 0;
183
184 private static final String DIGITS = "0123456789ABCDEF";
185
186 private static final String ENCODING = "UTF8";
187 private static final String MSG_ENCODING_NA = "Charset UTF8 not available";
188 private static final Pattern patternSingleFS = Pattern.compile("/{1}");
189
190 /**
191 * RFC 3986 section 2.3 Unreserved Characters (January 2005)
192 * <p>
193 * {@value} + {@code alphanum}
194 * </p>
195 */
196 public static final String UNRESERVED = "_-.~";
197 // Harmony: _ - ! . ~ ' ( ) *
198
199 private static final String punct = ",;:$&+=";
200 // Harmony: , ; : $ & + =
201
202 /**
203 * RFC 3986 section 2.2 Reserved Characters (January 2005)
204 * <p>
205 * {@value} + {@code alphanum}
206 * </p>
207 */
208 public static final String RESERVED = punct + "!*\'()@/?#[]";
209 // Harmony: , ; : $ & + = ? / [ ] @
210
211 public static final String RESERVED_2 = punct + "!*\'()@/?[]";
212 // Harmony: , ; : $ & + = ? / [ ] @
213
214 // Bug 908, issues w/ windows file path char: $ ^ ~ # [ ]
215 // Windows invalid File characters: * ? " < > |
216
217 /**
218 * Valid charset for RFC 2396 {@code authority}'s {@code user-info},
219 * additional to legal {@code alphanum} characters.
220 * <p>
221 * {@value} + {@code alphanum}
222 * </p>
223 */
224 public static final String USERINFO_LEGAL = UNRESERVED + punct;
225 // Harmony: someLegal = unreserved + punct -> _ - ! . ~ ' ( ) * , ; : $ & + =
226
227 /**
228 * Valid charset for RFC 2396 {@code authority},
229 * additional to legal {@code alphanum} characters.
230 * <p>
231 * {@value} + {@code alphanum}
232 * </p>
233 */
234 public static final String AUTHORITY_LEGAL = "@[]" + USERINFO_LEGAL;
235
236 /**
237 * Valid charset for RFC 2396 {@code path},
238 * additional to legal {@code alphanum} characters.
239 * <p>
240 * {@value} + {@code alphanum}
241 * </p>
242 */
243 public static final String PATH_LEGAL = "/!" + UNRESERVED; // no RESERVED chars but '!', to allow JAR Uris;
244 // Harmony: "/@" + unreserved + punct -> / @ _ - ! . ~ \ ' ( ) * , ; : $ & + =
245
246 /**
247 * Valid charset for RFC 2396 {@code query},
248 * additional to legal {@code alphanum} characters.
249 * <p>
250 * {@value} + {@code alphanum}
251 * </p>
252 */
253 public static final String QUERY_LEGAL = UNRESERVED + RESERVED_2 + "\\\"";
254 // Harmony: unreserved + reserved + "\\\""
255
256 /**
257 * Valid charset for RFC 2396 {@code scheme-specific-part},
258 * additional to legal {@code alphanum} characters.
259 * <p>
260 * {@value} + {@code alphanum}
261 * </p>
262 */
263 public static final String SSP_LEGAL = QUERY_LEGAL;
264 // Harmony: unreserved + reserved
265
266 /**
267 * Valid charset for RFC 2396 {@code fragment},
268 * additional to legal {@code alphanum} characters.
269 * <p>
270 * {@value} + {@code alphanum}
271 * </p>
272 */
273 public static final String FRAG_LEGAL = UNRESERVED + RESERVED;
274 // Harmony: unreserved + reserved
275
276 /** {@value} */
277 public static final char SCHEME_SEPARATOR = ':';
278 /** {@value} */
279 public static final char QUERY_SEPARATOR = '?';
280 /** {@value} */
281 public static final char FRAGMENT_SEPARATOR = '#';
282 /** {@value} */
283 public static final String FILE_SCHEME = "file";
284 /** {@value} */
285 public static final String HTTP_SCHEME = "http";
286 /** {@value} */
287 public static final String HTTPS_SCHEME = "https";
288 /** {@value} */
289 public static final String JAR_SCHEME = "jar";
290 /** A JAR sub-protocol is separated from the JAR entry w/ this separator {@value}. Even if no class is specified '!/' must follow!. */
291 public static final char JAR_SCHEME_SEPARATOR = '!';
292
293 /**
294 * Immutable RFC3986 encoded string.
295 */
296 public static class Encoded implements Comparable<Encoded>, CharSequence {
297 private final String s;
298
299 /**
300 * Casts the given encoded String by creating a new Encoded instance.
301 * <p>
302 * No encoding will be performed, use with care.
303 * </p>
304 */
305 public static Encoded cast(final String encoded) {
306 return new Encoded(encoded);
307 }
308
309 Encoded(final String encodedString) {
310 this.s = encodedString;
311 }
312
313 /**
314 * Encodes all characters into their hexadecimal value prepended by '%', except:
315 * <ol>
316 * <li>letters ('a'..'z', 'A'..'Z')</li>
317 * <li>numbers ('0'..'9')</li>
318 * <li>characters in the legal-set parameter</li>
319 * <li> others (unicode characters that are not in
320 * US-ASCII set, and are not ISO Control or are not ISO Space characters)</li>
321 * </ol>
322 * <p>
323 * Uses {@link Uri#encode(String, String)} for implementation..
324 * </p>
325 *
326 * @param vanilla the string to be encoded
327 * @param legal extended character set, allowed to be preserved in the vanilla string
328 */
329 public Encoded(final String vanilla, final String legal) {
330 this.s = encode(vanilla, legal);
331 }
332
333 public boolean isASCII() { return false; }
334
335 /** Returns the encoded String */
336 public final String get() { return s; }
337
338 /**
339 * Decodes the string argument which is assumed to be encoded in the {@code
340 * x-www-form-urlencoded} MIME content type using the UTF-8 encoding scheme.
341 * <p>
342 *'%' and two following hex digit characters are converted to the
343 * equivalent byte value. All other characters are passed through
344 * unmodified.
345 * </p>
346 * <p>
347 * e.g. "A%20B%20C %24%25" -> "A B C $%"
348 * </p>
349 * <p>
350 * Uses {@link Uri#decode(String)} for implementation..
351 * </p>
352 */
353 public final String decode() { return Uri.decode(s); }
354
355 //
356 // Basic Object / Identity
357 //
358
359 /**
360 * {@inheritDoc}
361 * <p>
362 * Returns the encoded String, same as {@link #get()}.
363 * </p>
364 */
365 @Override
366 public final String toString() { return s; }
367
368 @Override
369 public final int hashCode() { return s.hashCode(); }
370
371 /**
372 * {@inheritDoc}
373 *
374 * @param o The comparison argument, either a {@link Encoded} or a {@link String}
375 *
376 * @return {@code true} if the given object is equivalent to this instance,
377 * otherwise {@code false}.
378 *
379 * @see #compareTo(Encoded)
380 * @see #equalsIgnoreCase(Encoded)
381 */
382 @Override
383 public final boolean equals(final Object o) {
384 if (this == o) {
385 return true;
386 }
387 if (o instanceof Encoded) {
388 return s.equals(((Encoded)o).s);
389 }
390 return s.equals(o);
391 }
392
393 //
394 // CharSequence
395 //
396
397 @Override
398 public final int length() { return s.length(); }
399
400 @Override
401 public final char charAt(final int index) { return s.charAt(index); }
402
403 @Override
404 public final CharSequence subSequence(final int start, final int end) { return s.subSequence(start, end); }
405
406 @Override
407 public final int compareTo(final Encoded o) { return s.compareTo(o.s); }
408
409 //
410 // String derived ..
411 //
412 /** See {@link String#concat(String)}. */
413 public Encoded concat(final Encoded encoded) { return new Encoded(s.concat(encoded.s)); }
414
415 /** See {@link String#substring(int)}. */
416 public final Encoded substring(final int start) { return new Encoded(s.substring(start)); }
417 /** See {@link String#substring(int, int)}. */
418 public final Encoded substring(final int start, final int end) { return new Encoded(s.substring(start, end)); }
419
420 /** See {@link String#indexOf(int)}. */
421 public final int indexOf(final int ch) { return s.indexOf(ch); }
422 /** See {@link String#indexOf(int, int)}. */
423 public final int indexOf(final int ch, final int fromIndex) { return s.indexOf(ch, fromIndex); }
424 /** See {@link String#indexOf(String)}. */
425 public final int indexOf(final String str) { return s.indexOf(str); }
426 /** See {@link String#indexOf(String, int)}. */
427 public final int indexOf(final String str, final int fromIndex) { return s.indexOf(str, fromIndex); }
428
429 /** See {@link String#lastIndexOf(int)}. */
430 public final int lastIndexOf(final int ch) { return s.lastIndexOf(ch); }
431 /** See {@link String#lastIndexOf(int, int)}. */
432 public int lastIndexOf(final int ch, final int fromIndex) { return s.lastIndexOf(ch, fromIndex); }
433 /** See {@link String#lastIndexOf(String)}. */
434 public int lastIndexOf(final String str) { return s.lastIndexOf(str); }
435 /** See {@link String#lastIndexOf(String, int)}. */
436 public int lastIndexOf(final String str, final int fromIndex) { return s.lastIndexOf(str, fromIndex); }
437
438 /** See {@link String#startsWith(String)} */
439 public boolean startsWith(final String prefix) { return s.startsWith(prefix); }
440 /** See {@link String#startsWith(String, int)} */
441 public boolean startsWith(final String prefix, final int toffset) { return s.startsWith(prefix, toffset); }
442 /** See {@link String#endsWith(String)} */
443 public boolean endsWith(final String suffix) { return s.endsWith(suffix); }
444
445 /** See {@link String#equalsIgnoreCase(String)}. */
446 public final boolean equalsIgnoreCase(final Encoded anotherEncoded) { return s.equalsIgnoreCase(anotherEncoded.s); }
447 }
448
449 public static class ASCIIEncoded extends Encoded {
450 /**
451 * Casts the given encoded String by creating a new ASCIIEncoded instance.
452 * <p>
453 * No encoding will be performed, use with care.
454 * </p>
455 */
456 public static ASCIIEncoded cast(final String encoded) {
457 return new ASCIIEncoded(encoded, null);
458 }
459 private ASCIIEncoded(final String encoded, final Object unused) {
460 super(encoded);
461 }
462
463 /**
464 * Other characters, which are Unicode chars that are not US-ASCII, and are
465 * not ISO Control or are not ISO Space chars are not preserved
466 * and encoded into their hexidecimal value prepended by '%'.
467 * <p>
468 * For example: Euro currency symbol -> "%E2%82%AC".
469 * </p>
470 * <p>
471 * Uses {@link Uri#encodeToASCIIString(String)} for implementation.
472 * </p>
473 * @param unicode unencoded input
474 */
475 public ASCIIEncoded(final String unicode) {
476 super(encodeToASCIIString(unicode));
477 }
478 @Override
479 public boolean isASCII() { return true; }
480 }
481
482 private static void encodeChar2UTF8(final StringBuilder buf, final char ch) {
483 final byte[] bytes;
484 try {
485 bytes = new String(new char[] { ch }).getBytes(ENCODING);
486 } catch (final UnsupportedEncodingException e) {
487 throw new RuntimeException(MSG_ENCODING_NA, e);
488 }
489 // FIXME: UTF-8 produces more than one byte ? Optimization might be possible.
490 for (int j = 0; j < bytes.length; j++) {
491 final byte b = bytes[j];
492 buf.append('%');
493 buf.append(DIGITS.charAt( ( b & 0xf0 ) >> 4 ));
494 buf.append(DIGITS.charAt( b & 0xf ));
495 }
496 }
497
498 /**
499 * All characters are encoded into their hexadecimal value prepended by '%', except:
500 * <ol>
501 * <li>letters ('a'..'z', 'A'..'Z')</li>
502 * <li>numbers ('0'..'9')</li>
503 * <li>characters in the legal-set parameter</li>
504 * <li> others (unicode characters that are not in
505 * US-ASCII set, and are not ISO Control or are not ISO Space characters)</li>
506 * </ol>
507 * <p>
508 * Use {@link #encodeToASCIIString(String)} for US-ASCII encoding.
509 * </p>
510 * <p>
511 * Consider using {@link Encoded#Encoded(String, String)} in APIs
512 * to distinguish encoded from unencoded data by type.
513 * </p>
514 *
515 * @param vanilla the string to be encoded
516 * @param legal extended character set, allowed to be preserved in the vanilla string
517 * @return java.lang.String the converted string
518 */
519 public static String encode(final String vanilla, final String legal) {
520 if( null == vanilla ) {
521 return null;
522 }
523 final StringBuilder buf = new StringBuilder();
524 for (int i = 0; i < vanilla.length(); i++) {
525 final char ch = vanilla.charAt(i);
526 if ( (ch >= 'a' && ch <= 'z') ||
527 (ch >= 'A' && ch <= 'Z') ||
528 (ch >= '0' && ch <= '9') ||
529 legal.indexOf(ch) > -1 ||
530 ( ch > 127 && !Character.isSpaceChar(ch) && !Character.isISOControl(ch) )
531 ) {
532 buf.append(ch);
533 } else {
534 encodeChar2UTF8(buf, ch);
535 }
536 }
537 return buf.toString();
538 }
539
540 /**
541 * Other characters, which are Unicode chars that are not US-ASCII, and are
542 * not ISO Control or are not ISO Space chars are not preserved
543 * and encoded into their hexidecimal value prepended by '%'.
544 * <p>
545 * For example: Euro currency symbol -> "%E2%82%AC".
546 * </p>
547 * <p>
548 * Consider using {@link ASCIIEncoded#ASCIIEncoded(String)} in APIs
549 * to distinguish encoded from unencoded data by type.
550 * </p>
551 * @param unicode string to be converted
552 * @return java.lang.String the converted string
553 */
554 public static String encodeToASCIIString(final String unicode) {
555 final StringBuilder buf = new StringBuilder();
556 for (int i = 0; i < unicode.length(); i++) {
557 final char ch = unicode.charAt(i);
558 if (ch <= 127) {
559 buf.append(ch);
560 } else {
561 encodeChar2UTF8(buf, ch);
562 }
563 }
564 return buf.toString();
565 }
566
567 /**
568 * Safe {@link Encoded#decode()} call on optional {@code encoded} instance.
569 * @param encoded {@link Encoded} instance to be decoded, may be {@code null}.
570 * @return the {@link Encoded#decode() decoded} String or {@code null} if {@code encoded} was {@code null}.
571 */
572 public static String decode(final Encoded encoded) {
573 return null != encoded ? encoded.decode() : null;
574 }
575
576 /**
577 * Decodes the string argument which is assumed to be encoded in the {@code
578 * x-www-form-urlencoded} MIME content type using the UTF-8 encoding scheme.
579 * <p>
580 *'%' and two following hex digit characters are converted to the
581 * equivalent byte value. All other characters are passed through
582 * unmodified.
583 * </p>
584 * <p>
585 * e.g. "A%20B%20C %24%25" -> "A B C $%"
586 * </p>
587 *
588 * @param encoded The encoded string.
589 * @return java.lang.String The decoded version.
590 */
591 public static String decode(final String encoded) {
592 if( null == encoded ) {
593 return null;
594 }
595 final StringBuilder result = new StringBuilder();
596 final byte[] buf = new byte[32];
597 int bufI = 0;
598 for (int i = 0; i < encoded.length();) {
599 final char c = encoded.charAt(i);
600 if (c == '%') {
601 bufI = 0;
602 do {
603 if (i + 2 >= encoded.length()) {
604 throw new IllegalArgumentException("missing '%' hex-digits at index "+i);
605 }
606 final int d1 = Character.digit(encoded.charAt(i + 1), 16);
607 final int d2 = Character.digit(encoded.charAt(i + 2), 16);
608 if (d1 == -1 || d2 == -1) {
609 throw new IllegalArgumentException("invalid hex-digits at index "+i+": "+encoded.substring(i, i + 3));
610 }
611 buf[bufI++] = (byte) ((d1 << 4) + d2);
612 if( 32 == bufI ) {
613 appendUTF8(result, buf, bufI);
614 bufI = 0;
615 }
616 i += 3;
617 } while (i < encoded.length() && encoded.charAt(i) == '%');
618 if( 0 < bufI ) {
619 appendUTF8(result, buf, bufI);
620 }
621 } else {
622 result.append(c);
623 i++;
624 }
625 }
626 return result.toString();
627 }
628 private static void appendUTF8(final StringBuilder sb, final byte[] buf, final int count) {
629 try {
630 sb.append(new String(buf, 0, count, ENCODING));
631 } catch (final UnsupportedEncodingException e) {
632 throw new RuntimeException(MSG_ENCODING_NA, e);
633 }
634 }
635
636 /**
637 * Creates a new Uri instance using the given unencoded arguments.
638 * <p>
639 * This constructor first creates a temporary Uri string from the given unencoded components. This
640 * string will be parsed later on to create the Uri instance.
641 * </p>
642 * <p>
643 * {@code [scheme:]scheme-specific-part[#fragment]}
644 * </p>
645 * <p>
646 * {@code host} and {@code port} <i>may</i> be undefined or invalid within {@code scheme-specific-part}.
647 * </p>
648 *
649 * @param scheme the unencoded scheme part of the Uri.
650 * @param ssp the unencoded scheme-specific-part of the Uri.
651 * @param fragment the unencoded fragment part of the Uri.
652 * @throws URISyntaxException
653 * if the temporary created string doesn't fit to the
654 * specification RFC2396 or could not be parsed correctly.
655 */
656 public static Uri create(final String scheme, final String ssp, final String fragment) throws URISyntaxException {
657 if ( emptyString(scheme) && emptyString(ssp) && emptyString(fragment) ) {
658 throw new URISyntaxException("", "all empty parts");
659 }
660 final StringBuilder uri = new StringBuilder();
661 if ( !emptyString(scheme) ) {
662 uri.append(scheme);
663 uri.append(SCHEME_SEPARATOR);
664 }
665 if ( !emptyString(ssp) ) {
666 // QUOTE ILLEGAL CHARACTERS
667 uri.append(encode(ssp, SSP_LEGAL));
668 }
669 if ( !emptyString(fragment) ) {
670 uri.append(FRAGMENT_SEPARATOR);
671 // QUOTE ILLEGAL CHARACTERS
672 uri.append(encode(fragment, FRAG_LEGAL));
673 }
674 return new Uri(new Encoded(uri.toString()), false, 0);
675 }
676
677 /**
678 * Creates a new Uri instance using the given encoded arguments.
679 * <p>
680 * This constructor first creates a temporary Uri string from the given encoded components. This
681 * string will be parsed later on to create the Uri instance.
682 * </p>
683 * <p>
684 * The given encoded components are taken as-is, i.e. no re-encoding will be performed!
685 * However, Uri parsing will re-evaluate encoding of the resulting components.
686 * </p>
687 * <p>
688 * {@code [scheme:]scheme-specific-part[#fragment]}
689 * </p>
690 * <p>
691 * {@code host} and {@code port} <i>may</i> be undefined or invalid within {@code scheme-specific-part}.
692 * </p>
693 *
694 * @param scheme the encoded scheme part of the Uri.
695 * @param ssp the encoded scheme-specific-part of the Uri.
696 * @param fragment the encoded fragment part of the Uri.
697 * @throws URISyntaxException
698 * if the temporary created string doesn't fit to the
699 * specification RFC2396 or could not be parsed correctly.
700 */
701 public static Uri create(final Encoded scheme, final Encoded ssp, final Encoded fragment) throws URISyntaxException {
702 if ( emptyString(scheme) && emptyString(ssp) && emptyString(fragment) ) {
703 throw new URISyntaxException("", "all empty parts");
704 }
705 final StringBuilder uri = new StringBuilder();
706 if ( !emptyString(scheme) ) {
707 uri.append(scheme);
708 uri.append(SCHEME_SEPARATOR);
709 }
710 if ( !emptyString(ssp) ) {
711 uri.append(ssp.get());
712 }
713 if ( !emptyString(fragment) ) {
714 uri.append(FRAGMENT_SEPARATOR);
715 uri.append(fragment.get());
716 }
717 return new Uri(new Encoded(uri.toString()), false, 0);
718 }
719
720 /**
721 * Creates a new Uri instance using the given unencoded arguments.
722 * <p>
723 * This constructor first creates a temporary Uri string from the given unencoded components. This
724 * string will be parsed later on to create the Uri instance.
725 * </p>
726 * <p>
727 * {@code [scheme:][user-info@]host[:port][path][?query][#fragment]}
728 * </p>
729 * <p>
730 * {@code host} and {@code port} <i>must</i> be defined and valid, if any {@code authority} components are defined,
731 * i.e. {@code user-info}, {@code host} or {@code port}.
732 * </p>
733 *
734 * @param scheme the unencoded scheme part of the Uri.
735 * @param userinfo the unencoded user information of the Uri for authentication and authorization, {@code null} for undefined.
736 * @param host the unencoded host name of the Uri, {@code null} for undefined.
737 * @param port the port number of the Uri, -1 for undefined.
738 * @param path the unencoded path to the resource on the host.
739 * @param query the unencoded query part of the Uri to specify parameters for the resource.
740 * @param fragment the unencoded fragment part of the Uri.
741 * @throws URISyntaxException
742 * if the temporary created string doesn't fit to the
743 * specification RFC2396 or could not be parsed correctly.
744 */
745 public static Uri create (final String scheme, final String userinfo, String host, final int port,
746 final String path, final String query, final String fragment) throws URISyntaxException {
747 if ( emptyString(scheme) && emptyString(userinfo) && emptyString(host) && emptyString(path) &&
748 emptyString(query) && emptyString(fragment) ) {
749 throw new URISyntaxException("", "all empty parts");
750 }
751
752 if ( !emptyString(scheme) && !emptyString(path) && path.length() > 0 && path.charAt(0) != '/') {
753 throw new URISyntaxException(path, "path doesn't start with '/'");
754 }
755
756 final StringBuilder uri = new StringBuilder();
757 if ( !emptyString(scheme) ) {
758 uri.append(scheme);
759 uri.append(SCHEME_SEPARATOR);
760 }
761
762 if ( !emptyString(userinfo) || !emptyString(host) || port != -1) {
763 uri.append("//");
764 }
765
766 if ( !emptyString(userinfo) ) {
767 // QUOTE ILLEGAL CHARACTERS in userinfo
768 uri.append(encode(userinfo, USERINFO_LEGAL));
769 uri.append('@');
770 }
771
772 if ( !emptyString(host) ) {
773 // check for ipv6 addresses that hasn't been enclosed
774 // in square brackets
775 if (host.indexOf(SCHEME_SEPARATOR) != -1 && host.indexOf(']') == -1
776 && host.indexOf('[') == -1) {
777 host = "[" + host + "]";
778 }
779 uri.append(host);
780 }
781
782 if ( port != -1 ) {
783 uri.append(SCHEME_SEPARATOR);
784 uri.append(port);
785 }
786
787 if ( !emptyString(path) ) {
788 // QUOTE ILLEGAL CHARS
789 uri.append(encode(path, PATH_LEGAL));
790 }
791
792 if ( !emptyString(query) ) {
793 uri.append(QUERY_SEPARATOR);
794 // QUOTE ILLEGAL CHARS
795 uri.append(encode(query, QUERY_LEGAL));
796 }
797
798 if ( !emptyString(fragment) ) {
799 // QUOTE ILLEGAL CHARS
800 uri.append(FRAGMENT_SEPARATOR);
801 uri.append(encode(fragment, FRAG_LEGAL));
802 }
803 return new Uri(new Encoded(uri.toString()), true, 0);
804 }
805
806 /**
807 * Creates a new Uri instance using the given encoded arguments.
808 * <p>
809 * This constructor first creates a temporary Uri string from the given encoded components. This
810 * string will be parsed later on to create the Uri instance.
811 * </p>
812 * <p>
813 * The given encoded components are taken as-is, i.e. no re-encoding will be performed!
814 * However, Uri parsing will re-evaluate encoding of the resulting components.
815 * </p>
816 * <p>
817 * {@code [scheme:][user-info@]host[:port][path][?query][#fragment]}
818 * </p>
819 * <p>
820 * {@code host} and {@code port} <i>must</i> be defined and valid, if any {@code authority} components are defined,
821 * i.e. {@code user-info}, {@code host} or {@code port}.
822 * </p>
823 *
824 * @param scheme the encoded scheme part of the Uri.
825 * @param userinfo the encoded user information of the Uri for authentication and authorization, {@code null} for undefined.
826 * @param host the encoded host name of the Uri, {@code null} for undefined.
827 * @param port the port number of the Uri, -1 for undefined.
828 * @param path the encoded path to the resource on the host.
829 * @param query the encoded query part of the Uri to specify parameters for the resource.
830 * @param fragment the encoded fragment part of the Uri.
831 * @throws URISyntaxException
832 * if the temporary created string doesn't fit to the
833 * specification RFC2396 or could not be parsed correctly.
834 */
835 public static Uri create (final Encoded scheme, final Encoded userinfo, final Encoded host, final int port,
836 final Encoded path, final Encoded query, final Encoded fragment) throws URISyntaxException {
837 if ( emptyString(scheme) && emptyString(userinfo) && emptyString(host) && emptyString(path) &&
838 emptyString(query) && emptyString(fragment) ) {
839 throw new URISyntaxException("", "all empty parts");
840 }
841
842 if ( !emptyString(scheme) && !emptyString(path) && path.length() > 0 && path.charAt(0) != '/') {
843 throw new URISyntaxException(path.get(), "path doesn't start with '/'");
844 }
845
846 final StringBuilder uri = new StringBuilder();
847 if ( !emptyString(scheme) ) {
848 uri.append(scheme);
849 uri.append(SCHEME_SEPARATOR);
850 }
851
852 if ( !emptyString(userinfo) || !emptyString(host) || port != -1) {
853 uri.append("//");
854 }
855
856 if ( !emptyString(userinfo) ) {
857 uri.append(userinfo.get());
858 uri.append('@');
859 }
860
861 if ( !emptyString(host) ) {
862 uri.append(host.get());
863 }
864
865 if ( port != -1 ) {
866 uri.append(SCHEME_SEPARATOR);
867 uri.append(port);
868 }
869
870 if ( !emptyString(path) ) {
871 uri.append(path.get());
872 }
873
874 if ( !emptyString(query) ) {
875 uri.append(QUERY_SEPARATOR);
876 uri.append(query.get());
877 }
878
879 if ( !emptyString(fragment) ) {
880 uri.append(FRAGMENT_SEPARATOR);
881 uri.append(fragment.get());
882 }
883 return new Uri(new Encoded(uri.toString()), true, 0);
884 }
885
886 /**
887 * Creates a new Uri instance using the given unencoded arguments.
888 * <p>
889 * This constructor first creates a temporary Uri string from the given unencoded components. This
890 * string will be parsed later on to create the Uri instance.
891 * </p>
892 * <p>
893 * {@code [scheme:]host[path][#fragment]}
894 * </p>
895 * <p>
896 * {@code host} <i>must</i> be valid, if defined.
897 * </p>
898 *
899 * @param scheme the unencoded scheme part of the Uri.
900 * @param host the unencoded host name of the Uri.
901 * @param path the unencoded path to the resource on the host.
902 * @param fragment the unencoded fragment part of the Uri.
903 * @throws URISyntaxException
904 * if the temporary created string doesn't fit to the
905 * specification RFC2396 or could not be parsed correctly.
906 */
907 public static Uri create(final String scheme, final String host, final String path, final String fragment) throws URISyntaxException {
908 return create(scheme, null, host, -1, path, null, fragment);
909 }
910
911 /**
912 * Creates a new Uri instance using the given encoded arguments.
913 * <p>
914 * This constructor first creates a temporary Uri string from the given encoded components. This
915 * string will be parsed later on to create the Uri instance.
916 * </p>
917 * <p>
918 * The given encoded components are taken as-is, i.e. no re-encoding will be performed!
919 * However, Uri parsing will re-evaluate encoding of the resulting components.
920 * </p>
921 * <p>
922 * {@code [scheme:]host[path][#fragment]}
923 * </p>
924 * <p>
925 * {@code host} <i>must</i> be valid, if defined.
926 * </p>
927 *
928 * @param scheme the encoded scheme part of the Uri.
929 * @param host the encoded host name of the Uri.
930 * @param path the encoded path to the resource on the host.
931 * @param fragment the encoded fragment part of the Uri.
932 * @throws URISyntaxException
933 * if the temporary created string doesn't fit to the
934 * specification RFC2396 or could not be parsed correctly.
935 */
936 public static Uri create(final Encoded scheme, final Encoded host, final Encoded path, final Encoded fragment) throws URISyntaxException {
937 return create(scheme, null, host, -1, path, null, fragment);
938 }
939
940 /**
941 * Creates a new Uri instance using the given unencoded arguments.
942 * <p>
943 * This constructor first creates a temporary Uri string from the given unencoded components. This
944 * string will be parsed later on to create the Uri instance.
945 * </p>
946 * <p>
947 * {@code [scheme:][//authority][path][?query][#fragment]}
948 * </p>
949 * <p>
950 * {@code host} and {@code port} <i>may</i> be undefined or invalid, in the optional {@code authority}.
951 * </p>
952 *
953 * @param scheme the unencoded scheme part of the Uri.
954 * @param authority the unencoded authority part of the Uri.
955 * @param path the unencoded path to the resource on the host.
956 * @param query the unencoded query part of the Uri to specify parameters for the resource.
957 * @param fragment the unencoded fragment part of the Uri.
958 *
959 * @throws URISyntaxException
960 * if the temporary created string doesn't fit to the
961 * specification RFC2396 or could not be parsed correctly.
962 */
963 public static Uri create(final String scheme, final String authority, final String path, final String query, final String fragment) throws URISyntaxException {
964 if ( emptyString(scheme) && emptyString(authority) && emptyString(path) &&
965 emptyString(query) && emptyString(fragment) ) {
966 throw new URISyntaxException("", "all empty parts");
967 }
968 if ( !emptyString(scheme) && !emptyString(path) && path.length() > 0 && path.charAt(0) != '/') {
969 throw new URISyntaxException(path, "path doesn't start with '/'");
970 }
971
972 final StringBuilder uri = new StringBuilder();
973 if ( !emptyString(scheme) ) {
974 uri.append(scheme);
975 uri.append(SCHEME_SEPARATOR);
976 }
977 if ( !emptyString(authority) ) {
978 uri.append("//");
979 // QUOTE ILLEGAL CHARS
980 uri.append(encode(authority, AUTHORITY_LEGAL));
981 }
982
983 if ( !emptyString(path) ) {
984 // QUOTE ILLEGAL CHARS
985 uri.append(encode(path, PATH_LEGAL));
986 }
987 if ( !emptyString(query) ) {
988 // QUOTE ILLEGAL CHARS
989 uri.append(QUERY_SEPARATOR);
990 uri.append(encode(query, QUERY_LEGAL));
991 }
992 if ( !emptyString(fragment) ) {
993 // QUOTE ILLEGAL CHARS
994 uri.append(FRAGMENT_SEPARATOR);
995 uri.append(encode(fragment, FRAG_LEGAL));
996 }
997 return new Uri(new Encoded(uri.toString()), false, 0);
998 }
999
1000 /**
1001 * Creates a new Uri instance using the given encoded arguments.
1002 * <p>
1003 * This constructor first creates a temporary Uri string from the given encoded encoded components. This
1004 * string will be parsed later on to create the Uri instance.
1005 * </p>
1006 * <p>
1007 * The given encoded components are taken as-is, i.e. no re-encoding will be performed!
1008 * However, Uri parsing will re-evaluate encoding of the resulting components.
1009 * </p>
1010 * <p>
1011 * {@code [scheme:][//authority][path][?query][#fragment]}
1012 * </p>
1013 * <p>
1014 * {@code host} and {@code port} <i>may</i> be undefined or invalid, in the optional {@code authority}.
1015 * </p>
1016 *
1017 * @param scheme the encoded scheme part of the Uri.
1018 * @param authority the encoded authority part of the Uri.
1019 * @param path the encoded path to the resource on the host.
1020 * @param query the encoded query part of the Uri to specify parameters for the resource.
1021 * @param fragment the encoded fragment part of the Uri.
1022 *
1023 * @throws URISyntaxException
1024 * if the temporary created string doesn't fit to the
1025 * specification RFC2396 or could not be parsed correctly.
1026 */
1027 public static Uri create(final Encoded scheme, final Encoded authority, final Encoded path, final Encoded query, final Encoded fragment) throws URISyntaxException {
1028 if ( emptyString(scheme) && emptyString(authority) && emptyString(path) &&
1029 emptyString(query) && emptyString(fragment) ) {
1030 throw new URISyntaxException("", "all empty parts");
1031 }
1032 if ( !emptyString(scheme) && !emptyString(path) && path.length() > 0 && path.charAt(0) != '/') {
1033 throw new URISyntaxException(path.get(), "path doesn't start with '/'");
1034 }
1035
1036 final StringBuilder uri = new StringBuilder();
1037 if ( !emptyString(scheme) ) {
1038 uri.append(scheme);
1039 uri.append(SCHEME_SEPARATOR);
1040 }
1041 if ( !emptyString(authority) ) {
1042 uri.append("//");
1043 uri.append(authority.get());
1044 }
1045
1046 if ( !emptyString(path) ) {
1047 uri.append(path.get());
1048 }
1049 if ( !emptyString(query) ) {
1050 uri.append(QUERY_SEPARATOR);
1051 uri.append(query.get());
1052 }
1053 if ( !emptyString(fragment) ) {
1054 uri.append(FRAGMENT_SEPARATOR);
1055 uri.append(fragment.get());
1056 }
1057 return new Uri(new Encoded(uri.toString()), false, 0);
1058 }
1059
1060 /**
1061 * Casts the given encoded String to a {@link Encoded#cast(String) new Encoded instance}
1062 * used to create the resulting Uri instance via {@link #Uri(Encoded)}.
1063 * <p>
1064 * No encoding will be performed on the given {@code encodedUri}, use with care.
1065 * </p>
1066 * @throws URISyntaxException
1067 */
1068 public static Uri cast(final String encodedUri) throws URISyntaxException {
1069 return new Uri(Encoded.cast(encodedUri));
1070 }
1071
1072 /**
1073 * Creates a new Uri instance using the given file-path argument.
1074 * <p>
1075 * This constructor first creates a temporary Uri string from the given components. This
1076 * string will be parsed later on to create the Uri instance.
1077 * </p>
1078 * <p>
1079 * {@code file:path}
1080 * </p>
1081 *
1082 * @param path the unencoded path of the {@code file} {@code schema}.
1083 * @throws URISyntaxException
1084 * if the temporary created string doesn't fit to the
1085 * specification RFC2396 or could not be parsed correctly.
1086 */
1087 public static Uri valueOfFilepath(final String path) throws URISyntaxException {
1088 if ( emptyString(path) ) {
1089 throw new URISyntaxException("", "empty path");
1090 }
1091 if ( path.charAt(0) != '/' ) {
1092 throw new URISyntaxException(path, "path doesn't start with '/'");
1093 }
1094
1095 final StringBuilder uri = new StringBuilder();
1096 uri.append(FILE_SCHEME);
1097 uri.append(SCHEME_SEPARATOR);
1098
1099 // QUOTE ILLEGAL CHARS
1100 uri.append(encode(path, PATH_LEGAL));
1101
1102 return new Uri(new Encoded(uri.toString()), false, 0);
1103 }
1104
1105 /**
1106 * Creates a new Uri instance using the given File instance.
1107 * <p>
1108 * This constructor first creates a temporary Uri string from the given components. This
1109 * string will be parsed later on to create the Uri instance.
1110 * </p>
1111 * <p>
1112 * {@code file:path}
1113 * </p>
1114 *
1115 * @param file using {@link IOUtil#slashify(String, boolean, boolean) slashified} {@link File#getAbsolutePath() absolute-path}
1116 * for the path of the {@code file} {@code schema}, utilizing {@link #valueOfFilepath(String)}.
1117 * @throws URISyntaxException
1118 * if the temporary created string doesn't fit to the
1119 * specification RFC2396 or could not be parsed correctly.
1120 */
1121 public static Uri valueOf(final File file) throws URISyntaxException {
1122 return Uri.valueOfFilepath(IOUtil.slashify(file.getAbsolutePath(), true, file.isDirectory()));
1123 }
1124
1125 /**
1126 * Creates a new Uri instance using the given URI instance.
1127 * <p>
1128 * Re-encoding will be performed if the given URI is {@link URI#isOpaque() not opaque}.
1129 * </p>
1130 * <p>
1131 * See {@link #PARSE_HINT_FIX_PATH} for issues of injecting opaque URLs.
1132 * </p>
1133 *
1134 * @param uri A given URI instance
1135 * @throws URISyntaxException
1136 * if the temporary created string doesn't fit to the
1137 * specification RFC2396 or could not be parsed correctly.
1138 */
1139 public static Uri valueOf(final java.net.URI uri) throws URISyntaxException {
1140 if( uri.isOpaque()) {
1141 // opaque, without host validation.
1142 // Note: This may induce encoding errors of authority and path, see {@link #PARSE_HINT_FIX_PATH}
1143 return new Uri(new Encoded( uri.toString() ), false, 0);
1144 } else {
1145 // with host validation if authority is defined
1146 return Uri.create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
1147 uri.getPath(), uri.getQuery(), uri.getFragment());
1148 }
1149 }
1150
1151 /**
1152 * Creates a new Uri instance using the given URL instance,
1153 * convenient wrapper for {@link #valueOf(URI)} and {@link URL#toURI()}.
1154 * <p>
1155 * Re-encoding will be performed if the given URL is {@link URI#isOpaque() not opaque}, see {@link #valueOf(URI)}.
1156 * </p>
1157 * <p>
1158 * See {@link #PARSE_HINT_FIX_PATH} for issues of injecting opaque URLs.
1159 * </p>
1160 *
1161 * @param url A given URL instance
1162 *
1163 * @throws URISyntaxException
1164 * if the temporary created string doesn't fit to the
1165 * specification RFC2396 or could not be parsed correctly.
1166 */
1167 public static Uri valueOf(final java.net.URL url) throws URISyntaxException {
1168 return valueOf(url.toURI());
1169 }
1170
1171 /**
1172 * Return first successful resulting {@link Uri}.
1173 * Try {@link #cast(String)} first, then {@link #valueOfFilepath(String)} and {@link #valueOf(File)} at last.
1174 * @param uri_s a hopefully usable Uri location
1175 * @return a valid Uri instance or {@code null}
1176 */
1177 public static Uri tryUriOrFile(final String uri_s) {
1178 try {
1179 return Uri.cast( uri_s );
1180 } catch(final Throwable t) { }
1181 try {
1182 return valueOfFilepath( uri_s );
1183 } catch(final Throwable t) { }
1184 try {
1185 final File file = new File(uri_s);
1186 return Uri.valueOf(file);
1187 } catch(final Throwable t) { }
1188 return null;
1189 }
1190
1191 //
1192 // All string fields are encoded!
1193 //
1194
1195 /** Encoded input string used at construction, never {@code null}. */
1196 public final Encoded input;
1197
1198 private final Object lazyLock = new Object();
1199
1200 /** Encoded input string used at construction, in US-ASCII encoding. */
1201 private ASCIIEncoded inputASCII;
1202
1203 private int hash;
1204
1205 /** Encoded {@code scheme}, {@code null} if undefined. */
1206 public final Encoded scheme;
1207
1208 /** Encoded {@code scheme-specific-part}, never {@code null}. */
1210 /** Encoded {@code path} part of {@code scheme-specific-part}, never {@code null}. */
1211 public final Encoded path;
1212
1213 /** Indicating whether {@code authority} part is defined or not. */
1214 public final boolean hasAuthority;
1215 /** Encoded {@code authority} part of {@code scheme-specific-part}, {@code null} if undefined. */
1216 public final Encoded authority;
1217 /** Encoded {@code userinfo} part of {@code authority} and {@code scheme-specific-part}, {@code null} if undefined. */
1218 public final Encoded userInfo; // part of authority
1219 /** Encoded {@code host} part of {@code authority} and {@code scheme-specific-part}, {@code null} if undefined. */
1220 public final Encoded host; // part of authority
1221 /** Encoded {@code port} part of {@code authority} and {@code scheme-specific-part}, {@code -1} if undefined. */
1222 public final int port; // part of authority
1223
1224 /** Encoded {@code query} part of {@code scheme-specific-part}, {@code null} if undefined. */
1225 public final Encoded query;
1226
1227 /** Encoded {@code fragment}, {@code null} if undefined. */
1228 public final Encoded fragment;
1229
1230 /** Indicating whether this Uri is absolute, i.e. has a {@code scheme} and hence an absolute {@code scheme-specific-part}. */
1231 public final boolean absolute;
1232
1233 /**
1234 * Indicating whether this Uri is opaque, i.e. non-hierarchical {@code scheme-specific-part}.
1235 * <p>
1236 * An opaque Uri has no {@code scheme-specific-part} being parsed,
1237 * i.e. {@code path}, {@code query} and {@code authority} are {@code null}.
1238 * </p>
1239 */
1240 public final boolean opaque;
1241
1242 /**
1243 * Creates a new Uri instance according to the given encoded string {@code uri}.
1244 *
1245 * @param uri the RFC3986 encoded RFC2396 Uri representation to be parsed into a Uri object
1246 * @throws URISyntaxException
1247 * if the given string {@code uri} doesn't fit to the
1248 * specification RFC2396 and RFC3986 or could not be parsed correctly.
1249 */
1250 public Uri(final Encoded uri) throws URISyntaxException {
1251 this(uri, false, 0);
1252 }
1253
1254 /** Returns true, if this instance is a {@code file} {@code scheme}, otherwise false. */
1255 public final boolean isFileScheme() {
1256 return null != scheme && FILE_SCHEME.equals( scheme.get() );
1257 }
1258
1259 public static boolean isFileScheme(final String uri) {
1260 final String scheme = getScheme(uri);
1261 return scheme.equals(FILE_SCHEME);
1262 }
1263 public static boolean isHttpxScheme(final String uri) {
1264 final String scheme = getScheme(uri);
1266 }
1267 public static String getScheme(final String uri) {
1268 if( null == uri ) {
1269 return "";
1270 }
1271 final int pos = uri.indexOf(SCHEME_SEPARATOR);
1272 if (0 > pos ) {
1273 return "";
1274 }
1275 final String scheme = uri.substring(0, pos);
1276 if( ! isValidScheme( scheme ) ) {
1277 return "";
1278 }
1279 return scheme;
1280 }
1281
1282 /**
1283 * Returns true, if this instance is a {@code jar} {@code scheme}, otherwise false.
1284 * @since 2.3.2
1285 */
1286 public final boolean isJarScheme() {
1287 return null != scheme && JAR_SCHEME.equals( scheme.get() );
1288 }
1289
1290 /**
1291 * Returns the encoded {@link #input}, never {@code null}.
1292 */
1293 public final Encoded getEncoded() {
1294 return input;
1295 }
1296
1297 /**
1298 * Returns the encoded {@link #input} as String, never {@code null}, same as {@link #getEncoded()}.
1299 */
1300 @Override
1301 public final String toString() {
1302 return input.get();
1303 }
1304
1305 /**
1306 * Returns the encoded {@link #input} encoded in US-ASCII.
1307 */
1309 synchronized( lazyLock ) {
1310 if( null == inputASCII ) {
1311 inputASCII = new ASCIIEncoded(input.get());
1312 }
1313 return inputASCII;
1314 }
1315 }
1316
1317 /**
1318 * Returns a new {@link URI} instance using the encoded {@link #input} string, {@code new URI(uri.input)},
1319 * i.e. no re-encoding will be performed.
1320 * @see #toURIReencoded(boolean)
1321 * @see #valueOf(URI)
1322 */
1323 public final java.net.URI toURI() {
1324 try {
1325 return new java.net.URI(input.get());
1326 } catch (final URISyntaxException e) {
1327 throw new Error(e); // Can't happen
1328 }
1329 }
1330
1331 /**
1332 * Returns a new {@link URI} instance based upon this instance.
1333 * <p>
1334 * All Uri parts of this instance will be decoded
1335 * and encoded by the URI constructor, i.e. re-encoding will be performed.
1336 * </p>
1337 *
1338 * @throws URISyntaxException
1339 * if the given string {@code uri} doesn't fit to the
1340 * specification RFC2396 or could not be parsed correctly.
1341 * @see #toURI()
1342 * @see #valueOf(URI)
1343 */
1344 public final java.net.URI toURIReencoded() throws URISyntaxException {
1345 final java.net.URI recomposedURI;
1346 if( opaque ) {
1347 // opaque, without host validation
1348 recomposedURI = new java.net.URI(decode(scheme), decode(schemeSpecificPart), decode(fragment));
1349 } else if( null != host ) {
1350 // with host validation
1351 recomposedURI = new java.net.URI(decode(scheme), decode(userInfo), decode(host), port,
1353 } else {
1354 // without host validation
1355 recomposedURI = new java.net.URI(decode(scheme), decode(authority),
1357 }
1358 return recomposedURI;
1359 }
1360
1361
1362 /**
1363 * Returns a new {@link URL} instance using the encoded {@link #input} string, {@code new URL(uri.input)},
1364 * i.e. no re-encoding will be performed.
1365 * @throws MalformedURLException
1366 * if an error occurs while creating the URL or no protocol
1367 * handler could be found.
1368 */
1369 public final java.net.URL toURL() throws MalformedURLException {
1370 if (!absolute) {
1371 throw new IllegalArgumentException("Cannot convert relative Uri: "+input);
1372 }
1373 return new java.net.URL(input.get());
1374 }
1375
1376 /**
1377 * If this instance {@link #isFileScheme() is a file scheme},
1378 * implementation decodes <i>[ "//"+{@link #authority} ] + {@link #path}</i>,<br>
1379 * then it processes the result if {@link File#separatorChar} <code> == '\\'</code>
1380 * as follows:
1381 * <ul>
1382 * <li>slash -> backslash</li>
1383 * <li>drop a starting single backslash, preserving windows UNC</li>
1384 * </ul>
1385 * and returns the resulting new {@link File} instance.
1386 * <p>
1387 * Otherwise implementation returns {@code null}.
1388 * </p>
1389 */
1390 public final File toFile() {
1391 if( isFileScheme() && !emptyString(path) ) {
1392 final String authorityS;
1393 if( null == authority ) {
1394 authorityS = "";
1395 } else {
1396 authorityS = "//"+authority.decode();
1397 }
1398 final String path = authorityS+this.path.decode();
1399 if( File.separator.equals("\\") ) {
1400 final String r = patternSingleFS.matcher(path).replaceAll("\\\\");
1401 if( r.startsWith("\\") && !r.startsWith("\\\\") ) { // '\\\\' denotes UNC hostname, which shall not be cut-off
1402 return new File(r.substring(1));
1403 } else {
1404 return new File(r);
1405 }
1406 }
1407 return new File(path);
1408 }
1409 return null;
1410 }
1411
1412 /**
1413 * If this instance's {@link #schemeSpecificPart} contains a Uri itself, a sub-Uri,
1414 * return {@link #schemeSpecificPart} + {@code #} {@link #fragment} via it's own new Uri instance.
1415 * <p>
1416 * In case this Uri is a {@code jar-scheme}, the {@code query} is omitted,
1417 * since it shall be invalid for {@code jar-schemes} anyway.
1418 * </p>
1419 * <p>
1420 * Otherwise method returns {@code null}.
1421 * </p>
1422 * <pre>
1423 * Example 1:
1424 * This instance: <code>jar:<i>scheme2</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class</code>
1425 * Returned Uri: <code><i>scheme2</i>:/some/path/gluegen-rt.jar</code>
1426 *
1427 * Example 2:
1428 * This instance: <code>jar:<i>scheme2</i>:/some/path/gluegen-rt.jar!/com/jogamp/common/GlueGenVersion.class?lala=01#fragment</code>
1429 * Returned Uri: <code><i>scheme2</i>:/some/path/gluegen-rt.jar#fragment</code>
1430 *
1431 * Example 3:
1432 * This instance: <code>scheme1:<i>scheme2</i>:/some/path/gluegen-rt.jar!/?lala=01#fragment</code>
1433 * Returned Uri: <code><i>scheme2</i>:/some/path/gluegen-rt.jar?lala=01#fragment</code>
1434 * </pre>
1435 * @throws URISyntaxException if this Uri is a container Uri and does not comply with the container spec, i.e. a JAR Uri
1436 */
1437 public final Uri getContainedUri() throws URISyntaxException {
1438 if( !emptyString(schemeSpecificPart) ) {
1439 final StringBuilder sb = new StringBuilder();
1440
1441 if( isJarScheme() ) {
1443 if (0 > idx) {
1444 throw new URISyntaxException(input.get(), "missing jar separator");
1445 }
1446 sb.append( schemeSpecificPart.get().substring(0, idx) ); // exclude '!/'
1447 } else {
1448 sb.append( schemeSpecificPart.get() );
1449 }
1450 if ( !emptyString(fragment) ) {
1451 sb.append(FRAGMENT_SEPARATOR);
1452 sb.append(fragment);
1453 }
1454 try {
1455 final int parseHints = opaque ? PARSE_HINT_FIX_PATH : 0;
1456 final Uri res = new Uri(new Encoded(sb.toString()), false, parseHints);
1457 if( null != res.scheme ) {
1458 return res;
1459 }
1460 } catch(final URISyntaxException e) {
1461 // OK, does not contain uri
1462 if( DEBUG ) {
1463 System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
1464 e.printStackTrace();
1465 }
1466 }
1467 }
1468 return null;
1469 }
1470
1471 private static final boolean cutoffLastPathSegementImpl(final StringBuilder pathBuf,
1472 final boolean cutoffFile,
1473 final boolean cutoffDir,
1474 final Encoded appendPath) throws URISyntaxException {
1475 final boolean cleaned;
1476 {// clean-up existing path
1477 final String pathS = pathBuf.toString();
1478 if( 0 > pathS.indexOf("/") && emptyString(appendPath) ) {
1479 return false; // nothing to cut-off
1480 }
1481 pathBuf.setLength(0);
1482 pathBuf.append( IOUtil.cleanPathString( pathS ) );
1483 cleaned = pathBuf.length() != pathS.length();
1484 }
1485
1486 {// cut-off file or last dir-segment
1487 final String pathS = pathBuf.toString();
1488 final int jarSepIdx = pathS.lastIndexOf(JAR_SCHEME_SEPARATOR);
1489 final int e = pathS.lastIndexOf("/");
1490 if( 0 > jarSepIdx || e - 1 > jarSepIdx ) { // stop at jar-separator '!/', if exist
1491 if( cutoffFile && e < pathS.length() - 1 ) {
1492 // cut-off file
1493 pathBuf.setLength(0);
1494 pathBuf.append( pathS.substring(0, e+1) );
1495 } else if( cutoffDir ) {
1496 // cut-off dir-segment
1497 final int p = pathS.lastIndexOf("/", e-1);
1498 if( p >= 0 ) {
1499 pathBuf.setLength(0);
1500 pathBuf.append( pathS.substring(0, p+1) );
1501 } // else keep
1502 } // else keep
1503 }
1504 final boolean cutoff = pathBuf.length() != pathS.length();
1505 if( !cutoff && ( cutoffDir || !cleaned ) && emptyString(appendPath) ) {
1506 return false; // no modifications!
1507 }
1508 }
1509 if( !emptyString(appendPath) ) {
1510 pathBuf.append(appendPath.get());
1511 // 2nd round of cleaning!
1512 final String pathS = pathBuf.toString();
1513 pathBuf.setLength(0);
1514 pathBuf.append( IOUtil.cleanPathString( pathS ) );
1515 }
1516 return true; // continue processing w/ buffer
1517 }
1518 private final Uri cutoffLastPathSegementImpl(final boolean cutoffFile, final boolean cutoffDir, final Encoded appendPath) throws URISyntaxException {
1519 if( opaque ) {
1520 if( emptyString(schemeSpecificPart) ) {
1521 // nothing to cut-off
1522 if( !emptyString(appendPath) ) {
1523 return Uri.create(scheme, appendPath, fragment);
1524 } else {
1525 return null;
1526 }
1527 }
1528 final StringBuilder sspBuf = new StringBuilder(); // without path!
1529
1530 // save optional query in scheme-specific-part
1531 final Encoded queryTemp;
1533 if( queryI >= 0 ) {
1534 queryTemp = schemeSpecificPart.substring(queryI+1);
1535 sspBuf.append( schemeSpecificPart.substring(0, queryI).get() );
1536 } else {
1537 queryTemp = null;
1538 sspBuf.append( schemeSpecificPart.get() );
1539 }
1540
1541 if( !cutoffLastPathSegementImpl(sspBuf, cutoffFile, cutoffDir, appendPath) ) {
1542 return null; // no modifications
1543 }
1544
1545 if ( !emptyString(queryTemp) ) {
1546 sspBuf.append(QUERY_SEPARATOR);
1547 sspBuf.append( queryTemp.get() );
1548 }
1549
1550 // without host validation if authority is defined
1551 return Uri.create(scheme, new Encoded(sspBuf.toString()), fragment);
1552 } else {
1553 if( emptyString(path) ) {
1554 return null; // nothing to cut-off
1555 }
1556 final StringBuilder pathBuf = new StringBuilder();
1557 pathBuf.append( path.get() );
1558
1559 if( !cutoffLastPathSegementImpl(pathBuf, cutoffFile, cutoffDir, appendPath) ) {
1560 return null; // no modifications
1561 }
1562
1563 // with host validation if authority is defined
1564 return Uri.create(scheme, userInfo, host, port, new Encoded(pathBuf.toString()), query, fragment);
1565 }
1566 }
1567
1568 /**
1569 * {@link IOUtil#cleanPathString(String) Normalizes} this Uri's path and return the
1570 * {@link IOUtil#cleanPathString(String) normalized} form if it differs, otherwise {@code this} instance.
1571 * <p>
1572 * <pre>
1573 * Example-1:
1574 * This instance : <code>jar:http://some/path/../gluegen-rt.jar!/com/Test.class?arg=1#frag</code>
1575 * Normalized : <code>jar:http://some/gluegen-rt.jar!/com/Test.class?arg=1#frag</code>
1576 *
1577 * Example-2:
1578 * This instance : <code>http://some/path/../gluegen-rt.jar?arg=1#frag</code>
1579 * Normalized : <code>http://some/gluegen-rt.jar?arg=1#frag</code>
1580 * </pre>
1581 * </p>
1582 */
1583 public final Uri getNormalized() {
1584 try {
1585 final Uri res = cutoffLastPathSegementImpl(false, false, null);
1586 return null != res ? res : this;
1587 } catch (final URISyntaxException e) {
1588 if( DEBUG ) {
1589 System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
1590 e.printStackTrace();
1591 }
1592 return this;
1593 }
1594 }
1595
1596 /**
1597 * Returns this Uri's directory Uri.
1598 * <p>
1599 * This Uri path will be {@link IOUtil#cleanPathString(String) normalized} before returning the directory.
1600 * </p>
1601 * <p>
1602 * If this Uri's directory cannot be found, or already denotes a directory, method returns {@code this} instance.
1603 * </p>
1604 * <p>
1605 * <pre>
1606 * Example-1:
1607 * this-uri: http:/some/path/gluegen-rt.jar?arg=1#frag
1608 * result: http:/some/path/?arg=1#frag
1609 *
1610 * Example-2:
1611 * this-uri: file:/some/path/
1612 * result: file:/some/path/
1613 *
1614 * Example-3:
1615 * this-uri: file:/some/path/lala/lili/../../hello.txt
1616 * result: file:/some/path/
1617 * </pre>
1618 * </p>
1619 * @throws URISyntaxException if the new string {@code uri} doesn't fit to the
1620 * specification RFC2396 and RFC3986 or could not be parsed correctly.
1621 */
1623 try {
1624 final Uri res = cutoffLastPathSegementImpl(true, false, null);
1625 return null != res ? res : this;
1626 } catch (final URISyntaxException e) {
1627 if( DEBUG ) {
1628 System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
1629 e.printStackTrace();
1630 }
1631 return this;
1632 }
1633 }
1634
1635 /**
1636 * Returns this Uri's parent directory Uri..
1637 * <p>
1638 * This Uri path will be {@link IOUtil#cleanPathString(String) normalized} before traversing up one directory.
1639 * </p>
1640 * <p>
1641 * If a parent folder cannot be found, method returns {@code null}.
1642 * </p>
1643 * <p>
1644 * <pre>
1645 * Example-1:
1646 * This instance : <code>jar:http://some/path/gluegen-rt.jar!/com/Test.class?arg=1#frag</code>
1647 * Returned Uri #1: <code>jar:http://some/path/gluegen-rt.jar!/com/?arg=1#frag</code>
1648 * Returned Uri #2: <code>jar:http://some/path/gluegen-rt.jar!/?arg=1#frag</code>
1649 * Returned Uri #3: <code>null</code>
1650 *
1651 * Example-2:
1652 * This instance : <code>http://some/path/gluegen-rt.jar?arg=1#frag</code>
1653 * Returned Uri #1: <code>http://some/path/?arg=1#frag</code>
1654 * Returned Uri #2: <code>http://some/?arg=1#frag</code>
1655 * Returned Uri #2: <code>null</code>
1656 *
1657 * Example-3:
1658 * This instance : <code>http://some/path/../gluegen-rt.jar?arg=1#frag</code>
1659 * Returned Uri #1: <code>http://some/?arg=1#frag</code>
1660 * Returned Uri #2: <code>null</code>
1661 * </pre>
1662 * </p>
1663 */
1664 public final Uri getParent() {
1665 try {
1666 return cutoffLastPathSegementImpl(true, true, null);
1667 } catch (final URISyntaxException e) {
1668 if( DEBUG ) {
1669 System.err.println("Caught "+e.getClass().getSimpleName()+": "+e.getMessage());
1670 e.printStackTrace();
1671 }
1672 return null;
1673 }
1674 }
1675
1676 /**
1677 * Returns a new Uri appending the given {@code appendPath}
1678 * to this instance's {@link #getDirectory() directory}.
1679 * <p>
1680 * If {@code appendPath} is empty, method behaves like {@link #getNormalized()}.
1681 * </p>
1682 * <p>
1683 * This resulting path will be {@link IOUtil#cleanPathString(String) normalized}.
1684 * </p>
1685 * <p>
1686 * <pre>
1687 * Example-1:
1688 * append: null
1689 * this-uri: http:/some/path/gluegen-rt.jar
1690 * result: http:/some/path/gluegen-rt.jar
1691 *
1692 * Example-2:
1693 * append: test.txt
1694 * this-uri: file:/some/path/gluegen-rt.jar
1695 * result: file:/some/path/test.txt
1696 *
1697 * Example-3:
1698 * append: test.txt
1699 * this-uri: file:/some/path/lala/lili/../../hello.txt
1700 * result: file:/some/path/test.txt
1701 * </pre>
1702 * </p>
1703 *
1704 * @param appendPath denotes a relative path to be appended to this Uri's directory
1705 * @throws URISyntaxException
1706 * if the resulting {@code uri} doesn't fit to the
1707 * specification RFC2396 and RFC3986 or could not be parsed correctly.
1708 */
1709 public Uri getRelativeOf(final Encoded appendPath) throws URISyntaxException {
1710 if( emptyString(appendPath) ) {
1711 return getNormalized();
1712 } else {
1713 return cutoffLastPathSegementImpl(true, false, appendPath);
1714 }
1715 }
1716
1717 /**
1718 * Concatenates the given encoded string to the {@link #getEncoded() encoded uri}
1719 * of this instance and returns {@link #Uri(Encoded) a new Uri instance} with the result.
1720 *
1721 * @throws URISyntaxException
1722 * if the concatenated string {@code uri} doesn't fit to the
1723 * specification RFC2396 and RFC3986 or could not be parsed correctly.
1724 */
1725 public final Uri concat(final Encoded suffix) throws URISyntaxException {
1726 if( null == suffix ) {
1727 return this;
1728 } else {
1729 return new Uri( input.concat(suffix) );
1730 }
1731 }
1732
1733 /**
1734 * Returns a new Uri instance w/ the given new query {@code newQuery}.
1735 *
1736 * @throws URISyntaxException if this Uri is {@link #opaque}
1737 * or if the new string {@code uri} doesn't fit to the
1738 * specification RFC2396 and RFC3986 or could not be parsed correctly.
1739 */
1740 public final Uri getNewQuery(final Encoded newQuery) throws URISyntaxException {
1741 if( opaque ) {
1742 throw new URISyntaxException(input.decode(), "Opaque Uri cannot permute by query");
1743 } else {
1744 // with host validation if authority is defined
1745 return Uri.create(scheme, userInfo, host, port, path, newQuery, fragment);
1746 }
1747 }
1748
1749 /**
1750 * {@inheritDoc}
1751 * <p>
1752 * Compares this Uri instance with the given argument {@code o} and
1753 * determines if both are equal. Two Uri instances are equal if all single
1754 * parts are identical in their meaning.
1755 * </p>
1756 *
1757 * @param o
1758 * the Uri this instance has to be compared with.
1759 * @return {@code true} if both Uri instances point to the same resource,
1760 * {@code false} otherwise.
1761 */
1762 @Override
1763 public final boolean equals(final Object o) {
1764 if (!(o instanceof Uri)) {
1765 return false;
1766 }
1767 final Uri uri = (Uri) o;
1768
1769 if (uri.fragment == null && fragment != null || uri.fragment != null && fragment == null) {
1770 return false;
1771 } else if (uri.fragment != null && fragment != null) {
1772 if (!equalsHexCaseInsensitive(uri.fragment, fragment)) {
1773 return false;
1774 }
1775 }
1776
1777 if (uri.scheme == null && scheme != null || uri.scheme != null && scheme == null) {
1778 return false;
1779 } else if (uri.scheme != null && scheme != null) {
1780 if (!uri.scheme.equalsIgnoreCase(scheme)) {
1781 return false;
1782 }
1783 }
1784
1785 if (uri.opaque && opaque) {
1786 return equalsHexCaseInsensitive(uri.schemeSpecificPart, schemeSpecificPart);
1787 } else if (!uri.opaque && !opaque) {
1788 if (!equalsHexCaseInsensitive(path, uri.path)) {
1789 return false;
1790 }
1791
1792 if (uri.query != null && query == null || uri.query == null && query != null) {
1793 return false;
1794 } else if (uri.query != null && query != null) {
1795 if (!equalsHexCaseInsensitive(uri.query, query)) {
1796 return false;
1797 }
1798 }
1799
1800 if (uri.authority != null && authority == null || uri.authority == null && authority != null) {
1801 return false;
1802 } else if (uri.authority != null && authority != null) {
1803 if (uri.host != null && host == null || uri.host == null && host != null) {
1804 return false;
1805 } else if (uri.host == null && host == null) {
1806 // both are registry based, so compare the whole authority
1807 return equalsHexCaseInsensitive(uri.authority, authority);
1808 } else { // uri.host != null && host != null, so server-based
1809 if (!host.equalsIgnoreCase(uri.host)) {
1810 return false;
1811 }
1812
1813 if (port != uri.port) {
1814 return false;
1815 }
1816
1817 if ( uri.userInfo != null && userInfo == null ||
1818 uri.userInfo == null && userInfo != null
1819 ) {
1820 return false;
1821 } else if (uri.userInfo != null && userInfo != null) {
1822 return equalsHexCaseInsensitive(userInfo, uri.userInfo);
1823 } else {
1824 return true;
1825 }
1826 }
1827 } else {
1828 // no authority
1829 return true;
1830 }
1831
1832 } else {
1833 // one is opaque, the other hierarchical
1834 return false;
1835 }
1836 }
1837
1838 /**
1839 * {@inheritDoc}
1840 * <p>
1841 * Gets the hashcode value of this Uri instance.
1842 * </p>
1843 */
1844 @Override
1845 public final int hashCode() {
1846 synchronized( lazyLock ) {
1847 if (hash == -1) {
1848 hash = getHashString().hashCode();
1849 }
1850 return hash;
1851 }
1852 }
1853
1854 /*
1855 * Takes a string that may contain hex sequences like %F1 or %2b and
1856 * converts the hex values following the '%' to lowercase
1857 */
1858 private String convertHexToLowerCase(final String s) {
1859 if (s.indexOf('%') == -1) {
1860 return s;
1861 }
1862 final StringBuilder result = new StringBuilder("");
1863 int index = 0, previndex = 0;
1864 while ((index = s.indexOf('%', previndex)) != -1) {
1865 result.append(s.substring(previndex, index + 1));
1866 result.append(s.substring(index + 1, index + 3).toLowerCase());
1867 index += 3;
1868 previndex = index;
1869 }
1870 return result.toString();
1871 }
1872
1873 /*
1874 * Takes two strings that may contain hex sequences like %F1 or %2b and
1875 * compares them, ignoring case for the hex values. Hex values must always
1876 * occur in pairs as above
1877 */
1878 private boolean equalsHexCaseInsensitive(final Encoded first, final Encoded second) {
1879 if (first.indexOf('%') != second.indexOf('%')) {
1880 return first.equals(second);
1881 }
1882
1883 int index = 0, previndex = 0;
1884 while ( ( index = first.indexOf('%', previndex) ) != -1 &&
1885 second.indexOf('%', previndex) == index
1886 ) {
1887 if( !first.get().substring(previndex, index).equals( second.get().substring(previndex, index) ) ) {
1888 return false;
1889 }
1890 if( !first.get().substring(index + 1, index + 3).equalsIgnoreCase( second.get().substring(index + 1, index + 3) ) ) {
1891 return false;
1892 }
1893 index += 3;
1894 previndex = index;
1895 }
1896 return first.get().substring(previndex).equals( second.get().substring(previndex) );
1897 }
1898
1899 /*
1900 * Form a string from the components of this Uri, similarly to the
1901 * toString() method. But this method converts scheme and host to lowercase,
1902 * and converts escaped octets to lowercase.
1903 */
1904 private String getHashString() {
1905 final StringBuilder result = new StringBuilder();
1906 if (scheme != null) {
1907 result.append(scheme.get().toLowerCase());
1908 result.append(SCHEME_SEPARATOR);
1909 }
1910 if (opaque) {
1911 result.append(schemeSpecificPart.get());
1912 } else {
1913 if (authority != null) {
1914 result.append("//");
1915 if (host == null) {
1916 result.append(authority.get());
1917 } else {
1918 if (userInfo != null) {
1919 result.append(userInfo.get() + "@");
1920 }
1921 result.append(host.get().toLowerCase());
1922 if (port != -1) {
1923 result.append(SCHEME_SEPARATOR + port);
1924 }
1925 }
1926 }
1927
1928 if (path != null) {
1929 result.append(path.get());
1930 }
1931
1932 if (query != null) {
1933 result.append(QUERY_SEPARATOR);
1934 result.append(query.get());
1935 }
1936 }
1937
1938 if (fragment != null) {
1939 result.append(FRAGMENT_SEPARATOR);
1940 result.append(fragment.get());
1941 }
1942 return convertHexToLowerCase(result.toString());
1943 }
1944
1945 /**
1946 *
1947 * @param input
1948 * @param expectServer
1949 * @param parseHints TODO
1950 * @throws URISyntaxException
1951 */
1952 private Uri(final Encoded input, final boolean expectServer, final int parseHints) throws URISyntaxException {
1953 if( emptyString(input) ) {
1954 throw new URISyntaxException(input.get(), "empty input");
1955 }
1956 String temp = input.get();
1957 int index;
1958 // parse into Fragment, Scheme, and SchemeSpecificPart
1959 // then parse SchemeSpecificPart if necessary
1960
1961 // Fragment
1962 index = temp.indexOf(FRAGMENT_SEPARATOR);
1963 if (index != -1) {
1964 // remove the fragment from the end
1965 fragment = new Encoded( temp.substring(index + 1) );
1966 validateFragment(input, fragment, index + 1);
1967 temp = temp.substring(0, index);
1968 } else {
1969 fragment = null;
1970 }
1971
1972 String inputTemp = input.get(); // may get modified due to error correction
1973
1974 // Scheme and SchemeSpecificPart
1975 final int indexSchemeSep = temp.indexOf(SCHEME_SEPARATOR);
1976 index = indexSchemeSep;
1977 final int indexSSP = temp.indexOf('/');
1978 final int indexQuerySep = temp.indexOf(QUERY_SEPARATOR);
1979
1980 String sspTemp; // may get modified due to error correction
1981
1982 // if a '/' or '?' occurs before the first ':' the uri has no
1983 // specified scheme, and is therefore not absolute
1984 if ( indexSchemeSep != -1 &&
1985 ( indexSSP >= indexSchemeSep || indexSSP == -1 ) &&
1986 ( indexQuerySep >= indexSchemeSep || indexQuerySep == -1 )
1987 ) {
1988 // the characters up to the first ':' comprise the scheme
1989 absolute = true;
1990 scheme = new Encoded( temp.substring(0, indexSchemeSep) );
1991 if (scheme.length() == 0) {
1992 failExpecting(input, "scheme", indexSchemeSep);
1993 }
1994 validateScheme(input, scheme, 0);
1995 sspTemp = temp.substring(indexSchemeSep + 1);
1996 if (sspTemp.length() == 0) {
1997 failExpecting(input, "scheme-specific-part", indexSchemeSep);
1998 }
1999 } else {
2000 absolute = false;
2001 scheme = null;
2002 sspTemp = temp;
2003 }
2004
2005 if ( scheme == null || sspTemp.length() > 0 && sspTemp.charAt(0) == '/' ) {
2006 // Uri is hierarchical, not opaque
2007 opaque = false;
2008
2009 // Query
2010 temp = sspTemp;
2011 index = temp.indexOf(QUERY_SEPARATOR);
2012 if (index != -1) {
2013 query = new Encoded( temp.substring(index + 1) );
2014 temp = temp.substring(0, index);
2015 validateQuery(input, query, indexSSP + 1 + index);
2016 } else {
2017 query = null;
2018 }
2019
2020 String pathTemp; // may get modified due to error correction
2021 final int indexPathInSSP;
2022
2023 // Authority and Path
2024 if (temp.startsWith("//")) {
2025 index = temp.indexOf('/', 2);
2026 final String authorityS;
2027 if (index != -1) {
2028 authorityS = temp.substring(2, index);
2029 pathTemp = temp.substring(index);
2030 indexPathInSSP = index;
2031 } else {
2032 authorityS = temp.substring(2);
2033 if (authorityS.length() == 0 && query == null && fragment == null) {
2034 failExpecting(input, "authority, path [, query, fragment]", index);
2035 }
2036 pathTemp = "";
2037 indexPathInSSP = -1;
2038 // nothing left, so path is empty
2039 // (not null, path should never be null if hierarchical/non-opaque)
2040 }
2041 if ( emptyString(authorityS) ) {
2042 authority = null;
2043 } else {
2044 authority = new Encoded( authorityS );
2045 validateAuthority(input, authority, indexSchemeSep + 3);
2046 }
2047 } else { // no authority specified
2048 pathTemp = temp;
2049 indexPathInSSP = 0;
2050 authority = null;
2051 }
2052
2053 int indexPath = 0; // in input
2054 if (indexSSP > -1) {
2055 indexPath += indexSSP;
2056 }
2057 if (indexPathInSSP > -1) {
2058 indexPath += indexPathInSSP;
2059 }
2060
2061 final int pathErrIdx = validateEncoded(pathTemp, PATH_LEGAL);
2062 if( 0 <= pathErrIdx ) {
2063 // Perform error correction on PATH if requested!
2064 if( 0 != ( parseHints & PARSE_HINT_FIX_PATH ) ) {
2065 if( DEBUG_SHOWFIX ) {
2066 System.err.println("Uri FIX_FILEPATH: input at index "+(indexPath+pathErrIdx)+": "+inputTemp);
2067 System.err.println("Uri FIX_FILEPATH: ssp at index "+(indexPathInSSP+pathErrIdx)+": "+sspTemp);
2068 System.err.println("Uri FIX_FILEPATH: path at index "+pathErrIdx+": "+pathTemp);
2069 }
2070 final int pathTempOldLen = pathTemp.length();
2071 pathTemp = encode( decode( pathTemp ), PATH_LEGAL); // re-encode, and hope for the best!
2072 validatePath(input, pathTemp, indexPath); // re-validate!
2073 {
2074 // Patch SSP + INPUT !
2075 final StringBuilder sb = new StringBuilder();
2076 if( indexPathInSSP > 0 ) {
2077 sb.append( sspTemp.substring(0, indexPathInSSP) );
2078 }
2079 sb.append( pathTemp ).append( sspTemp.substring( indexPathInSSP + pathTempOldLen ) );
2080 sspTemp = sb.toString(); // update
2081
2082 sb.setLength(0);
2083 if( indexPath > 0 ) {
2084 sb.append( inputTemp.substring(0, indexPath) );
2085 }
2086 sb.append( pathTemp ).append( inputTemp.substring( indexPath + pathTempOldLen ) );
2087 inputTemp = sb.toString(); // update
2088 }
2089 if( DEBUG_SHOWFIX ) {
2090 System.err.println("Uri FIX_FILEPATH: result : "+pathTemp);
2091 System.err.println("Uri FIX_FILEPATH: ssp after : "+sspTemp);
2092 System.err.println("Uri FIX_FILEPATH: input after : "+inputTemp);
2093 }
2094 } else {
2095 fail(input, "invalid path", indexPath+pathErrIdx);
2096 }
2097 }
2098 path = new Encoded( pathTemp );
2099 } else {
2100 // Uri is not hierarchical, Uri is opaque
2101 opaque = true;
2102 query = null;
2103 path = null;
2104 authority = null;
2105 validateSsp(input, sspTemp, indexSchemeSep + 1);
2106 }
2107 schemeSpecificPart = new Encoded( sspTemp );
2108 this.input = inputTemp == input.get() ? input : new Encoded( inputTemp );
2109
2110 /**
2111 * determine the host, port and userinfo if the authority parses
2112 * successfully to a server based authority
2113 *
2114 * Behavior in error cases: if forceServer is true, throw
2115 * URISyntaxException with the proper diagnostic messages. if
2116 * forceServer is false assume this is a registry based uri, and just
2117 * return leaving the host, port and userinfo fields undefined.
2118 *
2119 * and there are some error cases where URISyntaxException is thrown
2120 * regardless of the forceServer parameter e.g. malformed ipv6 address
2121 */
2122 Encoded tempUserinfo = null, tempHost = null;
2123 int tempPort = -1;
2124 boolean authorityComplete;
2125
2126 if ( null != authority ) {
2127 authorityComplete = true; // set to false later
2128 int hostindex = 0;
2129
2130 temp = authority.get();
2131 index = temp.indexOf('@');
2132 if (index != -1) {
2133 // remove user info
2134 tempUserinfo = new Encoded( temp.substring(0, index) );
2135 validateUserinfo(authority, tempUserinfo, 0);
2136 temp = temp.substring(index + 1); // host[:port] is left
2137 hostindex = index + 1;
2138 }
2139
2140 index = temp.lastIndexOf(SCHEME_SEPARATOR);
2141 final int endindex = temp.indexOf(']');
2142
2143 if (index != -1 && endindex < index) {
2144 // determine port and host
2145 tempHost = new Encoded( temp.substring(0, index) );
2146
2147 if (index < (temp.length() - 1)) { // port part is not empty
2148 try {
2149 tempPort = Integer.parseInt(temp.substring(index + 1));
2150 if (tempPort < 0) {
2151 if (expectServer) {
2152 fail(authority, "invalid port <"+authority+">", hostindex + index + 1);
2153 }
2154 authorityComplete = false;
2155 }
2156 } catch (final NumberFormatException e) {
2157 if (expectServer) {
2158 fail(authority, "invalid port <"+authority+">, "+e.getMessage(), hostindex + index + 1);
2159 }
2160 authorityComplete = false;
2161 }
2162 }
2163 } else {
2164 tempHost = new Encoded( temp );
2165 }
2166
2167 if( authorityComplete ) {
2168 if ( emptyString(tempHost) ) {
2169 if (expectServer) {
2170 fail(authority, "empty host <"+authority+">", hostindex);
2171 }
2172 authorityComplete = false;
2173 } else if (!isValidHost(expectServer, tempHost)) {
2174 if (expectServer) {
2175 fail(authority, "invalid host <"+tempHost+">", hostindex);
2176 }
2177 authorityComplete = false;
2178 }
2179 }
2180 } else {
2181 authorityComplete = false;
2182 }
2183
2184 if( authorityComplete ) {
2185 // this is a server based uri,
2186 // fill in the userinfo, host and port fields
2187 userInfo = tempUserinfo;
2188 host = tempHost;
2189 port = tempPort;
2190 hasAuthority = true;
2191 } else {
2192 userInfo = null;
2193 host = null;
2194 port = -1;
2195 hasAuthority = false;
2196 }
2197 }
2198
2199 private static void validateScheme(final Encoded uri, final Encoded scheme, final int index) throws URISyntaxException {
2200 // first char needs to be an alpha char
2201 final char ch = scheme.charAt(0);
2202 if ( !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) ) {
2203 fail(uri, "invalid scheme", index);
2204 }
2205 final int errIdx = validateAlphaNum(scheme.get(), "+-.");
2206 if( 0 <= errIdx ) {
2207 fail(uri, "invalid scheme", index+errIdx);
2208 }
2209 }
2210 public static boolean isValidScheme(final String scheme) {
2211 // first char needs to be an alpha char
2212 final char ch = scheme.charAt(0);
2213 if ( !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) ) {
2214 return false;
2215 }
2216 final int errIdx = validateAlphaNum(scheme, "+-.");
2217 if( 0 <= errIdx ) {
2218 return false;
2219 }
2220 return true;
2221 }
2222
2223 private static void validateSsp(final Encoded uri, final String ssp, final int index) throws URISyntaxException {
2224 final int errIdx = validateEncoded(ssp, SSP_LEGAL);
2225 if( 0 <= errIdx ) {
2226 fail(uri, "invalid scheme-specific-part", index+errIdx);
2227 }
2228 }
2229
2230 private static void validateAuthority(final Encoded uri, final Encoded authority, final int index) throws URISyntaxException {
2231 final int errIdx = validateEncoded(authority.get(), AUTHORITY_LEGAL);
2232 if( 0 <= errIdx ) {
2233 fail(uri, "invalid authority", index+errIdx);
2234 }
2235 }
2236
2237 private static void validatePath(final Encoded uri, final String path, final int index) throws URISyntaxException {
2238 final int errIdx = validateEncoded(path, PATH_LEGAL);
2239 if( 0 <= errIdx ) {
2240 fail(uri, "invalid path", index+errIdx);
2241 }
2242 }
2243
2244 private static void validateQuery(final Encoded uri, final Encoded query, final int index) throws URISyntaxException {
2245 final int errIdx = validateEncoded(query.get(), QUERY_LEGAL);
2246 if( 0 <= errIdx ) {
2247 fail(uri, "invalid query", index+errIdx);
2248 }
2249 }
2250
2251 private static void validateFragment(final Encoded uri, final Encoded fragment, final int index) throws URISyntaxException {
2252 final int errIdx = validateEncoded(fragment.get(), FRAG_LEGAL);
2253 if( 0 <= errIdx ) {
2254 fail(uri, "invalid fragment", index+errIdx);
2255 }
2256 }
2257
2258 private static void validateUserinfo(final Encoded uri, final Encoded userinfo, final int index) throws URISyntaxException {
2259 for (int i = 0; i < userinfo.length(); i++) {
2260 final char ch = userinfo.charAt(i);
2261 if (ch == ']' || ch == '[') {
2262 fail(uri, "invalid userinfo", index+i);
2263 }
2264 }
2265 }
2266
2267 /**
2268 * distinguish between IPv4, IPv6, domain name and validate it based on
2269 * its type
2270 */
2271 private boolean isValidHost(final boolean expectServer, final Encoded host) throws URISyntaxException {
2272 if (host.charAt(0) == '[') {
2273 // ipv6 address
2274 if (host.charAt(host.length() - 1) != ']') {
2275 fail(input, "invalid host, missing closing ipv6: "+host, 0);
2276 }
2277 if (!isValidIP6Address(host.get())) {
2278 fail(input, "invalid ipv6: "+host, 0);
2279 }
2280 return true;
2281 }
2282
2283 // '[' and ']' can only be the first char and last char
2284 // of the host name
2285 if (host.indexOf('[') != -1 || host.indexOf(']') != -1) {
2286 fail(input, "invalid host: "+host, 0);
2287 }
2288
2289 final int index = host.lastIndexOf('.');
2290 if ( index < 0 || index == host.length() - 1 ||
2291 !Character.isDigit(host.charAt(index + 1)) )
2292 {
2293 // domain name
2294 if (isValidDomainName(host)) {
2295 return true;
2296 }
2297 if (expectServer) {
2298 fail(input, "invalid host, invalid domain-name or ipv4: "+host, 0);
2299 }
2300 return false;
2301 }
2302
2303 // IPv4 address
2304 if (isValidIPv4Address(host.get())) {
2305 return true;
2306 }
2307 if (expectServer) {
2308 fail(input, "invalid host, invalid ipv4: "+host, 0);
2309 }
2310 return false;
2311 }
2312
2313 private static boolean isValidDomainName(final Encoded host) {
2314 final String hostS = host.get();
2315 if( 0 <= validateAlphaNum(hostS, "-.") ) {
2316 return false;
2317 }
2318 String label = null;
2319 final StringTokenizer st = new StringTokenizer(hostS, ".");
2320 while (st.hasMoreTokens()) {
2321 label = st.nextToken();
2322 if (label.startsWith("-") || label.endsWith("-")) {
2323 return false;
2324 }
2325 }
2326
2327 if (!label.equals(hostS)) {
2328 final char ch = label.charAt(0);
2329 if (ch >= '0' && ch <= '9') {
2330 return false;
2331 }
2332 }
2333 return true;
2334 }
2335
2336 private static boolean isValidIPv4Address(final String ipv4Address) {
2337 int index;
2338 int index2;
2339 try {
2340 int num;
2341 index = ipv4Address.indexOf('.');
2342 num = Integer.parseInt(ipv4Address.substring(0, index));
2343 if (num < 0 || num > 255) {
2344 return false;
2345 }
2346 index2 = ipv4Address.indexOf('.', index + 1);
2347 num = Integer.parseInt(ipv4Address.substring(index + 1, index2));
2348 if (num < 0 || num > 255) {
2349 return false;
2350 }
2351 index = ipv4Address.indexOf('.', index2 + 1);
2352 num = Integer.parseInt(ipv4Address.substring(index2 + 1, index));
2353 if (num < 0 || num > 255) {
2354 return false;
2355 }
2356 num = Integer.parseInt(ipv4Address.substring(index + 1));
2357 if (num < 0 || num > 255) {
2358 return false;
2359 }
2360 } catch (final Exception e) {
2361 return false;
2362 }
2363 return true;
2364 }
2365
2366 private static boolean isValidIP6Address(final String ipv6Address) {
2367 final int length = ipv6Address.length();
2368 boolean doubleColon = false;
2369 int numberOfColons = 0;
2370 int numberOfPeriods = 0;
2371 String word = "";
2372 char c = 0;
2373 char prevChar = 0;
2374 int offset = 0; // offset for [] ip addresses
2375
2376 if (length < 2) {
2377 return false;
2378 }
2379
2380 for (int i = 0; i < length; i++) {
2381 prevChar = c;
2382 c = ipv6Address.charAt(i);
2383 switch (c) {
2384
2385 // case for an open bracket [x:x:x:...x]
2386 case '[':
2387 if (i != 0) {
2388 return false; // must be first character
2389 }
2390 if (ipv6Address.charAt(length - 1) != ']') {
2391 return false; // must have a close ]
2392 }
2393 if ((ipv6Address.charAt(1) == SCHEME_SEPARATOR)
2394 && (ipv6Address.charAt(2) != SCHEME_SEPARATOR)) {
2395 return false;
2396 }
2397 offset = 1;
2398 if (length < 4) {
2399 return false;
2400 }
2401 break;
2402
2403 // case for a closed bracket at end of IP [x:x:x:...x]
2404 case ']':
2405 if (i != length - 1) {
2406 return false; // must be last character
2407 }
2408 if (ipv6Address.charAt(0) != '[') {
2409 return false; // must have a open [
2410 }
2411 break;
2412
2413 // case for the last 32-bits represented as IPv4
2414 // x:x:x:x:x:x:d.d.d.d
2415 case '.':
2416 numberOfPeriods++;
2417 if (numberOfPeriods > 3) {
2418 return false;
2419 }
2420 if (!isValidIP4Word(word)) {
2421 return false;
2422 }
2423 if (numberOfColons != 6 && !doubleColon) {
2424 return false;
2425 }
2426 // a special case ::1:2:3:4:5:d.d.d.d allows 7 colons
2427 // with
2428 // an IPv4 ending, otherwise 7 :'s is bad
2429 if (numberOfColons == 7
2430 && ipv6Address.charAt(0 + offset) != SCHEME_SEPARATOR
2431 && ipv6Address.charAt(1 + offset) != SCHEME_SEPARATOR) {
2432 return false;
2433 }
2434 word = "";
2435 break;
2436
2437 case SCHEME_SEPARATOR:
2438 numberOfColons++;
2439 if (numberOfColons > 7) {
2440 return false;
2441 }
2442 if (numberOfPeriods > 0) {
2443 return false;
2444 }
2445 if (prevChar == SCHEME_SEPARATOR) {
2446 if (doubleColon) {
2447 return false;
2448 }
2449 doubleColon = true;
2450 }
2451 word = "";
2452 break;
2453
2454 default:
2455 if (word.length() > 3) {
2456 return false;
2457 }
2458 if (!isValidHexChar(c)) {
2459 return false;
2460 }
2461 word += c;
2462 }
2463 }
2464
2465 // Check if we have an IPv4 ending
2466 if (numberOfPeriods > 0) {
2467 if (numberOfPeriods != 3 || !isValidIP4Word(word)) {
2468 return false;
2469 }
2470 } else {
2471 // If we're at then end and we haven't had 7 colons then there
2472 // is a problem unless we encountered a doubleColon
2473 if (numberOfColons != 7 && !doubleColon) {
2474 return false;
2475 }
2476
2477 // If we have an empty word at the end, it means we ended in
2478 // either a : or a .
2479 // If we did not end in :: then this is invalid
2480 if (word == "" && ipv6Address.charAt(length - 1 - offset) != SCHEME_SEPARATOR
2481 && ipv6Address.charAt(length - 2 - offset) != SCHEME_SEPARATOR) {
2482 return false;
2483 }
2484 }
2485
2486 return true;
2487 }
2488
2489 private static boolean isValidIP4Word(final String word) {
2490 char c;
2491 if (word.length() < 1 || word.length() > 3) {
2492 return false;
2493 }
2494 for (int i = 0; i < word.length(); i++) {
2495 c = word.charAt(i);
2496 if (!(c >= '0' && c <= '9')) {
2497 return false;
2498 }
2499 }
2500 if (Integer.parseInt(word) > 255) {
2501 return false;
2502 }
2503 return true;
2504 }
2505
2506 /**
2507 * Validate a string by checking if it contains any characters other than:
2508 * <ol>
2509 * <li>letters ('a'..'z', 'A'..'Z')</li>
2510 * <li>numbers ('0'..'9')</li>
2511 * <li>characters in the legal-set parameter</li>
2512 * <li> others (unicode characters that are not in
2513 * US-ASCII set, and are not ISO Control or are not ISO Space characters)</li>
2514 * </ol>
2515 *
2516 * @param encoded
2517 * {@code java.lang.String} the string to be validated
2518 * @param legal
2519 * {@code java.lang.String} the characters allowed in the String
2520 * s
2521 */
2522 private static int validateEncoded(final String encoded, final String legal) {
2523 for (int i = 0; i < encoded.length();) {
2524 final char ch = encoded.charAt(i);
2525 if (ch == '%') {
2526 do {
2527 if (i + 2 >= encoded.length()) {
2528 throw new IllegalArgumentException("missing '%' hex-digits at index "+i);
2529 }
2530 final int d1 = Character.digit(encoded.charAt(i + 1), 16);
2531 final int d2 = Character.digit(encoded.charAt(i + 2), 16);
2532 if (d1 == -1 || d2 == -1) {
2533 throw new IllegalArgumentException("invalid hex-digits at index "+i+": "+encoded.substring(i, i + 3));
2534 }
2535 i += 3;
2536 } while (i < encoded.length() && encoded.charAt(i) == '%');
2537 continue;
2538 }
2539 if ( !( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
2540 (ch >= '0' && ch <= '9') || legal.indexOf(ch) > -1 ||
2541 (ch > 127 && !Character.isSpaceChar(ch) && !Character.isISOControl(ch))
2542 )
2543 ) {
2544 return i;
2545 }
2546 i++;
2547 }
2548 return -1;
2549 }
2550 private static int validateAlphaNum(final String s, final String legal) {
2551 for (int i = 0; i < s.length();) {
2552 final char ch = s.charAt(i);
2553 if ( !( (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
2554 (ch >= '0' && ch <= '9') || legal.indexOf(ch) > -1
2555 )
2556 ) {
2557 return i;
2558 }
2559 i++;
2560 }
2561 return -1;
2562 }
2563
2564 private static boolean isValidHexChar(final char c) {
2565 return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
2566 }
2567 private static boolean emptyString(final Encoded s) {
2568 return null == s || 0 == s.length();
2569 }
2570 private static boolean emptyString(final String s) {
2571 return null == s || 0 == s.length();
2572 }
2573
2574 private static void fail(final Encoded input, final String reason, final int p) throws URISyntaxException {
2575 throw new URISyntaxException(input.get(), reason, p);
2576 }
2577 private static void failExpecting(final Encoded input, final String expected, final int p) throws URISyntaxException {
2578 fail(input, "Expecting " + expected, p);
2579 }
2580}
ASCIIEncoded(final String unicode)
Other characters, which are Unicode chars that are not US-ASCII, and are not ISO Control or are not I...
Definition: Uri.java:475
static ASCIIEncoded cast(final String encoded)
Casts the given encoded String by creating a new ASCIIEncoded instance.
Definition: Uri.java:456
Immutable RFC3986 encoded string.
Definition: Uri.java:296
int lastIndexOf(final int ch, final int fromIndex)
See String#lastIndexOf(int, int).
Definition: Uri.java:432
final boolean equals(final Object o)
Definition: Uri.java:383
boolean startsWith(final String prefix, final int toffset)
See String#startsWith(String, int).
Definition: Uri.java:441
final String get()
Returns the encoded String.
Definition: Uri.java:336
boolean endsWith(final String suffix)
See String#endsWith(String).
Definition: Uri.java:443
final String decode()
Decodes the string argument which is assumed to be encoded in the x-www-form-urlencoded MIME conten...
Definition: Uri.java:353
int lastIndexOf(final String str, final int fromIndex)
See String#lastIndexOf(String, int).
Definition: Uri.java:436
final int indexOf(final int ch)
See String#indexOf(int).
Definition: Uri.java:421
final Encoded substring(final int start, final int end)
See String#substring(int, int).
Definition: Uri.java:418
final int compareTo(final Encoded o)
Definition: Uri.java:407
final boolean equalsIgnoreCase(final Encoded anotherEncoded)
See String#equalsIgnoreCase(String).
Definition: Uri.java:446
final int indexOf(final int ch, final int fromIndex)
See String#indexOf(int, int).
Definition: Uri.java:423
final Encoded substring(final int start)
See String#substring(int).
Definition: Uri.java:416
final CharSequence subSequence(final int start, final int end)
Definition: Uri.java:404
int lastIndexOf(final String str)
See String#lastIndexOf(String).
Definition: Uri.java:434
final int indexOf(final String str)
See String#indexOf(String).
Definition: Uri.java:425
static Encoded cast(final String encoded)
Casts the given encoded String by creating a new Encoded instance.
Definition: Uri.java:305
boolean startsWith(final String prefix)
See String#startsWith(String).
Definition: Uri.java:439
final int indexOf(final String str, final int fromIndex)
See String#indexOf(String, int).
Definition: Uri.java:427
final int lastIndexOf(final int ch)
See String#lastIndexOf(int).
Definition: Uri.java:430
Encoded(final String vanilla, final String legal)
Encodes all characters into their hexadecimal value prepended by '', except:
Definition: Uri.java:329
final char charAt(final int index)
Definition: Uri.java:401
Encoded concat(final Encoded encoded)
See String#concat(String).
Definition: Uri.java:413
This class implements an immutable Uri as defined by RFC 2396.
Definition: Uri.java:160
final Encoded schemeSpecificPart
Encoded scheme-specific-part, never null.
Definition: Uri.java:1209
static final String HTTP_SCHEME
{@value}
Definition: Uri.java:285
ASCIIEncoded toASCIIString()
Returns the encoded input encoded in US-ASCII.
Definition: Uri.java:1308
static Uri valueOf(final java.net.URL url)
Creates a new Uri instance using the given URL instance, convenient wrapper for valueOf(URI) and URL#...
Definition: Uri.java:1167
static String encodeToASCIIString(final String unicode)
Other characters, which are Unicode chars that are not US-ASCII, and are not ISO Control or are not I...
Definition: Uri.java:554
final Encoded input
Encoded input string used at construction, never null.
Definition: Uri.java:1196
static Uri valueOf(final File file)
Creates a new Uri instance using the given File instance.
Definition: Uri.java:1121
final Encoded scheme
Encoded scheme, null if undefined.
Definition: Uri.java:1206
final int hashCode()
Definition: Uri.java:1845
final Uri getNormalized()
Normalizes this Uri's path and return the normalized form if it differs, otherwise this instance.
Definition: Uri.java:1583
final Uri getNewQuery(final Encoded newQuery)
Returns a new Uri instance w/ the given new query newQuery.
Definition: Uri.java:1740
static boolean isValidScheme(final String scheme)
Definition: Uri.java:2210
static Uri create(final Encoded scheme, final Encoded host, final Encoded path, final Encoded fragment)
Creates a new Uri instance using the given encoded arguments.
Definition: Uri.java:936
static Uri create(final Encoded scheme, final Encoded authority, final Encoded path, final Encoded query, final Encoded fragment)
Creates a new Uri instance using the given encoded arguments.
Definition: Uri.java:1027
static boolean isHttpxScheme(final String uri)
Definition: Uri.java:1263
static final String RESERVED_2
Definition: Uri.java:211
static final String FRAG_LEGAL
Valid charset for RFC 2396 fragment, additional to legal alphanum characters.
Definition: Uri.java:273
static final String SSP_LEGAL
Valid charset for RFC 2396 scheme-specific-part, additional to legal alphanum characters.
Definition: Uri.java:263
static Uri create(final Encoded scheme, final Encoded userinfo, final Encoded host, final int port, final Encoded path, final Encoded query, final Encoded fragment)
Creates a new Uri instance using the given encoded arguments.
Definition: Uri.java:835
final int port
Encoded port part of authority and scheme-specific-part, -1 if undefined.
Definition: Uri.java:1222
static final String UNRESERVED
RFC 3986 section 2.3 Unreserved Characters (January 2005)
Definition: Uri.java:196
static Uri valueOf(final java.net.URI uri)
Creates a new Uri instance using the given URI instance.
Definition: Uri.java:1139
static final String PATH_LEGAL
Valid charset for RFC 2396 path, additional to legal alphanum characters.
Definition: Uri.java:243
static Uri valueOfFilepath(final String path)
Creates a new Uri instance using the given file-path argument.
Definition: Uri.java:1087
static final char JAR_SCHEME_SEPARATOR
A JAR sub-protocol is separated from the JAR entry w/ this separator {@value}.
Definition: Uri.java:291
final Encoded host
Encoded host part of authority and scheme-specific-part, null if undefined.
Definition: Uri.java:1220
final boolean hasAuthority
Indicating whether authority part is defined or not.
Definition: Uri.java:1214
final Encoded userInfo
Encoded userinfo part of authority and scheme-specific-part, null if undefined.
Definition: Uri.java:1218
static Uri create(final String scheme, final String authority, final String path, final String query, final String fragment)
Creates a new Uri instance using the given unencoded arguments.
Definition: Uri.java:963
Uri getRelativeOf(final Encoded appendPath)
Returns a new Uri appending the given appendPath to this instance's directory.
Definition: Uri.java:1709
final boolean opaque
Indicating whether this Uri is opaque, i.e.
Definition: Uri.java:1240
static final String RESERVED
RFC 3986 section 2.2 Reserved Characters (January 2005)
Definition: Uri.java:208
final boolean isFileScheme()
Returns true, if this instance is a file scheme, otherwise false.
Definition: Uri.java:1255
static Uri create(final String scheme, final String userinfo, String host, final int port, final String path, final String query, final String fragment)
Creates a new Uri instance using the given unencoded arguments.
Definition: Uri.java:745
final java.net.URL toURL()
Returns a new URL instance using the encoded input string, new URL(uri.input), i.e.
Definition: Uri.java:1369
final String toString()
Returns the encoded input as String, never null, same as getEncoded().
Definition: Uri.java:1301
final File toFile()
If this instance is a file scheme, implementation decodes [ "//"+authority ] + path,...
Definition: Uri.java:1390
static boolean isFileScheme(final String uri)
Definition: Uri.java:1259
Uri(final Encoded uri)
Creates a new Uri instance according to the given encoded string uri.
Definition: Uri.java:1250
static final String USERINFO_LEGAL
Valid charset for RFC 2396 authority's user-info, additional to legal alphanum characters.
Definition: Uri.java:224
final java.net.URI toURI()
Returns a new URI instance using the encoded input string, new URI(uri.input), i.e.
Definition: Uri.java:1323
final java.net.URI toURIReencoded()
Returns a new URI instance based upon this instance.
Definition: Uri.java:1344
static Uri create(final String scheme, final String host, final String path, final String fragment)
Creates a new Uri instance using the given unencoded arguments.
Definition: Uri.java:907
final Uri getContainedUri()
If this instance's schemeSpecificPart contains a Uri itself, a sub-Uri, return schemeSpecificPart + #...
Definition: Uri.java:1437
final Encoded fragment
Encoded fragment, null if undefined.
Definition: Uri.java:1228
static final String AUTHORITY_LEGAL
Valid charset for RFC 2396 authority, additional to legal alphanum characters.
Definition: Uri.java:234
final Encoded query
Encoded query part of scheme-specific-part, null if undefined.
Definition: Uri.java:1225
final boolean absolute
Indicating whether this Uri is absolute, i.e.
Definition: Uri.java:1231
static String getScheme(final String uri)
Definition: Uri.java:1267
static String decode(final Encoded encoded)
Safe Encoded#decode() call on optional encoded instance.
Definition: Uri.java:572
static final String HTTPS_SCHEME
{@value}
Definition: Uri.java:287
static final String FILE_SCHEME
{@value}
Definition: Uri.java:283
final Encoded path
Encoded path part of scheme-specific-part, never null.
Definition: Uri.java:1211
static String encode(final String vanilla, final String legal)
All characters are encoded into their hexadecimal value prepended by '', except:
Definition: Uri.java:519
static final String JAR_SCHEME
{@value}
Definition: Uri.java:289
final Uri concat(final Encoded suffix)
Concatenates the given encoded string to the encoded uri of this instance and returns a new Uri insta...
Definition: Uri.java:1725
final Encoded authority
Encoded authority part of scheme-specific-part, null if undefined.
Definition: Uri.java:1216
final Uri getParent()
Returns this Uri's parent directory Uri.
Definition: Uri.java:1664
static Uri create(final Encoded scheme, final Encoded ssp, final Encoded fragment)
Creates a new Uri instance using the given encoded arguments.
Definition: Uri.java:701
static final char SCHEME_SEPARATOR
{@value}
Definition: Uri.java:277
static final String QUERY_LEGAL
Valid charset for RFC 2396 query, additional to legal alphanum characters.
Definition: Uri.java:253
final boolean equals(final Object o)
Definition: Uri.java:1763
static Uri tryUriOrFile(final String uri_s)
Return first successful resulting Uri.
Definition: Uri.java:1177
static String decode(final String encoded)
Decodes the string argument which is assumed to be encoded in the x-www-form-urlencoded MIME conten...
Definition: Uri.java:591
static final char QUERY_SEPARATOR
{@value}
Definition: Uri.java:279
final boolean isJarScheme()
Returns true, if this instance is a jar scheme, otherwise false.
Definition: Uri.java:1286
static final char FRAGMENT_SEPARATOR
{@value}
Definition: Uri.java:281
Uri getDirectory()
Returns this Uri's directory Uri.
Definition: Uri.java:1622
static Uri cast(final String encodedUri)
Casts the given encoded String to a new Encoded instance used to create the resulting Uri instance vi...
Definition: Uri.java:1068
static Uri create(final String scheme, final String ssp, final String fragment)
Creates a new Uri instance using the given unencoded arguments.
Definition: Uri.java:656
final Encoded getEncoded()
Returns the encoded input, never null.
Definition: Uri.java:1293
static String slashify(final String path, final boolean startWithSlash, final boolean endWithSlash)
Definition: IOUtil.java:389
Helper routines for accessing properties.
static final boolean isPropertyDefined(final String property, final boolean jnlpAlias)