1 17 package org.eclipse.emf.common.util; 18 19 import java.io.File ; 20 import java.util.ArrayList ; 21 import java.util.Arrays ; 22 import java.util.Collections ; 23 import java.util.HashMap ; 24 import java.util.HashSet ; 25 import java.util.List ; 26 import java.util.Map ; 27 import java.util.Set ; 28 import java.util.StringTokenizer ; 29 30 136 public final class URI 137 { 138 private final int hashCode; 140 private final boolean hierarchical; 141 private final String scheme; private final String authority; 143 private final String fragment; 144 private URI cachedTrimFragment; 145 private String cachedToString; 146 149 private final String device; 151 private final boolean absolutePath; 152 private final String [] segments; private final String query; 154 155 private static final Map uriCache = Collections.synchronizedMap(new HashMap ()); 160 161 private static final Set archiveSchemes; 163 164 private static final String SCHEME_FILE = "file"; 166 private static final String SCHEME_JAR = "jar"; 167 private static final String SCHEME_ZIP = "zip"; 168 private static final String SCHEME_ARCHIVE = "archive"; 169 170 private static final String SEGMENT_EMPTY = ""; 172 private static final String SEGMENT_SELF = "."; 173 private static final String SEGMENT_PARENT = ".."; 174 private static final String [] NO_SEGMENTS = new String [0]; 175 176 private static final char SCHEME_SEPARATOR = ':'; 178 private static final String AUTHORITY_SEPARATOR = "//"; 179 private static final char DEVICE_IDENTIFIER = ':'; 180 private static final char SEGMENT_SEPARATOR = '/'; 181 private static final char QUERY_SEPARATOR = '?'; 182 private static final char FRAGMENT_SEPARATOR = '#'; 183 private static final char USER_INFO_SEPARATOR = '@'; 184 private static final char PORT_SEPARATOR = ':'; 185 private static final char FILE_EXTENSION_SEPARATOR = '.'; 186 private static final char ARCHIVE_IDENTIFIER = '!'; 187 private static final String ARCHIVE_SEPARATOR = "!/"; 188 189 private static final char ESCAPE = '%'; 191 private static final char[] HEX_DIGITS = { 192 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 193 194 private static final long ALPHA_HI = highBitmask('a', 'z') | highBitmask('A', 'Z'); 201 private static final long ALPHA_LO = lowBitmask('a', 'z') | lowBitmask('A', 'Z'); 202 private static final long DIGIT_HI = highBitmask('0', '9'); 203 private static final long DIGIT_LO = lowBitmask('0', '9'); 204 private static final long ALPHANUM_HI = ALPHA_HI | DIGIT_HI; 205 private static final long ALPHANUM_LO = ALPHA_LO | DIGIT_LO; 206 private static final long HEX_HI = DIGIT_HI | highBitmask('A', 'F') | highBitmask('a', 'f'); 207 private static final long HEX_LO = DIGIT_LO | lowBitmask('A', 'F') | lowBitmask('a', 'f'); 208 private static final long UNRESERVED_HI = ALPHANUM_HI | highBitmask("-_.!~*'()"); 209 private static final long UNRESERVED_LO = ALPHANUM_LO | lowBitmask("-_.!~*'()"); 210 private static final long RESERVED_HI = highBitmask(";/?:@&=+$,"); 211 private static final long RESERVED_LO = lowBitmask(";/?:@&=+$,"); 212 private static final long URIC_HI = RESERVED_HI | UNRESERVED_HI; private static final long URIC_LO = RESERVED_LO | UNRESERVED_LO; 214 215 private static final long SEGMENT_CHAR_HI = UNRESERVED_HI | highBitmask(";:@&=+$,"); private static final long SEGMENT_CHAR_LO = UNRESERVED_LO | lowBitmask(";:@&=+$,"); 220 private static final long PATH_CHAR_HI = SEGMENT_CHAR_HI | highBitmask('/'); private static final long PATH_CHAR_LO = SEGMENT_CHAR_LO | lowBitmask('/'); 222 private static final long MAJOR_SEPARATOR_HI = highBitmask(":/?#"); 225 private static final long MAJOR_SEPARATOR_LO = lowBitmask(":/?#"); 226 private static final long SEGMENT_END_HI = highBitmask("/?#"); 227 private static final long SEGMENT_END_LO = lowBitmask("/?#"); 228 229 private static final boolean ENCODE_PLATFORM_RESOURCE_URIS = 232 System.getProperty("org.eclipse.emf.common.util.URI.encodePlatformResourceURIs") != null && 233 !"false".equalsIgnoreCase(System.getProperty("org.eclipse.emf.common.util.URI.encodePlatformResourceURIs")); 234 235 static 237 { 238 Set set = new HashSet (); 239 String propertyValue = System.getProperty("org.eclipse.emf.common.util.URI.archiveSchemes"); 240 241 if (propertyValue == null) 242 { 243 set.add(SCHEME_JAR); 244 set.add(SCHEME_ZIP); 245 set.add(SCHEME_ARCHIVE); 246 } 247 else 248 { 249 for (StringTokenizer t = new StringTokenizer (propertyValue); t.hasMoreTokens(); ) 250 { 251 set.add(t.nextToken().toLowerCase()); 252 } 253 } 254 255 archiveSchemes = Collections.unmodifiableSet(set); 256 } 257 258 private static long lowBitmask(char c) 260 { 261 return c < 64 ? 1L << c : 0L; 262 } 263 264 private static long highBitmask(char c) 266 { 267 return c >= 64 && c < 128 ? 1L << (c - 64) : 0L; 268 } 269 270 private static long lowBitmask(char from, char to) 273 { 274 long result = 0L; 275 if (from < 64 && from <= to) 276 { 277 to = to < 64 ? to : 63; 278 for (char c = from; c <= to; c++) 279 { 280 result |= (1L << c); 281 } 282 } 283 return result; 284 } 285 286 private static long highBitmask(char from, char to) 289 { 290 return to < 64 ? 0 : lowBitmask((char)(from < 64 ? 0 : from - 64), (char)(to - 64)); 291 } 292 293 private static long lowBitmask(String chars) 296 { 297 long result = 0L; 298 for (int i = 0, len = chars.length(); i < len; i++) 299 { 300 char c = chars.charAt(i); 301 if (c < 64) result |= (1L << c); 302 } 303 return result; 304 } 305 306 private static long highBitmask(String chars) 309 { 310 long result = 0L; 311 for (int i = 0, len = chars.length(); i < len; i++) 312 { 313 char c = chars.charAt(i); 314 if (c >= 64 && c < 128) result |= (1L << (c - 64)); 315 } 316 return result; 317 } 318 319 private static boolean matches(char c, long highBitmask, long lowBitmask) 322 { 323 if (c >= 128) return false; 324 return c < 64 ? 325 ((1L << c) & lowBitmask) != 0 : 326 ((1L << (c - 64)) & highBitmask) != 0; 327 } 328 329 343 344 356 public static URI createGenericURI(String scheme, String opaquePart, 357 String fragment) 358 { 359 if (scheme == null) 360 { 361 throw new IllegalArgumentException ("relative non-hierarchical URI"); 362 } 363 364 if (isArchiveScheme(scheme)) 365 { 366 throw new IllegalArgumentException ("non-hierarchical archive URI"); 367 } 368 369 validateURI(false, scheme, opaquePart, null, false, NO_SEGMENTS, null, fragment); 370 return new URI(false, scheme, opaquePart, null, false, NO_SEGMENTS, null, fragment); 371 } 372 373 389 public static URI createHierarchicalURI(String scheme, String authority, 390 String device, String query, 391 String fragment) 392 { 393 if (scheme != null && authority == null && device == null) 394 { 395 throw new IllegalArgumentException ( 396 "absolute hierarchical URI without authority, device, path"); 397 } 398 399 if (isArchiveScheme(scheme)) 400 { 401 throw new IllegalArgumentException ("archive URI with no path"); 402 } 403 404 validateURI(true, scheme, authority, device, false, NO_SEGMENTS, query, fragment); 405 return new URI(true, scheme, authority, device, false, NO_SEGMENTS, query, fragment); 406 } 407 408 430 public static URI createHierarchicalURI(String scheme, String authority, 431 String device, String [] segments, 432 String query, String fragment) 433 { 434 if (isArchiveScheme(scheme) && device != null) 435 { 436 throw new IllegalArgumentException ("archive URI with device"); 437 } 438 439 segments = fix(segments); 440 validateURI(true, scheme, authority, device, true, segments, query, fragment); 441 return new URI(true, scheme, authority, device, true, segments, query, fragment); 442 } 443 444 457 public static URI createHierarchicalURI(String [] segments, String query, 458 String fragment) 459 { 460 segments = fix(segments); 461 validateURI(true, null, null, null, false, segments, query, fragment); 462 return new URI(true, null, null, null, false, segments, query, fragment); 463 } 464 465 private static String [] fix(String [] segments) 468 { 469 return segments == null ? NO_SEGMENTS : (String [])segments.clone(); 470 } 471 472 492 public static URI createURI(String uri) 493 { 494 return createURIWithCache(uri); 495 } 496 497 517 public static URI createURI(String uri, boolean ignoreEscaped) 518 { 519 return createURIWithCache(encodeURI(uri, ignoreEscaped)); 520 } 521 522 541 public static URI createDeviceURI(String uri) 542 { 543 return createURIWithCache(uri); 544 } 545 546 558 public static URI createURIWithCache(String uri) 559 { 560 int i = uri.indexOf(FRAGMENT_SEPARATOR); 561 String base = i == -1 ? uri : uri.substring(0, i); 562 String fragment = i == -1 ? null : uri.substring(i + 1); 563 564 URI result = (URI)uriCache.get(base); 565 566 if (result == null) 567 { 568 result = parseIntoURI(base); 569 uriCache.put(base, result); 570 } 571 572 if (fragment != null) 573 { 574 result = result.appendFragment(fragment); 575 } 576 return result; 577 } 578 579 private static URI parseIntoURI(String uri) 581 { 582 boolean hierarchical = true; 583 String scheme = null; 584 String authority = null; 585 String device = null; 586 boolean absolutePath = false; 587 String [] segments = NO_SEGMENTS; 588 String query = null; 589 String fragment = null; 590 591 int i = 0; 592 int j = find(uri, i, MAJOR_SEPARATOR_HI, MAJOR_SEPARATOR_LO); 593 594 if (j < uri.length() && uri.charAt(j) == SCHEME_SEPARATOR) 595 { 596 scheme = uri.substring(i, j); 597 i = j + 1; 598 } 599 600 boolean archiveScheme = isArchiveScheme(scheme); 601 if (archiveScheme) 602 { 603 j = uri.lastIndexOf(ARCHIVE_SEPARATOR); 604 if (j == -1) 605 { 606 throw new IllegalArgumentException ("no archive separator"); 607 } 608 hierarchical = true; 609 authority = uri.substring(i, ++j); 610 i = j; 611 } 612 else if (uri.startsWith(AUTHORITY_SEPARATOR, i)) 613 { 614 i += AUTHORITY_SEPARATOR.length(); 615 j = find(uri, i, SEGMENT_END_HI, SEGMENT_END_LO); 616 authority = uri.substring(i, j); 617 i = j; 618 } 619 else if (scheme != null && 620 (i == uri.length() || uri.charAt(i) != SEGMENT_SEPARATOR)) 621 { 622 hierarchical = false; 623 j = uri.indexOf(FRAGMENT_SEPARATOR, i); 624 if (j == -1) j = uri.length(); 625 authority = uri.substring(i, j); 626 i = j; 627 } 628 629 if (!archiveScheme && i < uri.length() && uri.charAt(i) == SEGMENT_SEPARATOR) 630 { 631 j = find(uri, i + 1, SEGMENT_END_HI, SEGMENT_END_LO); 632 String s = uri.substring(i + 1, j); 633 634 if (s.length() > 0 && s.charAt(s.length() - 1) == DEVICE_IDENTIFIER) 635 { 636 device = s; 637 i = j; 638 } 639 } 640 641 if (i < uri.length() && uri.charAt(i) == SEGMENT_SEPARATOR) 642 { 643 i++; 644 absolutePath = true; 645 } 646 647 if (segmentsRemain(uri, i)) 648 { 649 List segmentList = new ArrayList (); 650 651 while (segmentsRemain(uri, i)) 652 { 653 j = find(uri, i, SEGMENT_END_HI, SEGMENT_END_LO); 654 segmentList.add(uri.substring(i, j)); 655 i = j; 656 657 if (i < uri.length() && uri.charAt(i) == SEGMENT_SEPARATOR) 658 { 659 if (!segmentsRemain(uri, ++i)) segmentList.add(SEGMENT_EMPTY); 660 } 661 } 662 segments = new String [segmentList.size()]; 663 segmentList.toArray(segments); 664 } 665 666 if (i < uri.length() && uri.charAt(i) == QUERY_SEPARATOR) 667 { 668 j = uri.indexOf(FRAGMENT_SEPARATOR, ++i); 669 if (j == -1) j = uri.length(); 670 query = uri.substring(i, j); 671 i = j; 672 } 673 674 if (i < uri.length()) { 676 fragment = uri.substring(++i); 677 } 678 679 validateURI(hierarchical, scheme, authority, device, absolutePath, segments, query, fragment); 680 return new URI(hierarchical, scheme, authority, device, absolutePath, segments, query, fragment); 681 } 682 683 private static boolean segmentsRemain(String uri, int i) 686 { 687 return i < uri.length() && uri.charAt(i) != QUERY_SEPARATOR && 688 uri.charAt(i) != FRAGMENT_SEPARATOR; 689 } 690 691 private static int find(String s, int i, long highBitmask, long lowBitmask) 697 { 698 int len = s.length(); 699 if (i >= len) return len; 700 701 for (i = i > 0 ? i : 0; i < len; i++) 702 { 703 if (matches(s.charAt(i), highBitmask, lowBitmask)) break; 704 } 705 return i; 706 } 707 708 732 public static URI createFileURI(String pathName) 733 { 734 File file = new File (pathName); 735 String uri = File.separatorChar != '/' ? pathName.replace(File.separatorChar, SEGMENT_SEPARATOR) : pathName; 736 uri = encode(uri, PATH_CHAR_HI, PATH_CHAR_LO, false); 737 if (file.isAbsolute()) 738 { 739 URI result = createURI((uri.charAt(0) == SEGMENT_SEPARATOR ? "file:" : "file:/") + uri); 740 return result; 741 } 742 else 743 { 744 URI result = createURI(uri); 745 if (result.scheme() != null) 746 { 747 throw new IllegalArgumentException ("invalid relative pathName: " + pathName); 748 } 749 return result; 750 } 751 } 752 753 783 public static URI createPlatformResourceURI(String pathName) 784 { 785 return createPlatformResourceURI(pathName, ENCODE_PLATFORM_RESOURCE_URIS); 786 } 787 788 819 public static URI createPlatformResourceURI(String pathName, boolean encode) 820 { 821 if (File.separatorChar != SEGMENT_SEPARATOR) 822 { 823 pathName = pathName.replace(File.separatorChar, SEGMENT_SEPARATOR); 824 } 825 826 if (encode) 827 { 828 pathName = encode(pathName, PATH_CHAR_HI, PATH_CHAR_LO, false); 829 } 830 URI result = createURI((pathName.charAt(0) == SEGMENT_SEPARATOR ? "platform:/resource" : "platform:/resource/") + pathName); 831 return result; 832 } 833 834 private URI(boolean hierarchical, String scheme, String authority, 836 String device, boolean absolutePath, String [] segments, 837 String query, String fragment) 838 { 839 int hashCode = 0; 840 842 if (hierarchical) 843 { 844 ++hashCode; 845 } 846 if (absolutePath) 847 { 848 hashCode += 2; 849 } 850 if (scheme != null) 851 { 852 hashCode ^= scheme.toLowerCase().hashCode(); 853 } 854 if (authority != null) 855 { 856 hashCode ^= authority.hashCode(); 857 } 859 if (device != null) 860 { 861 hashCode ^= device.hashCode(); 862 } 864 if (query != null) 865 { 866 hashCode ^= query.hashCode(); 867 } 869 if (fragment != null) 870 { 871 hashCode ^= fragment.hashCode(); 872 } 874 875 for (int i = 0, len = segments.length; i < len; i++) 876 { 877 hashCode ^= segments[i].hashCode(); 878 } 880 881 this.hashCode = hashCode; 882 this.hierarchical = hierarchical; 884 this.scheme = scheme == null ? null : scheme.intern(); 885 this.authority = authority; 886 this.device = device; 887 this.absolutePath = absolutePath; 888 this.segments = segments; 889 this.query = query; 890 this.fragment = fragment; 891 } 892 893 private static void validateURI(boolean hierarchical, String scheme, 900 String authority, String device, 901 boolean absolutePath, String [] segments, 902 String query, String fragment) 903 { 904 if (!validScheme(scheme)) 905 { 906 throw new IllegalArgumentException ("invalid scheme: " + scheme); 907 } 908 if (!hierarchical && !validOpaquePart(authority)) 909 { 910 throw new IllegalArgumentException ("invalid opaquePart: " + authority); 911 } 912 if (hierarchical && !isArchiveScheme(scheme) && !validAuthority(authority)) 913 { 914 throw new IllegalArgumentException ("invalid authority: " + authority); 915 } 916 if (hierarchical && isArchiveScheme(scheme) && !validArchiveAuthority(authority)) 917 { 918 throw new IllegalArgumentException ("invalid authority: " + authority); 919 } 920 if (!validDevice(device)) 921 { 922 throw new IllegalArgumentException ("invalid device: " + device); 923 } 924 if (!validSegments(segments)) 925 { 926 String s = segments == null ? "invalid segments: " + segments : 927 "invalid segment: " + firstInvalidSegment(segments); 928 throw new IllegalArgumentException (s); 929 } 930 if (!validQuery(query)) 931 { 932 throw new IllegalArgumentException ("invalid query: " + query); 933 } 934 if (!validFragment(fragment)) 935 { 936 throw new IllegalArgumentException ("invalid fragment: " + fragment); 937 } 938 } 939 940 943 950 public static boolean validScheme(String value) 951 { 952 return value == null || !contains(value, MAJOR_SEPARATOR_HI, MAJOR_SEPARATOR_LO); 953 954 958 } 963 964 973 public static boolean validOpaquePart(String value) 974 { 975 return value != null && value.indexOf(FRAGMENT_SEPARATOR) == -1 && 976 value.length() > 0 && value.charAt(0) != SEGMENT_SEPARATOR; 977 978 982 } 986 987 994 public static boolean validAuthority(String value) 995 { 996 return value == null || !contains(value, SEGMENT_END_HI, SEGMENT_END_LO); 997 998 1001 } 1003 1004 1013 public static boolean validArchiveAuthority(String value) 1014 { 1015 if (value != null && value.length() > 0 && 1016 value.charAt(value.length() - 1) == ARCHIVE_IDENTIFIER) 1017 { 1018 try 1019 { 1020 URI archiveURI = createURI(value.substring(0, value.length() - 1)); 1021 return !archiveURI.hasFragment(); 1022 } 1023 catch (IllegalArgumentException e) 1024 { 1025 } 1026 } 1027 return false; 1028 } 1029 1030 1040 public static boolean validJarAuthority(String value) 1041 { 1042 return validArchiveAuthority(value); 1043 } 1044 1045 1053 public static boolean validDevice(String value) 1054 { 1055 if (value == null) return true; 1056 int len = value.length(); 1057 return len > 0 && value.charAt(len - 1) == DEVICE_IDENTIFIER && 1058 !contains(value, SEGMENT_END_HI, SEGMENT_END_LO); 1059 1060 1064 } 1069 1070 1077 public static boolean validSegment(String value) 1078 { 1079 return value != null && !contains(value, SEGMENT_END_HI, SEGMENT_END_LO); 1080 1081 1084 } 1086 1087 1094 public static boolean validSegments(String [] value) 1095 { 1096 if (value == null) return false; 1097 for (int i = 0, len = value.length; i < len; i++) 1098 { 1099 if (!validSegment(value[i])) return false; 1100 } 1101 return true; 1102 } 1103 1104 private static String firstInvalidSegment(String [] value) 1108 { 1109 if (value == null) return null; 1110 for (int i = 0, len = value.length; i < len; i++) 1111 { 1112 if (!validSegment(value[i])) return value[i]; 1113 } 1114 return null; 1115 } 1116 1117 1124 public static boolean validQuery(String value) 1125 { 1126 return value == null || value.indexOf(FRAGMENT_SEPARATOR) == -1; 1127 1128 1130 } 1132 1133 1139 public static boolean validFragment(String value) 1140 { 1141 return true; 1142 1143 1145 } 1147 1148 private static boolean contains(String s, long highBitmask, long lowBitmask) 1151 { 1152 for (int i = 0, len = s.length(); i < len; i++) 1153 { 1154 if (matches(s.charAt(i), highBitmask, lowBitmask)) return true; 1155 } 1156 return false; 1157 } 1158 1159 1185 1186 1190 public boolean isRelative() 1191 { 1192 return scheme == null; 1193 } 1194 1195 1199 public boolean isHierarchical() 1200 { 1201 return hierarchical; 1202 } 1203 1204 1208 public boolean hasAuthority() 1209 { 1210 return hierarchical && authority != null; 1211 } 1212 1213 1217 public boolean hasOpaquePart() 1218 { 1219 return !hierarchical; 1221 } 1222 1223 1227 public boolean hasDevice() 1228 { 1229 return device != null; 1231 } 1232 1233 1237 public boolean hasPath() 1238 { 1239 return absolutePath || (authority == null && device == null); 1242 } 1243 1244 1249 public boolean hasAbsolutePath() 1250 { 1251 return absolutePath; 1253 } 1254 1255 1260 public boolean hasRelativePath() 1261 { 1262 return authority == null && device == null && !absolutePath; 1265 } 1266 1267 1275 public boolean hasEmptyPath() 1276 { 1277 return authority == null && device == null && !absolutePath && 1280 segments.length == 0; 1281 } 1282 1283 1287 public boolean hasQuery() 1288 { 1289 return query != null; 1291 } 1292 1293 1297 public boolean hasFragment() 1298 { 1299 return fragment != null; 1300 } 1301 1302 1308 public boolean isCurrentDocumentReference() 1309 { 1310 return authority == null && device == null && !absolutePath && 1313 segments.length == 0 && query == null; 1314 } 1315 1316 1323 public boolean isEmpty() 1324 { 1325 return authority == null && device == null && !absolutePath && 1328 segments.length == 0 && query == null && fragment == null; 1329 } 1330 1331 1337 public boolean isFile() 1338 { 1339 return isHierarchical() && 1340 ((isRelative() && !hasQuery()) || SCHEME_FILE.equalsIgnoreCase(scheme)); 1341 } 1342 1343 private boolean isArchive() 1347 { 1348 return isArchiveScheme(scheme); 1349 } 1350 1351 1357 public static boolean isArchiveScheme(String value) 1358 { 1359 return value != null && archiveSchemes.contains(value.toLowerCase()); 1363 } 1364 1365 1368 public int hashCode() 1369 { 1370 return hashCode; 1371 } 1372 1373 1381 public boolean equals(Object obj) 1382 { 1383 if (this == obj) return true; 1384 if (!(obj instanceof URI)) return false; 1385 URI uri = (URI) obj; 1386 1387 return hashCode == uri.hashCode() && 1388 hierarchical == uri.isHierarchical() && 1389 absolutePath == uri.hasAbsolutePath() && 1390 equals(scheme, uri.scheme(), true) && 1391 equals(authority, hierarchical ? uri.authority() : uri.opaquePart()) && 1392 equals(device, uri.device()) && 1393 equals(query, uri.query()) && 1394 equals(fragment, uri.fragment()) && 1395 segmentsEqual(uri); 1396 } 1397 1398 private boolean segmentsEqual(URI uri) 1401 { 1402 if (segments.length != uri.segmentCount()) return false; 1403 for (int i = 0, len = segments.length; i < len; i++) 1404 { 1405 if (!segments[i].equals(uri.segment(i))) return false; 1406 } 1407 return true; 1408 } 1409 1410 private static boolean equals(Object o1, Object o2) 1413 { 1414 return o1 == null ? o2 == null : o1.equals(o2); 1415 } 1416 1417 private static boolean equals(String s1, String s2, boolean ignoreCase) 1420 { 1421 return s1 == null ? s2 == null : 1422 ignoreCase ? s1.equalsIgnoreCase(s2) : s1.equals(s2); 1423 } 1424 1425 1429 public String scheme() 1430 { 1431 return scheme; 1432 } 1433 1434 1438 public String opaquePart() 1439 { 1440 return isHierarchical() ? null : authority; 1441 } 1442 1443 1447 public String authority() 1448 { 1449 return isHierarchical() ? authority : null; 1450 } 1451 1452 1456 public String userInfo() 1457 { 1458 if (!hasAuthority()) return null; 1459 1460 int i = authority.indexOf(USER_INFO_SEPARATOR); 1461 return i < 0 ? null : authority.substring(0, i); 1462 } 1463 1464 1468 public String host() 1469 { 1470 if (!hasAuthority()) return null; 1471 1472 int i = authority.indexOf(USER_INFO_SEPARATOR); 1473 int j = authority.indexOf(PORT_SEPARATOR); 1474 return j < 0 ? authority.substring(i + 1) : authority.substring(i + 1, j); 1475 } 1476 1477 1481 public String port() 1482 { 1483 if (!hasAuthority()) return null; 1484 1485 int i = authority.indexOf(PORT_SEPARATOR); 1486 return i < 0 ? null : authority.substring(i + 1); 1487 } 1488 1489 1493 public String device() 1494 { 1495 return device; 1496 } 1497 1498 1505 public String [] segments() 1506 { 1507 return (String [])segments.clone(); 1508 } 1509 1510 1514 public List segmentsList() 1515 { 1516 return Collections.unmodifiableList(Arrays.asList(segments)); 1517 } 1518 1519 1523 public int segmentCount() 1524 { 1525 return segments.length; 1526 } 1527 1528 1535 public String segment(int i) 1536 { 1537 return segments[i]; 1538 } 1539 1540 1543 public String lastSegment() 1544 { 1545 int len = segments.length; 1546 if (len == 0) return null; 1547 return segments[len - 1]; 1548 } 1549 1550 1558 public String path() 1559 { 1560 if (!hasPath()) return null; 1561 1562 StringBuffer result = new StringBuffer (); 1563 if (hasAbsolutePath()) result.append(SEGMENT_SEPARATOR); 1564 1565 for (int i = 0, len = segments.length; i < len; i++) 1566 { 1567 if (i != 0) result.append(SEGMENT_SEPARATOR); 1568 result.append(segments[i]); 1569 } 1570 return result.toString(); 1571 } 1572 1573 1591 public String devicePath() 1592 { 1593 if (!hasPath()) return null; 1594 1595 StringBuffer result = new StringBuffer (); 1596 1597 if (hasAuthority()) 1598 { 1599 if (!isArchive()) result.append(AUTHORITY_SEPARATOR); 1600 result.append(authority); 1601 1602 if (hasDevice()) result.append(SEGMENT_SEPARATOR); 1603 } 1604 1605 if (hasDevice()) result.append(device); 1606 if (hasAbsolutePath()) result.append(SEGMENT_SEPARATOR); 1607 1608 for (int i = 0, len = segments.length; i < len; i++) 1609 { 1610 if (i != 0) result.append(SEGMENT_SEPARATOR); 1611 result.append(segments[i]); 1612 } 1613 return result.toString(); 1614 } 1615 1616 1620 public String query() 1621 { 1622 return query; 1623 } 1624 1625 1626 1633 public URI appendQuery(String query) 1634 { 1635 if (!validQuery(query)) 1636 { 1637 throw new IllegalArgumentException ( 1638 "invalid query portion: " + query); 1639 } 1640 return new URI(hierarchical, scheme, authority, device, absolutePath, segments, query, fragment); 1641 } 1642 1643 1647 public URI trimQuery() 1648 { 1649 if (query == null) 1650 { 1651 return this; 1652 } 1653 else 1654 { 1655 return new URI(hierarchical, scheme, authority, device, absolutePath, segments, null, fragment); 1656 } 1657 } 1658 1659 1663 public String fragment() 1664 { 1665 return fragment; 1666 } 1667 1668 1675 public URI appendFragment(String fragment) 1676 { 1677 if (!validFragment(fragment)) 1678 { 1679 throw new IllegalArgumentException ( 1680 "invalid fragment portion: " + fragment); 1681 } 1682 URI result = new URI(hierarchical, scheme, authority, device, absolutePath, segments, query, fragment); 1683 1684 if (!hasFragment()) 1685 { 1686 result.cachedTrimFragment = this; 1687 } 1688 return result; 1689 } 1690 1691 1695 public URI trimFragment() 1696 { 1697 if (fragment == null) 1698 { 1699 return this; 1700 } 1701 else if (cachedTrimFragment == null) 1702 { 1703 cachedTrimFragment = new URI(hierarchical, scheme, authority, device, absolutePath, segments, query, null); 1704 } 1705 1706 return cachedTrimFragment; 1707 } 1708 1709 1727 public URI resolve(URI base) 1728 { 1729 return resolve(base, true); 1730 } 1731 1732 1752 public URI resolve(URI base, boolean preserveRootParents) 1753 { 1754 if (!base.isHierarchical() || base.isRelative()) 1755 { 1756 throw new IllegalArgumentException ( 1757 "resolve against non-hierarchical or relative base"); 1758 } 1759 1760 if (!isRelative()) return this; 1762 1763 1765 String newAuthority = authority; 1766 String newDevice = device; 1767 boolean newAbsolutePath = absolutePath; 1768 String [] newSegments = segments; 1769 String newQuery = query; 1770 1773 if (authority == null) 1774 { 1775 newAuthority = base.authority(); 1777 1778 if (device == null) 1779 { 1780 newDevice = base.device(); 1782 1783 if (hasEmptyPath() && query == null) 1784 { 1785 newAbsolutePath = base.hasAbsolutePath(); 1787 newSegments = base.segments(); 1788 newQuery = base.query(); 1789 } 1790 else if (hasRelativePath()) 1791 { 1792 newAbsolutePath = base.hasAbsolutePath() || !hasEmptyPath(); 1796 newSegments = newAbsolutePath ? mergePath(base, preserveRootParents) 1797 : NO_SEGMENTS; 1798 } 1799 } 1801 } 1803 1805 return new URI(true, base.scheme(), newAuthority, newDevice, 1808 newAbsolutePath, newSegments, newQuery, fragment); 1809 } 1810 1811 private String [] mergePath(URI base, boolean preserveRootParents) 1815 { 1816 if (base.hasRelativePath()) 1817 { 1818 throw new IllegalArgumentException ("merge against relative path"); 1819 } 1820 if (!hasRelativePath()) 1821 { 1822 throw new IllegalStateException ("merge non-relative path"); 1823 } 1824 1825 int baseSegmentCount = base.segmentCount(); 1826 int segmentCount = segments.length; 1827 String [] stack = new String [baseSegmentCount + segmentCount]; 1828 int sp = 0; 1829 1830 for (int i = 0; i < baseSegmentCount - 1; i++) 1834 { 1835 sp = accumulate(stack, sp, base.segment(i), preserveRootParents); 1836 } 1837 1838 for (int i = 0; i < segmentCount; i++) 1839 { 1840 sp = accumulate(stack, sp, segments[i], preserveRootParents); 1841 } 1842 1843 if (sp > 0 && (segmentCount == 0 || 1847 SEGMENT_EMPTY.equals(segments[segmentCount - 1]) || 1848 SEGMENT_PARENT.equals(segments[segmentCount - 1]) || 1849 SEGMENT_SELF.equals(segments[segmentCount - 1]))) 1850 { 1851 stack[sp++] = SEGMENT_EMPTY; 1852 } 1853 1854 String [] result = new String [sp]; 1856 System.arraycopy(stack, 0, result, 0, sp); 1857 return result; 1858 } 1859 1860 private static int accumulate(String [] stack, int sp, String segment, 1863 boolean preserveRootParents) 1864 { 1865 if (SEGMENT_PARENT.equals(segment)) 1866 { 1867 if (sp == 0) 1868 { 1869 if (preserveRootParents) stack[sp++] = segment; 1872 } 1873 else 1874 { 1875 if (SEGMENT_PARENT.equals(stack[sp - 1])) stack[sp++] = segment; 1878 else sp--; 1879 } 1880 } 1881 else if (!SEGMENT_EMPTY.equals(segment) && !SEGMENT_SELF.equals(segment)) 1882 { 1883 stack[sp++] = segment; 1885 } 1886 return sp; 1887 } 1888 1889 1899 public URI deresolve(URI base) 1900 { 1901 return deresolve(base, true, false, true); 1902 } 1903 1904 1925 public URI deresolve(URI base, boolean preserveRootParents, 1926 boolean anyRelPath, boolean shorterRelPath) 1927 { 1928 if (!base.isHierarchical() || base.isRelative()) 1929 { 1930 throw new IllegalArgumentException ( 1931 "deresolve against non-hierarchical or relative base"); 1932 } 1933 if (isRelative()) 1934 { 1935 throw new IllegalStateException ("deresolve relative URI"); 1936 } 1937 1938 1941 if (!scheme.equalsIgnoreCase(base.scheme())) return this; 1943 1944 if (!isHierarchical()) return this; 1948 1949 String newAuthority = authority; 1950 String newDevice = device; 1951 boolean newAbsolutePath = absolutePath; 1952 String [] newSegments = segments; 1953 String newQuery = query; 1954 1955 if (equals(authority, base.authority()) && 1956 (hasDevice() || hasPath() || (!base.hasDevice() && !base.hasPath()))) 1957 { 1958 newAuthority = null; 1960 1961 if (equals(device, base.device()) && (hasPath() || !base.hasPath())) 1962 { 1963 newDevice = null; 1965 1966 1968 if (!anyRelPath && !shorterRelPath) 1969 { 1970 } 1972 else if (hasPath() == base.hasPath() && segmentsEqual(base) && 1973 equals(query, base.query())) 1974 { 1975 newAbsolutePath = false; 1977 newSegments = NO_SEGMENTS; 1978 newQuery = null; 1979 } 1980 else if (!hasPath() && !base.hasPath()) 1981 { 1982 newAbsolutePath = false; 1984 newSegments = NO_SEGMENTS; 1985 } 1986 else if (hasCollapsableSegments(preserveRootParents)) 1988 { 1989 } 1991 else 1992 { 1993 String [] rel = findRelativePath(base, preserveRootParents); 1995 if (anyRelPath || segments.length > rel.length) 1996 { 1997 newAbsolutePath = false; 1999 newSegments = rel; 2000 } 2001 } 2003 } 2004 } 2006 2008 return new URI(true, null, newAuthority, newDevice, newAbsolutePath, 2011 newSegments, newQuery, fragment); 2012 } 2013 2014 private boolean hasCollapsableSegments(boolean preserveRootParents) 2022 { 2023 if (hasRelativePath()) 2024 { 2025 throw new IllegalStateException ("test collapsability of relative path"); 2026 } 2027 2028 for (int i = 0, len = segments.length; i < len; i++) 2029 { 2030 String segment = segments[i]; 2031 if ((i < len - 1 && SEGMENT_EMPTY.equals(segment)) || 2032 SEGMENT_SELF.equals(segment) || 2033 SEGMENT_PARENT.equals(segment) && ( 2034 !preserveRootParents || ( 2035 i != 0 && !SEGMENT_PARENT.equals(segments[i - 1])))) 2036 { 2037 return true; 2038 } 2039 } 2040 return false; 2041 } 2042 2043 private String [] findRelativePath(URI base, boolean preserveRootParents) 2047 { 2048 if (base.hasRelativePath()) 2049 { 2050 throw new IllegalArgumentException ( 2051 "find relative path against base with relative path"); 2052 } 2053 if (!hasAbsolutePath()) 2054 { 2055 throw new IllegalArgumentException ( 2056 "find relative path of non-absolute path"); 2057 } 2058 2059 String [] startPath = base.collapseSegments(preserveRootParents); 2061 String [] endPath = segments; 2062 2063 int startCount = startPath.length > 0 ? startPath.length - 1 : 0; 2065 int endCount = endPath.length; 2066 2067 int diff = 0; 2069 2070 for (int count = startCount < endCount ? startCount : endCount - 1; 2076 diff < count && startPath[diff].equals(endPath[diff]); diff++); 2077 2078 int upCount = startCount - diff; 2079 int downCount = endCount - diff; 2080 2081 if (downCount == 1 && SEGMENT_EMPTY.equals(endPath[endCount - 1])) 2084 { 2085 downCount = 0; 2086 } 2087 2088 if (upCount + downCount == 0) 2091 { 2092 if (query == null) return new String [] { SEGMENT_SELF }; 2093 return NO_SEGMENTS; 2094 } 2095 2096 String [] result = new String [upCount + downCount]; 2098 Arrays.fill(result, 0, upCount, SEGMENT_PARENT); 2099 System.arraycopy(endPath, diff, result, upCount, downCount); 2100 return result; 2101 } 2102 2103 String [] collapseSegments(boolean preserveRootParents) 2107 { 2108 if (hasRelativePath()) 2109 { 2110 throw new IllegalStateException ("collapse relative path"); 2111 } 2112 2113 if (!hasCollapsableSegments(preserveRootParents)) return segments(); 2114 2115 int segmentCount = segments.length; 2117 String [] stack = new String [segmentCount]; 2118 int sp = 0; 2119 2120 for (int i = 0; i < segmentCount; i++) 2121 { 2122 sp = accumulate(stack, sp, segments[i], preserveRootParents); 2123 } 2124 2125 if (sp > 0 && (SEGMENT_EMPTY.equals(segments[segmentCount - 1]) || 2128 SEGMENT_PARENT.equals(segments[segmentCount - 1]) || 2129 SEGMENT_SELF.equals(segments[segmentCount - 1]))) 2130 { 2131 stack[sp++] = SEGMENT_EMPTY; 2132 } 2133 2134 String [] result = new String [sp]; 2136 System.arraycopy(stack, 0, result, 0, sp); 2137 return result; 2138 } 2139 2140 2155 public String toString() 2156 { 2157 if (cachedToString == null) 2158 { 2159 StringBuffer result = new StringBuffer (); 2160 if (!isRelative()) 2161 { 2162 result.append(scheme); 2163 result.append(SCHEME_SEPARATOR); 2164 } 2165 2166 if (isHierarchical()) 2167 { 2168 if (hasAuthority()) 2169 { 2170 if (!isArchive()) result.append(AUTHORITY_SEPARATOR); 2171 result.append(authority); 2172 } 2173 2174 if (hasDevice()) 2175 { 2176 result.append(SEGMENT_SEPARATOR); 2177 result.append(device); 2178 } 2179 2180 if (hasAbsolutePath()) result.append(SEGMENT_SEPARATOR); 2181 2182 for (int i = 0, len = segments.length; i < len; i++) 2183 { 2184 if (i != 0) result.append(SEGMENT_SEPARATOR); 2185 result.append(segments[i]); 2186 } 2187 2188 if (hasQuery()) 2189 { 2190 result.append(QUERY_SEPARATOR); 2191 result.append(query); 2192 } 2193 } 2194 else 2195 { 2196 result.append(authority); 2197 } 2198 2199 if (hasFragment()) 2200 { 2201 result.append(FRAGMENT_SEPARATOR); 2202 result.append(fragment); 2203 } 2204 cachedToString = result.toString(); 2205 } 2206 return cachedToString; 2207 } 2208 2209 String toString(boolean includeSimpleForm) 2212 { 2213 StringBuffer result = new StringBuffer (); 2214 if (includeSimpleForm) result.append(toString()); 2215 result.append("\n hierarchical: "); 2216 result.append(hierarchical); 2217 result.append("\n scheme: "); 2218 result.append(scheme); 2219 result.append("\n authority: "); 2220 result.append(authority); 2221 result.append("\n device: "); 2222 result.append(device); 2223 result.append("\n absolutePath: "); 2224 result.append(absolutePath); 2225 result.append("\n segments: "); 2226 if (segments.length == 0) result.append("<empty>"); 2227 for (int i = 0, len = segments.length; i < len; i++) 2228 { 2229 if (i > 0) result.append("\n "); 2230 result.append(segments[i]); 2231 } 2232 result.append("\n query: "); 2233 result.append(query); 2234 result.append("\n fragment: "); 2235 result.append(fragment); 2236 return result.toString(); 2237 } 2238 2239 2255 public String toFileString() 2256 { 2257 if (!isFile()) return null; 2258 2259 StringBuffer result = new StringBuffer (); 2260 char separator = File.separatorChar; 2261 2262 if (hasAuthority()) 2263 { 2264 result.append(separator); 2265 result.append(separator); 2266 result.append(authority); 2267 2268 if (hasDevice()) result.append(separator); 2269 } 2270 2271 if (hasDevice()) result.append(device); 2272 if (hasAbsolutePath()) result.append(separator); 2273 2274 for (int i = 0, len = segments.length; i < len; i++) 2275 { 2276 if (i != 0) result.append(separator); 2277 result.append(segments[i]); 2278 } 2279 2280 return decode(result.toString()); 2281 } 2282 2283 2292 public URI appendSegment(String segment) 2293 { 2294 if (!validSegment(segment)) 2295 { 2296 throw new IllegalArgumentException ("invalid segment: " + segment); 2297 } 2298 2299 if (!isHierarchical()) return this; 2300 2301 boolean newAbsolutePath = !hasRelativePath(); 2303 2304 int len = segments.length; 2305 String [] newSegments = new String [len + 1]; 2306 System.arraycopy(segments, 0, newSegments, 0, len); 2307 newSegments[len] = segment; 2308 2309 return new URI(true, scheme, authority, device, newAbsolutePath, 2310 newSegments, query, fragment); 2311 } 2312 2313 2327 public URI appendSegments(String [] segments) 2328 { 2329 if (!validSegments(segments)) 2330 { 2331 String s = segments == null ? "invalid segments: " + segments : 2332 "invalid segment: " + firstInvalidSegment(segments); 2333 throw new IllegalArgumentException (s); 2334 } 2335 2336 if (!isHierarchical()) return this; 2337 2338 boolean newAbsolutePath = !hasRelativePath(); 2340 2341 int len = this.segments.length; 2342 int segmentsCount = segments.length; 2343 String [] newSegments = new String [len + segmentsCount]; 2344 System.arraycopy(this.segments, 0, newSegments, 0, len); 2345 System.arraycopy(segments, 0, newSegments, len, segmentsCount); 2346 2347 return new URI(true, scheme, authority, device, newAbsolutePath, 2348 newSegments, query, fragment); 2349 } 2350 2351 2365 public URI trimSegments(int i) 2366 { 2367 if (!isHierarchical() || i < 1) return this; 2368 2369 String [] newSegments = NO_SEGMENTS; 2370 int len = segments.length - i; 2371 if (len > 0) 2372 { 2373 newSegments = new String [len]; 2374 System.arraycopy(segments, 0, newSegments, 0, len); 2375 } 2376 return new URI(true, scheme, authority, device, absolutePath, 2377 newSegments, query, fragment); 2378 } 2379 2380 2388 public boolean hasTrailingPathSeparator() 2389 { 2390 return segments.length > 0 && 2391 SEGMENT_EMPTY.equals(segments[segments.length - 1]); 2392 } 2393 2394 2403 public String fileExtension() 2404 { 2405 int len = segments.length; 2406 if (len == 0) return null; 2407 2408 String lastSegment = segments[len - 1]; 2409 int i = lastSegment.lastIndexOf(FILE_EXTENSION_SEPARATOR); 2410 return i < 0 ? null : lastSegment.substring(i + 1); 2411 } 2412 2413 2426 public URI appendFileExtension(String fileExtension) 2427 { 2428 if (!validSegment(fileExtension)) 2429 { 2430 throw new IllegalArgumentException ( 2431 "invalid segment portion: " + fileExtension); 2432 } 2433 2434 int len = segments.length; 2435 if (len == 0) return this; 2436 2437 String lastSegment = segments[len - 1]; 2438 if (SEGMENT_EMPTY.equals(lastSegment)) return this; 2439 StringBuffer newLastSegment = new StringBuffer (lastSegment); 2440 newLastSegment.append(FILE_EXTENSION_SEPARATOR); 2441 newLastSegment.append(fileExtension); 2442 2443 String [] newSegments = new String [len]; 2444 System.arraycopy(segments, 0, newSegments, 0, len - 1); 2445 newSegments[len - 1] = newLastSegment.toString(); 2446 2447 return new URI(true, scheme, authority, device, absolutePath, 2449 newSegments, query, fragment); 2450 } 2451 2452 2456 public URI trimFileExtension() 2457 { 2458 int len = segments.length; 2459 if (len == 0) return this; 2460 2461 String lastSegment = segments[len - 1]; 2462 int i = lastSegment.lastIndexOf(FILE_EXTENSION_SEPARATOR); 2463 if (i < 0) return this; 2464 2465 String newLastSegment = lastSegment.substring(0, i); 2466 String [] newSegments = new String [len]; 2467 System.arraycopy(segments, 0, newSegments, 0, len - 1); 2468 newSegments[len - 1] = newLastSegment; 2469 2470 return new URI(true, scheme, authority, device, absolutePath, 2472 newSegments, query, fragment); 2473 } 2474 2475 2481 public boolean isPrefix() 2482 { 2483 return hierarchical && query == null && fragment == null && 2484 (hasTrailingPathSeparator() || (absolutePath && segments.length == 0)); 2485 } 2486 2487 2501 public URI replacePrefix(URI oldPrefix, URI newPrefix) 2502 { 2503 if (!oldPrefix.isPrefix() || !newPrefix.isPrefix()) 2504 { 2505 String which = oldPrefix.isPrefix() ? "new" : "old"; 2506 throw new IllegalArgumentException ("non-prefix " + which + " value"); 2507 } 2508 2509 String [] tailSegments = getTailSegments(oldPrefix); 2511 if (tailSegments == null) return null; 2512 2513 String [] mergedSegments = tailSegments; 2517 if (newPrefix.segmentCount() != 0) 2518 { 2519 int segmentsToKeep = newPrefix.segmentCount() - 1; 2520 mergedSegments = new String [segmentsToKeep + tailSegments.length]; 2521 System.arraycopy(newPrefix.segments(), 0, mergedSegments, 0, 2522 segmentsToKeep); 2523 2524 if (tailSegments.length != 0) 2525 { 2526 System.arraycopy(tailSegments, 0, mergedSegments, segmentsToKeep, 2527 tailSegments.length); 2528 } 2529 } 2530 2531 return new URI(true, newPrefix.scheme(), newPrefix.authority(), 2533 newPrefix.device(), newPrefix.hasAbsolutePath(), 2534 mergedSegments, query, fragment); 2535 } 2536 2537 private String [] getTailSegments(URI prefix) 2541 { 2542 if (!prefix.isPrefix()) 2543 { 2544 throw new IllegalArgumentException ("non-prefix trim"); 2545 } 2546 2547 if (!hierarchical || 2550 !equals(scheme, prefix.scheme(), true) || 2551 !equals(authority, prefix.authority()) || 2552 !equals(device, prefix.device()) || 2553 absolutePath != prefix.hasAbsolutePath()) 2554 { 2555 return null; 2556 } 2557 2558 if (prefix.segmentCount() == 0) return segments; 2561 2562 int i = 0; 2566 int segmentsToCompare = prefix.segmentCount() - 1; 2567 if (segments.length <= segmentsToCompare) return null; 2568 2569 for (; i < segmentsToCompare; i++) 2570 { 2571 if (!segments[i].equals(prefix.segment(i))) return null; 2572 } 2573 2574 if (i == segments.length - 1 && SEGMENT_EMPTY.equals(segments[i])) 2577 { 2578 return NO_SEGMENTS; 2579 } 2580 2581 String [] newSegments = new String [segments.length - i]; 2583 System.arraycopy(segments, i, newSegments, 0, newSegments.length); 2584 return newSegments; 2585 } 2586 2587 2598 public static String encodeOpaquePart(String value, boolean ignoreEscaped) 2599 { 2600 String result = encode(value, URIC_HI, URIC_LO, ignoreEscaped); 2601 return result != null && result.length() > 0 && result.charAt(0) == SEGMENT_SEPARATOR ? 2602 "%2F" + result.substring(1) : 2603 result; 2604 } 2605 2606 2617 public static String encodeAuthority(String value, boolean ignoreEscaped) 2618 { 2619 return encode(value, SEGMENT_CHAR_HI, SEGMENT_CHAR_LO, ignoreEscaped); 2620 } 2621 2622 2633 public static String encodeSegment(String value, boolean ignoreEscaped) 2634 { 2635 return encode(value, SEGMENT_CHAR_HI, SEGMENT_CHAR_LO, ignoreEscaped); 2636 } 2637 2638 2648 public static String encodeQuery(String value, boolean ignoreEscaped) 2649 { 2650 return encode(value, URIC_HI, URIC_LO, ignoreEscaped); 2651 } 2652 2653 2664 public static String encodeFragment(String value, boolean ignoreEscaped) 2665 { 2666 return encode(value, URIC_HI, URIC_LO, ignoreEscaped); 2667 } 2668 2669 private static String encodeURI(String uri, boolean ignoreEscaped) 2673 { 2674 if (uri == null) return null; 2675 2676 StringBuffer result = new StringBuffer (); 2677 2678 int i = uri.indexOf(SCHEME_SEPARATOR); 2679 if (i != -1) 2680 { 2681 String scheme = uri.substring(0, i); 2682 result.append(scheme); 2683 result.append(SCHEME_SEPARATOR); 2684 } 2685 2686 int j = uri.lastIndexOf(FRAGMENT_SEPARATOR); 2687 if (j != -1) 2688 { 2689 String sspart = uri.substring(++i, j); 2690 result.append(encode(sspart, URIC_HI, URIC_LO, ignoreEscaped)); 2691 result.append(FRAGMENT_SEPARATOR); 2692 2693 String fragment = uri.substring(++j); 2694 result.append(encode(fragment, URIC_HI, URIC_LO, ignoreEscaped)); 2695 } 2696 else 2697 { 2698 String sspart = uri.substring(++i); 2699 result.append(encode(sspart, URIC_HI, URIC_LO, ignoreEscaped)); 2700 } 2701 2702 return result.toString(); 2703 } 2704 2705 private static String encode(String value, long highBitmask, long lowBitmask, boolean ignoreEscaped) 2711 { 2712 if (value == null) return null; 2713 2714 StringBuffer result = null; 2715 2716 for (int i = 0, len = value.length(); i < len; i++) 2717 { 2718 char c = value.charAt(i); 2719 2720 if (!matches(c, highBitmask, lowBitmask) && c < 160 && 2721 (!ignoreEscaped || !isEscaped(value, i))) 2722 { 2723 if (result == null) 2724 { 2725 result = new StringBuffer (value.substring(0, i)); 2726 } 2727 appendEscaped(result, (byte)c); 2728 } 2729 else if (result != null) 2730 { 2731 result.append(c); 2732 } 2733 } 2734 return result == null ? value : result.toString(); 2735 } 2736 2737 private static boolean isEscaped(String s, int i) 2740 { 2741 return s.charAt(i) == ESCAPE && s.length() > i + 2 && 2742 matches(s.charAt(i + 1), HEX_HI, HEX_LO) && 2743 matches(s.charAt(i + 2), HEX_HI, HEX_LO); 2744 } 2745 2746 private static void appendEscaped(StringBuffer result, byte b) 2750 { 2751 result.append(ESCAPE); 2752 2753 result.append(HEX_DIGITS[(b >> 4) & 0x0F]); 2758 result.append(HEX_DIGITS[b & 0x0F]); 2759 } 2760 2761 2766 public static String decode(String value) 2767 { 2768 if (value == null) return null; 2769 2770 StringBuffer result = null; 2771 2772 for (int i = 0, len = value.length(); i < len; i++) 2773 { 2774 if (isEscaped(value, i)) 2775 { 2776 if (result == null) 2777 { 2778 result = new StringBuffer (value.substring(0, i)); 2779 } 2780 result.append(unescape(value.charAt(i + 1), value.charAt(i + 2))); 2781 i += 2; 2782 } 2783 else if (result != null) 2784 { 2785 result.append(value.charAt(i)); 2786 } 2787 } 2788 return result == null ? value : result.toString(); 2789 } 2790 2791 private static char unescape(char highHexDigit, char lowHexDigit) 2795 { 2796 return (char)((valueOf(highHexDigit) << 4) | valueOf(lowHexDigit)); 2797 } 2798 2799 private static int valueOf(char hexDigit) 2801 { 2802 if (hexDigit >= 'A' && hexDigit <= 'F') 2803 { 2804 return hexDigit - 'A' + 10; 2805 } 2806 if (hexDigit >= 'a' && hexDigit <= 'f') 2807 { 2808 return hexDigit - 'a' + 10; 2809 } 2810 if (hexDigit >= '0' && hexDigit <= '9') 2811 { 2812 return hexDigit - '0'; 2813 } 2814 return 0; 2815 } 2816 2817 2823 2840 2841 2849 2926} 2927 | Popular Tags |