23package com.jogamp.common.net;
26import java.io.UnsupportedEncodingException;
27import java.net.MalformedURLException;
28import java.net.URISyntaxException;
29import java.util.StringTokenizer;
30import java.util.regex.Pattern;
32import jogamp.common.Debug;
34import com.jogamp.common.util.IOUtil;
35import com.jogamp.common.util.PropertyAccess;
161 private static final boolean DEBUG;
162 private static final boolean DEBUG_SHOWFIX;
165 Debug.initSingleton();
166 DEBUG = IOUtil.DEBUG || Debug.debug(
"Uri");
182 private static final int PARSE_HINT_FIX_PATH = 1 << 0;
184 private static final String DIGITS =
"0123456789ABCDEF";
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}");
199 private static final String punct =
",;:$&+=";
208 public static final String
RESERVED = punct +
"!*\'()@/?#[]";
211 public static final String
RESERVED_2 = punct +
"!*\'()@/?[]";
296 public static class Encoded implements Comparable<Encoded>, CharSequence {
297 private final String s;
309 Encoded(
final String encodedString) {
310 this.s = encodedString;
329 public Encoded(
final String vanilla,
final String legal) {
330 this.s =
encode(vanilla, legal);
336 public final String
get() {
return s; }
369 public final int hashCode() {
return s.hashCode(); }
383 public final boolean equals(
final Object o) {
388 return s.equals(((
Encoded)o).s);
398 public final int length() {
return s.length(); }
401 public final char charAt(
final int index) {
return s.charAt(index); }
404 public final CharSequence
subSequence(
final int start,
final int end) {
return s.subSequence(start, end); }
421 public final int indexOf(
final int ch) {
return s.indexOf(ch); }
423 public final int indexOf(
final int ch,
final int fromIndex) {
return s.indexOf(ch, fromIndex); }
425 public final int indexOf(
final String str) {
return s.indexOf(str); }
427 public final int indexOf(
final String str,
final int fromIndex) {
return s.indexOf(str, fromIndex); }
430 public final int lastIndexOf(
final int ch) {
return s.lastIndexOf(ch); }
432 public int lastIndexOf(
final int ch,
final int fromIndex) {
return s.lastIndexOf(ch, fromIndex); }
434 public int lastIndexOf(
final String str) {
return s.lastIndexOf(str); }
436 public int lastIndexOf(
final String str,
final int fromIndex) {
return s.lastIndexOf(str, fromIndex); }
439 public boolean startsWith(
final String prefix) {
return s.startsWith(prefix); }
441 public boolean startsWith(
final String prefix,
final int toffset) {
return s.startsWith(prefix, toffset); }
443 public boolean endsWith(
final String suffix) {
return s.endsWith(suffix); }
459 private ASCIIEncoded(
final String encoded,
final Object unused) {
482 private static void encodeChar2UTF8(
final StringBuilder buf,
final char ch) {
485 bytes =
new String(
new char[] { ch }).getBytes(ENCODING);
486 }
catch (
final UnsupportedEncodingException e) {
487 throw new RuntimeException(MSG_ENCODING_NA, e);
490 for (
int j = 0; j < bytes.length; j++) {
491 final byte b = bytes[j];
493 buf.append(DIGITS.charAt( ( b & 0xf0 ) >> 4 ));
494 buf.append(DIGITS.charAt( b & 0xf ));
519 public static String
encode(
final String vanilla,
final String legal) {
520 if(
null == vanilla ) {
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) )
534 encodeChar2UTF8(buf, ch);
537 return buf.toString();
555 final StringBuilder buf =
new StringBuilder();
556 for (
int i = 0; i < unicode.length(); i++) {
557 final char ch = unicode.charAt(i);
561 encodeChar2UTF8(buf, ch);
564 return buf.toString();
573 return null != encoded ? encoded.
decode() :
null;
591 public static String
decode(
final String encoded) {
592 if(
null == encoded ) {
595 final StringBuilder result =
new StringBuilder();
596 final byte[] buf =
new byte[32];
598 for (
int i = 0; i < encoded.length();) {
599 final char c = encoded.charAt(i);
603 if (i + 2 >= encoded.length()) {
604 throw new IllegalArgumentException(
"missing '%' hex-digits at index "+i);
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));
611 buf[bufI++] = (byte) ((d1 << 4) + d2);
613 appendUTF8(result, buf, bufI);
617 }
while (i < encoded.length() && encoded.charAt(i) ==
'%');
619 appendUTF8(result, buf, bufI);
626 return result.toString();
628 private static void appendUTF8(
final StringBuilder sb,
final byte[] buf,
final int count) {
630 sb.append(
new String(buf, 0, count, ENCODING));
631 }
catch (
final UnsupportedEncodingException e) {
632 throw new RuntimeException(MSG_ENCODING_NA, e);
657 if ( emptyString(
scheme) && emptyString(ssp) && emptyString(
fragment) ) {
658 throw new URISyntaxException(
"",
"all empty parts");
660 final StringBuilder uri =
new StringBuilder();
661 if ( !emptyString(
scheme) ) {
665 if ( !emptyString(ssp) ) {
674 return new Uri(
new Encoded(uri.toString()),
false, 0);
702 if ( emptyString(
scheme) && emptyString(ssp) && emptyString(
fragment) ) {
703 throw new URISyntaxException(
"",
"all empty parts");
705 final StringBuilder uri =
new StringBuilder();
706 if ( !emptyString(
scheme) ) {
710 if ( !emptyString(ssp) ) {
711 uri.append(ssp.get());
717 return new Uri(
new Encoded(uri.toString()),
false, 0);
746 final String
path,
final String
query,
final String
fragment)
throws URISyntaxException {
747 if ( emptyString(
scheme) && emptyString(userinfo) && emptyString(
host) && emptyString(
path) &&
749 throw new URISyntaxException(
"",
"all empty parts");
753 throw new URISyntaxException(
path,
"path doesn't start with '/'");
756 final StringBuilder uri =
new StringBuilder();
757 if ( !emptyString(
scheme) ) {
762 if ( !emptyString(userinfo) || !emptyString(
host) ||
port != -1) {
766 if ( !emptyString(userinfo) ) {
772 if ( !emptyString(
host) ) {
787 if ( !emptyString(
path) ) {
792 if ( !emptyString(
query) ) {
803 return new Uri(
new Encoded(uri.toString()),
true, 0);
837 if ( emptyString(
scheme) && emptyString(userinfo) && emptyString(
host) && emptyString(
path) &&
839 throw new URISyntaxException(
"",
"all empty parts");
843 throw new URISyntaxException(
path.
get(),
"path doesn't start with '/'");
846 final StringBuilder uri =
new StringBuilder();
847 if ( !emptyString(
scheme) ) {
852 if ( !emptyString(userinfo) || !emptyString(
host) ||
port != -1) {
856 if ( !emptyString(userinfo) ) {
857 uri.append(userinfo.get());
861 if ( !emptyString(
host) ) {
870 if ( !emptyString(
path) ) {
874 if ( !emptyString(
query) ) {
883 return new Uri(
new Encoded(uri.toString()),
true, 0);
966 throw new URISyntaxException(
"",
"all empty parts");
969 throw new URISyntaxException(
path,
"path doesn't start with '/'");
972 final StringBuilder uri =
new StringBuilder();
973 if ( !emptyString(
scheme) ) {
983 if ( !emptyString(
path) ) {
987 if ( !emptyString(
query) ) {
997 return new Uri(
new Encoded(uri.toString()),
false, 0);
1030 throw new URISyntaxException(
"",
"all empty parts");
1033 throw new URISyntaxException(
path.
get(),
"path doesn't start with '/'");
1036 final StringBuilder uri =
new StringBuilder();
1037 if ( !emptyString(
scheme) ) {
1046 if ( !emptyString(
path) ) {
1049 if ( !emptyString(
query) ) {
1057 return new Uri(
new Encoded(uri.toString()),
false, 0);
1068 public static Uri cast(
final String encodedUri)
throws URISyntaxException {
1088 if ( emptyString(
path) ) {
1089 throw new URISyntaxException(
"",
"empty path");
1092 throw new URISyntaxException(
path,
"path doesn't start with '/'");
1095 final StringBuilder uri =
new StringBuilder();
1102 return new Uri(
new Encoded(uri.toString()),
false, 0);
1121 public static Uri valueOf(
final File file)
throws URISyntaxException {
1139 public static Uri valueOf(
final java.net.URI uri)
throws URISyntaxException {
1140 if( uri.isOpaque()) {
1143 return new Uri(
new Encoded( uri.toString() ),
false, 0);
1146 return Uri.
create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
1147 uri.getPath(), uri.getQuery(), uri.getFragment());
1167 public static Uri valueOf(
final java.net.URL url)
throws URISyntaxException {
1180 }
catch(
final Throwable t) { }
1183 }
catch(
final Throwable t) { }
1185 final File file =
new File(uri_s);
1187 }
catch(
final Throwable t) { }
1198 private final Object lazyLock =
new Object();
1251 this(uri,
false, 0);
1309 synchronized( lazyLock ) {
1310 if(
null == inputASCII ) {
1325 return new java.net.URI(
input.
get());
1326 }
catch (
final URISyntaxException e) {
1345 final java.net.URI recomposedURI;
1349 }
else if(
null !=
host ) {
1358 return recomposedURI;
1369 public final java.net.URL
toURL() throws MalformedURLException {
1371 throw new IllegalArgumentException(
"Cannot convert relative Uri: "+
input);
1373 return new java.net.URL(
input.
get());
1392 final String authorityS;
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(
"\\\\") ) {
1402 return new File(r.substring(1));
1407 return new File(
path);
1439 final StringBuilder sb =
new StringBuilder();
1444 throw new URISyntaxException(
input.
get(),
"missing jar separator");
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 ) {
1460 }
catch(
final URISyntaxException e) {
1463 System.err.println(
"Caught "+e.getClass().getSimpleName()+
": "+e.getMessage());
1464 e.printStackTrace();
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;
1477 final String pathS = pathBuf.
toString();
1478 if( 0 > pathS.indexOf(
"/") && emptyString(appendPath) ) {
1481 pathBuf.setLength(0);
1482 pathBuf.append( IOUtil.cleanPathString( pathS ) );
1483 cleaned = pathBuf.length() != pathS.length();
1487 final String pathS = pathBuf.toString();
1489 final int e = pathS.lastIndexOf(
"/");
1490 if( 0 > jarSepIdx || e - 1 > jarSepIdx ) {
1491 if( cutoffFile && e < pathS.length() - 1 ) {
1493 pathBuf.setLength(0);
1494 pathBuf.append( pathS.substring(0, e+1) );
1495 }
else if( cutoffDir ) {
1497 final int p = pathS.lastIndexOf(
"/", e-1);
1499 pathBuf.setLength(0);
1500 pathBuf.append( pathS.substring(0, p+1) );
1504 final boolean cutoff = pathBuf.length() != pathS.length();
1505 if( !cutoff && ( cutoffDir || !cleaned ) && emptyString(appendPath) ) {
1509 if( !emptyString(appendPath) ) {
1510 pathBuf.append(appendPath.get());
1512 final String pathS = pathBuf.toString();
1513 pathBuf.setLength(0);
1514 pathBuf.append( IOUtil.cleanPathString( pathS ) );
1518 private final Uri cutoffLastPathSegementImpl(
final boolean cutoffFile,
final boolean cutoffDir,
final Encoded appendPath)
throws URISyntaxException {
1522 if( !emptyString(appendPath) ) {
1528 final StringBuilder sspBuf =
new StringBuilder();
1531 final Encoded queryTemp;
1541 if( !cutoffLastPathSegementImpl(sspBuf, cutoffFile, cutoffDir, appendPath) ) {
1545 if ( !emptyString(queryTemp) ) {
1547 sspBuf.append( queryTemp.get() );
1553 if( emptyString(
path) ) {
1556 final StringBuilder pathBuf =
new StringBuilder();
1559 if( !cutoffLastPathSegementImpl(pathBuf, cutoffFile, cutoffDir, appendPath) ) {
1585 final Uri res = cutoffLastPathSegementImpl(
false,
false,
null);
1586 return null != res ? res :
this;
1587 }
catch (
final URISyntaxException e) {
1589 System.err.println(
"Caught "+e.getClass().getSimpleName()+
": "+e.getMessage());
1590 e.printStackTrace();
1624 final Uri res = cutoffLastPathSegementImpl(
true,
false,
null);
1625 return null != res ? res :
this;
1626 }
catch (
final URISyntaxException e) {
1628 System.err.println(
"Caught "+e.getClass().getSimpleName()+
": "+e.getMessage());
1629 e.printStackTrace();
1666 return cutoffLastPathSegementImpl(
true,
true,
null);
1667 }
catch (
final URISyntaxException e) {
1669 System.err.println(
"Caught "+e.getClass().getSimpleName()+
": "+e.getMessage());
1670 e.printStackTrace();
1710 if( emptyString(appendPath) ) {
1713 return cutoffLastPathSegementImpl(
true,
false, appendPath);
1726 if(
null == suffix ) {
1742 throw new URISyntaxException(
input.
decode(),
"Opaque Uri cannot permute by query");
1763 public final boolean equals(
final Object o) {
1764 if (!(o instanceof
Uri)) {
1788 if (!equalsHexCaseInsensitive(
path, uri.
path)) {
1794 }
else if (uri.
query !=
null &&
query !=
null) {
1795 if (!equalsHexCaseInsensitive(uri.
query,
query)) {
1803 if (uri.
host !=
null &&
host ==
null || uri.
host ==
null &&
host !=
null) {
1805 }
else if (uri.
host ==
null &&
host ==
null) {
1846 synchronized( lazyLock ) {
1848 hash = getHashString().hashCode();
1858 private String convertHexToLowerCase(
final String s) {
1859 if (s.indexOf(
'%') == -1) {
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());
1870 return result.toString();
1878 private boolean equalsHexCaseInsensitive(
final Encoded first,
final Encoded second) {
1879 if (first.indexOf(
'%') != second.indexOf(
'%')) {
1880 return first.equals(second);
1883 int index = 0, previndex = 0;
1884 while ( ( index = first.indexOf(
'%', previndex) ) != -1 &&
1885 second.indexOf(
'%', previndex) == index
1887 if( !first.get().substring(previndex, index).equals( second.get().substring(previndex, index) ) ) {
1890 if( !first.get().substring(index + 1, index + 3).equalsIgnoreCase( second.get().substring(index + 1, index + 3) ) ) {
1896 return first.get().substring(previndex).equals( second.get().substring(previndex) );
1904 private String getHashString() {
1905 final StringBuilder result =
new StringBuilder();
1907 result.append(
scheme.
get().toLowerCase());
1914 result.append(
"//");
1921 result.append(
host.
get().toLowerCase());
1932 if (
query !=
null) {
1942 return convertHexToLowerCase(result.toString());
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");
1965 fragment =
new Encoded( temp.substring(index + 1) );
1967 temp = temp.substring(0, index);
1976 index = indexSchemeSep;
1977 final int indexSSP = temp.indexOf(
'/');
1984 if ( indexSchemeSep != -1 &&
1985 ( indexSSP >= indexSchemeSep || indexSSP == -1 ) &&
1986 ( indexQuerySep >= indexSchemeSep || indexQuerySep == -1 )
1990 scheme =
new Encoded( temp.substring(0, indexSchemeSep) );
1992 failExpecting(
input,
"scheme", indexSchemeSep);
1995 sspTemp = temp.substring(indexSchemeSep + 1);
1996 if (sspTemp.length() == 0) {
1997 failExpecting(
input,
"scheme-specific-part", indexSchemeSep);
2005 if (
scheme ==
null || sspTemp.
length() > 0 && sspTemp.charAt(0) ==
'/' ) {
2013 query =
new Encoded( temp.substring(index + 1) );
2014 temp = temp.substring(0, index);
2015 validateQuery(
input,
query, indexSSP + 1 + index);
2021 final int indexPathInSSP;
2024 if (temp.startsWith(
"//")) {
2026 final String authorityS;
2028 authorityS = temp.substring(2, index);
2029 pathTemp = temp.substring(index);
2030 indexPathInSSP = index;
2032 authorityS = temp.substring(2);
2033 if (authorityS.length() == 0 &&
query ==
null &&
fragment ==
null) {
2034 failExpecting(
input,
"authority, path [, query, fragment]", index);
2037 indexPathInSSP = -1;
2041 if ( emptyString(authorityS) ) {
2054 if (indexSSP > -1) {
2055 indexPath += indexSSP;
2057 if (indexPathInSSP > -1) {
2058 indexPath += indexPathInSSP;
2061 final int pathErrIdx = validateEncoded(pathTemp,
PATH_LEGAL);
2062 if( 0 <= pathErrIdx ) {
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);
2070 final int pathTempOldLen = pathTemp.length();
2072 validatePath(
input, pathTemp, indexPath);
2075 final StringBuilder sb =
new StringBuilder();
2076 if( indexPathInSSP > 0 ) {
2077 sb.append( sspTemp.substring(0, indexPathInSSP) );
2079 sb.append( pathTemp ).append( sspTemp.substring( indexPathInSSP + pathTempOldLen ) );
2080 sspTemp = sb.toString();
2083 if( indexPath > 0 ) {
2084 sb.append( inputTemp.substring(0, indexPath) );
2086 sb.append( pathTemp ).append( inputTemp.substring( indexPath + pathTempOldLen ) );
2087 inputTemp = sb.toString();
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);
2095 fail(
input,
"invalid path", indexPath+pathErrIdx);
2098 path =
new Encoded( pathTemp );
2105 validateSsp(
input, sspTemp, indexSchemeSep + 1);
2108 this.input = inputTemp ==
input.
get() ?
input :
new Encoded( inputTemp );
2122 Encoded tempUserinfo =
null, tempHost =
null;
2124 boolean authorityComplete;
2127 authorityComplete =
true;
2131 index = temp.indexOf(
'@');
2134 tempUserinfo =
new Encoded( temp.substring(0, index) );
2135 validateUserinfo(
authority, tempUserinfo, 0);
2136 temp = temp.substring(index + 1);
2137 hostindex = index + 1;
2141 final int endindex = temp.indexOf(
']');
2143 if (index != -1 && endindex < index) {
2145 tempHost =
new Encoded( temp.substring(0, index) );
2147 if (index < (temp.length() - 1)) {
2149 tempPort = Integer.parseInt(temp.substring(index + 1));
2154 authorityComplete =
false;
2156 }
catch (
final NumberFormatException e) {
2158 fail(
authority,
"invalid port <"+
authority+
">, "+e.getMessage(), hostindex + index + 1);
2160 authorityComplete =
false;
2164 tempHost =
new Encoded( temp );
2167 if( authorityComplete ) {
2168 if ( emptyString(tempHost) ) {
2172 authorityComplete =
false;
2173 }
else if (!isValidHost(expectServer, tempHost)) {
2175 fail(
authority,
"invalid host <"+tempHost+
">", hostindex);
2177 authorityComplete =
false;
2181 authorityComplete =
false;
2184 if( authorityComplete ) {
2199 private static void validateScheme(
final Encoded uri,
final Encoded
scheme,
final int index)
throws URISyntaxException {
2202 if ( !((ch >=
'a' && ch <=
'z') || (ch >=
'A' && ch <=
'Z')) ) {
2203 fail(uri,
"invalid scheme", index);
2205 final int errIdx = validateAlphaNum(
scheme.
get(),
"+-.");
2207 fail(uri,
"invalid scheme", index+errIdx);
2213 if ( !((ch >=
'a' && ch <=
'z') || (ch >=
'A' && ch <=
'Z')) ) {
2216 final int errIdx = validateAlphaNum(
scheme,
"+-.");
2223 private static void validateSsp(
final Encoded uri,
final String ssp,
final int index)
throws URISyntaxException {
2224 final int errIdx = validateEncoded(ssp,
SSP_LEGAL);
2226 fail(uri,
"invalid scheme-specific-part", index+errIdx);
2230 private static void validateAuthority(
final Encoded uri,
final Encoded
authority,
final int index)
throws URISyntaxException {
2233 fail(uri,
"invalid authority", index+errIdx);
2237 private static void validatePath(
final Encoded uri,
final String
path,
final int index)
throws URISyntaxException {
2240 fail(uri,
"invalid path", index+errIdx);
2244 private static void validateQuery(
final Encoded uri,
final Encoded
query,
final int index)
throws URISyntaxException {
2247 fail(uri,
"invalid query", index+errIdx);
2251 private static void validateFragment(
final Encoded uri,
final Encoded
fragment,
final int index)
throws URISyntaxException {
2254 fail(uri,
"invalid fragment", index+errIdx);
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);
2271 private boolean isValidHost(
final boolean expectServer,
final Encoded
host)
throws URISyntaxException {
2275 fail(
input,
"invalid host, missing closing ipv6: "+
host, 0);
2277 if (!isValidIP6Address(
host.
get())) {
2290 if ( index < 0 || index ==
host.
length() - 1 ||
2294 if (isValidDomainName(
host)) {
2298 fail(
input,
"invalid host, invalid domain-name or ipv4: "+
host, 0);
2304 if (isValidIPv4Address(
host.
get())) {
2308 fail(
input,
"invalid host, invalid ipv4: "+
host, 0);
2313 private static boolean isValidDomainName(
final Encoded
host) {
2314 final String hostS =
host.
get();
2315 if( 0 <= validateAlphaNum(hostS,
"-.") ) {
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(
"-")) {
2327 if (!label.equals(hostS)) {
2328 final char ch = label.charAt(0);
2329 if (ch >=
'0' && ch <=
'9') {
2336 private static boolean isValidIPv4Address(
final String ipv4Address) {
2341 index = ipv4Address.indexOf(
'.');
2342 num = Integer.parseInt(ipv4Address.substring(0, index));
2343 if (num < 0 || num > 255) {
2346 index2 = ipv4Address.indexOf(
'.', index + 1);
2347 num = Integer.parseInt(ipv4Address.substring(index + 1, index2));
2348 if (num < 0 || num > 255) {
2351 index = ipv4Address.indexOf(
'.', index2 + 1);
2352 num = Integer.parseInt(ipv4Address.substring(index2 + 1, index));
2353 if (num < 0 || num > 255) {
2356 num = Integer.parseInt(ipv4Address.substring(index + 1));
2357 if (num < 0 || num > 255) {
2360 }
catch (
final Exception e) {
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;
2380 for (
int i = 0; i < length; i++) {
2382 c = ipv6Address.charAt(i);
2390 if (ipv6Address.charAt(length - 1) !=
']') {
2405 if (i != length - 1) {
2408 if (ipv6Address.charAt(0) !=
'[') {
2417 if (numberOfPeriods > 3) {
2420 if (!isValidIP4Word(word)) {
2423 if (numberOfColons != 6 && !doubleColon) {
2429 if (numberOfColons == 7
2439 if (numberOfColons > 7) {
2442 if (numberOfPeriods > 0) {
2455 if (word.length() > 3) {
2458 if (!isValidHexChar(c)) {
2466 if (numberOfPeriods > 0) {
2467 if (numberOfPeriods != 3 || !isValidIP4Word(word)) {
2473 if (numberOfColons != 7 && !doubleColon) {
2480 if (word ==
"" && ipv6Address.charAt(length - 1 - offset) !=
SCHEME_SEPARATOR
2489 private static boolean isValidIP4Word(
final String word) {
2491 if (word.length() < 1 || word.length() > 3) {
2494 for (
int i = 0; i < word.length(); i++) {
2496 if (!(c >=
'0' && c <=
'9')) {
2500 if (Integer.parseInt(word) > 255) {
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);
2527 if (i + 2 >= encoded.length()) {
2528 throw new IllegalArgumentException(
"missing '%' hex-digits at index "+i);
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));
2536 }
while (i < encoded.length() && encoded.charAt(i) ==
'%');
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))
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
2564 private static boolean isValidHexChar(
final char c) {
2565 return (c >=
'0' && c <=
'9') || (c >=
'A' && c <=
'F') || (c >=
'a' && c <=
'f');
2567 private static boolean emptyString(
final Encoded s) {
2568 return null == s || 0 == s.length();
2570 private static boolean emptyString(
final String s) {
2571 return null == s || 0 == s.length();
2574 private static void fail(
final Encoded
input,
final String reason,
final int p)
throws URISyntaxException {
2575 throw new URISyntaxException(
input.
get(), reason, p);
2577 private static void failExpecting(
final Encoded
input,
final String expected,
final int p)
throws URISyntaxException {
2578 fail(
input,
"Expecting " + expected, p);
ASCIIEncoded(final String unicode)
Other characters, which are Unicode chars that are not US-ASCII, and are not ISO Control or are not I...
static ASCIIEncoded cast(final String encoded)
Casts the given encoded String by creating a new ASCIIEncoded instance.
Immutable RFC3986 encoded string.
int lastIndexOf(final int ch, final int fromIndex)
See String#lastIndexOf(int, int).
final boolean equals(final Object o)
boolean startsWith(final String prefix, final int toffset)
See String#startsWith(String, int).
final String get()
Returns the encoded String.
boolean endsWith(final String suffix)
See String#endsWith(String).
final String decode()
Decodes the string argument which is assumed to be encoded in the x-www-form-urlencoded MIME conten...
int lastIndexOf(final String str, final int fromIndex)
See String#lastIndexOf(String, int).
final int indexOf(final int ch)
See String#indexOf(int).
final Encoded substring(final int start, final int end)
See String#substring(int, int).
final int compareTo(final Encoded o)
final boolean equalsIgnoreCase(final Encoded anotherEncoded)
See String#equalsIgnoreCase(String).
final int indexOf(final int ch, final int fromIndex)
See String#indexOf(int, int).
final Encoded substring(final int start)
See String#substring(int).
final CharSequence subSequence(final int start, final int end)
int lastIndexOf(final String str)
See String#lastIndexOf(String).
final int indexOf(final String str)
See String#indexOf(String).
static Encoded cast(final String encoded)
Casts the given encoded String by creating a new Encoded instance.
boolean startsWith(final String prefix)
See String#startsWith(String).
final int indexOf(final String str, final int fromIndex)
See String#indexOf(String, int).
final int lastIndexOf(final int ch)
See String#lastIndexOf(int).
Encoded(final String vanilla, final String legal)
Encodes all characters into their hexadecimal value prepended by '', except:
final char charAt(final int index)
Encoded concat(final Encoded encoded)
See String#concat(String).
This class implements an immutable Uri as defined by RFC 2396.
final Encoded schemeSpecificPart
Encoded scheme-specific-part, never null.
static final String HTTP_SCHEME
{@value}
ASCIIEncoded toASCIIString()
Returns the encoded input encoded in US-ASCII.
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#...
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...
final Encoded input
Encoded input string used at construction, never null.
static Uri valueOf(final File file)
Creates a new Uri instance using the given File instance.
final Encoded scheme
Encoded scheme, null if undefined.
final Uri getNormalized()
Normalizes this Uri's path and return the normalized form if it differs, otherwise this instance.
final Uri getNewQuery(final Encoded newQuery)
Returns a new Uri instance w/ the given new query newQuery.
static boolean isValidScheme(final String scheme)
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.
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.
static boolean isHttpxScheme(final String uri)
static final String RESERVED_2
static final String FRAG_LEGAL
Valid charset for RFC 2396 fragment, additional to legal alphanum characters.
static final String SSP_LEGAL
Valid charset for RFC 2396 scheme-specific-part, additional to legal alphanum characters.
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.
final int port
Encoded port part of authority and scheme-specific-part, -1 if undefined.
static final String UNRESERVED
RFC 3986 section 2.3 Unreserved Characters (January 2005)
static Uri valueOf(final java.net.URI uri)
Creates a new Uri instance using the given URI instance.
static final String PATH_LEGAL
Valid charset for RFC 2396 path, additional to legal alphanum characters.
static Uri valueOfFilepath(final String path)
Creates a new Uri instance using the given file-path argument.
static final char JAR_SCHEME_SEPARATOR
A JAR sub-protocol is separated from the JAR entry w/ this separator {@value}.
final Encoded host
Encoded host part of authority and scheme-specific-part, null if undefined.
final boolean hasAuthority
Indicating whether authority part is defined or not.
final Encoded userInfo
Encoded userinfo part of authority and scheme-specific-part, null if undefined.
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.
Uri getRelativeOf(final Encoded appendPath)
Returns a new Uri appending the given appendPath to this instance's directory.
final boolean opaque
Indicating whether this Uri is opaque, i.e.
static final String RESERVED
RFC 3986 section 2.2 Reserved Characters (January 2005)
final boolean isFileScheme()
Returns true, if this instance is a file scheme, otherwise false.
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.
final java.net.URL toURL()
Returns a new URL instance using the encoded input string, new URL(uri.input), i.e.
final String toString()
Returns the encoded input as String, never null, same as getEncoded().
final File toFile()
If this instance is a file scheme, implementation decodes [ "//"+authority ] + path,...
static boolean isFileScheme(final String uri)
Uri(final Encoded uri)
Creates a new Uri instance according to the given encoded string uri.
static final String USERINFO_LEGAL
Valid charset for RFC 2396 authority's user-info, additional to legal alphanum characters.
final java.net.URI toURI()
Returns a new URI instance using the encoded input string, new URI(uri.input), i.e.
final java.net.URI toURIReencoded()
Returns a new URI instance based upon this instance.
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.
final Uri getContainedUri()
If this instance's schemeSpecificPart contains a Uri itself, a sub-Uri, return schemeSpecificPart + #...
final Encoded fragment
Encoded fragment, null if undefined.
static final String AUTHORITY_LEGAL
Valid charset for RFC 2396 authority, additional to legal alphanum characters.
final Encoded query
Encoded query part of scheme-specific-part, null if undefined.
final boolean absolute
Indicating whether this Uri is absolute, i.e.
static String getScheme(final String uri)
static String decode(final Encoded encoded)
Safe Encoded#decode() call on optional encoded instance.
static final String HTTPS_SCHEME
{@value}
static final String FILE_SCHEME
{@value}
final Encoded path
Encoded path part of scheme-specific-part, never null.
static String encode(final String vanilla, final String legal)
All characters are encoded into their hexadecimal value prepended by '', except:
static final String JAR_SCHEME
{@value}
final Uri concat(final Encoded suffix)
Concatenates the given encoded string to the encoded uri of this instance and returns a new Uri insta...
final Encoded authority
Encoded authority part of scheme-specific-part, null if undefined.
final Uri getParent()
Returns this Uri's parent directory Uri.
static Uri create(final Encoded scheme, final Encoded ssp, final Encoded fragment)
Creates a new Uri instance using the given encoded arguments.
static final char SCHEME_SEPARATOR
{@value}
static final String QUERY_LEGAL
Valid charset for RFC 2396 query, additional to legal alphanum characters.
final boolean equals(final Object o)
static Uri tryUriOrFile(final String uri_s)
Return first successful resulting Uri.
static String decode(final String encoded)
Decodes the string argument which is assumed to be encoded in the x-www-form-urlencoded MIME conten...
static final char QUERY_SEPARATOR
{@value}
final boolean isJarScheme()
Returns true, if this instance is a jar scheme, otherwise false.
static final char FRAGMENT_SEPARATOR
{@value}
Uri getDirectory()
Returns this Uri's directory Uri.
static Uri cast(final String encodedUri)
Casts the given encoded String to a new Encoded instance used to create the resulting Uri instance vi...
static Uri create(final String scheme, final String ssp, final String fragment)
Creates a new Uri instance using the given unencoded arguments.
final Encoded getEncoded()
Returns the encoded input, never null.
static String slashify(final String path, final boolean startWithSlash, final boolean endWithSlash)
Helper routines for accessing properties.
static final boolean isPropertyDefined(final String property, final boolean jnlpAlias)