1 package org.mortbay.util; 16 17 import java.io.UnsupportedEncodingException ; 18 import java.util.Collections ; 19 import java.util.List ; 20 import java.util.Map ; 21 import java.util.Set ; 22 23 import org.apache.commons.logging.Log; 24 import org.mortbay.log.LogFactory; 25 26 27 38 public class URI 39 implements Cloneable 40 { 41 private static Log log = LogFactory.getLog(URI.class); 42 43 public static final String __CHARSET=System.getProperty("org.mortbay.util.URI.charset",StringUtil.__UTF_8); 44 public static final boolean __CHARSET_IS_DEFAULT=__CHARSET.equals(StringUtil.__UTF_8); 45 46 47 private String _uri; 48 private String _scheme; 49 private String _host; 50 private int _port; 51 private String _path; 52 private String _encodedPath; 53 private String _query; 54 private UrlEncoded _parameters; 55 private boolean _dirty; 56 57 58 61 public URI(URI uri) 62 throws IllegalArgumentException 63 { 64 _uri=uri.toString(); 65 _scheme=uri._scheme; 66 _host=uri._host; 67 _port=uri._port; 68 _path=uri._path; 69 _encodedPath=uri._encodedPath; 70 _query=uri._query; 71 if (uri._parameters!=null) 72 _parameters=(UrlEncoded)uri._parameters.clone(); 73 _dirty=false; 74 } 75 76 77 83 public URI(String uri) 84 throws IllegalArgumentException 85 { 86 setURI(uri); 87 } 88 89 90 public void setURI(String uri) 91 throws IllegalArgumentException 92 { 93 try 94 { 95 _uri=uri; 96 _scheme=null; 97 _host=null; 98 _port=0; 99 _path=null; 100 _encodedPath=null; 101 _query=null; 102 if (_parameters!=null) 103 _parameters.clear(); 104 105 int maxi=uri.length()-1; 107 int mark=0; 108 int state=0; 109 int i=0; 110 111 if (maxi==0 || uri.charAt(0)=='/' && uri.charAt(1)!='/') 112 { 113 state=3; 114 _scheme=null; 115 _host=null; 116 _port=0; 117 } 118 else 119 { 120 for (i=0;state<3 && i<=maxi;i++) 121 { 122 char c=uri.charAt(i); 123 switch(state) 124 { 125 case 0: if (c==':' && 127 uri.charAt(i+1)=='/' && 128 uri.charAt(i+2)=='/') 129 { 130 _scheme=uri.substring(mark,i); 132 i+=2; 133 mark=i+1; 134 state=1; 135 } 136 else if (i==0 && c=='/') 137 { 138 state=3; 140 } 141 else if (i==0 && c=='*') 142 { 143 state=5; 144 _path="*"; 145 _encodedPath="*"; 146 } 147 continue; 148 149 case 1: if (c==':') 151 { 152 _host=uri.substring(mark,i); 154 mark=i+1; 155 state=2; 156 } 157 else if (c=='/') 158 { 159 _host=uri.substring(mark,i); 161 mark=i; 162 state=3; 163 } 164 continue; 165 166 case 2: if (c=='/') 168 { 169 _port=TypeUtil.parseInt(uri,mark,i-mark,10); 170 mark=i; 171 state=3; 172 } 173 continue; 174 } 175 } 176 } 177 178 _query=null; 180 for (i++;i<=maxi;i++) 181 { 182 char c=uri.charAt(i); 183 if (c=='?') 184 { 185 _encodedPath=uri.substring(mark,i); 187 _path=decodePath(_encodedPath); 188 189 mark=i+1; 190 state=4; 191 break; 192 } 193 } 194 195 switch(state) 197 { 198 case 0: 199 _dirty=false; 200 _encodedPath=_uri; 201 _path=decodePath(_encodedPath); 202 break; 203 204 case 1: 205 _dirty=true; 206 _encodedPath="/"; 207 _path=_encodedPath; 208 _host=uri.substring(mark); 209 break; 210 211 case 2: 212 _dirty=true; 213 _encodedPath="/"; 214 _path=_encodedPath; 215 _port=TypeUtil.parseInt(uri,mark,-1,10); 216 break; 217 case 3: 218 _dirty=(mark==maxi); 219 _encodedPath=uri.substring(mark); 220 _path=decodePath(_encodedPath); 221 break; 222 223 case 4: 224 _dirty=false; 225 if (mark<=maxi) 226 _query=uri.substring(mark); 227 break; 228 229 case 5: 230 _dirty=false; 231 } 232 233 if (_query!=null && _query.length()>0) 234 { 235 if (_parameters==null) 236 _parameters= new UrlEncoded(); 237 else 238 _parameters.clear(); 239 _parameters.decode(_query,__CHARSET); 240 } 241 else 242 _query=null; 243 } 244 catch (Exception e) 245 { 246 LogSupport.ignore(log,e); 247 throw new IllegalArgumentException ("Malformed URI '"+uri+ 248 "' : "+e.toString()); 249 } 250 } 251 252 253 256 public boolean isAbsolute() 257 { 258 return _scheme!=null || _host!=null; 259 } 260 261 262 265 public String getScheme() 266 { 267 return _scheme; 268 } 269 270 271 274 public void setScheme(String scheme) 275 { 276 _scheme=scheme; 277 _dirty=true; 278 } 279 280 281 284 public String getHost() 285 { 286 return _host; 287 } 288 289 290 293 public void setHost(String host) 294 { 295 _host=host; 296 _dirty=true; 297 } 298 299 300 303 public int getPort() 304 { 305 return _port; 306 } 307 308 309 313 public void setPort(int port) 314 { 315 _port=port; 316 _dirty=true; 317 } 318 319 320 323 public String getPath() 324 { 325 return _path; 326 } 327 328 329 332 public String getEncodedPath() 333 { 334 return _encodedPath; 335 } 336 337 338 341 public void setPath(String path) 342 { 343 _path=path; 344 _encodedPath=encodePath(_path); 345 _dirty=true; 346 } 347 348 349 350 353 public String getQuery() 354 { 355 if (_dirty && _parameters!=null) 356 { 357 _query = _parameters.encode(__CHARSET); 358 if (_query!=null && _query.length()==0) 359 _query=null; 360 } 361 return _query; 362 } 363 364 365 368 public void setQuery(String query) 369 { 370 _query=query; 371 372 if (_parameters!=null) 373 _parameters.clear(); 374 else if (query!=null) 375 _parameters=new UrlEncoded(); 376 377 if (query!=null) 378 _parameters.decode(query,__CHARSET); 379 380 cleanURI(); 381 } 382 383 384 387 public Set getParameterNames() 388 { 389 if (_parameters==null) 390 return Collections.EMPTY_SET; 391 return _parameters.keySet(); 392 } 393 394 395 398 public MultiMap getParameters() 399 { 400 if (_parameters==null) 401 _parameters=new UrlEncoded(); 402 _dirty=true; 403 return _parameters; 404 } 405 406 407 410 public Map getUnmodifiableParameters() 411 { 412 if (_parameters==null) 413 return Collections.EMPTY_MAP; 414 return Collections.unmodifiableMap(_parameters); 415 } 416 417 418 420 public void putParametersTo(MultiMap map) 421 { 422 if (_parameters!=null && _parameters.size()>0) 423 map.putAll(_parameters); 424 } 425 426 427 429 public void clearParameters() 430 { 431 if (_parameters!=null) 432 { 433 _dirty=true; 434 _parameters.clear(); 435 } 436 } 437 438 439 442 public void put(String encoded) 443 { 444 UrlEncoded params = new UrlEncoded(encoded); 445 put(params); 446 } 447 448 449 454 public Object put(Object name, Object value) 455 { 456 return getParameters().put(name,value); 457 } 458 459 460 462 public void put(Map values) 463 { 464 getParameters().putAll(values); 465 } 466 467 468 470 public String get(String name) 471 { 472 if (_parameters==null) 473 return null; 474 return (String )_parameters.get(name); 475 } 476 477 478 482 public List getValues(String name) 483 { 484 if (_parameters==null) 485 return null; 486 return _parameters.getValues(name); 487 } 488 489 490 492 public void remove(String name) 493 { 494 if (_parameters!=null) 495 { 496 _dirty= 497 _parameters.remove(name)!=null; 498 } 499 } 500 501 502 504 public String toString() 505 { 506 if (_dirty) 507 { 508 getQuery(); 509 cleanURI(); 510 } 511 return _uri; 512 } 513 514 515 private void cleanURI() 516 { 517 StringBuffer buf = new StringBuffer (_uri.length()*2); 518 synchronized(buf) 519 { 520 if (_scheme!=null) 521 { 522 buf.append(_scheme); 523 buf.append("://"); 524 buf.append(_host); 525 if (_port>0) 526 { 527 buf.append(':'); 528 buf.append(_port); 529 } 530 } 531 532 buf.append(_encodedPath); 533 534 if (_query!=null && _query.length()>0) 535 { 536 buf.append('?'); 537 buf.append(_query); 538 } 539 _uri=buf.toString(); 540 _dirty=false; 541 } 542 } 543 544 545 546 552 public static String encodePath(String path) 553 { 554 if (path==null || path.length()==0) 555 return path; 556 557 StringBuffer buf = encodePath(null,path); 558 return buf==null?path:buf.toString(); 559 } 560 561 562 567 public static StringBuffer encodePath(StringBuffer buf, String path) 568 { 569 if (buf==null) 570 { 571 loop: 572 for (int i=0;i<path.length();i++) 573 { 574 char c=path.charAt(i); 575 switch(c) 576 { 577 case '%': 578 case '?': 579 case ';': 580 case '#': 581 case ' ': 582 buf=new StringBuffer (path.length()<<1); 583 break loop; 584 } 585 } 586 if (buf==null) 587 return null; 588 } 589 590 synchronized(buf) 591 { 592 for (int i=0;i<path.length();i++) 593 { 594 char c=path.charAt(i); 595 switch(c) 596 { 597 case '%': 598 buf.append("%25"); 599 continue; 600 case '?': 601 buf.append("%3F"); 602 continue; 603 case ';': 604 buf.append("%3B"); 605 continue; 606 case '#': 607 buf.append("%23"); 608 continue; 609 case ' ': 610 buf.append("%20"); 611 continue; 612 default: 613 buf.append(c); 614 continue; 615 } 616 } 617 } 618 619 return buf; 620 } 621 622 623 629 public static StringBuffer encodeString(StringBuffer buf, 630 String path, 631 String encode) 632 { 633 if (buf==null) 634 { 635 loop: 636 for (int i=0;i<path.length();i++) 637 { 638 char c=path.charAt(i); 639 if (c=='%' || encode.indexOf(c)>=0) 640 { 641 buf=new StringBuffer (path.length()<<1); 642 break loop; 643 } 644 } 645 if (buf==null) 646 return null; 647 } 648 649 synchronized(buf) 650 { 651 for (int i=0;i<path.length();i++) 652 { 653 char c=path.charAt(i); 654 if (c=='%' || encode.indexOf(c)>=0) 655 { 656 buf.append('%'); 657 StringUtil.append(buf,(byte)(0xff&c),16); 658 } 659 else 660 buf.append(c); 661 } 662 } 663 664 return buf; 665 } 666 667 668 672 public static String decodePath(String path) 673 { 674 int len=path.length(); 675 byte[] bytes=null; 676 int n=0; 677 boolean noDecode=true; 678 679 for (int i=0;i<len;i++) 680 { 681 char c = path.charAt(i); 682 683 byte b = (byte)(0xff & c); 684 685 if (c=='%' && (i+2)<len) 686 { 687 noDecode=false; 688 b=(byte)(0xff&TypeUtil.parseInt(path,i+1,2,16)); 689 i+=2; 690 } 691 else if (bytes==null) 692 { 693 n++; 694 continue; 695 } 696 697 if (bytes==null) 698 { 699 noDecode=false; 700 bytes=new byte[len]; 701 for (int j=0;j<n;j++) 702 bytes[j]=(byte)(0xff & path.charAt(j)); 703 } 704 705 bytes[n++]=b; 706 } 707 708 if (noDecode) 709 return path; 710 711 try 712 { 713 return new String (bytes,0,n,__CHARSET); 714 } 715 catch(UnsupportedEncodingException e) 716 { 717 log.warn(LogSupport.EXCEPTION,e); 718 return new String (bytes,0,n); 719 } 720 } 721 722 723 726 public Object clone() 727 throws CloneNotSupportedException 728 { 729 URI u = (URI)super.clone(); 730 if (_parameters!=null) 731 u._parameters=(UrlEncoded)_parameters.clone(); 732 _dirty=false; 733 734 return u; 735 } 736 737 738 739 746 public static String addPaths(String p1, String p2) 747 { 748 if (p1==null || p1.length()==0) 749 { 750 if (p2==null || p2.length()==0) 751 return p1; 752 return p2; 753 } 754 if (p2==null || p2.length()==0) 755 return p1; 756 757 int split=p1.indexOf(';'); 758 if (split<0) 759 split=p1.indexOf('?'); 760 if (split==0) 761 return p2+p1; 762 if (split<0) 763 split=p1.length(); 764 765 StringBuffer buf = new StringBuffer (p1.length()+p2.length()+2); 766 buf.append(p1); 767 768 if (buf.charAt(split-1)=='/') 769 { 770 if (p2.startsWith("/")) 771 { 772 buf.deleteCharAt(split-1); 773 buf.insert(split-1,p2); 774 } 775 else 776 buf.insert(split,p2); 777 } 778 else 779 { 780 if (p2.startsWith("/")) 781 buf.insert(split,p2); 782 else 783 { 784 buf.insert(split,'/'); 785 buf.insert(split+1,p2); 786 } 787 } 788 789 return buf.toString(); 790 } 791 792 793 796 public static String parentPath(String p) 797 { 798 if (p==null || "/".equals(p)) 799 return null; 800 int slash=p.lastIndexOf('/',p.length()-2); 801 if (slash>=0) 802 return p.substring(0,slash+1); 803 return null; 804 } 805 806 807 810 public static String stripPath(String path) 811 { 812 if (path==null) 813 return null; 814 int semi=path.indexOf(';'); 815 if (semi<0) 816 return path; 817 return path.substring(0,semi); 818 } 819 820 821 827 public static String canonicalPath(String path) 828 { 829 if (path==null || path.length()==0) 830 return path; 831 832 int end=path.length(); 833 int queryIdx=path.indexOf('?'); 834 int start = path.lastIndexOf('/', (queryIdx > 0 ? queryIdx : end)); 835 836 search: 837 while (end>0) 838 { 839 switch(end-start) 840 { 841 case 2: if (path.charAt(start+1)!='.') 843 break; 844 break search; 845 case 3: if (path.charAt(start+1)!='.' || path.charAt(start+2)!='.') 847 break; 848 break search; 849 } 850 851 end=start; 852 start=path.lastIndexOf('/',end-1); 853 } 854 855 if (start>=end) 857 return path; 858 859 StringBuffer buf = new StringBuffer (path); 860 int delStart=-1; 861 int delEnd=-1; 862 int skip=0; 863 864 while (end>0) 865 { 866 switch(end-start) 867 { 868 case 2: if (buf.charAt(start+1)!='.') 870 { 871 if (skip>0 && --skip==0) 872 { 873 delStart=start>=0?start:0; 874 if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') 875 delStart++; 876 } 877 break; 878 } 879 880 if(start<0 && buf.length()>2 && buf.charAt(1)=='/' && buf.charAt(2)=='/') 881 break; 882 883 if(delEnd<0) 884 delEnd=end; 885 delStart=start; 886 if (delStart<0 || delStart==0&&buf.charAt(delStart)=='/') 887 { 888 delStart++; 889 if (delEnd<buf.length() && buf.charAt(delEnd)=='/') 890 delEnd++; 891 break; 892 } 893 if (end==buf.length()) 894 delStart++; 895 896 end=start--; 897 while (start>=0 && buf.charAt(start)!='/') 898 start--; 899 continue; 900 901 case 3: if (buf.charAt(start+1)!='.' || buf.charAt(start+2)!='.') 903 { 904 if (skip>0 && --skip==0) 905 { delStart=start>=0?start:0; 906 if(delStart>0 && delEnd==buf.length() && buf.charAt(delEnd-1)=='.') 907 delStart++; 908 } 909 break; 910 } 911 912 delStart=start; 913 if (delEnd<0) 914 delEnd=end; 915 916 skip++; 917 end=start--; 918 while (start>=0 && buf.charAt(start)!='/') 919 start--; 920 continue; 921 922 default: 923 if (skip>0 && --skip==0) 924 { 925 delStart=start>=0?start:0; 926 if(delEnd==buf.length() && buf.charAt(delEnd-1)=='.') 927 delStart++; 928 } 929 } 930 931 if (skip<=0 && delStart>=0 && delStart>=0) 933 { 934 buf.delete(delStart,delEnd); 935 delStart=delEnd=-1; 936 if (skip>0) 937 delEnd=end; 938 } 939 940 end=start--; 941 while (start>=0 && buf.charAt(start)!='/') 942 start--; 943 } 944 945 if (skip>0) 947 return null; 948 949 if (delEnd>=0) 951 buf.delete(delStart,delEnd); 952 953 return buf.toString(); 954 } 955 956 957 961 public static boolean hasScheme(String uri) 962 { 963 for (int i=0;i<uri.length();i++) 964 { 965 char c=uri.charAt(i); 966 if (c==':') 967 return true; 968 if (!(c>='a'&&c<='z' || 969 c>='A'&&c<='Z' || 970 (i>0 &&(c>='0'&&c<='9' || 971 c=='.' || 972 c=='+' || 973 c=='-')) 974 )) 975 break; 976 } 977 return false; 978 } 979 980 } 981 982 983 984 | Popular Tags |