33import java.io.UnsupportedEncodingException;
34import java.net.MalformedURLException;
35import java.net.URISyntaxException;
36import java.util.StringTokenizer;
37import java.util.regex.Pattern;
163 private static final boolean DEBUG =
false;
164 private static final boolean DEBUG_SHOWFIX =
false;
187 private static final int PARSE_HINT_FIX_PATH = 1 << 0;
189 private static final String DIGITS =
"0123456789ABCDEF";
191 private static final String ENCODING =
"UTF8";
192 private static final String MSG_ENCODING_NA =
"Charset UTF8 not available";
193 private static final Pattern patternSingleFS = Pattern.compile(
"/{1}");
204 private static final String punct =
",;:$&+=";
213 public static final String
RESERVED = punct +
"!*\'()@/?#[]";
216 public static final String
RESERVED_2 = punct +
"!*\'()@/?[]";
301 public static class Encoded implements Comparable<Encoded>, CharSequence {
302 private final String s;
314 Encoded(
final String encodedString) {
315 this.s = encodedString;
334 public Encoded(
final String vanilla,
final String legal) {
335 this.s =
encode(vanilla, legal);
341 public final String
get() {
return s; }
374 public final int hashCode() {
return s.hashCode(); }
388 public final boolean equals(
final Object o) {
393 return s.equals(((
Encoded)o).s);
403 public final int length() {
return s.length(); }
406 public final char charAt(
final int index) {
return s.charAt(index); }
409 public final CharSequence
subSequence(
final int start,
final int end) {
return s.subSequence(start, end); }
426 public final int indexOf(
final int ch) {
return s.indexOf(ch); }
428 public final int indexOf(
final int ch,
final int fromIndex) {
return s.indexOf(ch, fromIndex); }
430 public final int indexOf(
final String str) {
return s.indexOf(str); }
432 public final int indexOf(
final String str,
final int fromIndex) {
return s.indexOf(str, fromIndex); }
435 public final int lastIndexOf(
final int ch) {
return s.lastIndexOf(ch); }
437 public int lastIndexOf(
final int ch,
final int fromIndex) {
return s.lastIndexOf(ch, fromIndex); }
439 public int lastIndexOf(
final String str) {
return s.lastIndexOf(str); }
441 public int lastIndexOf(
final String str,
final int fromIndex) {
return s.lastIndexOf(str, fromIndex); }
444 public boolean startsWith(
final String prefix) {
return s.startsWith(prefix); }
446 public boolean startsWith(
final String prefix,
final int toffset) {
return s.startsWith(prefix, toffset); }
448 public boolean endsWith(
final String suffix) {
return s.endsWith(suffix); }
464 private ASCIIEncoded(
final String encoded,
final Object unused) {
487 private static void encodeChar2UTF8(
final StringBuilder buf,
final char ch) {
490 bytes =
new String(
new char[] { ch }).getBytes(ENCODING);
491 }
catch (
final UnsupportedEncodingException e) {
492 throw new RuntimeException(MSG_ENCODING_NA, e);
495 for (
int j = 0; j < bytes.length; j++) {
496 final byte b = bytes[j];
498 buf.append(DIGITS.charAt( ( b & 0xf0 ) >> 4 ));
499 buf.append(DIGITS.charAt( b & 0xf ));
524 public static String
encode(
final String vanilla,
final String legal) {
525 if(
null == vanilla ) {
528 final StringBuilder buf =
new StringBuilder();
529 for (
int i = 0; i < vanilla.length(); i++) {
530 final char ch = vanilla.charAt(i);
531 if ( (ch >=
'a' && ch <=
'z') ||
532 (ch >=
'A' && ch <=
'Z') ||
533 (ch >=
'0' && ch <=
'9') ||
534 legal.indexOf(ch) > -1 ||
535 ( ch > 127 && !Character.isSpaceChar(ch) && !Character.isISOControl(ch) )
539 encodeChar2UTF8(buf, ch);
542 return buf.toString();
560 final StringBuilder buf =
new StringBuilder();
561 for (
int i = 0; i < unicode.length(); i++) {
562 final char ch = unicode.charAt(i);
566 encodeChar2UTF8(buf, ch);
569 return buf.toString();
578 return null != encoded ? encoded.
decode() :
null;
596 public static String
decode(
final String encoded) {
597 if(
null == encoded ) {
600 final StringBuilder result =
new StringBuilder();
601 final byte[] buf =
new byte[32];
603 for (
int i = 0; i < encoded.length();) {
604 final char c = encoded.charAt(i);
608 if (i + 2 >= encoded.length()) {
609 throw new IllegalArgumentException(
"missing '%' hex-digits at index "+i);
611 final int d1 = Character.digit(encoded.charAt(i + 1), 16);
612 final int d2 = Character.digit(encoded.charAt(i + 2), 16);
613 if (d1 == -1 || d2 == -1) {
614 throw new IllegalArgumentException(
"invalid hex-digits at index "+i+
": "+encoded.substring(i, i + 3));
616 buf[bufI++] = (byte) ((d1 << 4) + d2);
618 appendUTF8(result, buf, bufI);
622 }
while (i < encoded.length() && encoded.charAt(i) ==
'%');
624 appendUTF8(result, buf, bufI);
631 return result.toString();
633 private static void appendUTF8(
final StringBuilder sb,
final byte[] buf,
final int count) {
635 sb.append(
new String(buf, 0, count, ENCODING));
636 }
catch (
final UnsupportedEncodingException e) {
637 throw new RuntimeException(MSG_ENCODING_NA, e);
662 if ( emptyString(
scheme) && emptyString(ssp) && emptyString(
fragment) ) {
663 throw new URISyntaxException(
"",
"all empty parts");
665 final StringBuilder uri =
new StringBuilder();
666 if ( !emptyString(
scheme) ) {
670 if ( !emptyString(ssp) ) {
679 return new Uri(
new Encoded(uri.toString()),
false, 0);
707 if ( emptyString(
scheme) && emptyString(ssp) && emptyString(
fragment) ) {
708 throw new URISyntaxException(
"",
"all empty parts");
710 final StringBuilder uri =
new StringBuilder();
711 if ( !emptyString(
scheme) ) {
715 if ( !emptyString(ssp) ) {
716 uri.append(ssp.get());
722 return new Uri(
new Encoded(uri.toString()),
false, 0);
751 final String
path,
final String
query,
final String
fragment)
throws URISyntaxException {
752 if ( emptyString(
scheme) && emptyString(userinfo) && emptyString(
host) && emptyString(
path) &&
754 throw new URISyntaxException(
"",
"all empty parts");
758 throw new URISyntaxException(
path,
"path doesn't start with '/'");
761 final StringBuilder uri =
new StringBuilder();
762 if ( !emptyString(
scheme) ) {
767 if ( !emptyString(userinfo) || !emptyString(
host) ||
port != -1) {
771 if ( !emptyString(userinfo) ) {
777 if ( !emptyString(
host) ) {
792 if ( !emptyString(
path) ) {
797 if ( !emptyString(
query) ) {
808 return new Uri(
new Encoded(uri.toString()),
true, 0);
842 if ( emptyString(
scheme) && emptyString(userinfo) && emptyString(
host) && emptyString(
path) &&
844 throw new URISyntaxException(
"",
"all empty parts");
848 throw new URISyntaxException(
path.
get(),
"path doesn't start with '/'");
851 final StringBuilder uri =
new StringBuilder();
852 if ( !emptyString(
scheme) ) {
857 if ( !emptyString(userinfo) || !emptyString(
host) ||
port != -1) {
861 if ( !emptyString(userinfo) ) {
862 uri.append(userinfo.get());
866 if ( !emptyString(
host) ) {
875 if ( !emptyString(
path) ) {
879 if ( !emptyString(
query) ) {
888 return new Uri(
new Encoded(uri.toString()),
true, 0);
971 throw new URISyntaxException(
"",
"all empty parts");
974 throw new URISyntaxException(
path,
"path doesn't start with '/'");
977 final StringBuilder uri =
new StringBuilder();
978 if ( !emptyString(
scheme) ) {
988 if ( !emptyString(
path) ) {
992 if ( !emptyString(
query) ) {
1002 return new Uri(
new Encoded(uri.toString()),
false, 0);
1035 throw new URISyntaxException(
"",
"all empty parts");
1038 throw new URISyntaxException(
path.
get(),
"path doesn't start with '/'");
1041 final StringBuilder uri =
new StringBuilder();
1042 if ( !emptyString(
scheme) ) {
1051 if ( !emptyString(
path) ) {
1054 if ( !emptyString(
query) ) {
1062 return new Uri(
new Encoded(uri.toString()),
false, 0);
1073 public static Uri cast(
final String encodedUri)
throws URISyntaxException {
1093 if ( emptyString(
path) ) {
1094 throw new URISyntaxException(
"",
"empty path");
1097 throw new URISyntaxException(
path,
"path doesn't start with '/'");
1100 final StringBuilder uri =
new StringBuilder();
1107 return new Uri(
new Encoded(uri.toString()),
false, 0);
1126 public static Uri valueOf(
final File file)
throws URISyntaxException {
1127 return Uri.
valueOfFilepath(Util.slashify(file.getAbsolutePath(),
true, file.isDirectory()));
1144 public static Uri valueOf(
final java.net.URI uri)
throws URISyntaxException {
1145 if( uri.isOpaque()) {
1148 return new Uri(
new Encoded( uri.toString() ),
false, 0);
1151 return Uri.
create(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
1152 uri.getPath(), uri.getQuery(), uri.getFragment());
1172 public static Uri valueOf(
final java.net.URL url)
throws URISyntaxException {
1183 private final Object lazyLock =
new Object();
1236 this(uri,
false, 0);
1271 synchronized( lazyLock ) {
1272 if(
null == inputASCII ) {
1287 return new java.net.URI(
input.
get());
1288 }
catch (
final URISyntaxException e) {
1307 final java.net.URI recomposedURI;
1311 }
else if(
null !=
host ) {
1320 return recomposedURI;
1331 public final java.net.URL
toURL() throws MalformedURLException {
1333 throw new IllegalArgumentException(
"Cannot convert relative Uri: "+
input);
1335 return new java.net.URL(
input.
get());
1354 final String authorityS;
1360 final String
path = authorityS+this.path.
decode();
1361 if( File.separator.equals(
"\\") ) {
1362 final String r = patternSingleFS.matcher(
path).replaceAll(
"\\\\");
1363 if( r.startsWith(
"\\") && !r.startsWith(
"\\\\") ) {
1364 return new File(r.substring(1));
1369 return new File(
path);
1383 return toFile().getPath();
1416 final StringBuilder sb =
new StringBuilder();
1421 throw new URISyntaxException(
input.
get(),
"missing jar separator");
1432 final int parseHints =
opaque ? PARSE_HINT_FIX_PATH : 0;
1433 final Uri res =
new Uri(
new Encoded(sb.toString()),
false, parseHints);
1434 if(
null != res.
scheme ) {
1437 }
catch(
final URISyntaxException e) {
1440 System.err.println(
"Caught "+e.getClass().getSimpleName()+
": "+e.getMessage());
1441 e.printStackTrace();
1448 private static final boolean cutoffLastPathSegementImpl(
final StringBuilder pathBuf,
1449 final boolean cutoffFile,
1450 final boolean cutoffDir,
1451 final Encoded appendPath)
throws URISyntaxException {
1452 final boolean cleaned;
1454 final String pathS = pathBuf.
toString();
1455 if( 0 > pathS.indexOf(
"/") && emptyString(appendPath) ) {
1458 pathBuf.setLength(0);
1459 pathBuf.append( Util.cleanPathString( pathS ) );
1460 cleaned = pathBuf.length() != pathS.length();
1464 final String pathS = pathBuf.toString();
1466 final int e = pathS.lastIndexOf(
"/");
1467 if( 0 > jarSepIdx || e - 1 > jarSepIdx ) {
1468 if( cutoffFile && e < pathS.length() - 1 ) {
1470 pathBuf.setLength(0);
1471 pathBuf.append( pathS.substring(0, e+1) );
1472 }
else if( cutoffDir ) {
1474 final int p = pathS.lastIndexOf(
"/", e-1);
1476 pathBuf.setLength(0);
1477 pathBuf.append( pathS.substring(0, p+1) );
1481 final boolean cutoff = pathBuf.length() != pathS.length();
1482 if( !cutoff && ( cutoffDir || !cleaned ) && emptyString(appendPath) ) {
1486 if( !emptyString(appendPath) ) {
1487 pathBuf.append(appendPath.get());
1489 final String pathS = pathBuf.toString();
1490 pathBuf.setLength(0);
1491 pathBuf.append( Util.cleanPathString( pathS ) );
1495 private final Uri cutoffLastPathSegementImpl(
final boolean cutoffFile,
final boolean cutoffDir,
final Encoded appendPath)
throws URISyntaxException {
1499 if( !emptyString(appendPath) ) {
1505 final StringBuilder sspBuf =
new StringBuilder();
1508 final Encoded queryTemp;
1518 if( !cutoffLastPathSegementImpl(sspBuf, cutoffFile, cutoffDir, appendPath) ) {
1522 if ( !emptyString(queryTemp) ) {
1524 sspBuf.append( queryTemp.get() );
1530 if( emptyString(
path) ) {
1533 final StringBuilder pathBuf =
new StringBuilder();
1536 if( !cutoffLastPathSegementImpl(pathBuf, cutoffFile, cutoffDir, appendPath) ) {
1562 final Uri res = cutoffLastPathSegementImpl(
false,
false,
null);
1563 return null != res ? res :
this;
1564 }
catch (
final URISyntaxException e) {
1566 System.err.println(
"Caught "+e.getClass().getSimpleName()+
": "+e.getMessage());
1567 e.printStackTrace();
1601 final Uri res = cutoffLastPathSegementImpl(
true,
false,
null);
1602 return null != res ? res :
this;
1603 }
catch (
final URISyntaxException e) {
1605 System.err.println(
"Caught "+e.getClass().getSimpleName()+
": "+e.getMessage());
1606 e.printStackTrace();
1643 return cutoffLastPathSegementImpl(
true,
true,
null);
1644 }
catch (
final URISyntaxException e) {
1646 System.err.println(
"Caught "+e.getClass().getSimpleName()+
": "+e.getMessage());
1647 e.printStackTrace();
1687 if( emptyString(appendPath) ) {
1690 return cutoffLastPathSegementImpl(
true,
false, appendPath);
1703 if(
null == suffix ) {
1719 throw new URISyntaxException(
input.
decode(),
"Opaque Uri cannot permute by query");
1740 public final boolean equals(
final Object o) {
1741 if (!(o instanceof
Uri)) {
1765 if (!equalsHexCaseInsensitive(
path, uri.
path)) {
1771 }
else if (uri.
query !=
null &&
query !=
null) {
1772 if (!equalsHexCaseInsensitive(uri.
query,
query)) {
1780 if (uri.
host !=
null &&
host ==
null || uri.
host ==
null &&
host !=
null) {
1782 }
else if (uri.
host ==
null &&
host ==
null) {
1823 synchronized( lazyLock ) {
1825 hash = getHashString().hashCode();
1835 private String convertHexToLowerCase(
final String s) {
1836 if (s.indexOf(
'%') == -1) {
1839 final StringBuilder result =
new StringBuilder(
"");
1840 int index = 0, previndex = 0;
1841 while ((index = s.indexOf(
'%', previndex)) != -1) {
1842 result.append(s.substring(previndex, index + 1));
1843 result.append(s.substring(index + 1, index + 3).toLowerCase());
1847 return result.toString();
1855 private boolean equalsHexCaseInsensitive(
final Encoded first,
final Encoded second) {
1856 if (first.indexOf(
'%') != second.indexOf(
'%')) {
1857 return first.equals(second);
1860 int index = 0, previndex = 0;
1861 while ( ( index = first.indexOf(
'%', previndex) ) != -1 &&
1862 second.indexOf(
'%', previndex) == index
1864 if( !first.get().substring(previndex, index).equals( second.get().substring(previndex, index) ) ) {
1867 if( !first.get().substring(index + 1, index + 3).equalsIgnoreCase( second.get().substring(index + 1, index + 3) ) ) {
1873 return first.get().substring(previndex).equals( second.get().substring(previndex) );
1881 private String getHashString() {
1882 final StringBuilder result =
new StringBuilder();
1884 result.append(
scheme.
get().toLowerCase());
1891 result.append(
"//");
1898 result.append(
host.
get().toLowerCase());
1909 if (
query !=
null) {
1919 return convertHexToLowerCase(result.toString());
1929 private Uri(
final Encoded
input,
final boolean expectServer,
final int parseHints)
throws URISyntaxException {
1930 if( emptyString(
input) ) {
1931 throw new URISyntaxException(
input.
get(),
"empty input");
1942 fragment =
new Encoded( temp.substring(index + 1) );
1944 temp = temp.substring(0, index);
1953 index = indexSchemeSep;
1954 final int indexSSP = temp.indexOf(
'/');
1961 if ( indexSchemeSep != -1 &&
1962 ( indexSSP >= indexSchemeSep || indexSSP == -1 ) &&
1963 ( indexQuerySep >= indexSchemeSep || indexQuerySep == -1 )
1967 scheme =
new Encoded( temp.substring(0, indexSchemeSep) );
1969 failExpecting(
input,
"scheme", indexSchemeSep);
1972 sspTemp = temp.substring(indexSchemeSep + 1);
1973 if (sspTemp.length() == 0) {
1974 failExpecting(
input,
"scheme-specific-part", indexSchemeSep);
1982 if (
scheme ==
null || sspTemp.
length() > 0 && sspTemp.charAt(0) ==
'/' ) {
1990 query =
new Encoded( temp.substring(index + 1) );
1991 temp = temp.substring(0, index);
1992 validateQuery(
input,
query, indexSSP + 1 + index);
1998 final int indexPathInSSP;
2001 if (temp.startsWith(
"//")) {
2003 final String authorityS;
2005 authorityS = temp.substring(2, index);
2006 pathTemp = temp.substring(index);
2007 indexPathInSSP = index;
2009 authorityS = temp.substring(2);
2010 if (authorityS.length() == 0 &&
query ==
null &&
fragment ==
null) {
2011 failExpecting(
input,
"authority, path [, query, fragment]", index);
2014 indexPathInSSP = -1;
2018 if ( emptyString(authorityS) ) {
2031 if (indexSSP > -1) {
2032 indexPath += indexSSP;
2034 if (indexPathInSSP > -1) {
2035 indexPath += indexPathInSSP;
2038 final int pathErrIdx = validateEncoded(pathTemp,
PATH_LEGAL);
2039 if( 0 <= pathErrIdx ) {
2041 if( 0 != ( parseHints & PARSE_HINT_FIX_PATH ) ) {
2042 if( DEBUG_SHOWFIX ) {
2043 System.err.println(
"Uri FIX_FILEPATH: input at index "+(indexPath+pathErrIdx)+
": "+inputTemp);
2044 System.err.println(
"Uri FIX_FILEPATH: ssp at index "+(indexPathInSSP+pathErrIdx)+
": "+sspTemp);
2045 System.err.println(
"Uri FIX_FILEPATH: path at index "+pathErrIdx+
": "+pathTemp);
2047 final int pathTempOldLen = pathTemp.length();
2049 validatePath(
input, pathTemp, indexPath);
2052 final StringBuilder sb =
new StringBuilder();
2053 if( indexPathInSSP > 0 ) {
2054 sb.append( sspTemp.substring(0, indexPathInSSP) );
2056 sb.append( pathTemp ).append( sspTemp.substring( indexPathInSSP + pathTempOldLen ) );
2057 sspTemp = sb.toString();
2060 if( indexPath > 0 ) {
2061 sb.append( inputTemp.substring(0, indexPath) );
2063 sb.append( pathTemp ).append( inputTemp.substring( indexPath + pathTempOldLen ) );
2064 inputTemp = sb.toString();
2066 if( DEBUG_SHOWFIX ) {
2067 System.err.println(
"Uri FIX_FILEPATH: result : "+pathTemp);
2068 System.err.println(
"Uri FIX_FILEPATH: ssp after : "+sspTemp);
2069 System.err.println(
"Uri FIX_FILEPATH: input after : "+inputTemp);
2072 fail(
input,
"invalid path", indexPath+pathErrIdx);
2075 path =
new Encoded( pathTemp );
2082 validateSsp(
input, sspTemp, indexSchemeSep + 1);
2085 this.input = inputTemp ==
input.
get() ?
input :
new Encoded( inputTemp );
2099 Encoded tempUserinfo =
null, tempHost =
null;
2101 boolean authorityComplete;
2104 authorityComplete =
true;
2108 index = temp.indexOf(
'@');
2111 tempUserinfo =
new Encoded( temp.substring(0, index) );
2112 validateUserinfo(
authority, tempUserinfo, 0);
2113 temp = temp.substring(index + 1);
2114 hostindex = index + 1;
2118 final int endindex = temp.indexOf(
']');
2120 if (index != -1 && endindex < index) {
2122 tempHost =
new Encoded( temp.substring(0, index) );
2124 if (index < (temp.length() - 1)) {
2126 tempPort = Integer.parseInt(temp.substring(index + 1));
2131 authorityComplete =
false;
2133 }
catch (
final NumberFormatException e) {
2135 fail(
authority,
"invalid port <"+
authority+
">, "+e.getMessage(), hostindex + index + 1);
2137 authorityComplete =
false;
2141 tempHost =
new Encoded( temp );
2144 if( authorityComplete ) {
2145 if ( emptyString(tempHost) ) {
2149 authorityComplete =
false;
2150 }
else if (!isValidHost(expectServer, tempHost)) {
2152 fail(
authority,
"invalid host <"+tempHost+
">", hostindex);
2154 authorityComplete =
false;
2158 authorityComplete =
false;
2161 if( authorityComplete ) {
2176 private static void validateScheme(
final Encoded uri,
final Encoded
scheme,
final int index)
throws URISyntaxException {
2179 if ( !((ch >=
'a' && ch <=
'z') || (ch >=
'A' && ch <=
'Z')) ) {
2180 fail(uri,
"invalid scheme", index);
2182 final int errIdx = validateAlphaNum(
scheme.
get(),
"+-.");
2184 fail(uri,
"invalid scheme", index+errIdx);
2188 private static void validateSsp(
final Encoded uri,
final String ssp,
final int index)
throws URISyntaxException {
2189 final int errIdx = validateEncoded(ssp,
SSP_LEGAL);
2191 fail(uri,
"invalid scheme-specific-part", index+errIdx);
2195 private static void validateAuthority(
final Encoded uri,
final Encoded
authority,
final int index)
throws URISyntaxException {
2198 fail(uri,
"invalid authority", index+errIdx);
2202 private static void validatePath(
final Encoded uri,
final String
path,
final int index)
throws URISyntaxException {
2205 fail(uri,
"invalid path", index+errIdx);
2209 private static void validateQuery(
final Encoded uri,
final Encoded
query,
final int index)
throws URISyntaxException {
2212 fail(uri,
"invalid query", index+errIdx);
2216 private static void validateFragment(
final Encoded uri,
final Encoded
fragment,
final int index)
throws URISyntaxException {
2219 fail(uri,
"invalid fragment", index+errIdx);
2223 private static void validateUserinfo(
final Encoded uri,
final Encoded userinfo,
final int index)
throws URISyntaxException {
2224 for (
int i = 0; i < userinfo.length(); i++) {
2225 final char ch = userinfo.charAt(i);
2226 if (ch ==
']' || ch ==
'[') {
2227 fail(uri,
"invalid userinfo", index+i);
2236 private boolean isValidHost(
final boolean expectServer,
final Encoded
host)
throws URISyntaxException {
2240 fail(
input,
"invalid host, missing closing ipv6: "+
host, 0);
2242 if (!isValidIP6Address(
host.
get())) {
2255 if ( index < 0 || index ==
host.
length() - 1 ||
2259 if (isValidDomainName(
host)) {
2263 fail(
input,
"invalid host, invalid domain-name or ipv4: "+
host, 0);
2269 if (isValidIPv4Address(
host.
get())) {
2273 fail(
input,
"invalid host, invalid ipv4: "+
host, 0);
2278 private static boolean isValidDomainName(
final Encoded
host) {
2279 final String hostS =
host.
get();
2280 if( 0 <= validateAlphaNum(hostS,
"-.") ) {
2283 String label =
null;
2284 final StringTokenizer st =
new StringTokenizer(hostS,
".");
2285 while (st.hasMoreTokens()) {
2286 label = st.nextToken();
2287 if (label.startsWith(
"-") || label.endsWith(
"-")) {
2292 if (!label.equals(hostS)) {
2293 final char ch = label.charAt(0);
2294 if (ch >=
'0' && ch <=
'9') {
2301 private static boolean isValidIPv4Address(
final String ipv4Address) {
2306 index = ipv4Address.indexOf(
'.');
2307 num = Integer.parseInt(ipv4Address.substring(0, index));
2308 if (num < 0 || num > 255) {
2311 index2 = ipv4Address.indexOf(
'.', index + 1);
2312 num = Integer.parseInt(ipv4Address.substring(index + 1, index2));
2313 if (num < 0 || num > 255) {
2316 index = ipv4Address.indexOf(
'.', index2 + 1);
2317 num = Integer.parseInt(ipv4Address.substring(index2 + 1, index));
2318 if (num < 0 || num > 255) {
2321 num = Integer.parseInt(ipv4Address.substring(index + 1));
2322 if (num < 0 || num > 255) {
2325 }
catch (
final Exception e) {
2331 private static boolean isValidIP6Address(
final String ipv6Address) {
2332 final int length = ipv6Address.length();
2333 boolean doubleColon =
false;
2334 int numberOfColons = 0;
2335 int numberOfPeriods = 0;
2345 for (
int i = 0; i < length; i++) {
2347 c = ipv6Address.charAt(i);
2355 if (ipv6Address.charAt(length - 1) !=
']') {
2370 if (i != length - 1) {
2373 if (ipv6Address.charAt(0) !=
'[') {
2382 if (numberOfPeriods > 3) {
2385 if (!isValidIP4Word(word)) {
2388 if (numberOfColons != 6 && !doubleColon) {
2394 if (numberOfColons == 7
2404 if (numberOfColons > 7) {
2407 if (numberOfPeriods > 0) {
2420 if (word.length() > 3) {
2423 if (!isValidHexChar(c)) {
2431 if (numberOfPeriods > 0) {
2432 if (numberOfPeriods != 3 || !isValidIP4Word(word)) {
2438 if (numberOfColons != 7 && !doubleColon) {
2445 if (word ==
"" && ipv6Address.charAt(length - 1 - offset) !=
SCHEME_SEPARATOR
2454 private static boolean isValidIP4Word(
final String word) {
2456 if (word.length() < 1 || word.length() > 3) {
2459 for (
int i = 0; i < word.length(); i++) {
2461 if (!(c >=
'0' && c <=
'9')) {
2465 if (Integer.parseInt(word) > 255) {
2487 private static int validateEncoded(
final String encoded,
final String legal) {
2488 for (
int i = 0; i < encoded.length();) {
2489 final char ch = encoded.charAt(i);
2492 if (i + 2 >= encoded.length()) {
2493 throw new IllegalArgumentException(
"missing '%' hex-digits at index "+i);
2495 final int d1 = Character.digit(encoded.charAt(i + 1), 16);
2496 final int d2 = Character.digit(encoded.charAt(i + 2), 16);
2497 if (d1 == -1 || d2 == -1) {
2498 throw new IllegalArgumentException(
"invalid hex-digits at index "+i+
": "+encoded.substring(i, i + 3));
2501 }
while (i < encoded.length() && encoded.charAt(i) ==
'%');
2504 if ( !( (ch >=
'a' && ch <=
'z') || (ch >=
'A' && ch <=
'Z') ||
2505 (ch >=
'0' && ch <=
'9') || legal.indexOf(ch) > -1 ||
2506 (ch > 127 && !Character.isSpaceChar(ch) && !Character.isISOControl(ch))
2515 private static int validateAlphaNum(
final String s,
final String legal) {
2516 for (
int i = 0; i < s.length();) {
2517 final char ch = s.charAt(i);
2518 if ( !( (ch >=
'a' && ch <=
'z') || (ch >=
'A' && ch <=
'Z') ||
2519 (ch >=
'0' && ch <=
'9') || legal.indexOf(ch) > -1
2529 private static boolean isValidHexChar(
final char c) {
2530 return (c >=
'0' && c <=
'9') || (c >=
'A' && c <=
'F') || (c >=
'a' && c <=
'f');
2532 private static boolean emptyString(
final Encoded s) {
2533 return null == s || 0 == s.length();
2535 private static boolean emptyString(
final String s) {
2536 return null == s || 0 == s.length();
2539 private static void fail(
final Encoded
input,
final String reason,
final int p)
throws URISyntaxException {
2540 throw new URISyntaxException(
input.
get(), reason, p);
2542 private static void failExpecting(
final Encoded
input,
final String expected,
final int p)
throws URISyntaxException {
2543 fail(
input,
"Expecting " + expected, p);
2547 private static final Pattern patternSingleBS = Pattern.compile(
"\\\\{1}");
2556 public static String slashify(
final String
path,
final boolean startWithSlash,
final boolean endWithSlash)
throws URISyntaxException {
2557 String p = patternSingleBS.matcher(
path).replaceAll(
"/");
2558 if (startWithSlash && !p.startsWith(
"/")) {
2561 if (endWithSlash && !p.endsWith(
"/")) {
2564 return cleanPathString(p);
2571 public static String getParentOf(
final String
path)
throws URISyntaxException {
2574 throw new IllegalArgumentException(
"path is empty <"+
path+
">");
2579 throw new URISyntaxException(
path,
"path contains no '/': <"+
path+
">");
2583 throw new URISyntaxException(
path,
"path has no parents: <"+
path+
">");
2598 if( parent.equals(
"..") ) {
2599 throw new URISyntaxException(
path,
"parent is unresolved: <"+
path+
">");
2613 public static String cleanPathString(String
path)
throws URISyntaxException {
2625 while ( ( idx =
path.
indexOf(
"../", idx) ) >= 0 ) {
static ASCIIEncoded cast(final String encoded)
Casts the given encoded String by creating a new ASCIIEncoded instance.
ASCIIEncoded(final String unicode)
Other characters, which are Unicode chars that are not US-ASCII, and are not ISO Control or are not I...
Immutable RFC3986 encoded string.
final int indexOf(final String str, final int fromIndex)
See String#indexOf(String, int).
final int indexOf(final String str)
See String#indexOf(String).
Encoded(final String vanilla, final String legal)
Encodes all characters into their hexadecimal value prepended by '', except:
boolean startsWith(final String prefix)
See String#startsWith(String).
static Encoded cast(final String encoded)
Casts the given encoded String by creating a new Encoded instance.
final String get()
Returns the encoded String.
int lastIndexOf(final String str)
See String#lastIndexOf(String).
final CharSequence subSequence(final int start, final int end)
Encoded concat(final Encoded encoded)
See String#concat(String).
final int lastIndexOf(final int ch)
See String#lastIndexOf(int).
final int indexOf(final int ch, final int fromIndex)
See String#indexOf(int, int).
final int indexOf(final int ch)
See String#indexOf(int).
int lastIndexOf(final int ch, final int fromIndex)
See String#lastIndexOf(int, int).
int lastIndexOf(final String str, final int fromIndex)
See String#lastIndexOf(String, int).
boolean startsWith(final String prefix, final int toffset)
See String#startsWith(String, int).
final boolean equalsIgnoreCase(final Encoded anotherEncoded)
See String#equalsIgnoreCase(String).
boolean endsWith(final String suffix)
See String#endsWith(String).
final Encoded substring(final int start, final int end)
See String#substring(int, int).
final int compareTo(final Encoded o)
final boolean equals(final Object o)
final Encoded substring(final int start)
See String#substring(int).
final char charAt(final int index)
final String decode()
Decodes the string argument which is assumed to be encoded in the x-www-form-urlencoded MIME conten...
This class implements an immutable Uri as defined by RFC 2396.
Uri getDirectory()
Returns this Uri's directory Uri.
static final String PATH_LEGAL
Valid charset for RFC 2396 path, 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 Encoded scheme
Encoded scheme, null if undefined.
final int port
Encoded port part of authority and scheme-specific-part, -1 if undefined.
final String toString()
Returns the encoded input as String, never null, same as getEncoded().
final Uri concat(final Encoded suffix)
Concatenates the given encoded string to the encoded uri of this instance and returns a new Uri insta...
static Uri create(final Encoded scheme, final Encoded ssp, final Encoded fragment)
Creates a new Uri instance using the given encoded arguments.
final boolean isFileScheme()
Returns true, if this instance is a file scheme, otherwise false.
static final String RESERVED
RFC 3986 section 2.2 Reserved Characters (January 2005)
final String getUriFilePathOrASCII()
If this uri is a file scheme implementation returns toFile().
final Encoded getEncoded()
Returns the encoded input, never null.
final Encoded host
Encoded host part of authority and scheme-specific-part, null if undefined.
static Uri valueOf(final java.net.URI uri)
Creates a new Uri instance using the given URI instance.
final Encoded authority
Encoded authority part of scheme-specific-part, null if undefined.
final Uri getParent()
Returns this Uri's parent directory Uri.
static String decode(final Encoded encoded)
Safe Encoded#decode() call on optional encoded instance.
static final String QUERY_LEGAL
Valid charset for RFC 2396 query, additional to legal alphanum characters.
ASCIIEncoded toASCIIString()
Returns the encoded input encoded in US-ASCII.
final java.net.URL toURL()
Returns a new URL instance using the encoded input string, new URL(uri.input), i.e.
static final String RESERVED_2
final Encoded schemeSpecificPart
Encoded scheme-specific-part, never null.
final Uri getNewQuery(final Encoded newQuery)
Returns a new Uri instance w/ the given new query newQuery.
static final String HTTP_SCHEME
{@value}
final boolean isJarScheme()
Returns true, if this instance is a jar scheme, otherwise false.
static final char QUERY_SEPARATOR
{@value}
static final String UNRESERVED
RFC 3986 section 2.3 Unreserved Characters (January 2005)
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.
static String decode(final String encoded)
Decodes the string argument which is assumed to be encoded in the x-www-form-urlencoded MIME conten...
final boolean hasAuthority
Indicating whether authority part is defined or not.
static Uri cast(final String encodedUri)
Casts the given encoded String to a new Encoded instance used to create the resulting Uri instance vi...
Uri(final Encoded uri)
Creates a new Uri instance according to the given encoded string uri.
final boolean equals(final Object o)
final Uri getContainedUri()
If this instance's schemeSpecificPart contains a Uri itself, a sub-Uri, return schemeSpecificPart + #...
final Encoded userInfo
Encoded userinfo part of authority and scheme-specific-part, null if undefined.
static Uri valueOfFilepath(final String path)
Creates a new Uri instance using the given file-path argument.
final Encoded fragment
Encoded fragment, null if undefined.
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.
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 valueOf(final java.net.URL url)
Creates a new Uri instance using the given URL instance, convenient wrapper for valueOf(URI) and URL#...
static Uri valueOf(final File file)
Creates a new Uri instance using the given File instance.
static final String FILE_SCHEME
{@value}
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.
static final String JAR_SCHEME
{@value}
final File toFile()
If this instance is a file scheme, implementation decodes [ "//"+authority ] + path,...
static final String AUTHORITY_LEGAL
Valid charset for RFC 2396 authority, additional to legal alphanum characters.
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.
final java.net.URI toURIReencoded()
Returns a new URI instance based upon this instance.
static final String USERINFO_LEGAL
Valid charset for RFC 2396 authority's user-info, additional to legal alphanum characters.
final Encoded input
Encoded input string used at construction, never null.
final Encoded query
Encoded query part of scheme-specific-part, null if undefined.
static String encode(final String vanilla, final String legal)
All characters are encoded into their hexadecimal value prepended by '', except:
final boolean opaque
Indicating whether this Uri is opaque, i.e.
static final String HTTPS_SCHEME
{@value}
static final char FRAGMENT_SEPARATOR
{@value}
final Encoded path
Encoded path part of scheme-specific-part, never null.
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.
final boolean absolute
Indicating whether this Uri is absolute, i.e.
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.
static Uri create(final String scheme, final String ssp, final String fragment)
Creates a new Uri instance using the given unencoded arguments.
final Uri getNormalized()
Normalizes this Uri's path and return the normalized form if it differs, otherwise this instance.
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...
static final char JAR_SCHEME_SEPARATOR
A JAR sub-protocol is separated from the JAR entry w/ this separator {@value}.
Uri getRelativeOf(final Encoded appendPath)
Returns a new Uri appending the given appendPath to this instance's directory.
static final char SCHEME_SEPARATOR
{@value}