URI.java
上传用户:demmber
上传日期:2007-12-22
资源大小:717k
文件大小:56k
源码类别:

Java编程

开发平台:

Java

  1. /*
  2.  * @(#)URI.java 0.3-3 06/05/2001
  3.  *
  4.  *  This file is part of the HTTPClient package
  5.  *  Copyright (C) 1996-2001 Ronald Tschal鋜
  6.  *
  7.  *  This library is free software; you can redistribute it and/or
  8.  *  modify it under the terms of the GNU Lesser General Public
  9.  *  License as published by the Free Software Foundation; either
  10.  *  version 2 of the License, or (at your option) any later version.
  11.  *
  12.  *  This library is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  *  Lesser General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU Lesser General Public
  18.  *  License along with this library; if not, write to the Free
  19.  *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  20.  *  MA 02111-1307, USA
  21.  *
  22.  *  For questions, suggestions, bug-reports, enhancement-requests etc.
  23.  *  I may be contacted at:
  24.  *
  25.  *  ronald@innovation.ch
  26.  *
  27.  *  The HTTPClient's home page is located at:
  28.  *
  29.  *  http://www.innovation.ch/java/HTTPClient/ 
  30.  *
  31.  */
  32. package HTTPClient;
  33. import java.net.URL;
  34. import java.net.MalformedURLException;
  35. import java.util.BitSet;
  36. import java.util.Hashtable;
  37. /**
  38.  * This class represents a generic URI, as defined in RFC-2396.
  39.  * This is similar to java.net.URL, with the following enhancements:
  40.  * <UL>
  41.  * <LI>it doesn't require a URLStreamhandler to exist for the scheme; this
  42.  *     allows this class to be used to hold any URI, construct absolute
  43.  *     URIs from relative ones, etc.
  44.  * <LI>it handles escapes correctly
  45.  * <LI>equals() works correctly
  46.  * <LI>relative URIs are correctly constructed
  47.  * <LI>it has methods for accessing various fields such as userinfo,
  48.  *     fragment, params, etc.
  49.  * <LI>it handles less common forms of resources such as the "*" used in
  50.  *     http URLs.
  51.  * </UL>
  52.  *
  53.  * <P>The elements are always stored in escaped form.
  54.  *
  55.  * <P>While RFC-2396 distinguishes between just two forms of URI's, those that
  56.  * follow the generic syntax and those that don't, this class knows about a
  57.  * third form, named semi-generic, used by quite a few popular schemes.
  58.  * Semi-generic syntax treats the path part as opaque, i.e. has the form
  59.  * &lt;scheme&gt;://&lt;authority&gt;/&lt;opaque&gt; . Relative URI's of this
  60.  * type are only resolved as far as absolute paths - relative paths do not
  61.  * exist.
  62.  *
  63.  * <P>Ideally, java.net.URL should subclass URI.
  64.  *
  65.  * @see <A HREF="http://www.ics.uci.edu/pub/ietf/uri/rfc2396.txt">rfc-2396</A>
  66.  * @version 0.3-3  06/05/2001
  67.  * @author Ronald Tschal鋜
  68.  * @since V0.3-1
  69.  */
  70. public class URI
  71. {
  72.     /**
  73.      * If true, then the parser will resolve certain URI's in backwards
  74.      * compatible (but technically incorrect) manner. Example:
  75.      *
  76.      *<PRE>
  77.      * base   = http://a/b/c/d;p?q
  78.      * rel    = http:g
  79.      * result = http:g (correct)
  80.      * result = http://a/b/c/g (backwards compatible)
  81.      *</PRE>
  82.      *
  83.      * See rfc-2396, section 5.2, step 3, second paragraph.
  84.      */
  85.     public static final boolean ENABLE_BACKWARDS_COMPATIBILITY = true;
  86.     protected static final Hashtable defaultPorts          = new Hashtable();
  87.     protected static final Hashtable usesGenericSyntax     = new Hashtable();
  88.     protected static final Hashtable usesSemiGenericSyntax = new Hashtable();
  89.     /* various character classes as defined in the draft */
  90.     protected static final BitSet alphanumChar;
  91.     protected static final BitSet markChar;
  92.     protected static final BitSet reservedChar;
  93.     protected static final BitSet unreservedChar;
  94.     protected static final BitSet uricChar;
  95.     protected static final BitSet pcharChar;
  96.     protected static final BitSet userinfoChar;
  97.     protected static final BitSet schemeChar;
  98.     protected static final BitSet hostChar;
  99.     protected static final BitSet opaqueChar;
  100.     protected static final BitSet reg_nameChar;
  101.     /* These are not directly in the spec, but used for escaping and
  102.      * unescaping parts
  103.      */
  104.     /** list of characters which must not be unescaped when unescaping a scheme */
  105.     public static final BitSet resvdSchemeChar;
  106.     /** list of characters which must not be unescaped when unescaping a userinfo */
  107.     public static final BitSet resvdUIChar;
  108.     /** list of characters which must not be unescaped when unescaping a host */
  109.     public static final BitSet resvdHostChar;
  110.     /** list of characters which must not be unescaped when unescaping a path */
  111.     public static final BitSet resvdPathChar;
  112.     /** list of characters which must not be unescaped when unescaping a query string */
  113.     public static final BitSet resvdQueryChar;
  114.     /** list of characters which must not be escaped when escaping a path */
  115.     public static final BitSet escpdPathChar;
  116.     /** list of characters which must not be escaped when escaping a query string */
  117.     public static final BitSet escpdQueryChar;
  118.     /** list of characters which must not be escaped when escaping a fragment identifier */
  119.     public static final BitSet escpdFragChar;
  120.     static
  121.     {
  122. defaultPorts.put("http",      new Integer(80));
  123. defaultPorts.put("shttp",     new Integer(80));
  124. defaultPorts.put("http-ng",   new Integer(80));
  125. defaultPorts.put("coffee",    new Integer(80));
  126. defaultPorts.put("https",     new Integer(443));
  127. defaultPorts.put("ftp",       new Integer(21));
  128. defaultPorts.put("telnet",    new Integer(23));
  129. defaultPorts.put("nntp",      new Integer(119));
  130. defaultPorts.put("news",      new Integer(119));
  131. defaultPorts.put("snews",     new Integer(563));
  132. defaultPorts.put("hnews",     new Integer(80));
  133. defaultPorts.put("smtp",      new Integer(25));
  134. defaultPorts.put("gopher",    new Integer(70));
  135. defaultPorts.put("wais",      new Integer(210));
  136. defaultPorts.put("whois",     new Integer(43));
  137. defaultPorts.put("whois++",   new Integer(63));
  138. defaultPorts.put("rwhois",    new Integer(4321));
  139. defaultPorts.put("imap",      new Integer(143));
  140. defaultPorts.put("pop",       new Integer(110));
  141. defaultPorts.put("prospero",  new Integer(1525));
  142. defaultPorts.put("irc",       new Integer(194));
  143. defaultPorts.put("ldap",      new Integer(389));
  144. defaultPorts.put("nfs",       new Integer(2049));
  145. defaultPorts.put("z39.50r",   new Integer(210));
  146. defaultPorts.put("z39.50s",   new Integer(210));
  147. defaultPorts.put("vemmi",     new Integer(575));
  148. defaultPorts.put("videotex",  new Integer(516));
  149. defaultPorts.put("cmp",       new Integer(829));
  150. usesGenericSyntax.put("http", Boolean.TRUE);
  151. usesGenericSyntax.put("https", Boolean.TRUE);
  152. usesGenericSyntax.put("shttp", Boolean.TRUE);
  153. usesGenericSyntax.put("coffee", Boolean.TRUE);
  154. usesGenericSyntax.put("ftp", Boolean.TRUE);
  155. usesGenericSyntax.put("file", Boolean.TRUE);
  156. usesGenericSyntax.put("nntp", Boolean.TRUE);
  157. usesGenericSyntax.put("news", Boolean.TRUE);
  158. usesGenericSyntax.put("snews", Boolean.TRUE);
  159. usesGenericSyntax.put("hnews", Boolean.TRUE);
  160. usesGenericSyntax.put("imap", Boolean.TRUE);
  161. usesGenericSyntax.put("wais", Boolean.TRUE);
  162. usesGenericSyntax.put("nfs", Boolean.TRUE);
  163. usesGenericSyntax.put("sip", Boolean.TRUE);
  164. usesGenericSyntax.put("sips", Boolean.TRUE);
  165. usesGenericSyntax.put("sipt", Boolean.TRUE);
  166. usesGenericSyntax.put("sipu", Boolean.TRUE);
  167. /* Note: schemes which definitely don't use the generic-URI syntax
  168.  * and must therefore never appear in the above list:
  169.  * "urn", "mailto", "sdp", "service", "tv", "gsm-sms", "tel", "fax",
  170.  * "modem", "eid", "cid", "mid", "data", "ldap"
  171.  */
  172. usesSemiGenericSyntax.put("ldap", Boolean.TRUE);
  173. usesSemiGenericSyntax.put("irc", Boolean.TRUE);
  174. usesSemiGenericSyntax.put("gopher", Boolean.TRUE);
  175. usesSemiGenericSyntax.put("videotex", Boolean.TRUE);
  176. usesSemiGenericSyntax.put("rwhois", Boolean.TRUE);
  177. usesSemiGenericSyntax.put("whois++", Boolean.TRUE);
  178. usesSemiGenericSyntax.put("smtp", Boolean.TRUE);
  179. usesSemiGenericSyntax.put("telnet", Boolean.TRUE);
  180. usesSemiGenericSyntax.put("prospero", Boolean.TRUE);
  181. usesSemiGenericSyntax.put("pop", Boolean.TRUE);
  182. usesSemiGenericSyntax.put("vemmi", Boolean.TRUE);
  183. usesSemiGenericSyntax.put("z39.50r", Boolean.TRUE);
  184. usesSemiGenericSyntax.put("z39.50s", Boolean.TRUE);
  185. usesSemiGenericSyntax.put("stream", Boolean.TRUE);
  186. usesSemiGenericSyntax.put("cmp", Boolean.TRUE);
  187. alphanumChar = new BitSet(128);
  188. for (int ch='0'; ch<='9'; ch++)  alphanumChar.set(ch);
  189. for (int ch='A'; ch<='Z'; ch++)  alphanumChar.set(ch);
  190. for (int ch='a'; ch<='z'; ch++)  alphanumChar.set(ch);
  191. markChar = new BitSet(128);
  192. markChar.set('-');
  193. markChar.set('_');
  194. markChar.set('.');
  195. markChar.set('!');
  196. markChar.set('~');
  197. markChar.set('*');
  198. markChar.set(''');
  199. markChar.set('(');
  200. markChar.set(')');
  201. reservedChar = new BitSet(128);
  202. reservedChar.set(';');
  203. reservedChar.set('/');
  204. reservedChar.set('?');
  205. reservedChar.set(':');
  206. reservedChar.set('@');
  207. reservedChar.set('&');
  208. reservedChar.set('=');
  209. reservedChar.set('+');
  210. reservedChar.set('$');
  211. reservedChar.set(',');
  212. unreservedChar = new BitSet(128);
  213. unreservedChar.or(alphanumChar);
  214. unreservedChar.or(markChar);
  215. uricChar = new BitSet(128);
  216. uricChar.or(unreservedChar);
  217. uricChar.or(reservedChar);
  218. uricChar.set('%');
  219. pcharChar = new BitSet(128);
  220. pcharChar.or(unreservedChar);
  221. pcharChar.set('%');
  222. pcharChar.set(':');
  223. pcharChar.set('@');
  224. pcharChar.set('&');
  225. pcharChar.set('=');
  226. pcharChar.set('+');
  227. pcharChar.set('$');
  228. pcharChar.set(',');
  229. userinfoChar = new BitSet(128);
  230. userinfoChar.or(unreservedChar);
  231. userinfoChar.set('%');
  232. userinfoChar.set(';');
  233. userinfoChar.set(':');
  234. userinfoChar.set('&');
  235. userinfoChar.set('=');
  236. userinfoChar.set('+');
  237. userinfoChar.set('$');
  238. userinfoChar.set(',');
  239. // this actually shouldn't contain uppercase letters...
  240. schemeChar = new BitSet(128);
  241. schemeChar.or(alphanumChar);
  242. schemeChar.set('+');
  243. schemeChar.set('-');
  244. schemeChar.set('.');
  245. opaqueChar = new BitSet(128);
  246. opaqueChar.or(uricChar);
  247. hostChar = new BitSet(128);
  248. hostChar.or(alphanumChar);
  249. hostChar.set('-');
  250. hostChar.set('.');
  251. reg_nameChar = new BitSet(128);
  252. reg_nameChar.or(unreservedChar);
  253. reg_nameChar.set('$');
  254. reg_nameChar.set(',');
  255. reg_nameChar.set(';');
  256. reg_nameChar.set(':');
  257. reg_nameChar.set('@');
  258. reg_nameChar.set('&');
  259. reg_nameChar.set('=');
  260. reg_nameChar.set('+');
  261. resvdSchemeChar = new BitSet(128);
  262. resvdSchemeChar.set(':');
  263. resvdUIChar = new BitSet(128);
  264. resvdUIChar.set('@');
  265. resvdHostChar = new BitSet(128);
  266. resvdHostChar.set(':');
  267. resvdHostChar.set('/');
  268. resvdHostChar.set('?');
  269. resvdHostChar.set('#');
  270. resvdPathChar = new BitSet(128);
  271. resvdPathChar.set('/');
  272. resvdPathChar.set(';');
  273. resvdPathChar.set('?');
  274. resvdPathChar.set('#');
  275. resvdQueryChar = new BitSet(128);
  276. resvdQueryChar.set('#');
  277. escpdPathChar = new BitSet(128);
  278. escpdPathChar.or(pcharChar);
  279. escpdPathChar.set('%');
  280. escpdPathChar.set('/');
  281. escpdPathChar.set(';');
  282. escpdQueryChar = new BitSet(128);
  283. escpdQueryChar.or(uricChar);
  284. escpdQueryChar.clear('#');
  285. escpdFragChar = new BitSet(128);
  286. escpdFragChar.or(uricChar);
  287.     }
  288.     /* our uri in pieces */
  289.     protected static final int OPAQUE       = 0;
  290.     protected static final int SEMI_GENERIC = 1;
  291.     protected static final int GENERIC      = 2;
  292.     protected int     type;
  293.     protected String  scheme;
  294.     protected String  opaque;
  295.     protected String  userinfo;
  296.     protected String  host;
  297.     protected int     port = -1;
  298.     protected String  path;
  299.     protected String  query;
  300.     protected String  fragment;
  301.     /* cache the java.net.URL */
  302.     protected URL     url = null;
  303.     // Constructors
  304.     /**
  305.      * Constructs a URI from the given string representation. The string
  306.      * must be an absolute URI.
  307.      *
  308.      * @param uri a String containing an absolute URI
  309.      * @exception ParseException if no scheme can be found or a specified
  310.      *                           port cannot be parsed as a number
  311.      */
  312.     public URI(String uri)  throws ParseException
  313.     {
  314. this((URI) null, uri);
  315.     }
  316.     /**
  317.      * Constructs a URI from the given string representation, relative to
  318.      * the given base URI.
  319.      *
  320.      * @param base    the base URI, relative to which <var>rel_uri</var>
  321.      *                is to be parsed
  322.      * @param rel_uri a String containing a relative or absolute URI
  323.      * @exception ParseException if <var>base</var> is null and
  324.      *                           <var>rel_uri</var> is not an absolute URI, or
  325.      *                           if <var>base</var> is not null and the scheme
  326.      *                           is not known to use the generic syntax, or
  327.      *                           if a given port cannot be parsed as a number
  328.      */
  329.     public URI(URI base, String rel_uri)  throws ParseException
  330.     {
  331. /* Parsing is done according to the following RE:
  332.  *
  333.  *  ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(?([^#]*))?(#(.*))?
  334.  *   12            3  4          5       6  7        8 9
  335.  *
  336.  * 2: scheme
  337.  * 4: authority
  338.  * 5: path
  339.  * 7: query
  340.  * 9: fragment
  341.  */
  342. char[] uri = rel_uri.toCharArray();
  343. int pos = 0, idx, len = uri.length;
  344. // trim()
  345. while (pos < len  &&  Character.isWhitespace(uri[pos]))    pos++;
  346. while (len > 0    &&  Character.isWhitespace(uri[len-1]))  len--;
  347. // strip the special "url" or "uri" scheme
  348. if (pos < len-3  &&  uri[pos+3] == ':'  &&
  349.     (uri[pos+0] == 'u'  ||  uri[pos+0] == 'U')  &&
  350.     (uri[pos+1] == 'r'  ||  uri[pos+1] == 'R')  &&
  351.     (uri[pos+2] == 'i'  ||  uri[pos+2] == 'I'  ||
  352.      uri[pos+2] == 'l'  ||  uri[pos+2] == 'L'))
  353.     pos += 4;
  354. // get scheme: (([^:/?#]+):)?
  355. idx = pos;
  356. while (idx < len  &&  uri[idx] != ':'  &&  uri[idx] != '/'  &&
  357.        uri[idx] != '?'  &&  uri[idx] != '#')
  358.     idx++;
  359. if (idx < len  &&  uri[idx] == ':')
  360. {
  361.     scheme = rel_uri.substring(pos, idx).trim().toLowerCase();
  362.     pos = idx + 1;
  363. }
  364. // check and resolve scheme
  365. String final_scheme = scheme;
  366. if (scheme == null)
  367. {
  368.     if (base == null)
  369. throw new ParseException("No scheme found");
  370.     final_scheme = base.scheme;
  371. }
  372. // check for generic vs. opaque
  373. type = usesGenericSyntax(final_scheme) ? GENERIC :
  374.        usesSemiGenericSyntax(final_scheme) ? SEMI_GENERIC : OPAQUE;
  375. if (type == OPAQUE)
  376. {
  377.     if (base != null  &&  scheme == null)
  378. throw new ParseException("Can't resolve relative URI for " +
  379.  "scheme " + final_scheme);
  380.     opaque = escape(rel_uri.substring(pos), opaqueChar, true);
  381.     if (opaque.length() > 0  &&  opaque.charAt(0) == '/')
  382. opaque = "%2F" + opaque.substring(1);
  383.     return;
  384. }
  385. // get authority: (//([^/?#]*))?
  386. if (pos+1 < len  &&  uri[pos] == '/'  &&  uri[pos+1] == '/')
  387. {
  388.     pos += 2;
  389.     idx = pos;
  390.     while (idx < len  &&  uri[idx] != '/'  &&  uri[idx] != '?'  &&
  391.    uri[idx] != '#')
  392. idx++;
  393.     parse_authority(rel_uri.substring(pos, idx), final_scheme);
  394.     pos = idx;
  395. }
  396. // handle semi-generic and generic uri's
  397. if (type == SEMI_GENERIC)
  398. {
  399.     path = escape(rel_uri.substring(pos), uricChar, true);
  400.     if (path.length() > 0  &&  path.charAt(0) != '/')
  401. path = '/' + path;
  402. }
  403. else
  404. {
  405.     // get path: ([^?#]*)
  406.     idx = pos;
  407.     while (idx < len  &&  uri[idx] != '?'  &&  uri[idx] != '#')
  408. idx++;
  409.     path = escape(rel_uri.substring(pos, idx), escpdPathChar, true);
  410.     pos = idx;
  411.     // get query: (?([^#]*))?
  412.     if (pos < len  &&  uri[pos] == '?')
  413.     {
  414. pos += 1;
  415. idx = pos;
  416. while (idx < len  &&  uri[idx] != '#')
  417.     idx++;
  418. this.query = escape(rel_uri.substring(pos, idx), escpdQueryChar, true);
  419. pos = idx;
  420.     }
  421.     // get fragment: (#(.*))?
  422.     if (pos < len  &&  uri[pos] == '#')
  423. this.fragment = escape(rel_uri.substring(pos+1, len), escpdFragChar, true);
  424. }
  425. // now resolve the parts relative to the base
  426. if (base != null)
  427. {
  428.     if (scheme != null  && // resolve scheme
  429. !(scheme.equals(base.scheme)  &&  ENABLE_BACKWARDS_COMPATIBILITY))
  430.       return;
  431.     scheme = base.scheme;
  432.     if (host != null) // resolve authority
  433. return;
  434.     userinfo = base.userinfo;
  435.     host     = base.host;
  436.     port     = base.port;
  437.     if (type == SEMI_GENERIC) // can't resolve relative paths
  438. return;
  439.     if (path.length() == 0  &&  query == null) // current doc
  440.     {
  441. path  = base.path;
  442. query = base.query;
  443. return;
  444.     }
  445.     if (path.length() == 0  ||  path.charAt(0) != '/') // relative path
  446.     {
  447. idx = (base.path != null) ? base.path.lastIndexOf('/') : -1;
  448. if (idx < 0)
  449.     path = '/' + path;
  450. else
  451.     path = base.path.substring(0, idx+1) + path;
  452. path = canonicalizePath(path);
  453.     }
  454. }
  455.     }
  456.     /**
  457.      * Remove all "/../" and "/./" from path, where possible. Leading "/../"'s
  458.      * are not removed.
  459.      *
  460.      * @param path the path to canonicalize
  461.      * @return the canonicalized path
  462.      */
  463.     public static String canonicalizePath(String path)
  464.     {
  465. int idx, len = path.length();
  466. if (!((idx = path.indexOf("/.")) != -1  &&
  467.       (idx == len-2  ||  path.charAt(idx+2) == '/'  ||
  468.        (path.charAt(idx+2) == '.'  &&
  469. (idx == len-3  ||  path.charAt(idx+3) == '/')) )))
  470.     return path;
  471. char[] p = new char[path.length()]; // clean path
  472. path.getChars(0, p.length, p, 0);
  473. int beg = 0;
  474. for (idx=1; idx<len; idx++)
  475. {
  476.     if (p[idx] == '.'  &&  p[idx-1] == '/')
  477.     {
  478. int end;
  479. if (idx == len-1) // trailing "/."
  480. {
  481.     end  = idx;
  482.     idx += 1;
  483. }
  484. else if (p[idx+1] == '/') // "/./"
  485. {
  486.     end  = idx - 1;
  487.     idx += 1;
  488. }
  489. else if (p[idx+1] == '.'  &&
  490.  (idx == len-2  ||  p[idx+2] == '/')) // "/../"
  491. {
  492.     if (idx < beg + 2) // keep from backing up too much
  493.     {
  494. beg = idx + 2;
  495. continue;
  496.     }
  497.     end  = idx - 2;
  498.     while (end > beg  &&  p[end] != '/')  end--;
  499.     if (p[end] != '/')  continue;
  500.     if (idx == len-2) end++;
  501.     idx += 2;
  502. }
  503. else
  504.     continue;
  505. System.arraycopy(p, idx, p, end, len-idx);
  506. len -= idx - end;
  507. idx = end;
  508.     }
  509. }
  510. return new String(p, 0, len);
  511.     }
  512.     /**
  513.      * Parse the authority specific part
  514.      */
  515.     private void parse_authority(String authority, String scheme)
  516.     throws ParseException
  517.     {
  518. /* The authority is further parsed according to:
  519.  *
  520.  *  ^(([^@]*)@?)([[^]]*]|[^:]*)?(:(.*))?
  521.  *   12         3       4 5
  522.  *
  523.  * 2: userinfo
  524.  * 3: host
  525.  * 5: port
  526.  */
  527. char[] uri = authority.toCharArray();
  528. int pos = 0, idx, len = uri.length;
  529. // get userinfo: (([^@]*)@?)
  530. idx = pos;
  531. while (idx < len  &&  uri[idx] != '@')
  532.     idx++;
  533. if (idx < len  &&  uri[idx] == '@')
  534. {
  535.     this.userinfo = escape(authority.substring(pos, idx), userinfoChar, true);
  536.     pos = idx + 1;
  537. }
  538. // get host: ([[^]]*]|[^:]*)?
  539. idx = pos;
  540. if (idx < len  &&  uri[idx] == '[') // IPv6
  541. {
  542.     while (idx < len  &&  uri[idx] != ']')
  543. idx++;
  544.     if (idx == len)
  545. throw new ParseException("No closing ']' found for opening '['"+
  546.  " at position " + pos +
  547.  " in authority `" + authority + "'");
  548.     this.host = authority.substring(pos+1, idx);
  549.     idx++;
  550. }
  551. else
  552. {
  553.     while (idx < len  &&  uri[idx] != ':')
  554. idx++;
  555.     this.host = escape(authority.substring(pos, idx), uricChar, true);
  556. }
  557. pos = idx;
  558. // get port: (:(.*))?
  559. if (pos < (len-1)  &&  uri[pos] == ':')
  560. {
  561.     int p;
  562.     try
  563.     {
  564. p = Integer.parseInt(
  565.     unescape(authority.substring(pos+1, len), null));
  566. if (p < 0)  throw new NumberFormatException();
  567.     }
  568.     catch (NumberFormatException e)
  569.     {
  570. throw new ParseException(authority.substring(pos+1, len) +
  571.  " is an invalid port number");
  572.     }
  573.     if (p == defaultPort(scheme))
  574. this.port = -1;
  575.     else
  576. this.port = p;
  577. }
  578.     }
  579.     /**
  580.      * Construct a URI from the given URL.
  581.      *
  582.      * @param url the URL
  583.      * @exception ParseException if <code>url.toExternalForm()</code> generates
  584.      *                           an invalid string representation
  585.      */
  586.     public URI(URL url)  throws ParseException
  587.     {
  588. this((URI) null, url.toExternalForm());
  589.     }
  590.     /**
  591.      * Constructs a URI from the given parts, using the default port for
  592.      * this scheme (if known). The parts must be in unescaped form.
  593.      *
  594.      * @param scheme the scheme (sometimes known as protocol)
  595.      * @param host   the host
  596.      * @param path   the path part
  597.      * @exception ParseException if <var>scheme</var> is null
  598.      */
  599.     public URI(String scheme, String host, String path)  throws ParseException
  600.     {
  601. this(scheme, null, host, -1, path, null, null);
  602.     }
  603.     /**
  604.      * Constructs a URI from the given parts. The parts must be in unescaped
  605.      * form.
  606.      *
  607.      * @param scheme the scheme (sometimes known as protocol)
  608.      * @param host   the host
  609.      * @param port   the port
  610.      * @param path   the path part
  611.      * @exception ParseException if <var>scheme</var> is null
  612.      */
  613.     public URI(String scheme, String host, int port, String path)
  614.     throws ParseException
  615.     {
  616. this(scheme, null, host, port, path, null, null);
  617.     }
  618.     /**
  619.      * Constructs a URI from the given parts. Any part except for the
  620.      * the scheme may be null. The parts must be in unescaped form.
  621.      *
  622.      * @param scheme   the scheme (sometimes known as protocol)
  623.      * @param userinfo the userinfo
  624.      * @param host     the host
  625.      * @param port     the port
  626.      * @param path     the path part
  627.      * @param query    the query string
  628.      * @param fragment the fragment identifier
  629.      * @exception ParseException if <var>scheme</var> is null
  630.      */
  631.     public URI(String scheme, String userinfo, String host, int port,
  632.        String path, String query, String fragment)
  633.     throws ParseException
  634.     {
  635. if (scheme == null)
  636.     throw new ParseException("missing scheme");
  637. this.scheme = escape(scheme.trim().toLowerCase(), schemeChar, true);
  638. if (userinfo != null)
  639.     this.userinfo = escape(userinfo.trim(), userinfoChar, true);
  640. if (host != null)
  641. {
  642.     host = host.trim();
  643.     this.host = isIPV6Addr(host) ? host : escape(host, hostChar, true);
  644. }
  645. if (port != defaultPort(scheme))
  646.     this.port     = port;
  647. if (path != null)
  648.     this.path     = escape(path.trim(), escpdPathChar, true); // ???
  649. if (query != null)
  650.     this.query    = escape(query.trim(), escpdQueryChar, true);
  651. if (fragment != null)
  652.     this.fragment = escape(fragment.trim(), escpdFragChar, true);
  653. type = usesGenericSyntax(scheme) ? GENERIC : SEMI_GENERIC;
  654.     }
  655.     private static final boolean isIPV6Addr(String host)
  656.     {
  657. if (host.indexOf(':') < 0)
  658.     return false;
  659. for (int idx=0; idx<host.length(); idx++)
  660. {
  661.     char ch = host.charAt(idx);
  662.     if ((ch < '0'  ||  ch > '9')  &&  ch != ':')
  663. return false;
  664. }
  665. return true;
  666.     }
  667.     /**
  668.      * Constructs an opaque URI from the given parts.
  669.      *
  670.      * @param scheme the scheme (sometimes known as protocol)
  671.      * @param opaque the opaque part
  672.      * @exception ParseException if <var>scheme</var> is null
  673.      */
  674.     public URI(String scheme, String opaque)
  675.     throws ParseException
  676.     {
  677. if (scheme == null)
  678.     throw new ParseException("missing scheme");
  679. this.scheme = escape(scheme.trim().toLowerCase(), schemeChar, true);
  680. this.opaque = escape(opaque, opaqueChar, true);
  681. type = OPAQUE;
  682.     }
  683.     // Class Methods
  684.     /**
  685.      * @return true if the scheme should be parsed according to the
  686.      *         generic-URI syntax
  687.      */
  688.     public static boolean usesGenericSyntax(String scheme)
  689.     {
  690. return usesGenericSyntax.containsKey(scheme.trim().toLowerCase());
  691.     }
  692.     /**
  693.      * @return true if the scheme should be parsed according to a
  694.      *         semi-generic-URI syntax &lt;scheme&tgt;://&lt;hostport&gt;/&lt;opaque&gt;
  695.      */
  696.     public static boolean usesSemiGenericSyntax(String scheme)
  697.     {
  698. return usesSemiGenericSyntax.containsKey(scheme.trim().toLowerCase());
  699.     }
  700.     /**
  701.      * Return the default port used by a given protocol.
  702.      *
  703.      * @param protocol the protocol
  704.      * @return the port number, or 0 if unknown
  705.      */
  706.     public final static int defaultPort(String protocol)
  707.     {
  708. Integer port = (Integer) defaultPorts.get(protocol.trim().toLowerCase());
  709. return (port != null) ? port.intValue() : 0;
  710.     }
  711.     // Instance Methods
  712.     /**
  713.      * @return the scheme (often also referred to as protocol)
  714.      */
  715.     public String getScheme()
  716.     {
  717. return scheme;
  718.     }
  719.     /**
  720.      * @return the opaque part, or null if this URI is generic
  721.      */
  722.     public String getOpaque()
  723.     {
  724. return opaque;
  725.     }
  726.     /**
  727.      * @return the host
  728.      */
  729.     public String getHost()
  730.     {
  731. return host;
  732.     }
  733.     /**
  734.      * @return the port, or -1 if it's the default port, or 0 if unknown
  735.      */
  736.     public int getPort()
  737.     {
  738. return port;
  739.     }
  740.     /**
  741.      * @return the user info
  742.      */
  743.     public String getUserinfo()
  744.     {
  745. return userinfo;
  746.     }
  747.     /**
  748.      * @return the path
  749.      */
  750.     public String getPath()
  751.     {
  752. return path;
  753.     }
  754.     /**
  755.      * @return the query string
  756.      */
  757.     public String getQueryString()
  758.     {
  759. return query;
  760.     }
  761.     /**
  762.      * @return the path and query
  763.      */
  764.     public String getPathAndQuery()
  765.     {
  766. if (query == null)
  767.     return path;
  768. if (path == null)
  769.     return "?" + query;
  770. return path + "?" + query;
  771.     }
  772.     /**
  773.      * @return the fragment
  774.      */
  775.     public String getFragment()
  776.     {
  777. return fragment;
  778.     }
  779.     /**
  780.      * Does the scheme specific part of this URI use the generic-URI syntax?
  781.      *
  782.      * <P>In general URI are split into two categories: opaque-URI and
  783.      * generic-URI. The generic-URI syntax is the syntax most are familiar
  784.      * with from URLs such as ftp- and http-URLs, which is roughly:
  785.      * <PRE>
  786.      * generic-URI = scheme ":" [ "//" server ] [ "/" ] [ path_segments ] [ "?" query ]
  787.      * </PRE>
  788.      * (see RFC-2396 for exact syntax). Only URLs using the generic-URI syntax
  789.      * can be used to create and resolve relative URIs.
  790.      *
  791.      * <P>Whether a given scheme is parsed according to the generic-URI
  792.      * syntax or wether it is treated as opaque is determined by an internal
  793.      * table of URI schemes.
  794.      *
  795.      * @see <A HREF="http://www.ics.uci.edu/pub/ietf/uri/rfc2396.txt">rfc-2396</A>
  796.      */
  797.     public boolean isGenericURI()
  798.     {
  799. return (type == GENERIC);
  800.     }
  801.     /**
  802.      * Does the scheme specific part of this URI use the semi-generic-URI syntax?
  803.      *
  804.      * <P>Many schemes which don't follow the full generic syntax actually
  805.      * follow a reduced form where the path part is treated is opaque. This
  806.      * is used for example by ldap, smtp, pop, etc, and is roughly
  807.      * <PRE>
  808.      * generic-URI = scheme ":" [ "//" server ] [ "/" [ opaque_path ] ]
  809.      * </PRE>
  810.      * I.e. parsing is identical to the generic-syntax, except that the path
  811.      * part is not further parsed. URLs using the semi-generic-URI syntax can
  812.      * be used to create and resolve relative URIs with the restriction that
  813.      * all paths are treated as absolute.
  814.      *
  815.      * <P>Whether a given scheme is parsed according to the semi-generic-URI
  816.      * syntax is determined by an internal table of URI schemes.
  817.      *
  818.      * @see #isGenericURI()
  819.      */
  820.     public boolean isSemiGenericURI()
  821.     {
  822. return (type == SEMI_GENERIC);
  823.     }
  824.     /**
  825.      * Will try to create a java.net.URL object from this URI.
  826.      *
  827.      * @return the URL
  828.      * @exception MalformedURLException if no handler is available for the
  829.      *            scheme
  830.      */
  831.     public URL toURL()  throws MalformedURLException
  832.     {
  833. if (url != null)  return url;
  834. if (opaque != null)
  835.     return (url = new URL(scheme + ":" + opaque));
  836. String hostinfo;
  837. if (userinfo != null  &&  host != null)
  838.     hostinfo = userinfo + "@" + host;
  839. else if (userinfo != null)
  840.     hostinfo = userinfo + "@";
  841. else
  842.     hostinfo = host;
  843. StringBuffer file = new StringBuffer(100);
  844. assemblePath(file, true, true, false);
  845. url = new URL(scheme, hostinfo, port, file.toString());
  846. return url;
  847.     }
  848.     private final void assemblePath(StringBuffer buf, boolean printEmpty,
  849.     boolean incFragment, boolean unescape)
  850.     {
  851. if ((path == null  ||  path.length() == 0)  &&  printEmpty)
  852.     buf.append('/');
  853. if (path != null)
  854.     buf.append(unescape ? unescapeNoPE(path, resvdPathChar) : path);
  855. if (query != null)
  856. {
  857.     buf.append('?');
  858.     buf.append(unescape ? unescapeNoPE(query, resvdQueryChar) : query);
  859. }
  860. if (fragment != null  &&  incFragment)
  861. {
  862.     buf.append('#');
  863.     buf.append(unescape ? unescapeNoPE(fragment, null) : fragment);
  864. }
  865.     }
  866.     private final String stringify(boolean unescape)
  867.     {
  868. StringBuffer uri = new StringBuffer(100);
  869. if (scheme != null)
  870. {
  871.     uri.append(unescape ? unescapeNoPE(scheme, resvdSchemeChar) : scheme);
  872.     uri.append(':');
  873. }
  874. if (opaque != null) // it's an opaque-uri
  875. {
  876.     uri.append(unescape ? unescapeNoPE(opaque, null) : opaque);
  877.     return uri.toString();
  878. }
  879. if (userinfo != null  ||  host != null  ||  port != -1)
  880.     uri.append("//");
  881. if (userinfo != null)
  882. {
  883.     uri.append(unescape ? unescapeNoPE(userinfo, resvdUIChar) : userinfo);
  884.     uri.append('@');
  885. }
  886. if (host != null)
  887. {
  888.     if (host.indexOf(':') < 0)
  889. uri.append(unescape ? unescapeNoPE(host, resvdHostChar) : host);
  890.     else
  891. uri.append('[').append(host).append(']');
  892. }
  893. if (port != -1)
  894. {
  895.     uri.append(':');
  896.     uri.append(port);
  897. }
  898. assemblePath(uri, false, true, unescape);
  899. return uri.toString();
  900.     }
  901.     /**
  902.      * @return a string representation of this URI suitable for use in
  903.      *         links, headers, etc.
  904.      */
  905.     public String toExternalForm()
  906.     {
  907. return stringify(false);
  908.     }
  909.     /**
  910.      * Return the URI as string. This differs from toExternalForm() in that
  911.      * all elements are unescaped before assembly. This is <em>not suitable</em>
  912.      * for passing to other apps or in header fields and such, and is usually
  913.      * not what you want.
  914.      *
  915.      * @return the URI as a string
  916.      * @see #toExternalForm()
  917.      */
  918.     public String toString()
  919.     {
  920. return stringify(true);
  921.     }
  922.     /**
  923.      * @return true if <var>other</var> is either a URI or URL and it
  924.      *         matches the current URI
  925.      */
  926.     public boolean equals(Object other)
  927.     {
  928. if (other instanceof URI)
  929. {
  930.     URI o = (URI) other;
  931.     return (scheme.equals(o.scheme)  &&
  932.     (
  933.      type == OPAQUE  &&  areEqual(opaque, o.opaque)  ||
  934.      type == SEMI_GENERIC  &&
  935.       areEqual(userinfo, o.userinfo)  &&
  936.       areEqualIC(host, o.host)  &&
  937.       port == o.port  &&
  938.       areEqual(path, o.path)  ||
  939.      type == GENERIC  &&
  940.       areEqual(userinfo, o.userinfo)  &&
  941.       areEqualIC(host, o.host)  &&
  942.       port == o.port  &&
  943.       pathsEqual(path, o.path)  &&
  944.       areEqual(query, o.query)  &&
  945.       areEqual(fragment, o.fragment)
  946.     ));
  947. }
  948. if (other instanceof URL)
  949. {
  950.     URL o = (URL) other;
  951.     String h, f;
  952.     if (userinfo != null)
  953. h = userinfo + "@" + host;
  954.     else
  955. h = host;
  956.     f = getPathAndQuery();
  957.     return (scheme.equalsIgnoreCase(o.getProtocol())  &&
  958.     (type == OPAQUE  &&  opaque.equals(o.getFile())  ||
  959.      type == SEMI_GENERIC  &&
  960.        areEqualIC(h, o.getHost())  &&
  961.        (port == o.getPort()  ||
  962. o.getPort() == defaultPort(scheme))  &&
  963.        areEqual(f, o.getFile())  ||
  964.      type == GENERIC  &&
  965.        areEqualIC(h, o.getHost())  &&
  966.        (port == o.getPort()  ||
  967. o.getPort() == defaultPort(scheme))  &&
  968.        pathsEqual(f, o.getFile())  &&
  969.        areEqual(fragment, o.getRef())
  970.     )
  971.    );
  972. }
  973. return false;
  974.     }
  975.     private static final boolean areEqual(String s1, String s2)
  976.     {
  977. return (s1 == null  &&  s2 == null  ||
  978. s1 != null  &&  s2 != null  &&
  979.   (s1.equals(s2)  ||
  980.    unescapeNoPE(s1, null).equals(unescapeNoPE(s2, null)))
  981.        );
  982.     }
  983.     private static final boolean areEqualIC(String s1, String s2)
  984.     {
  985. return (s1 == null  &&  s2 == null  ||
  986. s1 != null  &&  s2 != null  &&
  987.   (s1.equalsIgnoreCase(s2)  ||
  988.    unescapeNoPE(s1, null).equalsIgnoreCase(unescapeNoPE(s2, null)))
  989.        );
  990.     }
  991.     private static final boolean pathsEqual(String p1, String p2)
  992.     {
  993. if (p1 == null  &&  p2 == null)
  994.     return true;
  995. if (p1 == null  ||  p2 == null)
  996.     return false;
  997. if (p1.equals(p2))
  998.     return true;
  999. // ok, so it wasn't that simple. Let's split into parts and compare
  1000. // unescaped.
  1001. int pos1 = 0, end1 = p1.length(), pos2 = 0, end2 = p2.length();
  1002. while (pos1 < end1  &&  pos2 < end2)
  1003. {
  1004.     int start1 = pos1, start2 = pos2;
  1005.     char ch;
  1006.     while (pos1 < end1  &&  (ch = p1.charAt(pos1)) != '/'  &&  ch != ';')
  1007. pos1++;
  1008.     while (pos2 < end2  &&  (ch = p2.charAt(pos2)) != '/'  &&  ch != ';')
  1009. pos2++;
  1010.     if (pos1 == end1  &&  pos2 < end2  ||
  1011. pos2 == end2  &&  pos1 < end1  ||
  1012. pos1 < end1  &&  pos2 < end2  &&  p1.charAt(pos1) != p2.charAt(pos2))
  1013. return false;
  1014.     if ((!p1.regionMatches(start1, p2, start2, pos1-start1)  ||  (pos1-start1) != (pos2-start2))  &&
  1015. !unescapeNoPE(p1.substring(start1, pos1), null).equals(unescapeNoPE(p2.substring(start2, pos2), null)))
  1016. return false;
  1017.     pos1++;
  1018.     pos2++;
  1019. }
  1020. return (pos1 == end1  &&  pos2 == end2);
  1021.     }
  1022.     private int hashCode = -1;
  1023.     /**
  1024.      * The hash code is calculated over scheme, host, path, and query.
  1025.      *
  1026.      * @return the hash code
  1027.      */
  1028.     public int hashCode()
  1029.     {
  1030. if (hashCode == -1)
  1031.     hashCode = (scheme != null ? unescapeNoPE(scheme, null).hashCode() : 0) + 
  1032.        (type == OPAQUE ?
  1033.   (opaque != null ? unescapeNoPE(opaque, null).hashCode() : 0) * 7
  1034. : (host != null ? unescapeNoPE(host, null).toLowerCase().hashCode() : 0) * 7 +
  1035.   (path != null ? unescapeNoPE(path, null).hashCode() : 0) * 13 +
  1036.   (query != null ? unescapeNoPE(query, null).hashCode() : 0) * 17);
  1037. return hashCode;
  1038.     }
  1039.     /**
  1040.      * Escape any character not in the given character class. Characters
  1041.      * greater 255 are always escaped according to ??? .
  1042.      *
  1043.      * @param elem         the string to escape
  1044.      * @param allowed_char the BitSet of all allowed characters
  1045.      * @param utf8         if true, will first UTF-8 encode unallowed characters
  1046.      * @return the string with all characters not in allowed_char
  1047.      *         escaped
  1048.      */
  1049.     public static String escape(String elem, BitSet allowed_char, boolean utf8)
  1050.     {
  1051. return new String(escape(elem.toCharArray(), allowed_char, utf8));
  1052.     }
  1053.     /**
  1054.      * Escape any character not in the given character class. Characters
  1055.      * greater 255 are always escaped according to ??? .
  1056.      *
  1057.      * @param elem         the array of characters to escape
  1058.      * @param allowed_char the BitSet of all allowed characters
  1059.      * @param utf8         if true, will first UTF-8 encode unallowed characters
  1060.      * @return the elem array with all characters not in allowed_char
  1061.      *         escaped
  1062.      */
  1063.     public static char[] escape(char[] elem, BitSet allowed_char, boolean utf8)
  1064.     {
  1065. int cnt=0;
  1066. for (int idx=0; idx<elem.length; idx++)
  1067. {
  1068.     if (!allowed_char.get(elem[idx]))
  1069.     {
  1070. cnt += 2;
  1071. if (utf8)
  1072. {
  1073.     if (elem[idx] >= 0x0080)
  1074. cnt += 3;
  1075.     if (elem[idx] >= 0x00800)
  1076. cnt += 3;
  1077.     if ((elem[idx] & 0xFC00) == 0xD800  &&  idx+1 < elem.length  &&
  1078. (elem[idx+1] & 0xFC00) == 0xDC00)
  1079.       cnt -= 6;
  1080. }
  1081.     }
  1082. }
  1083. if (cnt == 0)  return elem;
  1084. char[] tmp = new char[elem.length + cnt];
  1085. for (int idx=0, pos=0; idx<elem.length; idx++)
  1086. {
  1087.     char c = elem[idx];
  1088.     if (allowed_char.get(c))
  1089. tmp[pos++] = c;
  1090.     else if (utf8)
  1091.     {
  1092. /* We're UTF-8 encoding the chars first, as recommended in
  1093.  * the HTML 4.0 specification:
  1094.  * http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.2.1
  1095.  * Note that this doesn't change things for ASCII chars
  1096.  */
  1097. if (c <= 0x007F)
  1098. {
  1099.     pos = enc(tmp, pos, c);
  1100. }
  1101. else if (c <= 0x07FF)
  1102. {
  1103.     pos = enc(tmp, pos, 0xC0 | ((c >>  6) & 0x1F));
  1104.     pos = enc(tmp, pos, 0x80 | ((c >>  0) & 0x3F));
  1105. }
  1106. else if (!((c & 0xFC00) == 0xD800  &&  idx+1 < elem.length  &&
  1107.      (elem[idx+1] & 0xFC00) == 0xDC00))
  1108. {
  1109.     pos = enc(tmp, pos, 0xE0 | ((c >> 12) & 0x0F));
  1110.     pos = enc(tmp, pos, 0x80 | ((c >>  6) & 0x3F));
  1111.     pos = enc(tmp, pos, 0x80 | ((c >>  0) & 0x3F));
  1112. }
  1113. else
  1114. {
  1115.     int ch = ((c & 0x03FF) << 10) | (elem[++idx] & 0x03FF);
  1116.     ch += 0x10000;
  1117.     pos = enc(tmp, pos, 0xF0 | ((ch >> 18) & 0x07));
  1118.     pos = enc(tmp, pos, 0x80 | ((ch >> 12) & 0x3F));
  1119.     pos = enc(tmp, pos, 0x80 | ((ch >>  6) & 0x3F));
  1120.     pos = enc(tmp, pos, 0x80 | ((ch >>  0) & 0x3F));
  1121. }
  1122.     }
  1123.     else
  1124. pos = enc(tmp, pos, c);
  1125. }
  1126. return tmp;
  1127.     }
  1128.     private static final char[] hex =
  1129.     {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
  1130.     private static final int enc(char[] out, int pos, int c)
  1131.     {
  1132. out[pos++] = '%';
  1133. out[pos++] = hex[(c >> 4) & 0xf];
  1134. out[pos++] = hex[c & 0xf];
  1135. return pos;
  1136.     }
  1137.     /**
  1138.      * Unescape escaped characters (i.e. %xx) except reserved ones.
  1139.      *
  1140.      * @param str      the string to unescape
  1141.      * @param reserved the characters which may not be unescaped, or null
  1142.      * @return the unescaped string
  1143.      * @exception ParseException if the two digits following a `%' are
  1144.      *            not a valid hex number
  1145.      */
  1146.     public static final String unescape(String str, BitSet reserved)
  1147.     throws ParseException
  1148.     {
  1149. if (str == null  ||  str.indexOf('%') == -1)
  1150.     return str;   // an optimization
  1151. char[] buf = str.toCharArray();
  1152. char[] res = new char[buf.length];
  1153. char[] utf = new char[4];
  1154. int utf_idx = 0, utf_len = -1;
  1155. int didx = 0;
  1156. for (int sidx=0; sidx<buf.length; sidx++)
  1157. {
  1158.     if (buf[sidx] == '%')
  1159.     {
  1160. int ch;
  1161.                 try
  1162.                 {
  1163.     if (sidx + 3 > buf.length)
  1164. throw new NumberFormatException();
  1165.     ch = Integer.parseInt(str.substring(sidx+1,sidx+3), 16);
  1166.     if (ch < 0)
  1167. throw new NumberFormatException();
  1168.     sidx += 2;
  1169.                 }
  1170.                 catch (NumberFormatException e)
  1171.                 {
  1172.     /* Hmm, people not reading specs again, so we just
  1173.      * ignore it...
  1174.                     throw new ParseException(str.substring(sidx,sidx+3) +
  1175.                                             " is an invalid code");
  1176.     */
  1177.     ch = buf[sidx];
  1178.                 }
  1179. // check if we're working on a utf-char
  1180. if (utf_len > 0)
  1181. {
  1182.     if ((ch & 0xC0) != 0x80) // oops, we misinterpreted
  1183.     {
  1184. didx = copyBuf(utf, utf_idx, ch, res, didx, reserved, false);
  1185. utf_len = -1;
  1186.     }
  1187.     else if (utf_idx == utf_len - 1) // end-of-char
  1188.     {
  1189. if ((utf[0] & 0xE0) == 0xC0)
  1190.     ch = (utf[0] & 0x1F) <<  6 |
  1191.  (ch & 0x3F);
  1192. else if ((utf[0] & 0xF0) == 0xE0)
  1193.     ch = (utf[0] & 0x0F) << 12 |
  1194.  (utf[1] & 0x3F) <<  6 |
  1195.  (ch & 0x3F);
  1196. else
  1197.     ch = (utf[0] & 0x07) << 18 |
  1198.  (utf[1] & 0x3F) << 12 |
  1199.  (utf[2] & 0x3F) <<  6 |
  1200.  (ch & 0x3F);
  1201. if (reserved != null  &&  reserved.get(ch))
  1202.     didx = copyBuf(utf, utf_idx, ch, res, didx, null, true);
  1203. else if (utf_len < 4)
  1204.     res[didx++] = (char) ch;
  1205. else
  1206. {
  1207.     ch -= 0x10000;
  1208.     res[didx++] = (char) ((ch >> 10)    | 0xD800);
  1209.     res[didx++] = (char) ((ch & 0x03FF) | 0xDC00);
  1210. }
  1211. utf_len = -1;
  1212.     }
  1213.     else // continue
  1214. utf[utf_idx++] = (char) ch;
  1215. }
  1216. // check if this is the start of a utf-char
  1217. else if ((ch & 0xE0) == 0xC0  ||  (ch & 0xF0) == 0xE0  ||
  1218.  (ch & 0xF8) == 0xF0)
  1219. {
  1220.     if ((ch & 0xE0) == 0xC0)
  1221. utf_len = 2;
  1222.     else if ((ch & 0xF0) == 0xE0)
  1223. utf_len = 3;
  1224.     else
  1225. utf_len = 4;
  1226.     utf[0] = (char) ch;
  1227.     utf_idx = 1;
  1228. }
  1229. // leave reserved alone
  1230. else if (reserved != null  &&  reserved.get(ch))
  1231. {
  1232.     res[didx++] = buf[sidx];
  1233.     sidx -= 2;
  1234. }
  1235. // just use the decoded version
  1236. else
  1237.     res[didx++] = (char) ch;
  1238.     }
  1239.     else if (utf_len > 0) // oops, we misinterpreted
  1240.     {
  1241. didx = copyBuf(utf, utf_idx, buf[sidx], res, didx, reserved, false);
  1242. utf_len = -1;
  1243.     }
  1244.     else
  1245. res[didx++] = buf[sidx];
  1246. }
  1247. if (utf_len > 0) // oops, we misinterpreted
  1248.     didx = copyBuf(utf, utf_idx, -1, res, didx, reserved, false);
  1249. return new String(res, 0, didx);
  1250.     }
  1251.     private static final int copyBuf(char[] utf, int utf_idx, int ch,
  1252.      char[] res, int didx, BitSet reserved,
  1253.      boolean escapeAll)
  1254.     {
  1255. if (ch >= 0)
  1256.     utf[utf_idx++] = (char) ch;
  1257. for (int idx=0; idx<utf_idx; idx++)
  1258. {
  1259.     if (reserved != null  &&  reserved.get(utf[idx])  ||  escapeAll)
  1260. didx = enc(res, didx, utf[idx]);
  1261.     else
  1262. res[didx++] = utf[idx];
  1263. }
  1264. return didx;
  1265.     }
  1266.     /**
  1267.      * Unescape escaped characters (i.e. %xx). If a ParseException would
  1268.      * be thrown then just return the original string.
  1269.      *
  1270.      * @param str      the string to unescape
  1271.      * @param reserved the characters which may not be unescaped, or null
  1272.      * @return the unescaped string, or the original string if unescaping
  1273.      *         would throw a ParseException
  1274.      * @see #unescape(java.lang.String, java.util.BitSet)
  1275.      */
  1276.     private static final String unescapeNoPE(String str, BitSet reserved)
  1277.     {
  1278. try
  1279.     { return unescape(str, reserved); }
  1280. catch (ParseException pe)
  1281.     { return str; }
  1282.     }
  1283.     /**
  1284.      * Run test set.
  1285.      *
  1286.      * @exception Exception if any test fails
  1287.      */
  1288.     public static void main(String args[])  throws Exception
  1289.     {
  1290. System.err.println();
  1291. System.err.println("*** URI Tests ...");
  1292. /* Relative URI test set, taken from Section C of rfc-2396 and
  1293.  * Roy's test1. All Roy's URI parser tests can be found at
  1294.  * http://www.ics.uci.edu/~fielding/url/
  1295.  * The tests have been augmented by a few for the IPv6 syntax
  1296.  */
  1297. URI base = new URI("http://a/b/c/d;p?q");
  1298. // normal examples
  1299. testParser(base, "g:h",        "g:h");
  1300. testParser(base, "g",          "http://a/b/c/g");
  1301. testParser(base, "./g",        "http://a/b/c/g");
  1302. testParser(base, "g/",         "http://a/b/c/g/");
  1303. testParser(base, "/g",         "http://a/g");
  1304. testParser(base, "//g",        "http://g");
  1305. testParser(base, "//[23:54]",  "http://[23:54]");
  1306. testParser(base, "?y",         "http://a/b/c/?y");
  1307. testParser(base, "g?y",        "http://a/b/c/g?y");
  1308. testParser(base, "#s",         "http://a/b/c/d;p?q#s");
  1309. testParser(base, "g#s",        "http://a/b/c/g#s");
  1310. testParser(base, "g?y#s",      "http://a/b/c/g?y#s");
  1311. testParser(base, ";x",         "http://a/b/c/;x");
  1312. testParser(base, "g;x",        "http://a/b/c/g;x");
  1313. testParser(base, "g;x?y#s",    "http://a/b/c/g;x?y#s");
  1314. testParser(base, ".",          "http://a/b/c/");
  1315. testParser(base, "./",         "http://a/b/c/");
  1316. testParser(base, "..",         "http://a/b/");
  1317. testParser(base, "../",        "http://a/b/");
  1318. testParser(base, "../g",       "http://a/b/g");
  1319. testParser(base, "../..",      "http://a/");
  1320. testParser(base, "../../",     "http://a/");
  1321. testParser(base, "../../g",    "http://a/g");
  1322. // abnormal examples
  1323. testParser(base, "",              "http://a/b/c/d;p?q");
  1324. testParser(base, "/./g",          "http://a/./g");
  1325. testParser(base, "/../g",         "http://a/../g");
  1326. testParser(base, "../../../g",    "http://a/../g");
  1327. testParser(base, "../../../../g", "http://a/../../g");
  1328. testParser(base, "g.",            "http://a/b/c/g.");
  1329. testParser(base, ".g",            "http://a/b/c/.g");
  1330. testParser(base, "g..",           "http://a/b/c/g..");
  1331. testParser(base, "..g",           "http://a/b/c/..g");
  1332. testParser(base, "./../g",        "http://a/b/g");
  1333. testParser(base, "./g/.",         "http://a/b/c/g/");
  1334. testParser(base, "g/./h",         "http://a/b/c/g/h");
  1335. testParser(base, "g/../h",        "http://a/b/c/h");
  1336. testParser(base, "g;x=1/./y",     "http://a/b/c/g;x=1/y");
  1337. testParser(base, "g;x=1/../y",    "http://a/b/c/y");
  1338. testParser(base, "g?y/./x",       "http://a/b/c/g?y/./x");
  1339. testParser(base, "g?y/../x",      "http://a/b/c/g?y/../x");
  1340. testParser(base, "g#s/./x",       "http://a/b/c/g#s/./x");
  1341. testParser(base, "g#s/../x",      "http://a/b/c/g#s/../x");
  1342. if (ENABLE_BACKWARDS_COMPATIBILITY)
  1343.     testParser(base, "http:g",        "http://a/b/c/g");
  1344. else
  1345.     testParser(base, "http:g",        "http:g");
  1346. if (ENABLE_BACKWARDS_COMPATIBILITY)
  1347.     testParser(base, "http:",         "http://a/b/c/d;p?q");
  1348. else
  1349.     testParser(base, "http:",         "http:");
  1350. testParser(base, "./g:h",         "http://a/b/c/g:h");
  1351. /* Roy's test2
  1352.  */
  1353. base = new URI("http://a/b/c/d;p?q=1/2");
  1354. testParser(base, "g",        "http://a/b/c/g");
  1355. testParser(base, "./g",      "http://a/b/c/g");
  1356. testParser(base, "g/",       "http://a/b/c/g/");
  1357. testParser(base, "/g",       "http://a/g");
  1358. testParser(base, "//g",      "http://g");
  1359. testParser(base, "//[23:54]","http://[23:54]");
  1360. testParser(base, "?y",       "http://a/b/c/?y");
  1361. testParser(base, "g?y",      "http://a/b/c/g?y");
  1362. testParser(base, "g?y/./x",  "http://a/b/c/g?y/./x");
  1363. testParser(base, "g?y/../x", "http://a/b/c/g?y/../x");
  1364. testParser(base, "g#s",      "http://a/b/c/g#s");
  1365. testParser(base, "g#s/./x",  "http://a/b/c/g#s/./x");
  1366. testParser(base, "g#s/../x", "http://a/b/c/g#s/../x");
  1367. testParser(base, "./",       "http://a/b/c/");
  1368. testParser(base, "../",      "http://a/b/");
  1369. testParser(base, "../g",     "http://a/b/g");
  1370. testParser(base, "../../",   "http://a/");
  1371. testParser(base, "../../g",  "http://a/g");
  1372. /* Roy's test3
  1373.  */
  1374. base = new URI("http://a/b/c/d;p=1/2?q");
  1375. testParser(base, "g",          "http://a/b/c/d;p=1/g");
  1376. testParser(base, "./g",        "http://a/b/c/d;p=1/g");
  1377. testParser(base, "g/",         "http://a/b/c/d;p=1/g/");
  1378. testParser(base, "g?y",        "http://a/b/c/d;p=1/g?y");
  1379. testParser(base, ";x",         "http://a/b/c/d;p=1/;x");
  1380. testParser(base, "g;x",        "http://a/b/c/d;p=1/g;x");
  1381. testParser(base, "g;x=1/./y",  "http://a/b/c/d;p=1/g;x=1/y");
  1382. testParser(base, "g;x=1/../y", "http://a/b/c/d;p=1/y");
  1383. testParser(base, "./",         "http://a/b/c/d;p=1/");
  1384. testParser(base, "../",        "http://a/b/c/");
  1385. testParser(base, "../g",       "http://a/b/c/g");
  1386. testParser(base, "../../",     "http://a/b/");
  1387. testParser(base, "../../g",    "http://a/b/g");
  1388. /* Roy's test4
  1389.  */
  1390. base = new URI("fred:///s//a/b/c");
  1391. testParser(base, "g:h",           "g:h");
  1392. /* we have to skip these, as usesGeneraicSyntax("fred") returns false
  1393.  * and we therefore don't parse relative URI's here. But test5 is
  1394.  * the same except that the http scheme is used.
  1395. testParser(base, "g",             "fred:///s//a/b/g");
  1396. testParser(base, "./g",           "fred:///s//a/b/g");
  1397. testParser(base, "g/",            "fred:///s//a/b/g/");
  1398. testParser(base, "/g",            "fred:///g");
  1399. testParser(base, "//g",           "fred://g");
  1400. testParser(base, "//g/x",         "fred://g/x");
  1401. testParser(base, "///g",          "fred:///g");
  1402. testParser(base, "./",            "fred:///s//a/b/");
  1403. testParser(base, "../",           "fred:///s//a/");
  1404. testParser(base, "../g",          "fred:///s//a/g");
  1405. testParser(base, "../../",        "fred:///s//");
  1406. testParser(base, "../../g",       "fred:///s//g");
  1407. testParser(base, "../../../g",    "fred:///s/g");
  1408. testParser(base, "../../../../g", "fred:///g");
  1409.  */
  1410. testPE(base, "g");
  1411. /* Roy's test5
  1412.  */
  1413. base = new URI("http:///s//a/b/c");
  1414. testParser(base, "g:h",           "g:h");
  1415. testParser(base, "g",             "http:///s//a/b/g");
  1416. testParser(base, "./g",           "http:///s//a/b/g");
  1417. testParser(base, "g/",            "http:///s//a/b/g/");
  1418. testParser(base, "/g",            "http:///g");
  1419. testParser(base, "//g",           "http://g");
  1420. testParser(base, "//[23:54]",     "http://[23:54]");
  1421. testParser(base, "//g/x",         "http://g/x");
  1422. testParser(base, "///g",          "http:///g");
  1423. testParser(base, "./",            "http:///s//a/b/");
  1424. testParser(base, "../",           "http:///s//a/");
  1425. testParser(base, "../g",          "http:///s//a/g");
  1426. testParser(base, "../../",        "http:///s//");
  1427. testParser(base, "../../g",       "http:///s//g");
  1428. testParser(base, "../../../g",    "http:///s/g");
  1429. testParser(base, "../../../../g", "http:///g");
  1430. /* Some additional parser tests
  1431.  */
  1432. base = new URI("http://s");
  1433. testParser(base, "ftp:h",         "ftp:h");
  1434. testParser(base, "ftp://h",       "ftp://h");
  1435. testParser(base, "//g",           "http://g");
  1436. testParser(base, "//g?h",         "http://g?h");
  1437. testParser(base, "g",             "http://s/g");
  1438. testParser(base, "./g",           "http://s/g");
  1439. testParser(base, "?g",            "http://s/?g");
  1440. testParser(base, "#g",            "http://s#g");
  1441. base = new URI("http:");
  1442. testParser(base, "ftp:h",         "ftp:h");
  1443. testParser(base, "ftp://h",       "ftp://h");
  1444. testParser(base, "//g",           "http://g");
  1445. testParser(base, "g",             "http:/g");
  1446. testParser(base, "?g",            "http:/?g");
  1447. testParser(base, "#g",            "http:#g");
  1448. base = new URI("http://s/t");
  1449. testParser(base, "ftp:/h",        "ftp:/h");
  1450. if (ENABLE_BACKWARDS_COMPATIBILITY)
  1451.     testParser(base, "http:/h",       "http://s/h");
  1452. else
  1453.     testParser(base, "http:/h",       "http:/h");
  1454. base = new URI("http://s/g?h/j");
  1455. testParser(base, "k",             "http://s/k");
  1456. testParser(base, "k?l",           "http://s/k?l");
  1457. /* Parser tests for semi-generic syntax
  1458.  */
  1459. base = new URI("ldap:");
  1460. testParser(base, "ldap:",         "ldap:");
  1461. testParser(base, "ldap://a",      "ldap://a");
  1462. testParser(base, "ldap://a/b",    "ldap://a/b");
  1463. testParser(base, "ldap:/b",       "ldap:/b");
  1464. testParser(base, "ftp:h",         "ftp:h");
  1465. testParser(base, "ftp://h",       "ftp://h");
  1466. testParser(base, "//g",           "ldap://g");
  1467. testParser(base, "//g?h",         "ldap://g/?h");
  1468. testParser(base, "g",             "ldap:/g");
  1469. testParser(base, "./g",           "ldap:/./g");
  1470. testParser(base, "?g",            "ldap:/?g");
  1471. testParser(base, "#g",            "ldap:/%23g");
  1472. base = new URI("ldap://s");
  1473. if (ENABLE_BACKWARDS_COMPATIBILITY)
  1474.     testParser(base, "ldap:",         "ldap://s");
  1475. else
  1476.     testParser(base, "ldap:",         "ldap:");
  1477. testParser(base, "ldap://a",      "ldap://a");
  1478. testParser(base, "ldap://a/b",    "ldap://a/b");
  1479. if (ENABLE_BACKWARDS_COMPATIBILITY)
  1480.     testParser(base, "ldap:/b",       "ldap://s/b");
  1481. else
  1482.     testParser(base, "ldap:/b",       "ldap:/b");
  1483. testParser(base, "ftp:h",         "ftp:h");
  1484. testParser(base, "ftp://h",       "ftp://h");
  1485. testParser(base, "//g",           "ldap://g");
  1486. testParser(base, "//g?h",         "ldap://g/?h");
  1487. testParser(base, "g",             "ldap://s/g");
  1488. testParser(base, "./g",           "ldap://s/./g");
  1489. testParser(base, "?g",            "ldap://s/?g");
  1490. testParser(base, "#g",            "ldap://s/%23g");
  1491. base = new URI("ldap://s/t");
  1492. testParser(base, "ftp:/h",        "ftp:/h");
  1493. if (ENABLE_BACKWARDS_COMPATIBILITY)
  1494.     testParser(base, "ldap:/h",       "ldap://s/h");
  1495. else
  1496.     testParser(base, "ldap:/h",       "ldap:/h");
  1497. if (ENABLE_BACKWARDS_COMPATIBILITY)
  1498.     testParser(base, "ldap:",         "ldap://s");
  1499. else
  1500.     testParser(base, "ldap:",         "ldap:");
  1501. testParser(base, "ldap://a",      "ldap://a");
  1502. testParser(base, "ldap://a/b",    "ldap://a/b");
  1503. testParser(base, "ftp:h",         "ftp:h");
  1504. testParser(base, "ftp://h",       "ftp://h");
  1505. testParser(base, "//g",           "ldap://g");
  1506. testParser(base, "//g?h",         "ldap://g/?h");
  1507. testParser(base, "g",             "ldap://s/g");
  1508. testParser(base, "./g",           "ldap://s/./g");
  1509. testParser(base, "?g",            "ldap://s/?g");
  1510. testParser(base, "#g",            "ldap://s/%23g");
  1511. /* equality tests */
  1512. // protocol
  1513. testNotEqual("http://a/", "nntp://a/");
  1514. testNotEqual("http://a/", "https://a/");
  1515. testNotEqual("http://a/", "shttp://a/");
  1516. testEqual("http://a/", "Http://a/");
  1517. testEqual("http://a/", "hTTP://a/");
  1518. testEqual("url:http://a/", "hTTP://a/");
  1519. testEqual("urI:http://a/", "hTTP://a/");
  1520. // host
  1521. testEqual("http://a/", "Http://A/");
  1522. testEqual("http://a.b.c/", "Http://A.b.C/");
  1523. testEqual("http:///", "Http:///");
  1524. testEqual("http://[]/", "Http:///");
  1525. testNotEqual("http:///", "Http://a/");
  1526. testNotEqual("http://[]/", "Http://a/");
  1527. testPE(null, "ftp://[23::43:1/");
  1528. testPE(null, "ftp://[/");
  1529. // port
  1530. testEqual("http://a.b.c/", "Http://A.b.C:80/");
  1531. testEqual("http://a.b.c:/", "Http://A.b.C:80/");
  1532. testEqual("http://[23::45:::5:]/", "Http://[23::45:::5:]:80/");
  1533. testEqual("http://[23::45:::5:]:/", "Http://[23::45:::5:]:80/");
  1534. testEqual("nntp://a", "nntp://a:119");
  1535. testEqual("nntp://a:", "nntp://a:119");
  1536. testEqual("nntp://a/", "nntp://a:119/");
  1537. testNotEqual("nntp://a", "nntp://a:118");
  1538. testNotEqual("nntp://a", "nntp://a:0");
  1539. testNotEqual("nntp://a:", "nntp://a:0");
  1540. testEqual("telnet://:23/", "telnet:///");
  1541. testPE(null, "ftp://:a/");
  1542. testPE(null, "ftp://:-1/");
  1543. testPE(null, "ftp://::1/");
  1544. // userinfo
  1545. testNotEqual("ftp://me@a", "ftp://a");
  1546. testNotEqual("ftp://me@a", "ftp://Me@a");
  1547. testEqual("ftp://Me@a", "ftp://Me@a");
  1548. testEqual("ftp://Me:My@a:21", "ftp://Me:My@a");
  1549. testEqual("ftp://Me:My@a:", "ftp://Me:My@a");
  1550. testNotEqual("ftp://Me:My@a:21", "ftp://Me:my@a");
  1551. testNotEqual("ftp://Me:My@a:", "ftp://Me:my@a");
  1552. // path
  1553. testEqual("ftp://a/b%2b/", "ftp://a/b+/");
  1554. testEqual("ftp://a/b%2b/", "ftp://a/b+/");
  1555. testEqual("ftp://a/b%5E/", "ftp://a/b^/");
  1556. testEqual("ftp://a/b%4C/", "ftp://a/bL/");
  1557. testNotEqual("ftp://a/b/", "ftp://a//b/");
  1558. testNotEqual("ftp://a/b/", "ftp://a/b//");
  1559. testNotEqual("ftp://a/b%4C/", "ftp://a/bl/");
  1560. testNotEqual("ftp://a/b%3f/", "ftp://a/b?/");
  1561. testNotEqual("ftp://a/b%2f/", "ftp://a/b//");
  1562. testNotEqual("ftp://a/b%2fc/", "ftp://a/b/c/");
  1563. testNotEqual("ftp://a/bc/", "ftp://a/b//");
  1564. testNotEqual("ftp://a/bc/", "ftp://a/b/");
  1565. testNotEqual("ftp://a/bc//", "ftp://a/b/");
  1566. testNotEqual("ftp://a/b/", "ftp://a/bc//");
  1567. testNotEqual("ftp://a/b/", "ftp://a/bc/");
  1568. testNotEqual("ftp://a/b//", "ftp://a/bc/");
  1569. testNotEqual("ftp://a/b;fc/", "ftp://a/bf;c/");
  1570. testNotEqual("ftp://a/b%3bfc/", "ftp://a/b;fc/");
  1571. testEqual("ftp://a/b;/;/", "ftp://a/b;/;/");
  1572. testNotEqual("ftp://a/b;/", "ftp://a/b//");
  1573. testNotEqual("ftp://a/b//", "ftp://a/b;/");
  1574. testNotEqual("ftp://a/b/;", "ftp://a/b//");
  1575. testNotEqual("ftp://a/b//", "ftp://a/b/;");
  1576. testNotEqual("ftp://a/b;/", "ftp://a/b;//");
  1577. testNotEqual("ftp://a/b;//", "ftp://a/b;/");
  1578. // escaping/unescaping
  1579. testEscape("hellou1212there", "hello%E1%88%92there");
  1580. testEscape("hellou0232there", "hello%C8%B2there");
  1581. testEscape("hellouDA42uDD42there", "hello%F2%A0%A5%82there");
  1582. testEscape("hellouDA42", "hello%ED%A9%82");
  1583. testEscape("hellouDA42there", "hello%ED%A9%82there");
  1584. testUnescape("hello%F2%A0%A5%82there", "hellouDA42uDD42there");
  1585. testUnescape("hello%F2%A0%A5there", "hellou00F2u00A0u00A5there");
  1586. testUnescape("hello%F2%A0there", "hellou00F2u00A0there");
  1587. testUnescape("hello%F2there", "hellou00F2there");
  1588. testUnescape("hello%F2%A0%A5%82", "hellouDA42uDD42");
  1589. testUnescape("hello%F2%A0%A5", "hellou00F2u00A0u00A5");
  1590. testUnescape("hello%F2%A0", "hellou00F2u00A0");
  1591. testUnescape("hello%F2", "hellou00F2");
  1592. testUnescape("hello%E1%88%92there", "hellou1212there");
  1593. testUnescape("hello%E1%88there", "hellou00E1u0088there");
  1594. testUnescape("hello%E1there", "hellou00E1there");
  1595. testUnescape("hello%E1%71there", "hellou00E1qthere");
  1596. testUnescape("hello%E1%88", "hellou00E1u0088");
  1597. testUnescape("hello%E1%71", "hellou00E1q");
  1598. testUnescape("hello%E1", "hellou00E1");
  1599. testUnescape("hello%C8%B2there", "hellou0232there");
  1600. testUnescape("hello%C8there", "hellou00C8there");
  1601. testUnescape("hello%C8%71there", "hellou00C8qthere");
  1602. testUnescape("hello%C8%71", "hellou00C8q");
  1603. testUnescape("hello%C8", "hellou00C8");
  1604. testUnescape("%71there", "qthere");
  1605. testUnescape("%B1there", "u00B1there");
  1606. System.err.println("*** Tests finished successfuly");
  1607.     }
  1608.     private static final String nl = System.getProperty("line.separator");
  1609.     private static void testParser(URI base, String relURI, String result)
  1610.     throws Exception
  1611.     {
  1612. if (!(new URI(base, relURI).toExternalForm().equals(result)))
  1613. {
  1614.     throw new Exception("Test failed: " + nl +
  1615. "  base-URI = <" + base + ">" + nl +
  1616. "  rel-URI  = <" + relURI + ">" + nl+
  1617. "  expected   <" + result + ">" + nl+
  1618. "  but got    <" + new URI(base, relURI) + ">");
  1619. }
  1620.     }
  1621.     private static void testEqual(String one, String two)  throws Exception
  1622.     {
  1623. URI u1 = new URI(one);
  1624. URI u2 = new URI(two);
  1625. if (!u1.equals(u2))
  1626. {
  1627.     throw new Exception("Test failed: " + nl +
  1628. "  <" + one + "> != <" + two + ">");
  1629. }
  1630. if (u1.hashCode() != u2.hashCode())
  1631. {
  1632.     throw new Exception("Test failed: " + nl +
  1633. "  hashCode <" + one + "> != hashCode <" + two + ">");
  1634. }
  1635.     }
  1636.     private static void testNotEqual(String one, String two)  throws Exception
  1637.     {
  1638. URI u1 = new URI(one);
  1639. URI u2 = new URI(two);
  1640. if (u1.equals(u2))
  1641. {
  1642.     throw new Exception("Test failed: " + nl +
  1643. "  <" + one + "> == <" + two + ">");
  1644. }
  1645.     }
  1646.     private static void testPE(URI base, String uri)  throws Exception
  1647.     {
  1648. boolean got_pe = false;
  1649. try
  1650.     { new URI(base, uri); }
  1651. catch (ParseException pe)
  1652.     { got_pe = true; }
  1653. if (!got_pe)
  1654. {
  1655.     throw new Exception("Test failed: " + nl +
  1656. "  <" + uri + "> should be invalid");
  1657. }
  1658.     }
  1659.     private static void testEscape(String raw, String escaped)  throws Exception
  1660.     {
  1661. String test = new String(escape(raw.toCharArray(), uricChar, true));
  1662. if (!test.equals(escaped))
  1663.     throw new Exception("Test failed: " + nl +
  1664. "  raw-string: " + raw + nl +
  1665. "  escaped:    " + test + nl +
  1666. "  expected:   " + escaped);
  1667.     }
  1668.     private static void testUnescape(String escaped, String raw)
  1669. throws Exception
  1670.     {
  1671. if (!unescape(escaped, null).equals(raw))
  1672.     throw new Exception("Test failed: " + nl +
  1673. "  escaped-string: " + escaped + nl +
  1674. "  unescaped:      " + unescape(escaped, null) + nl +
  1675. "  expected:       " + raw);
  1676.     }
  1677. }