/[classpath]/classpath/java/net/URI.java
ViewVC logotype

Diff of /classpath/java/net/URI.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.13 by tromey, Mon May 16 20:28:39 2005 UTC revision 1.14 by gnu_andrew, Thu May 19 21:00:59 2005 UTC
# Line 48  import java.util.regex.Pattern; Line 48  import java.util.regex.Pattern;
48  /**  /**
49   * <p>   * <p>
50   * A URI instance represents that defined by   * A URI instance represents that defined by
51   * <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC2396</a>,   * <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC3986</a>,
52   * with some deviations.   * with some deviations.
53   * </p>   * </p>
54   * <p>   * <p>
# Line 98  import java.util.regex.Pattern; Line 98  import java.util.regex.Pattern;
98   * this that the path sub-part may also not be undefined, so as to ensure   * this that the path sub-part may also not be undefined, so as to ensure
99   * the former.   * the former.
100   * </p>   * </p>
101   *   * <h2>Character Escaping and Quoting</h2>
102     * <p>
103     * The characters that can be used within a valid URI are restricted.
104     * There are two main classes of characters which can't be used as is
105     * within the URI:
106     * </p>
107     * <ol>
108     * <li><strong>Characters outside the US-ASCII character set</strong>.
109     * These have to be <strong>escaped</strong> in order to create
110     * an RFC-compliant URI; this means replacing the character with the
111     * appropriate hexadecimal value, preceded by a `%'.</li>
112     * <li><strong>Illegal characters</strong> (e.g. space characters,
113     * control characters) are quoted, which results in them being encoded
114     * in the same way as non-US-ASCII characters.</li>
115     * </ol>
116     * <p>
117     * The set of valid characters differs depending on the section of the URI:
118     * </p>
119     * <ul>
120     * <li><strong>Scheme</strong>: Must be an alphanumeric, `-', `.' or '+'.</li>
121     * <li><strong>Authority</strong>:Composed of the username, host, port, `@'
122     * and `:'.</li>
123     * <li><strong>Username</strong>: Allows unreserved or percent-encoded
124     * characters, sub-delimiters and `:'.</li>
125     * <li><strong>Host</strong>: Allows unreserved or percent-encoded
126     * characters, sub-delimiters and square brackets (`[' and `]') for IPv6
127     * addresses.</li>
128     * <li><strong>Port</strong>: Digits only.</li>
129     * <li><strong>Path</strong>: Allows the path characters and `/'.
130     * <li><strong>Query</strong>: Allows the path characters, `?' and '/'.
131     * <li><strong>Fragment</strong>: Allows the path characters, `?' and '/'.
132     * </ul>
133     * <p>
134     * These definitions reference the following sets of characters:
135     * </p>
136     * <ul>
137     * <li><strong>Unreserved characters</strong>: The alphanumerics plus
138     * `-', `.', `_', and `~'.</li>
139     * <li><strong>Sub-delimiters</strong>: `!', `$', `&', `(', `)', `*',
140     * `+', `,', `;', `=' and the single-quote itself.</li>
141     * <li><strong>Path characters</strong>: Unreserved and percent-encoded
142     * characters and the sub-delimiters along with `@' and `:'.</li>
143     * </ul>
144     * <p>
145     * The constructors and accessor methods allow the use and retrieval of
146     * URI components which contain non-US-ASCII characters directly.
147     * They are only escaped when the <code>toASCIIString()</code> method
148     * is used.  In contrast, illegal characters are always quoted, with the
149     * exception of the return values of the non-raw accessors.
150     * </p>
151     *
152   * @author Ito Kazumitsu (ito.kazumitsu@hitachi-cable.co.jp)   * @author Ito Kazumitsu (ito.kazumitsu@hitachi-cable.co.jp)
153   * @author Dalibor Topic (robilad@kaffe.org)   * @author Dalibor Topic (robilad@kaffe.org)
154   * @author Michael Koch (konqueror@gmx.de)   * @author Michael Koch (konqueror@gmx.de)
# Line 108  import java.util.regex.Pattern; Line 158  import java.util.regex.Pattern;
158  public final class URI  public final class URI
159    implements Comparable, Serializable    implements Comparable, Serializable
160  {  {
161      /**
162       * For serialization compatability.
163       */
164    static final long serialVersionUID = -6052424284110960213L;    static final long serialVersionUID = -6052424284110960213L;
165    
166    /**    /**
# Line 119  public final class URI Line 172  public final class URI
172    private static final String URI_REGEXP =    private static final String URI_REGEXP =
173      "^(([^:/?#]+):)?((//([^/?#]*))?([^?#]*)(\\?([^#]*))?)?(#(.*))?";      "^(([^:/?#]+):)?((//([^/?#]*))?([^?#]*)(\\?([^#]*))?)?(#(.*))?";
174    
175      /**
176       * Regular expression for parsing the authority segment.
177       */
178    private static final String AUTHORITY_REGEXP =    private static final String AUTHORITY_REGEXP =
179      "(([^?#]*)@)?([^?#:]*)(:([^?#]*))?";      "(([^?#]*)@)?([^?#:]*)(:([0-9]*))?";
180    
181    /**    /**
182     * Valid characters (taken from rfc2396)     * Valid characters (taken from rfc2396/3986)
183     */     */
184    private static final String RFC2396_DIGIT = "0123456789";    private static final String RFC2396_DIGIT = "0123456789";
185    private static final String RFC2396_LOWALPHA = "abcdefghijklmnopqrstuvwxyz";    private static final String RFC2396_LOWALPHA = "abcdefghijklmnopqrstuvwxyz";
# Line 131  public final class URI Line 187  public final class URI
187    private static final String RFC2396_ALPHA =    private static final String RFC2396_ALPHA =
188      RFC2396_LOWALPHA + RFC2396_UPALPHA;      RFC2396_LOWALPHA + RFC2396_UPALPHA;
189    private static final String RFC2396_ALPHANUM = RFC2396_DIGIT + RFC2396_ALPHA;    private static final String RFC2396_ALPHANUM = RFC2396_DIGIT + RFC2396_ALPHA;
190    private static final String RFC2396_MARK = "-_.!~*'()";    private static final String RFC3986_UNRESERVED = RFC2396_ALPHANUM + "-._~";
191    private static final String RFC2396_UNRESERVED =    private static final String RFC3986_SUBDELIMS = "!$&'()*+,;=";
192      RFC2396_ALPHANUM + RFC2396_MARK;    private static final String RFC3986_REG_NAME =
193    private static final String RFC2396_REG_NAME =      RFC3986_UNRESERVED + RFC3986_SUBDELIMS + "%";
194      RFC2396_UNRESERVED + "$,;:@&=+";    private static final String RFC3986_PCHAR = RFC3986_UNRESERVED +
195    private static final String RFC2396_PCHAR = RFC2396_UNRESERVED + ":@&=+$,";      RFC3986_SUBDELIMS + ":@%";
196    private static final String RFC2396_SEGMENT = RFC2396_PCHAR + ";";    private static final String RFC3986_SEGMENT = RFC3986_PCHAR;
197    private static final String RFC2396_PATH_SEGMENTS = RFC2396_SEGMENT + "/";    private static final String RFC3986_PATH_SEGMENTS = RFC3986_SEGMENT + "/";
198      private static final String RFC3986_SSP = RFC3986_PCHAR + "?/";
199      private static final String RFC3986_HOST = RFC3986_REG_NAME + "[]";
200      private static final String RFC3986_USERINFO = RFC3986_REG_NAME + ":";
201    
202    /**    /**
203     * Index of scheme component in parsed URI.     * Index of scheme component in parsed URI.
# Line 170  public final class URI Line 229  public final class URI
229     */     */
230    private static final int FRAGMENT_GROUP = 10;    private static final int FRAGMENT_GROUP = 10;
231        
232      /**
233       * Index of userinfo component in parsed authority section.
234       */
235    private static final int AUTHORITY_USERINFO_GROUP = 2;    private static final int AUTHORITY_USERINFO_GROUP = 2;
236    
237      /**
238       * Index of host component in parsed authority section.
239       */
240    private static final int AUTHORITY_HOST_GROUP = 3;    private static final int AUTHORITY_HOST_GROUP = 3;
241    
242      /**
243       * Index of port component in parsed authority section.
244       */
245    private static final int AUTHORITY_PORT_GROUP = 5;    private static final int AUTHORITY_PORT_GROUP = 5;
246      
247      /**
248       * The compiled version of the URI regular expression.
249       */
250      private static final Pattern URI_PATTERN;
251    
252      /**
253       * The compiled version of the authority regular expression.
254       */
255      private static final Pattern AUTHORITY_PATTERN;
256    
257      /**
258       * The set of valid hexadecimal characters.
259       */
260      private static final String HEX = "0123456789ABCDEF";
261    
262    private transient String scheme;    private transient String scheme;
263    private transient String rawSchemeSpecificPart;    private transient String rawSchemeSpecificPart;
264    private transient String schemeSpecificPart;    private transient String schemeSpecificPart;
# Line 192  public final class URI Line 277  public final class URI
277    private transient String fragment;    private transient String fragment;
278    private String string;    private String string;
279    
280      /**
281       * Static initializer to pre-compile the regular expressions.
282       */
283      static
284      {
285        URI_PATTERN = Pattern.compile(URI_REGEXP);
286        AUTHORITY_PATTERN = Pattern.compile(AUTHORITY_REGEXP);
287      }
288    
289    private void readObject(ObjectInputStream is)    private void readObject(ObjectInputStream is)
290      throws ClassNotFoundException, IOException      throws ClassNotFoundException, IOException
291    {    {
# Line 229  public final class URI Line 323  public final class URI
323     */     */
324    private void parseURI(String str) throws URISyntaxException    private void parseURI(String str) throws URISyntaxException
325    {    {
326      Pattern pattern = Pattern.compile(URI_REGEXP);      Matcher matcher = URI_PATTERN.matcher(str);
     Matcher matcher = pattern.matcher(str);  
327            
328      if (matcher.matches())      if (matcher.matches())
329        {        {
# Line 246  public final class URI Line 339  public final class URI
339          rawFragment = getURIGroup(matcher, FRAGMENT_GROUP);          rawFragment = getURIGroup(matcher, FRAGMENT_GROUP);
340        }        }
341      else      else
342        throw new URISyntaxException(str, "doesn't match URI regular expression");        throw new URISyntaxException(str,
343                                       "doesn't match URI regular expression");
344      if (rawAuthority != null)      parseServerAuthority();
       {  
         pattern = Pattern.compile(AUTHORITY_REGEXP);  
         matcher = pattern.matcher(rawAuthority);  
   
         if (matcher.matches())  
           {  
             rawUserInfo = getURIGroup(matcher, AUTHORITY_USERINFO_GROUP);  
             rawHost = getURIGroup(matcher, AUTHORITY_HOST_GROUP);  
   
             String portStr = getURIGroup(matcher, AUTHORITY_PORT_GROUP);  
   
             if (portStr != null)  
               try  
                 {  
                   port = Integer.parseInt(portStr);  
                 }  
               catch (NumberFormatException e)  
                 {  
                   URISyntaxException use =  
                     new URISyntaxException  
                       (str, "doesn't match URI regular expression");  
                   use.initCause(e);  
                   throw use;  
                 }  
           }  
         else  
           throw new URISyntaxException(str, "doesn't match URI regular expression");  
       }  
345    
346      // We must eagerly unquote the parts, because this is the only time      // We must eagerly unquote the parts, because this is the only time
347      // we may throw an exception.      // we may throw an exception.
# Line 307  public final class URI Line 372  public final class URI
372      for (int i = 0; i < str.length(); i++)      for (int i = 0; i < str.length(); i++)
373        {        {
374          char c = str.charAt(i);          char c = str.charAt(i);
         if (c > 127)  
           throw new URISyntaxException(str, "Invalid character");  
375          if (c == '%')          if (c == '%')
376            {            {
377              if (i + 2 >= str.length())              if (i + 2 >= str.length())
# Line 345  public final class URI Line 408  public final class URI
408     */     */
409    private static String quote(String str)    private static String quote(String str)
410    {    {
411      // FIXME: unimplemented.      return quote(str, RFC3986_SSP);
     return str;  
412    }    }
413    
414    /**    /**
# Line 364  public final class URI Line 426  public final class URI
426    {    {
427      // Technically, we should be using RFC2396_AUTHORITY, but      // Technically, we should be using RFC2396_AUTHORITY, but
428      // it contains no additional characters.      // it contains no additional characters.
429      return quote(str, RFC2396_REG_NAME);      return quote(str, RFC3986_REG_NAME);
430    }    }
431    
432    /**    /**
433     * Quote characters in str that are not part of legalCharacters.     * Quotes the characters in the supplied string that are not part of
434     *     * the specified set of legal characters.
    * Replace illegal characters by encoding their UTF-8  
    * representation as "%" + hex code for each resulting  
    * UTF-8 character.  
435     *     *
436     * @param str The string to quote     * @param str the string to quote
437     * @param legalCharacters The set of legal characters     * @param legalCharacters the set of legal characters
438     *     *
439     * @return The quoted string.     * @return the quoted string.
440     */     */
441    private static String quote(String str, String legalCharacters)    private static String quote(String str, String legalCharacters)
442    {    {
# Line 387  public final class URI Line 446  public final class URI
446          char c = str.charAt(i);          char c = str.charAt(i);
447          if (legalCharacters.indexOf(c) == -1)          if (legalCharacters.indexOf(c) == -1)
448            {            {
             String hex = "0123456789ABCDEF";  
449              if (c <= 127)              if (c <= 127)
               sb.append('%').append(hex.charAt(c / 16)).append(hex.charAt(c % 16));  
             else  
450                {                {
451                  try                  sb.append('%');
452                    {                  sb.append(HEX.charAt(c / 16));
453                      // this is far from optimal, but it works                  sb.append(HEX.charAt(c % 16));
                     byte[] utf8 = str.substring(i, i + 1).getBytes("utf-8");  
                     for (int j = 0; j < utf8.length; j++)  
                       sb.append('%').append(hex.charAt((utf8[j] & 0xff) / 16))  
                         .append(hex.charAt((utf8[j] & 0xff) % 16));  
                   }  
                 catch (java.io.UnsupportedEncodingException x)  
                   {  
                     throw (Error) new InternalError().initCause(x);  
                   }  
454                }                }
455            }            }
456          else          else
# Line 425  public final class URI Line 472  public final class URI
472     */     */
473    private static String quoteHost(String str)    private static String quoteHost(String str)
474    {    {
475      // FIXME: unimplemented.      return quote(str, RFC3986_HOST);
     return str;  
476    }    }
477    
478    /**    /**
# Line 444  public final class URI Line 490  public final class URI
490    {    {
491      // Technically, we should be using RFC2396_PATH, but      // Technically, we should be using RFC2396_PATH, but
492      // it contains no additional characters.      // it contains no additional characters.
493      return quote(str, RFC2396_PATH_SEGMENTS);      return quote(str, RFC3986_PATH_SEGMENTS);
494    }    }
495    
496    /**    /**
# Line 460  public final class URI Line 506  public final class URI
506     */     */
507    private static String quoteUserInfo(String str)    private static String quoteUserInfo(String str)
508    {    {
509      // FIXME: unimplemented.      return quote(str, RFC3986_USERINFO);
     return str;  
510    }    }
511    
512    /**    /**
# Line 503  public final class URI Line 548  public final class URI
548           + (path == null ? "" : quotePath(path))           + (path == null ? "" : quotePath(path))
549           + (query == null ? "" : "?" + quote(query))           + (query == null ? "" : "?" + quote(query))
550           + (fragment == null ? "" : "#" + quote(fragment)));           + (fragment == null ? "" : "#" + quote(fragment)));
   
     parseServerAuthority();  
551    }    }
552    
553    /**    /**
# Line 584  public final class URI Line 627  public final class URI
627    
628    /**    /**
629     * Attempts to parse this URI's authority component, if defined,     * Attempts to parse this URI's authority component, if defined,
630     * into user-information, host, and port components     * into user-information, host, and port components.  The purpose
631     *     * of this method was to disambiguate between some authority sections,
632     * @exception URISyntaxException If the given string violates RFC 2396     * which form invalid server-based authories, but valid registry
633       * based authorities.  In the updated RFC 3986, the authority section
634       * is defined differently, with registry-based authorities part of
635       * the host section.  Thus, this method is now simply an explicit
636       * way of parsing any authority section.
637       *
638       * @return the URI, with the authority section parsed into user
639       *         information, host and port components.
640       * @throws URISyntaxException if the given string violates RFC 2396
641     */     */
642    public URI parseServerAuthority() throws URISyntaxException    public URI parseServerAuthority() throws URISyntaxException
643    {    {
644      return null;      if (rawAuthority != null)
645          {
646            Matcher matcher = AUTHORITY_PATTERN.matcher(rawAuthority);
647    
648            if (matcher.matches())
649              {
650                rawUserInfo = getURIGroup(matcher, AUTHORITY_USERINFO_GROUP);
651                rawHost = getURIGroup(matcher, AUTHORITY_HOST_GROUP);
652                
653                String portStr = getURIGroup(matcher, AUTHORITY_PORT_GROUP);
654                
655                if (portStr != null)
656                  try
657                    {
658                      port = Integer.parseInt(portStr);
659                    }
660                  catch (NumberFormatException e)
661                    {
662                      URISyntaxException use =
663                        new URISyntaxException
664                          (string, "doesn't match URI regular expression");
665                      use.initCause(e);
666                      throw use;
667                    }
668              }
669            else
670              throw new URISyntaxException(string,
671                                           "doesn't match URI regular expression");
672          }
673        return this;
674    }    }
675    
676    /**    /**
677     * Returns a normalizes versions of the URI     * <p>
678       * Returns a normalized version of the URI.  If the URI is opaque,
679       * or its path is already in normal form, then this URI is simply
680       * returned.  Otherwise, the following transformation of the path
681       * element takes place:
682       * </p>
683       * <ol>
684       * <li>All `.' segments are removed.</li>
685       * <li>Each `..' segment which can be paired with a prior non-`..' segment
686       * is removed along with the preceding segment.</li>
687       * <li>A `.' segment is added to the front if the first segment contains
688       * a colon (`:').  This is a deviation from the RFC, which prevents
689       * confusion between the path and the scheme.</li>
690       * </ol>
691       * <p>
692       * The resulting URI will be free of `.' and `..' segments, barring those
693       * that were prepended or which couldn't be paired, respectively.
694       * </p>
695       *
696       * @return the normalized URI.
697     */     */
698    public URI normalize()    public URI normalize()
699    {    {
700      return null;      if (isOpaque() || path.indexOf("/./") == -1 && path.indexOf("/../") == -1)
701          return this;
702        try
703          {
704            return new URI(scheme, authority, normalizePath(path), query,
705                           fragment);
706          }
707        catch (URISyntaxException e)
708          {
709            throw (Error) new InternalError("Normalized URI variant could not "+
710                                            "be constructed").initCause(e);
711          }
712      }
713    
714      /**
715       * <p>
716       * Normalize the given path.  The following transformation takes place:
717       * </p>
718       * <ol>
719       * <li>All `.' segments are removed.</li>
720       * <li>Each `..' segment which can be paired with a prior non-`..' segment
721       * is removed along with the preceding segment.</li>
722       * <li>A `.' segment is added to the front if the first segment contains
723       * a colon (`:').  This is a deviation from the RFC, which prevents
724       * confusion between the path and the scheme.</li>
725       * </ol>
726       * <p>
727       * The resulting URI will be free of `.' and `..' segments, barring those
728       * that were prepended or which couldn't be paired, respectively.
729       * </p>
730       *
731       * @param relativePath the relative path to be normalized.
732       * @return the normalized path.
733       */
734      private String normalizePath(String relativePath)
735      {
736        /*
737           This follows the algorithm in section 5.2.4. of RFC3986,
738           but doesn't modify the input buffer.
739        */
740        StringBuffer input = new StringBuffer(relativePath);
741        StringBuffer output = new StringBuffer();
742        int start = 0;
743        while (start < input.length())
744          {
745            /* A */
746            if (input.indexOf("../",start) == start)
747              {
748                start += 3;
749                continue;
750              }
751            if (input.indexOf("./",start) == start)
752              {
753                start += 2;
754                continue;
755              }
756            /* B */
757            if (input.indexOf("/./",start) == start)
758              {
759                start += 2;
760                continue;
761              }
762            if (input.indexOf("/.",start) == start
763                && input.charAt(start + 2) != '.')
764              {
765                start += 1;
766                input.setCharAt(start,'/');
767                continue;
768              }
769            /* C */
770            if (input.indexOf("/../",start) == start)
771              {
772                start += 3;
773                removeLastSegment(output);
774                continue;
775              }
776            if (input.indexOf("/..",start) == start)
777              {
778                start += 2;
779                input.setCharAt(start,'/');
780                removeLastSegment(output);
781                continue;
782              }
783            /* D */
784            if (start == input.length() - 1 && input.indexOf(".",start) == start)
785              {
786                input.delete(0,1);
787                continue;
788              }
789            if (start == input.length() - 2 && input.indexOf("..",start) == start)
790              {
791                input.delete(0,2);
792                continue;
793              }
794            /* E */
795            int indexOfSlash = input.indexOf("/",start);
796            while (indexOfSlash == start)
797              {
798                output.append("/");
799                ++start;
800                indexOfSlash = input.indexOf("/",start);
801              }
802            if (indexOfSlash == -1)
803              indexOfSlash = input.length();
804            output.append(input.substring(start, indexOfSlash));
805            start = indexOfSlash;
806          }
807        return output.toString();
808      }
809    
810      /**
811       * Removes the last segment of the path from the specified buffer.
812       *
813       * @param buffer the buffer containing the path.
814       */
815      private void removeLastSegment(StringBuffer buffer)
816      {
817        int lastSlash = buffer.lastIndexOf("/");
818        if (lastSlash == -1)
819          buffer.setLength(0);
820        else
821          buffer.setLength(lastSlash);
822    }    }
823    
824    /**    /**
# Line 609  public final class URI Line 829  public final class URI
829     * @return The resulting URI, or null when it couldn't be resolved     * @return The resulting URI, or null when it couldn't be resolved
830     * for some reason.     * for some reason.
831     *     *
832     * @exception NullPointerException If uri is null     * @throws NullPointerException if uri is null
833     */     */
834    public URI resolve(URI uri)    public URI resolve(URI uri)
835    {    {
# Line 645  public final class URI Line 865  public final class URI
865                    basepath.delete(i + 1, basepath.length());                    basepath.delete(i + 1, basepath.length());
866    
867                  basepath.append(path);                  basepath.append(path);
868                  path = basepath.toString();                  path = normalizePath(basepath.toString());
                 //  FIXME We must normalize the path here.  
                 //  Normalization process omitted.  
869                }                }
870            }            }
871          return new URI(this.scheme, authority, path, query, fragment);          return new URI(this.scheme, authority, path, query, fragment);
872        }        }
873      catch (URISyntaxException e)      catch (URISyntaxException e)
874        {        {
875          return null;          throw (Error) new InternalError("Resolved URI variant could not "+
876                                            "be constructed").initCause(e);
877        }        }
878    }    }
879    
# Line 665  public final class URI Line 884  public final class URI
884     *     *
885     * @return The resulting URI     * @return The resulting URI
886     *     *
887     * @exception IllegalArgumentException If the given URI string     * @throws IllegalArgumentException If the given URI string
888     * violates RFC 2396     * violates RFC 2396
889     * @exception NullPointerException If uri is null     * @throws NullPointerException If uri is null
890     */     */
891    public URI resolve(String str) throws IllegalArgumentException    public URI resolve(String str) throws IllegalArgumentException
892    {    {
# Line 675  public final class URI Line 894  public final class URI
894    }    }
895    
896    /**    /**
897     * Relativizes the given URI against this URI     * <p>
898     *     * Relativizes the given URI against this URI using the following
899     * @param uri The URI to relativize this URI     * algorithm:
900     *     * </p>
901     * @return The resulting URI     * <ul>
902     *     * <li>If either URI is opaque, the given URI is returned.</li>
903     * @exception NullPointerException If uri is null     * <li>If the schemes of the URIs differ, the given URI is returned.</li>
904       * <li>If the authority components of the URIs differ, then the given
905       * URI is returned.</li>
906       * <li>If the path of this URI is not a prefix of the supplied URI,
907       * then the given URI is returned.</li>
908       * <li>If all the above conditions hold, a new URI is created using the
909       * query and fragment components of the given URI, along with a path
910       * computed by removing the path of this URI from the start of the path
911       * of the supplied URI.</li>
912       * </ul>
913       *
914       * @param uri the URI to relativize agsint this URI
915       * @return the resulting URI
916       * @throws NullPointerException if the uri is null
917     */     */
918    public URI relativize(URI uri)    public URI relativize(URI uri)
919    {    {
920      return null;      if (isOpaque() || uri.isOpaque())
921          return uri;
922        if (scheme == null && uri.getScheme() != null)
923          return uri;
924        if (scheme != null && !(scheme.equals(uri.getScheme())))
925          return uri;
926        if (rawAuthority == null && uri.getRawAuthority() != null)
927          return uri;
928        if (rawAuthority != null && !(rawAuthority.equals(uri.getRawAuthority())))
929          return uri;
930        if (!(uri.getRawPath().startsWith(rawPath)))
931          return uri;
932        try
933          {
934            return new URI(null, null,
935                           uri.getRawPath().substring(rawPath.length()),
936                           uri.getRawQuery(), uri.getRawFragment());
937          }
938        catch (URISyntaxException e)
939          {
940            throw (Error) new InternalError("Relativized URI variant could not "+
941                                            "be constructed").initCause(e);      
942          }
943    }    }
944    
945    /**    /**
946     * Creates an URL from an URI     * Creates an URL from an URI
947     *     *
948     * @exception MalformedURLException If a protocol handler for the URL could     * @throws MalformedURLException If a protocol handler for the URL could
949     * not be found, or if some other error occurred while constructing the URL     * not be found, or if some other error occurred while constructing the URL
950     * @exception IllegalArgumentException If the URI is not absolute     * @throws IllegalArgumentException If the URI is not absolute
951     */     */
952    public URL toURL() throws IllegalArgumentException, MalformedURLException    public URL toURL() throws IllegalArgumentException, MalformedURLException
953    {    {
# Line 745  public final class URI Line 999  public final class URI
999    }    }
1000    
1001    /**    /**
1002     * Returns the rae authority part of this URI     * Returns the raw authority part of this URI
1003     */     */
1004    public String getRawAuthority()    public String getRawAuthority()
1005    {    {
# Line 841  public final class URI Line 1095  public final class URI
1095    }    }
1096    
1097    /**    /**
1098     * Compares the URI with a given object     * <p>
1099     *     * Compares the URI with the given object for equality.  If the
1100     * @param obj The obj to compare the URI with     * object is not a <code>URI</code>, then the method returns false.
1101       * Otherwise, the following criteria are observed:
1102       * </p>
1103       * <ul>
1104       * <li>The scheme of the URIs must either be null (undefined) in both cases,
1105       * or equal, ignorant of case.</li>
1106       * <li>The raw fragment of the URIs must either be null (undefined) in both
1107       * cases, or equal, ignorant of case.</li>
1108       * <li>Both URIs must be of the same type (opaque or hierarchial)</li>
1109       * <li><strong>For opaque URIs:</strong></li>
1110       * <ul>
1111       * <li>The raw scheme-specific parts must be equal.</li>
1112       * </ul>
1113       * <li>For hierarchical URIs:</li>
1114       * <ul>
1115       * <li>The raw paths must be equal, ignorant of case.</li>
1116       * <li>The raw queries are either both undefined or both equal, ignorant
1117       * of case.</li>
1118       * <li>The raw authority sections are either both undefined or:</li>
1119       * <li><strong>For registry-based authorities:</strong></li>
1120       * <ul><li>they are equal.</li></ul>
1121       * <li><strong>For server-based authorities:</strong></li>
1122       * <ul>
1123       * <li>the hosts are equal, ignoring case</li>
1124       * <li>the ports are equal</li>
1125       * <li>the user information components are equal</li>
1126       * </ul>
1127       * </ul>
1128       * </ul>
1129       *
1130       * @param obj the obj to compare the URI with.
1131       * @return <code>true</code> if the objects are equal, according to
1132       *         the specification above.
1133     */     */
1134    public boolean equals(Object obj)    public boolean equals(Object obj)
1135    {    {
1136      return false;      if (!(obj instanceof URI))
1137          return false;
1138        URI uriObj = (URI) obj;
1139        if (scheme == null)
1140          {
1141            if (uriObj.getScheme() != null)
1142              return false;
1143          }
1144        else
1145          if (!(scheme.equalsIgnoreCase(uriObj.getScheme())))
1146            return false;
1147        if (rawFragment == null)
1148          {
1149            if (uriObj.getRawFragment() != null)
1150              return false;
1151          }
1152        else
1153          if (!(rawFragment.equalsIgnoreCase(uriObj.getRawFragment())))
1154            return false;
1155        boolean opaqueThis = isOpaque();
1156        boolean opaqueObj = uriObj.isOpaque();
1157        if (opaqueThis && opaqueObj)
1158          return rawSchemeSpecificPart.equals(uriObj.getRawSchemeSpecificPart());
1159        else if (!opaqueThis && !opaqueObj)
1160          {
1161            boolean common = rawPath.equalsIgnoreCase(uriObj.getRawPath())
1162              && ((rawQuery == null && uriObj.getRawQuery() == null)
1163                  || rawQuery.equalsIgnoreCase(uriObj.getRawQuery()));
1164            if (rawAuthority == null && uriObj.getRawAuthority() == null)
1165              return common;
1166            if (host == null)
1167              return common
1168                && rawAuthority.equalsIgnoreCase(uriObj.getRawAuthority());
1169            return common
1170              && host.equalsIgnoreCase(uriObj.getHost())
1171              && port == uriObj.getPort()
1172              && (rawUserInfo == null ?
1173                  uriObj.getRawUserInfo() == null :
1174                  rawUserInfo.equalsIgnoreCase(uriObj.getRawUserInfo()));
1175          }
1176        else
1177          return false;
1178    }    }
1179    
1180    /**    /**
1181     * Computes the hascode of the URI     * Computes the hashcode of the URI
1182     */     */
1183    public int hashCode()    public int hashCode()
1184    {    {
1185      return 0;      return (getScheme() == null ? 0 : 13 * getScheme().hashCode())
1186          + 17 * getRawSchemeSpecificPart().hashCode()
1187          + (getRawFragment() == null ? 0 : 21 + getRawFragment().hashCode());
1188    }    }
1189    
1190    /**    /**
1191     * Compare the URI with another object that must be an URI too     * Compare the URI with another object that must also be a URI.
1192       * Undefined components are taken to be less than any other component.
1193       * The following criteria are observed:
1194       * </p>
1195       * <ul>
1196       * <li>Two URIs with different schemes are compared according to their
1197       * scheme, regardless of case.</li>
1198       * <li>A hierarchical URI is less than an opaque URI with the same
1199       * scheme.</li>
1200       * <li><strong>For opaque URIs:</strong></li>
1201       * <ul>
1202       * <li>URIs with differing scheme-specific parts are ordered according
1203       * to the ordering of the scheme-specific part.</li>
1204       * <li>URIs with the same scheme-specific part are ordered by the
1205       * raw fragment.</li>
1206       * </ul>
1207       * <li>For hierarchical URIs:</li>
1208       * <ul>
1209       * <li>URIs are ordered according to their raw authority sections,
1210       * if they are unequal.</li>
1211       * <li><strong>For registry-based authorities:</strong></li>
1212       * <ul><li>they are ordered according to the ordering of the authority
1213       * component.</li></ul>
1214       * <li><strong>For server-based authorities:</strong></li>
1215       * <ul>
1216       * <li>URIs are ordered according to the raw user information.</li>
1217       * <li>URIs with the same user information are ordered by the host,
1218       * ignoring case.</li>
1219       * <lI>URIs with the same host are ordered by the port.</li>
1220       * </ul>
1221       * <li>URIs with the same authority section are ordered by the raw path.</li>
1222       * <li>URIs with the same path are ordered by their raw query.</li>
1223       * <li>URIs with the same query are ordered by their raw fragments.</li>
1224       * </ul>
1225       * </ul>
1226     *     *
1227     * @param obj This object to compare this URI with     * @param obj This object to compare this URI with
1228       * @return a negative integer, zero or a positive integer depending
1229       *         on whether this URI is less than, equal to or greater
1230       *         than that supplied, respectively.
1231       * @throws ClassCastException if the given object is not a URI
1232       */
1233      public int compareTo(Object obj)
1234        throws ClassCastException
1235      {
1236        URI uri = (URI) obj;
1237        if (scheme == null && uri.getScheme() != null)
1238          return -1;
1239        if (scheme != null)
1240          {
1241            int sCompare = scheme.compareToIgnoreCase(uri.getScheme());
1242            if (sCompare != 0)
1243              return sCompare;
1244          }
1245        boolean opaqueThis = isOpaque();
1246        boolean opaqueObj = uri.isOpaque();
1247        if (opaqueThis && !opaqueObj)
1248          return 1;
1249        if (!opaqueThis && opaqueObj)
1250          return -1;
1251        if (opaqueThis)
1252          {
1253            int ssCompare =
1254              rawSchemeSpecificPart.compareTo(uri.getRawSchemeSpecificPart());
1255            if (ssCompare == 0)
1256              return compareFragments(uri);
1257            else
1258              return ssCompare;
1259          }
1260        if (rawAuthority == null && uri.getRawAuthority() != null)
1261          return -1;
1262        if (rawAuthority != null)
1263          {
1264            int aCompare = rawAuthority.compareTo(uri.getRawAuthority());
1265            if (aCompare != 0)
1266              {
1267                if (host == null)
1268                  return aCompare;
1269                if (rawUserInfo == null && uri.getRawUserInfo() != null)
1270                  return -1;
1271                int uCompare = rawUserInfo.compareTo(uri.getRawUserInfo());
1272                if (uCompare != 0)
1273                  return uCompare;
1274                if (host == null && uri.getHost() != null)
1275                  return -1;
1276                int hCompare = host.compareTo(uri.getHost());
1277                if (hCompare != 0)
1278                  return hCompare;
1279                return new Integer(port).compareTo(new Integer(uri.getPort()));
1280              }
1281          }
1282        if (rawPath == null && uri.getRawPath() != null)
1283          return -1;
1284        if (rawPath != null)
1285          {
1286            int pCompare = rawPath.compareTo(uri.getRawPath());
1287            if (pCompare != 0)
1288              return pCompare;
1289          }
1290        if (rawQuery == null && uri.getRawQuery() != null)
1291          return -1;
1292        if (rawQuery != null)
1293          {
1294            int qCompare = rawQuery.compareTo(uri.getRawQuery());
1295            if (qCompare != 0)
1296              return qCompare;
1297          }
1298        return compareFragments(uri);
1299      }
1300    
1301      /**
1302       * Compares the fragment of this URI with that of the supplied URI.
1303     *     *
1304     * @exception ClassCastException If given object ist not an URI     * @param uri the URI to compare with this one.
1305       * @return a negative integer, zero or a positive integer depending
1306       *         on whether this uri's fragment is less than, equal to
1307       *         or greater than the fragment of the uri supplied, respectively.
1308     */     */
1309    public int compareTo(Object obj) throws ClassCastException    private int compareFragments(URI uri)
1310    {    {
1311      return 0;      if (rawFragment == null && uri.getRawFragment() != null)
1312          return -1;
1313        else if (rawFragment == null)
1314          return 0;
1315        else
1316          return rawFragment.compareTo(uri.getRawFragment());
1317    }    }
1318    
1319    /**    /**
# Line 878  public final class URI Line 1324  public final class URI
1324     */     */
1325    public String toString()    public String toString()
1326    {    {
1327      return (getScheme() == null ? "" : getScheme() + ":")      return (scheme == null ? "" : scheme + ":")
1328             + getRawSchemeSpecificPart()        + rawSchemeSpecificPart
1329             + (getRawFragment() == null ? "" : "#" + getRawFragment());        + (rawFragment == null ? "" : "#" + rawFragment);
1330    }    }
1331    
1332    /**    /**
1333     * Returns the URI as US-ASCII string     * Returns the URI as US-ASCII string.  This is the same as the result
1334       * from <code>toString()</code> for URIs that don't contain any non-US-ASCII
1335       * characters.  Otherwise, the non-US-ASCII characters are replaced
1336       * by their percent-encoded representations.
1337       *
1338       * @return a string representation of the URI, containing only US-ASCII
1339       *         characters.
1340     */     */
1341    public String toASCIIString()    public String toASCIIString()
1342    {    {
1343      return "";      String strRep = toString();
1344        boolean inNonAsciiBlock = false;
1345        StringBuffer buffer = new StringBuffer();
1346        StringBuffer encBuffer = null;
1347        for (int i = 0; i < strRep.length(); i++)
1348          {
1349            char c = strRep.charAt(i);
1350            if (c <= 127)
1351              {
1352                if (inNonAsciiBlock)
1353                  {
1354                    buffer.append(escapeCharacters(encBuffer.toString()));
1355                    inNonAsciiBlock = false;
1356                  }
1357                buffer.append(c);
1358              }
1359            else
1360              {
1361                if (!inNonAsciiBlock)
1362                  {
1363                    encBuffer = new StringBuffer();
1364                    inNonAsciiBlock = true;
1365                  }
1366                encBuffer.append(c);
1367              }
1368          }
1369        return buffer.toString();
1370    }    }
1371    
1372      /**
1373       * Converts the non-ASCII characters in the supplied string
1374       * to their equivalent percent-encoded representations.
1375       * That is, they are replaced by "%" followed by their hexadecimal value.
1376       *
1377       * @param str a string including non-ASCII characters.
1378       * @return the string with the non-ASCII characters converted to their
1379       *         percent-encoded representations.
1380       */
1381      private static String escapeCharacters(String str)
1382      {
1383        try
1384          {
1385            StringBuffer sb = new StringBuffer();
1386            // this is far from optimal, but it works
1387            byte[] utf8 = str.getBytes("utf-8");
1388            for (int j = 0; j < utf8.length; j++)
1389              {
1390                sb.append('%');
1391                sb.append(HEX.charAt((utf8[j] & 0xff) / 16));
1392                sb.append(HEX.charAt((utf8[j] & 0xff) % 16));
1393              }
1394            return sb.toString();
1395          }
1396        catch (java.io.UnsupportedEncodingException x)
1397          {
1398            throw (Error) new InternalError("Escaping error").initCause(x);
1399          }
1400      }
1401    
1402  }  }

Legend:
Removed from v.1.13  
changed lines
  Added in v.1.14

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26