emul/src/main/java/java/net/URL.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 17 Dec 2012 09:55:07 +0100
changeset 339 7579a0ee92fb
parent 122 0a582b5a2737
child 340 785b53689e29
child 342 60f9aad12731
permissions -rw-r--r--
Patching URL and URLStreamHandler to compile
     1 /*
     2  * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    25 
    26 package java.net;
    27 
    28 import java.io.IOException;
    29 import java.io.InputStream;
    30 
    31 /**
    32  * Class <code>URL</code> represents a Uniform Resource
    33  * Locator, a pointer to a "resource" on the World
    34  * Wide Web. A resource can be something as simple as a file or a
    35  * directory, or it can be a reference to a more complicated object,
    36  * such as a query to a database or to a search engine. More
    37  * information on the types of URLs and their formats can be found at:
    38  * <blockquote>
    39  *     <a href="http://www.socs.uts.edu.au/MosaicDocs-old/url-primer.html">
    40  *    <i>http://www.socs.uts.edu.au/MosaicDocs-old/url-primer.html</i></a>
    41  * </blockquote>
    42  * <p>
    43  * In general, a URL can be broken into several parts. The previous
    44  * example of a URL indicates that the protocol to use is
    45  * <code>http</code> (HyperText Transfer Protocol) and that the
    46  * information resides on a host machine named
    47  * <code>www.socs.uts.edu.au</code>. The information on that host
    48  * machine is named <code>/MosaicDocs-old/url-primer.html</code>. The exact
    49  * meaning of this name on the host machine is both protocol
    50  * dependent and host dependent. The information normally resides in
    51  * a file, but it could be generated on the fly. This component of
    52  * the URL is called the <i>path</i> component.
    53  * <p>
    54  * A URL can optionally specify a "port", which is the
    55  * port number to which the TCP connection is made on the remote host
    56  * machine. If the port is not specified, the default port for
    57  * the protocol is used instead. For example, the default port for
    58  * <code>http</code> is <code>80</code>. An alternative port could be
    59  * specified as:
    60  * <blockquote><pre>
    61  *     http://www.socs.uts.edu.au:80/MosaicDocs-old/url-primer.html
    62  * </pre></blockquote>
    63  * <p>
    64  * The syntax of <code>URL</code> is defined by  <a
    65  * href="http://www.ietf.org/rfc/rfc2396.txt"><i>RFC&nbsp;2396: Uniform
    66  * Resource Identifiers (URI): Generic Syntax</i></a>, amended by <a
    67  * href="http://www.ietf.org/rfc/rfc2732.txt"><i>RFC&nbsp;2732: Format for
    68  * Literal IPv6 Addresses in URLs</i></a>. The Literal IPv6 address format
    69  * also supports scope_ids. The syntax and usage of scope_ids is described
    70  * <a href="Inet6Address.html#scoped">here</a>.
    71  * <p>
    72  * A URL may have appended to it a "fragment", also known
    73  * as a "ref" or a "reference". The fragment is indicated by the sharp
    74  * sign character "#" followed by more characters. For example,
    75  * <blockquote><pre>
    76  *     http://java.sun.com/index.html#chapter1
    77  * </pre></blockquote>
    78  * <p>
    79  * This fragment is not technically part of the URL. Rather, it
    80  * indicates that after the specified resource is retrieved, the
    81  * application is specifically interested in that part of the
    82  * document that has the tag <code>chapter1</code> attached to it. The
    83  * meaning of a tag is resource specific.
    84  * <p>
    85  * An application can also specify a "relative URL",
    86  * which contains only enough information to reach the resource
    87  * relative to another URL. Relative URLs are frequently used within
    88  * HTML pages. For example, if the contents of the URL:
    89  * <blockquote><pre>
    90  *     http://java.sun.com/index.html
    91  * </pre></blockquote>
    92  * contained within it the relative URL:
    93  * <blockquote><pre>
    94  *     FAQ.html
    95  * </pre></blockquote>
    96  * it would be a shorthand for:
    97  * <blockquote><pre>
    98  *     http://java.sun.com/FAQ.html
    99  * </pre></blockquote>
   100  * <p>
   101  * The relative URL need not specify all the components of a URL. If
   102  * the protocol, host name, or port number is missing, the value is
   103  * inherited from the fully specified URL. The file component must be
   104  * specified. The optional fragment is not inherited.
   105  * <p>
   106  * The URL class does not itself encode or decode any URL components
   107  * according to the escaping mechanism defined in RFC2396. It is the
   108  * responsibility of the caller to encode any fields, which need to be
   109  * escaped prior to calling URL, and also to decode any escaped fields,
   110  * that are returned from URL. Furthermore, because URL has no knowledge
   111  * of URL escaping, it does not recognise equivalence between the encoded
   112  * or decoded form of the same URL. For example, the two URLs:<br>
   113  * <pre>    http://foo.com/hello world/ and http://foo.com/hello%20world</pre>
   114  * would be considered not equal to each other.
   115  * <p>
   116  * Note, the {@link java.net.URI} class does perform escaping of its
   117  * component fields in certain circumstances. The recommended way
   118  * to manage the encoding and decoding of URLs is to use {@link java.net.URI},
   119  * and to convert between these two classes using {@link #toURI()} and
   120  * {@link URI#toURL()}.
   121  * <p>
   122  * The {@link URLEncoder} and {@link URLDecoder} classes can also be
   123  * used, but only for HTML form encoding, which is not the same
   124  * as the encoding scheme defined in RFC2396.
   125  *
   126  * @author  James Gosling
   127  * @since JDK1.0
   128  */
   129 public final class URL implements java.io.Serializable {
   130 
   131     static final long serialVersionUID = -7627629688361524110L;
   132 
   133     /**
   134      * The property which specifies the package prefix list to be scanned
   135      * for protocol handlers.  The value of this property (if any) should
   136      * be a vertical bar delimited list of package names to search through
   137      * for a protocol handler to load.  The policy of this class is that
   138      * all protocol handlers will be in a class called <protocolname>.Handler,
   139      * and each package in the list is examined in turn for a matching
   140      * handler.  If none are found (or the property is not specified), the
   141      * default package prefix, sun.net.www.protocol, is used.  The search
   142      * proceeds from the first package in the list to the last and stops
   143      * when a match is found.
   144      */
   145     private static final String protocolPathProp = "java.protocol.handler.pkgs";
   146 
   147     /**
   148      * The protocol to use (ftp, http, nntp, ... etc.) .
   149      * @serial
   150      */
   151     private String protocol;
   152 
   153     /**
   154      * The host name to connect to.
   155      * @serial
   156      */
   157     private String host;
   158 
   159     /**
   160      * The protocol port to connect to.
   161      * @serial
   162      */
   163     private int port = -1;
   164 
   165     /**
   166      * The specified file name on that host. <code>file</code> is
   167      * defined as <code>path[?query]</code>
   168      * @serial
   169      */
   170     private String file;
   171 
   172     /**
   173      * The query part of this URL.
   174      */
   175     private transient String query;
   176 
   177     /**
   178      * The authority part of this URL.
   179      * @serial
   180      */
   181     private String authority;
   182 
   183     /**
   184      * The path part of this URL.
   185      */
   186     private transient String path;
   187 
   188     /**
   189      * The userinfo part of this URL.
   190      */
   191     private transient String userInfo;
   192 
   193     /**
   194      * # reference.
   195      * @serial
   196      */
   197     private String ref;
   198 
   199     /**
   200      * The host's IP address, used in equals and hashCode.
   201      * Computed on demand. An uninitialized or unknown hostAddress is null.
   202      */
   203     transient Object hostAddress;
   204 
   205     /**
   206      * The URLStreamHandler for this URL.
   207      */
   208     transient URLStreamHandler handler;
   209 
   210     /* Our hash code.
   211      * @serial
   212      */
   213     private int hashCode = -1;
   214 
   215     /**
   216      * Creates a <code>URL</code> object from the specified
   217      * <code>protocol</code>, <code>host</code>, <code>port</code>
   218      * number, and <code>file</code>.<p>
   219      *
   220      * <code>host</code> can be expressed as a host name or a literal
   221      * IP address. If IPv6 literal address is used, it should be
   222      * enclosed in square brackets (<tt>'['</tt> and <tt>']'</tt>), as
   223      * specified by <a
   224      * href="http://www.ietf.org/rfc/rfc2732.txt">RFC&nbsp;2732</a>;
   225      * However, the literal IPv6 address format defined in <a
   226      * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC&nbsp;2373: IP
   227      * Version 6 Addressing Architecture</i></a> is also accepted.<p>
   228      *
   229      * Specifying a <code>port</code> number of <code>-1</code>
   230      * indicates that the URL should use the default port for the
   231      * protocol.<p>
   232      *
   233      * If this is the first URL object being created with the specified
   234      * protocol, a <i>stream protocol handler</i> object, an instance of
   235      * class <code>URLStreamHandler</code>, is created for that protocol:
   236      * <ol>
   237      * <li>If the application has previously set up an instance of
   238      *     <code>URLStreamHandlerFactory</code> as the stream handler factory,
   239      *     then the <code>createURLStreamHandler</code> method of that instance
   240      *     is called with the protocol string as an argument to create the
   241      *     stream protocol handler.
   242      * <li>If no <code>URLStreamHandlerFactory</code> has yet been set up,
   243      *     or if the factory's <code>createURLStreamHandler</code> method
   244      *     returns <code>null</code>, then the constructor finds the
   245      *     value of the system property:
   246      *     <blockquote><pre>
   247      *         java.protocol.handler.pkgs
   248      *     </pre></blockquote>
   249      *     If the value of that system property is not <code>null</code>,
   250      *     it is interpreted as a list of packages separated by a vertical
   251      *     slash character '<code>|</code>'. The constructor tries to load
   252      *     the class named:
   253      *     <blockquote><pre>
   254      *         &lt;<i>package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler
   255      *     </pre></blockquote>
   256      *     where &lt;<i>package</i>&gt; is replaced by the name of the package
   257      *     and &lt;<i>protocol</i>&gt; is replaced by the name of the protocol.
   258      *     If this class does not exist, or if the class exists but it is not
   259      *     a subclass of <code>URLStreamHandler</code>, then the next package
   260      *     in the list is tried.
   261      * <li>If the previous step fails to find a protocol handler, then the
   262      *     constructor tries to load from a system default package.
   263      *     <blockquote><pre>
   264      *         &lt;<i>system default package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler
   265      *     </pre></blockquote>
   266      *     If this class does not exist, or if the class exists but it is not a
   267      *     subclass of <code>URLStreamHandler</code>, then a
   268      *     <code>MalformedURLException</code> is thrown.
   269      * </ol>
   270      *
   271      * <p>Protocol handlers for the following protocols are guaranteed
   272      * to exist on the search path :-
   273      * <blockquote><pre>
   274      *     http, https, ftp, file, and jar
   275      * </pre></blockquote>
   276      * Protocol handlers for additional protocols may also be
   277      * available.
   278      *
   279      * <p>No validation of the inputs is performed by this constructor.
   280      *
   281      * @param      protocol   the name of the protocol to use.
   282      * @param      host       the name of the host.
   283      * @param      port       the port number on the host.
   284      * @param      file       the file on the host
   285      * @exception  MalformedURLException  if an unknown protocol is specified.
   286      * @see        java.lang.System#getProperty(java.lang.String)
   287      * @see        java.net.URL#setURLStreamHandlerFactory(
   288      *                  java.net.URLStreamHandlerFactory)
   289      * @see        java.net.URLStreamHandler
   290      * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(
   291      *                  java.lang.String)
   292      */
   293     public URL(String protocol, String host, int port, String file)
   294         throws MalformedURLException
   295     {
   296         this(protocol, host, port, file, null);
   297     }
   298 
   299     /**
   300      * Creates a URL from the specified <code>protocol</code>
   301      * name, <code>host</code> name, and <code>file</code> name. The
   302      * default port for the specified protocol is used.
   303      * <p>
   304      * This method is equivalent to calling the four-argument
   305      * constructor with the arguments being <code>protocol</code>,
   306      * <code>host</code>, <code>-1</code>, and <code>file</code>.
   307      *
   308      * No validation of the inputs is performed by this constructor.
   309      *
   310      * @param      protocol   the name of the protocol to use.
   311      * @param      host       the name of the host.
   312      * @param      file       the file on the host.
   313      * @exception  MalformedURLException  if an unknown protocol is specified.
   314      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   315      *                  int, java.lang.String)
   316      */
   317     public URL(String protocol, String host, String file)
   318             throws MalformedURLException {
   319         this(protocol, host, -1, file);
   320     }
   321 
   322     /**
   323      * Creates a <code>URL</code> object from the specified
   324      * <code>protocol</code>, <code>host</code>, <code>port</code>
   325      * number, <code>file</code>, and <code>handler</code>. Specifying
   326      * a <code>port</code> number of <code>-1</code> indicates that
   327      * the URL should use the default port for the protocol. Specifying
   328      * a <code>handler</code> of <code>null</code> indicates that the URL
   329      * should use a default stream handler for the protocol, as outlined
   330      * for:
   331      *     java.net.URL#URL(java.lang.String, java.lang.String, int,
   332      *                      java.lang.String)
   333      *
   334      * <p>If the handler is not null and there is a security manager,
   335      * the security manager's <code>checkPermission</code>
   336      * method is called with a
   337      * <code>NetPermission("specifyStreamHandler")</code> permission.
   338      * This may result in a SecurityException.
   339      *
   340      * No validation of the inputs is performed by this constructor.
   341      *
   342      * @param      protocol   the name of the protocol to use.
   343      * @param      host       the name of the host.
   344      * @param      port       the port number on the host.
   345      * @param      file       the file on the host
   346      * @param      handler    the stream handler for the URL.
   347      * @exception  MalformedURLException  if an unknown protocol is specified.
   348      * @exception  SecurityException
   349      *        if a security manager exists and its
   350      *        <code>checkPermission</code> method doesn't allow
   351      *        specifying a stream handler explicitly.
   352      * @see        java.lang.System#getProperty(java.lang.String)
   353      * @see        java.net.URL#setURLStreamHandlerFactory(
   354      *                  java.net.URLStreamHandlerFactory)
   355      * @see        java.net.URLStreamHandler
   356      * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(
   357      *                  java.lang.String)
   358      * @see        SecurityManager#checkPermission
   359      * @see        java.net.NetPermission
   360      */
   361     public URL(String protocol, String host, int port, String file,
   362                URLStreamHandler handler) throws MalformedURLException {
   363         if (handler != null) {
   364             throw new SecurityException();
   365         }
   366 
   367         protocol = protocol.toLowerCase();
   368         this.protocol = protocol;
   369         if (host != null) {
   370 
   371             /**
   372              * if host is a literal IPv6 address,
   373              * we will make it conform to RFC 2732
   374              */
   375             if (host.indexOf(':') >= 0 && !host.startsWith("[")) {
   376                 host = "["+host+"]";
   377             }
   378             this.host = host;
   379 
   380             if (port < -1) {
   381                 throw new MalformedURLException("Invalid port number :" +
   382                                                     port);
   383             }
   384             this.port = port;
   385             authority = (port == -1) ? host : host + ":" + port;
   386         }
   387 
   388         Parts parts = new Parts(file);
   389         path = parts.getPath();
   390         query = parts.getQuery();
   391 
   392         if (query != null) {
   393             this.file = path + "?" + query;
   394         } else {
   395             this.file = path;
   396         }
   397         ref = parts.getRef();
   398 
   399         // Note: we don't do validation of the URL here. Too risky to change
   400         // right now, but worth considering for future reference. -br
   401         if (handler == null &&
   402             (handler = getURLStreamHandler(protocol)) == null) {
   403             throw new MalformedURLException("unknown protocol: " + protocol);
   404         }
   405         this.handler = handler;
   406     }
   407 
   408     /**
   409      * Creates a <code>URL</code> object from the <code>String</code>
   410      * representation.
   411      * <p>
   412      * This constructor is equivalent to a call to the two-argument
   413      * constructor with a <code>null</code> first argument.
   414      *
   415      * @param      spec   the <code>String</code> to parse as a URL.
   416      * @exception  MalformedURLException  if no protocol is specified, or an
   417      *               unknown protocol is found, or <tt>spec</tt> is <tt>null</tt>.
   418      * @see        java.net.URL#URL(java.net.URL, java.lang.String)
   419      */
   420     public URL(String spec) throws MalformedURLException {
   421         this(null, spec);
   422     }
   423 
   424     /**
   425      * Creates a URL by parsing the given spec within a specified context.
   426      *
   427      * The new URL is created from the given context URL and the spec
   428      * argument as described in
   429      * RFC2396 &quot;Uniform Resource Identifiers : Generic * Syntax&quot; :
   430      * <blockquote><pre>
   431      *          &lt;scheme&gt;://&lt;authority&gt;&lt;path&gt;?&lt;query&gt;#&lt;fragment&gt;
   432      * </pre></blockquote>
   433      * The reference is parsed into the scheme, authority, path, query and
   434      * fragment parts. If the path component is empty and the scheme,
   435      * authority, and query components are undefined, then the new URL is a
   436      * reference to the current document. Otherwise, the fragment and query
   437      * parts present in the spec are used in the new URL.
   438      * <p>
   439      * If the scheme component is defined in the given spec and does not match
   440      * the scheme of the context, then the new URL is created as an absolute
   441      * URL based on the spec alone. Otherwise the scheme component is inherited
   442      * from the context URL.
   443      * <p>
   444      * If the authority component is present in the spec then the spec is
   445      * treated as absolute and the spec authority and path will replace the
   446      * context authority and path. If the authority component is absent in the
   447      * spec then the authority of the new URL will be inherited from the
   448      * context.
   449      * <p>
   450      * If the spec's path component begins with a slash character
   451      * &quot;/&quot; then the
   452      * path is treated as absolute and the spec path replaces the context path.
   453      * <p>
   454      * Otherwise, the path is treated as a relative path and is appended to the
   455      * context path, as described in RFC2396. Also, in this case,
   456      * the path is canonicalized through the removal of directory
   457      * changes made by occurences of &quot;..&quot; and &quot;.&quot;.
   458      * <p>
   459      * For a more detailed description of URL parsing, refer to RFC2396.
   460      *
   461      * @param      context   the context in which to parse the specification.
   462      * @param      spec      the <code>String</code> to parse as a URL.
   463      * @exception  MalformedURLException  if no protocol is specified, or an
   464      *               unknown protocol is found, or <tt>spec</tt> is <tt>null</tt>.
   465      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   466      *                  int, java.lang.String)
   467      * @see        java.net.URLStreamHandler
   468      * @see        java.net.URLStreamHandler#parseURL(java.net.URL,
   469      *                  java.lang.String, int, int)
   470      */
   471     public URL(URL context, String spec) throws MalformedURLException {
   472         this(context, spec, null);
   473     }
   474 
   475     /**
   476      * Creates a URL by parsing the given spec with the specified handler
   477      * within a specified context. If the handler is null, the parsing
   478      * occurs as with the two argument constructor.
   479      *
   480      * @param      context   the context in which to parse the specification.
   481      * @param      spec      the <code>String</code> to parse as a URL.
   482      * @param      handler   the stream handler for the URL.
   483      * @exception  MalformedURLException  if no protocol is specified, or an
   484      *               unknown protocol is found, or <tt>spec</tt> is <tt>null</tt>.
   485      * @exception  SecurityException
   486      *        if a security manager exists and its
   487      *        <code>checkPermission</code> method doesn't allow
   488      *        specifying a stream handler.
   489      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   490      *                  int, java.lang.String)
   491      * @see        java.net.URLStreamHandler
   492      * @see        java.net.URLStreamHandler#parseURL(java.net.URL,
   493      *                  java.lang.String, int, int)
   494      */
   495     public URL(URL context, String spec, URLStreamHandler handler)
   496         throws MalformedURLException
   497     {
   498         String original = spec;
   499         int i, limit, c;
   500         int start = 0;
   501         String newProtocol = null;
   502         boolean aRef=false;
   503         boolean isRelative = false;
   504 
   505         // Check for permission to specify a handler
   506         if (handler != null) {
   507             throw new SecurityException();
   508         }
   509 
   510         try {
   511             limit = spec.length();
   512             while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
   513                 limit--;        //eliminate trailing whitespace
   514             }
   515             while ((start < limit) && (spec.charAt(start) <= ' ')) {
   516                 start++;        // eliminate leading whitespace
   517             }
   518 
   519             if (spec.regionMatches(true, start, "url:", 0, 4)) {
   520                 start += 4;
   521             }
   522             if (start < spec.length() && spec.charAt(start) == '#') {
   523                 /* we're assuming this is a ref relative to the context URL.
   524                  * This means protocols cannot start w/ '#', but we must parse
   525                  * ref URL's like: "hello:there" w/ a ':' in them.
   526                  */
   527                 aRef=true;
   528             }
   529             for (i = start ; !aRef && (i < limit) &&
   530                      ((c = spec.charAt(i)) != '/') ; i++) {
   531                 if (c == ':') {
   532 
   533                     String s = spec.substring(start, i).toLowerCase();
   534                     if (isValidProtocol(s)) {
   535                         newProtocol = s;
   536                         start = i + 1;
   537                     }
   538                     break;
   539                 }
   540             }
   541 
   542             // Only use our context if the protocols match.
   543             protocol = newProtocol;
   544             if ((context != null) && ((newProtocol == null) ||
   545                             newProtocol.equalsIgnoreCase(context.protocol))) {
   546                 // inherit the protocol handler from the context
   547                 // if not specified to the constructor
   548                 if (handler == null) {
   549                     handler = context.handler;
   550                 }
   551 
   552                 // If the context is a hierarchical URL scheme and the spec
   553                 // contains a matching scheme then maintain backwards
   554                 // compatibility and treat it as if the spec didn't contain
   555                 // the scheme; see 5.2.3 of RFC2396
   556                 if (context.path != null && context.path.startsWith("/"))
   557                     newProtocol = null;
   558 
   559                 if (newProtocol == null) {
   560                     protocol = context.protocol;
   561                     authority = context.authority;
   562                     userInfo = context.userInfo;
   563                     host = context.host;
   564                     port = context.port;
   565                     file = context.file;
   566                     path = context.path;
   567                     isRelative = true;
   568                 }
   569             }
   570 
   571             if (protocol == null) {
   572                 throw new MalformedURLException("no protocol: "+original);
   573             }
   574 
   575             // Get the protocol handler if not specified or the protocol
   576             // of the context could not be used
   577             if (handler == null &&
   578                 (handler = getURLStreamHandler(protocol)) == null) {
   579                 throw new MalformedURLException("unknown protocol: "+protocol);
   580             }
   581             this.handler = handler;
   582 
   583             i = spec.indexOf('#', start);
   584             if (i >= 0) {
   585 //thrw(protocol + " hnd: " + handler.getClass().getName() + " i: " + i);
   586                 ref = spec.substring(i + 1, limit);
   587                 limit = i;
   588             }
   589 
   590             /*
   591              * Handle special case inheritance of query and fragment
   592              * implied by RFC2396 section 5.2.2.
   593              */
   594             if (isRelative && start == limit) {
   595                 query = context.query;
   596                 if (ref == null) {
   597                     ref = context.ref;
   598                 }
   599             }
   600 
   601             handler.parseURL(this, spec, start, limit);
   602 
   603         } catch(MalformedURLException e) {
   604             throw e;
   605         } catch(Exception e) {
   606             MalformedURLException exception = new MalformedURLException(e.getMessage());
   607             exception.initCause(e);
   608             throw exception;
   609         }
   610     }
   611     
   612     /*
   613      * Returns true if specified string is a valid protocol name.
   614      */
   615     private boolean isValidProtocol(String protocol) {
   616         int len = protocol.length();
   617         if (len < 1)
   618             return false;
   619         char c = protocol.charAt(0);
   620         if (!Character.isLetter(c))
   621             return false;
   622         for (int i = 1; i < len; i++) {
   623             c = protocol.charAt(i);
   624             if (!Character.isLetterOrDigit(c) && c != '.' && c != '+' &&
   625                 c != '-') {
   626                 return false;
   627             }
   628         }
   629         return true;
   630     }
   631 
   632     /**
   633      * Sets the fields of the URL. This is not a public method so that
   634      * only URLStreamHandlers can modify URL fields. URLs are
   635      * otherwise constant.
   636      *
   637      * @param protocol the name of the protocol to use
   638      * @param host the name of the host
   639        @param port the port number on the host
   640      * @param file the file on the host
   641      * @param ref the internal reference in the URL
   642      */
   643     protected void set(String protocol, String host,
   644                        int port, String file, String ref) {
   645         synchronized (this) {
   646             this.protocol = protocol;
   647             this.host = host;
   648             authority = port == -1 ? host : host + ":" + port;
   649             this.port = port;
   650             this.file = file;
   651             this.ref = ref;
   652             /* This is very important. We must recompute this after the
   653              * URL has been changed. */
   654             hashCode = -1;
   655             hostAddress = null;
   656             int q = file.lastIndexOf('?');
   657             if (q != -1) {
   658                 query = file.substring(q+1);
   659                 path = file.substring(0, q);
   660             } else
   661                 path = file;
   662         }
   663     }
   664 
   665     /**
   666      * Sets the specified 8 fields of the URL. This is not a public method so
   667      * that only URLStreamHandlers can modify URL fields. URLs are otherwise
   668      * constant.
   669      *
   670      * @param protocol the name of the protocol to use
   671      * @param host the name of the host
   672      * @param port the port number on the host
   673      * @param authority the authority part for the url
   674      * @param userInfo the username and password
   675      * @param path the file on the host
   676      * @param ref the internal reference in the URL
   677      * @param query the query part of this URL
   678      * @since 1.3
   679      */
   680     protected void set(String protocol, String host, int port,
   681                        String authority, String userInfo, String path,
   682                        String query, String ref) {
   683         synchronized (this) {
   684             this.protocol = protocol;
   685             this.host = host;
   686             this.port = port;
   687             this.file = query == null ? path : path + "?" + query;
   688             this.userInfo = userInfo;
   689             this.path = path;
   690             this.ref = ref;
   691             /* This is very important. We must recompute this after the
   692              * URL has been changed. */
   693             hashCode = -1;
   694             hostAddress = null;
   695             this.query = query;
   696             this.authority = authority;
   697         }
   698     }
   699 
   700     /**
   701      * Gets the query part of this <code>URL</code>.
   702      *
   703      * @return  the query part of this <code>URL</code>,
   704      * or <CODE>null</CODE> if one does not exist
   705      * @since 1.3
   706      */
   707     public String getQuery() {
   708         return query;
   709     }
   710 
   711     /**
   712      * Gets the path part of this <code>URL</code>.
   713      *
   714      * @return  the path part of this <code>URL</code>, or an
   715      * empty string if one does not exist
   716      * @since 1.3
   717      */
   718     public String getPath() {
   719         return path;
   720     }
   721 
   722     /**
   723      * Gets the userInfo part of this <code>URL</code>.
   724      *
   725      * @return  the userInfo part of this <code>URL</code>, or
   726      * <CODE>null</CODE> if one does not exist
   727      * @since 1.3
   728      */
   729     public String getUserInfo() {
   730         return userInfo;
   731     }
   732 
   733     /**
   734      * Gets the authority part of this <code>URL</code>.
   735      *
   736      * @return  the authority part of this <code>URL</code>
   737      * @since 1.3
   738      */
   739     public String getAuthority() {
   740         return authority;
   741     }
   742 
   743     /**
   744      * Gets the port number of this <code>URL</code>.
   745      *
   746      * @return  the port number, or -1 if the port is not set
   747      */
   748     public int getPort() {
   749         return port;
   750     }
   751 
   752     /**
   753      * Gets the default port number of the protocol associated
   754      * with this <code>URL</code>. If the URL scheme or the URLStreamHandler
   755      * for the URL do not define a default port number,
   756      * then -1 is returned.
   757      *
   758      * @return  the port number
   759      * @since 1.4
   760      */
   761     public int getDefaultPort() {
   762         return handler.getDefaultPort();
   763     }
   764 
   765     /**
   766      * Gets the protocol name of this <code>URL</code>.
   767      *
   768      * @return  the protocol of this <code>URL</code>.
   769      */
   770     public String getProtocol() {
   771         return protocol;
   772     }
   773 
   774     /**
   775      * Gets the host name of this <code>URL</code>, if applicable.
   776      * The format of the host conforms to RFC 2732, i.e. for a
   777      * literal IPv6 address, this method will return the IPv6 address
   778      * enclosed in square brackets (<tt>'['</tt> and <tt>']'</tt>).
   779      *
   780      * @return  the host name of this <code>URL</code>.
   781      */
   782     public String getHost() {
   783         return host;
   784     }
   785 
   786     /**
   787      * Gets the file name of this <code>URL</code>.
   788      * The returned file portion will be
   789      * the same as <CODE>getPath()</CODE>, plus the concatenation of
   790      * the value of <CODE>getQuery()</CODE>, if any. If there is
   791      * no query portion, this method and <CODE>getPath()</CODE> will
   792      * return identical results.
   793      *
   794      * @return  the file name of this <code>URL</code>,
   795      * or an empty string if one does not exist
   796      */
   797     public String getFile() {
   798         return file;
   799     }
   800 
   801     /**
   802      * Gets the anchor (also known as the "reference") of this
   803      * <code>URL</code>.
   804      *
   805      * @return  the anchor (also known as the "reference") of this
   806      *          <code>URL</code>, or <CODE>null</CODE> if one does not exist
   807      */
   808     public String getRef() {
   809         return ref;
   810     }
   811 
   812     /**
   813      * Compares this URL for equality with another object.<p>
   814      *
   815      * If the given object is not a URL then this method immediately returns
   816      * <code>false</code>.<p>
   817      *
   818      * Two URL objects are equal if they have the same protocol, reference
   819      * equivalent hosts, have the same port number on the host, and the same
   820      * file and fragment of the file.<p>
   821      *
   822      * Two hosts are considered equivalent if both host names can be resolved
   823      * into the same IP addresses; else if either host name can't be
   824      * resolved, the host names must be equal without regard to case; or both
   825      * host names equal to null.<p>
   826      *
   827      * Since hosts comparison requires name resolution, this operation is a
   828      * blocking operation. <p>
   829      *
   830      * Note: The defined behavior for <code>equals</code> is known to
   831      * be inconsistent with virtual hosting in HTTP.
   832      *
   833      * @param   obj   the URL to compare against.
   834      * @return  <code>true</code> if the objects are the same;
   835      *          <code>false</code> otherwise.
   836      */
   837     public boolean equals(Object obj) {
   838         if (!(obj instanceof URL))
   839             return false;
   840         URL u2 = (URL)obj;
   841 
   842         return handler.equals(this, u2);
   843     }
   844 
   845     /**
   846      * Creates an integer suitable for hash table indexing.<p>
   847      *
   848      * The hash code is based upon all the URL components relevant for URL
   849      * comparison. As such, this operation is a blocking operation.<p>
   850      *
   851      * @return  a hash code for this <code>URL</code>.
   852      */
   853     public synchronized int hashCode() {
   854         if (hashCode != -1)
   855             return hashCode;
   856 
   857         hashCode = handler.hashCode(this);
   858         return hashCode;
   859     }
   860 
   861     /**
   862      * Compares two URLs, excluding the fragment component.<p>
   863      *
   864      * Returns <code>true</code> if this <code>URL</code> and the
   865      * <code>other</code> argument are equal without taking the
   866      * fragment component into consideration.
   867      *
   868      * @param   other   the <code>URL</code> to compare against.
   869      * @return  <code>true</code> if they reference the same remote object;
   870      *          <code>false</code> otherwise.
   871      */
   872     public boolean sameFile(URL other) {
   873         return handler.sameFile(this, other);
   874     }
   875 
   876     /**
   877      * Constructs a string representation of this <code>URL</code>. The
   878      * string is created by calling the <code>toExternalForm</code>
   879      * method of the stream protocol handler for this object.
   880      *
   881      * @return  a string representation of this object.
   882      * @see     java.net.URL#URL(java.lang.String, java.lang.String, int,
   883      *                  java.lang.String)
   884      * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
   885      */
   886     public String toString() {
   887         return toExternalForm();
   888     }
   889 
   890     /**
   891      * Constructs a string representation of this <code>URL</code>. The
   892      * string is created by calling the <code>toExternalForm</code>
   893      * method of the stream protocol handler for this object.
   894      *
   895      * @return  a string representation of this object.
   896      * @see     java.net.URL#URL(java.lang.String, java.lang.String,
   897      *                  int, java.lang.String)
   898      * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
   899      */
   900     public String toExternalForm() {
   901         return handler.toExternalForm(this);
   902     }
   903 
   904     /**
   905      * Returns a {@link java.net.URLConnection URLConnection} instance that
   906      * represents a connection to the remote object referred to by the
   907      * {@code URL}.
   908      *
   909      * <P>A new instance of {@linkplain java.net.URLConnection URLConnection} is
   910      * created every time when invoking the
   911      * {@linkplain java.net.URLStreamHandler#openConnection(URL)
   912      * URLStreamHandler.openConnection(URL)} method of the protocol handler for
   913      * this URL.</P>
   914      *
   915      * <P>It should be noted that a URLConnection instance does not establish
   916      * the actual network connection on creation. This will happen only when
   917      * calling {@linkplain java.net.URLConnection#connect() URLConnection.connect()}.</P>
   918      *
   919      * <P>If for the URL's protocol (such as HTTP or JAR), there
   920      * exists a public, specialized URLConnection subclass belonging
   921      * to one of the following packages or one of their subpackages:
   922      * java.lang, java.io, java.util, java.net, the connection
   923      * returned will be of that subclass. For example, for HTTP an
   924      * HttpURLConnection will be returned, and for JAR a
   925      * JarURLConnection will be returned.</P>
   926      *
   927      * @return     a {@link java.net.URLConnection URLConnection} linking
   928      *             to the URL.
   929      * @exception  IOException  if an I/O exception occurs.
   930      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   931      *             int, java.lang.String)
   932      */
   933 //    public URLConnection openConnection() throws java.io.IOException {
   934 //        return handler.openConnection(this);
   935 //    }
   936 
   937 
   938     /**
   939      * Opens a connection to this <code>URL</code> and returns an
   940      * <code>InputStream</code> for reading from that connection. This
   941      * method is a shorthand for:
   942      * <blockquote><pre>
   943      *     openConnection().getInputStream()
   944      * </pre></blockquote>
   945      *
   946      * @return     an input stream for reading from the URL connection.
   947      * @exception  IOException  if an I/O exception occurs.
   948      * @see        java.net.URL#openConnection()
   949      * @see        java.net.URLConnection#getInputStream()
   950      */
   951     public final InputStream openStream() throws java.io.IOException {
   952         throw new IOException();
   953 //        return openConnection().getInputStream();
   954     }
   955 
   956     /**
   957      * Gets the contents of this URL. This method is a shorthand for:
   958      * <blockquote><pre>
   959      *     openConnection().getContent()
   960      * </pre></blockquote>
   961      *
   962      * @return     the contents of this URL.
   963      * @exception  IOException  if an I/O exception occurs.
   964      * @see        java.net.URLConnection#getContent()
   965      */
   966     public final Object getContent() throws java.io.IOException {
   967         throw new IOException();
   968 //        return openConnection().getContent();
   969     }
   970 
   971     /**
   972      * Gets the contents of this URL. This method is a shorthand for:
   973      * <blockquote><pre>
   974      *     openConnection().getContent(Class[])
   975      * </pre></blockquote>
   976      *
   977      * @param classes an array of Java types
   978      * @return     the content object of this URL that is the first match of
   979      *               the types specified in the classes array.
   980      *               null if none of the requested types are supported.
   981      * @exception  IOException  if an I/O exception occurs.
   982      * @see        java.net.URLConnection#getContent(Class[])
   983      * @since 1.3
   984      */
   985     public final Object getContent(Class[] classes)
   986     throws java.io.IOException {
   987         throw new IOException();
   988 //        return openConnection().getContent(classes);
   989     }
   990 
   991     static URLStreamHandler getURLStreamHandler(String protocol) {
   992         Class<URLStreamHandler> c = URLStreamHandler.class; // XXX only here to pre-initialize URLStreamHandler
   993         URLStreamHandler universal = new URLStreamHandler() {};
   994         return universal;
   995     }
   996 
   997 }
   998 
   999 class Parts {
  1000     String path, query, ref;
  1001 
  1002     Parts(String file) {
  1003         int ind = file.indexOf('#');
  1004         ref = ind < 0 ? null: file.substring(ind + 1);
  1005         file = ind < 0 ? file: file.substring(0, ind);
  1006         int q = file.lastIndexOf('?');
  1007         if (q != -1) {
  1008             query = file.substring(q+1);
  1009             path = file.substring(0, q);
  1010         } else {
  1011             path = file;
  1012         }
  1013     }
  1014 
  1015     String getPath() {
  1016         return path;
  1017     }
  1018 
  1019     String getQuery() {
  1020         return query;
  1021     }
  1022 
  1023     String getRef() {
  1024         return ref;
  1025     }
  1026 }