emul/src/main/java/java/net/URL.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 30 Oct 2012 09:24:41 +0100
changeset 122 0a582b5a2737
parent 120 d739cdce3891
child 339 7579a0ee92fb
permissions -rw-r--r--
Merging in support for Class and ClassLoader operations
     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     /* Our hash code.
   200      * @serial
   201      */
   202     private int hashCode = -1;
   203 
   204     /**
   205      * Creates a <code>URL</code> object from the specified
   206      * <code>protocol</code>, <code>host</code>, <code>port</code>
   207      * number, and <code>file</code>.<p>
   208      *
   209      * <code>host</code> can be expressed as a host name or a literal
   210      * IP address. If IPv6 literal address is used, it should be
   211      * enclosed in square brackets (<tt>'['</tt> and <tt>']'</tt>), as
   212      * specified by <a
   213      * href="http://www.ietf.org/rfc/rfc2732.txt">RFC&nbsp;2732</a>;
   214      * However, the literal IPv6 address format defined in <a
   215      * href="http://www.ietf.org/rfc/rfc2373.txt"><i>RFC&nbsp;2373: IP
   216      * Version 6 Addressing Architecture</i></a> is also accepted.<p>
   217      *
   218      * Specifying a <code>port</code> number of <code>-1</code>
   219      * indicates that the URL should use the default port for the
   220      * protocol.<p>
   221      *
   222      * If this is the first URL object being created with the specified
   223      * protocol, a <i>stream protocol handler</i> object, an instance of
   224      * class <code>URLStreamHandler</code>, is created for that protocol:
   225      * <ol>
   226      * <li>If the application has previously set up an instance of
   227      *     <code>URLStreamHandlerFactory</code> as the stream handler factory,
   228      *     then the <code>createURLStreamHandler</code> method of that instance
   229      *     is called with the protocol string as an argument to create the
   230      *     stream protocol handler.
   231      * <li>If no <code>URLStreamHandlerFactory</code> has yet been set up,
   232      *     or if the factory's <code>createURLStreamHandler</code> method
   233      *     returns <code>null</code>, then the constructor finds the
   234      *     value of the system property:
   235      *     <blockquote><pre>
   236      *         java.protocol.handler.pkgs
   237      *     </pre></blockquote>
   238      *     If the value of that system property is not <code>null</code>,
   239      *     it is interpreted as a list of packages separated by a vertical
   240      *     slash character '<code>|</code>'. The constructor tries to load
   241      *     the class named:
   242      *     <blockquote><pre>
   243      *         &lt;<i>package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler
   244      *     </pre></blockquote>
   245      *     where &lt;<i>package</i>&gt; is replaced by the name of the package
   246      *     and &lt;<i>protocol</i>&gt; is replaced by the name of the protocol.
   247      *     If this class does not exist, or if the class exists but it is not
   248      *     a subclass of <code>URLStreamHandler</code>, then the next package
   249      *     in the list is tried.
   250      * <li>If the previous step fails to find a protocol handler, then the
   251      *     constructor tries to load from a system default package.
   252      *     <blockquote><pre>
   253      *         &lt;<i>system default package</i>&gt;.&lt;<i>protocol</i>&gt;.Handler
   254      *     </pre></blockquote>
   255      *     If this class does not exist, or if the class exists but it is not a
   256      *     subclass of <code>URLStreamHandler</code>, then a
   257      *     <code>MalformedURLException</code> is thrown.
   258      * </ol>
   259      *
   260      * <p>Protocol handlers for the following protocols are guaranteed
   261      * to exist on the search path :-
   262      * <blockquote><pre>
   263      *     http, https, ftp, file, and jar
   264      * </pre></blockquote>
   265      * Protocol handlers for additional protocols may also be
   266      * available.
   267      *
   268      * <p>No validation of the inputs is performed by this constructor.
   269      *
   270      * @param      protocol   the name of the protocol to use.
   271      * @param      host       the name of the host.
   272      * @param      port       the port number on the host.
   273      * @param      file       the file on the host
   274      * @exception  MalformedURLException  if an unknown protocol is specified.
   275      * @see        java.lang.System#getProperty(java.lang.String)
   276      * @see        java.net.URL#setURLStreamHandlerFactory(
   277      *                  java.net.URLStreamHandlerFactory)
   278      * @see        java.net.URLStreamHandler
   279      * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(
   280      *                  java.lang.String)
   281      */
   282     public URL(String protocol, String host, int port, String file)
   283         throws MalformedURLException
   284     {
   285         this(protocol, host, port, file, null);
   286     }
   287 
   288     /**
   289      * Creates a URL from the specified <code>protocol</code>
   290      * name, <code>host</code> name, and <code>file</code> name. The
   291      * default port for the specified protocol is used.
   292      * <p>
   293      * This method is equivalent to calling the four-argument
   294      * constructor with the arguments being <code>protocol</code>,
   295      * <code>host</code>, <code>-1</code>, and <code>file</code>.
   296      *
   297      * No validation of the inputs is performed by this constructor.
   298      *
   299      * @param      protocol   the name of the protocol to use.
   300      * @param      host       the name of the host.
   301      * @param      file       the file on the host.
   302      * @exception  MalformedURLException  if an unknown protocol is specified.
   303      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   304      *                  int, java.lang.String)
   305      */
   306     public URL(String protocol, String host, String file)
   307             throws MalformedURLException {
   308         this(protocol, host, -1, file);
   309     }
   310 
   311     private URL(String protocol, String host, int port, String file,
   312                Object handler) throws MalformedURLException {
   313         if (handler != null) {
   314             throw new SecurityException();
   315         }
   316 
   317         protocol = protocol.toLowerCase();
   318         this.protocol = protocol;
   319         if (host != null) {
   320 
   321             /**
   322              * if host is a literal IPv6 address,
   323              * we will make it conform to RFC 2732
   324              */
   325             if (host.indexOf(':') >= 0 && !host.startsWith("[")) {
   326                 host = "["+host+"]";
   327             }
   328             this.host = host;
   329 
   330             if (port < -1) {
   331                 throw new MalformedURLException("Invalid port number :" +
   332                                                     port);
   333             }
   334             this.port = port;
   335             authority = (port == -1) ? host : host + ":" + port;
   336         }
   337 
   338         Parts parts = new Parts(file);
   339         path = parts.getPath();
   340         query = parts.getQuery();
   341 
   342         if (query != null) {
   343             this.file = path + "?" + query;
   344         } else {
   345             this.file = path;
   346         }
   347         ref = parts.getRef();
   348 
   349         // Note: we don't do validation of the URL here. Too risky to change
   350         // right now, but worth considering for future reference. -br
   351 //        if (handler == null &&
   352 //            (handler = getURLStreamHandler(protocol)) == null) {
   353 //            throw new MalformedURLException("unknown protocol: " + protocol);
   354 //        }
   355     }
   356 
   357     /**
   358      * Creates a <code>URL</code> object from the <code>String</code>
   359      * representation.
   360      * <p>
   361      * This constructor is equivalent to a call to the two-argument
   362      * constructor with a <code>null</code> first argument.
   363      *
   364      * @param      spec   the <code>String</code> to parse as a URL.
   365      * @exception  MalformedURLException  if no protocol is specified, or an
   366      *               unknown protocol is found, or <tt>spec</tt> is <tt>null</tt>.
   367      * @see        java.net.URL#URL(java.net.URL, java.lang.String)
   368      */
   369     public URL(String spec) throws MalformedURLException {
   370         this(null, spec);
   371     }
   372 
   373     /**
   374      * Creates a URL by parsing the given spec within a specified context.
   375      *
   376      * The new URL is created from the given context URL and the spec
   377      * argument as described in
   378      * RFC2396 &quot;Uniform Resource Identifiers : Generic * Syntax&quot; :
   379      * <blockquote><pre>
   380      *          &lt;scheme&gt;://&lt;authority&gt;&lt;path&gt;?&lt;query&gt;#&lt;fragment&gt;
   381      * </pre></blockquote>
   382      * The reference is parsed into the scheme, authority, path, query and
   383      * fragment parts. If the path component is empty and the scheme,
   384      * authority, and query components are undefined, then the new URL is a
   385      * reference to the current document. Otherwise, the fragment and query
   386      * parts present in the spec are used in the new URL.
   387      * <p>
   388      * If the scheme component is defined in the given spec and does not match
   389      * the scheme of the context, then the new URL is created as an absolute
   390      * URL based on the spec alone. Otherwise the scheme component is inherited
   391      * from the context URL.
   392      * <p>
   393      * If the authority component is present in the spec then the spec is
   394      * treated as absolute and the spec authority and path will replace the
   395      * context authority and path. If the authority component is absent in the
   396      * spec then the authority of the new URL will be inherited from the
   397      * context.
   398      * <p>
   399      * If the spec's path component begins with a slash character
   400      * &quot;/&quot; then the
   401      * path is treated as absolute and the spec path replaces the context path.
   402      * <p>
   403      * Otherwise, the path is treated as a relative path and is appended to the
   404      * context path, as described in RFC2396. Also, in this case,
   405      * the path is canonicalized through the removal of directory
   406      * changes made by occurences of &quot;..&quot; and &quot;.&quot;.
   407      * <p>
   408      * For a more detailed description of URL parsing, refer to RFC2396.
   409      *
   410      * @param      context   the context in which to parse the specification.
   411      * @param      spec      the <code>String</code> to parse as a URL.
   412      * @exception  MalformedURLException  if no protocol is specified, or an
   413      *               unknown protocol is found, or <tt>spec</tt> is <tt>null</tt>.
   414      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   415      *                  int, java.lang.String)
   416      * @see        java.net.URLStreamHandler
   417      * @see        java.net.URLStreamHandler#parseURL(java.net.URL,
   418      *                  java.lang.String, int, int)
   419      */
   420     public URL(URL context, String spec) throws MalformedURLException {
   421         this(context, spec, null);
   422     }
   423 
   424     /**
   425      * Creates a URL by parsing the given spec with the specified handler
   426      * within a specified context. If the handler is null, the parsing
   427      * occurs as with the two argument constructor.
   428      *
   429      * @param      context   the context in which to parse the specification.
   430      * @param      spec      the <code>String</code> to parse as a URL.
   431      * @param      handler   the stream handler for the URL.
   432      * @exception  MalformedURLException  if no protocol is specified, or an
   433      *               unknown protocol is found, or <tt>spec</tt> is <tt>null</tt>.
   434      * @exception  SecurityException
   435      *        if a security manager exists and its
   436      *        <code>checkPermission</code> method doesn't allow
   437      *        specifying a stream handler.
   438      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   439      *                  int, java.lang.String)
   440      * @see        java.net.URLStreamHandler
   441      * @see        java.net.URLStreamHandler#parseURL(java.net.URL,
   442      *                  java.lang.String, int, int)
   443      */
   444     private URL(URL context, String spec, Object handler)
   445         throws MalformedURLException
   446     {
   447         String original = spec;
   448         int i, limit, c;
   449         int start = 0;
   450         String newProtocol = null;
   451         boolean aRef=false;
   452         boolean isRelative = false;
   453 
   454         // Check for permission to specify a handler
   455         if (handler != null) {
   456             throw new SecurityException();
   457         }
   458 
   459         try {
   460             limit = spec.length();
   461             while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
   462                 limit--;        //eliminate trailing whitespace
   463             }
   464             while ((start < limit) && (spec.charAt(start) <= ' ')) {
   465                 start++;        // eliminate leading whitespace
   466             }
   467 
   468             if (spec.regionMatches(true, start, "url:", 0, 4)) {
   469                 start += 4;
   470             }
   471             if (start < spec.length() && spec.charAt(start) == '#') {
   472                 /* we're assuming this is a ref relative to the context URL.
   473                  * This means protocols cannot start w/ '#', but we must parse
   474                  * ref URL's like: "hello:there" w/ a ':' in them.
   475                  */
   476                 aRef=true;
   477             }
   478             for (i = start ; !aRef && (i < limit) &&
   479                      ((c = spec.charAt(i)) != '/') ; i++) {
   480                 if (c == ':') {
   481 
   482                     String s = spec.substring(start, i).toLowerCase();
   483                     if (isValidProtocol(s)) {
   484                         newProtocol = s;
   485                         start = i + 1;
   486                     }
   487                     break;
   488                 }
   489             }
   490 
   491             // Only use our context if the protocols match.
   492             protocol = newProtocol;
   493             if ((context != null) && ((newProtocol == null) ||
   494                             newProtocol.equalsIgnoreCase(context.protocol))) {
   495                 // inherit the protocol handler from the context
   496                 // if not specified to the constructor
   497 //                if (handler == null) {
   498 //                    handler = context.handler;
   499 //                }
   500 
   501                 // If the context is a hierarchical URL scheme and the spec
   502                 // contains a matching scheme then maintain backwards
   503                 // compatibility and treat it as if the spec didn't contain
   504                 // the scheme; see 5.2.3 of RFC2396
   505                 if (context.path != null && context.path.startsWith("/"))
   506                     newProtocol = null;
   507 
   508                 if (newProtocol == null) {
   509                     protocol = context.protocol;
   510                     authority = context.authority;
   511                     userInfo = context.userInfo;
   512                     host = context.host;
   513                     port = context.port;
   514                     file = context.file;
   515                     path = context.path;
   516                     isRelative = true;
   517                 }
   518             }
   519 
   520             if (protocol == null) {
   521                 throw new MalformedURLException("no protocol: "+original);
   522             }
   523 
   524             // Get the protocol handler if not specified or the protocol
   525             // of the context could not be used
   526 //            if (handler == null &&
   527 //                (handler = getURLStreamHandler(protocol)) == null) {
   528 //                throw new MalformedURLException("unknown protocol: "+protocol);
   529 //            }
   530 
   531 //            this.handler = handler;
   532 
   533             i = spec.indexOf('#', start);
   534             if (i >= 0) {
   535                 ref = spec.substring(i + 1, limit);
   536                 limit = i;
   537             }
   538 
   539             /*
   540              * Handle special case inheritance of query and fragment
   541              * implied by RFC2396 section 5.2.2.
   542              */
   543             if (isRelative && start == limit) {
   544                 query = context.query;
   545                 if (ref == null) {
   546                     ref = context.ref;
   547                 }
   548             }
   549 
   550 //            handler.parseURL(this, spec, start, limit);
   551 
   552         } catch(MalformedURLException e) {
   553             throw e;
   554         } catch(Exception e) {
   555             MalformedURLException exception = new MalformedURLException(e.getMessage());
   556             exception.initCause(e);
   557             throw exception;
   558         }
   559     }
   560 
   561     /*
   562      * Returns true if specified string is a valid protocol name.
   563      */
   564     private boolean isValidProtocol(String protocol) {
   565         int len = protocol.length();
   566         if (len < 1)
   567             return false;
   568         char c = protocol.charAt(0);
   569         if (!Character.isLetter(c))
   570             return false;
   571         for (int i = 1; i < len; i++) {
   572             c = protocol.charAt(i);
   573             if (!Character.isLetterOrDigit(c) && c != '.' && c != '+' &&
   574                 c != '-') {
   575                 return false;
   576             }
   577         }
   578         return true;
   579     }
   580 
   581     /**
   582      * Sets the fields of the URL. This is not a public method so that
   583      * only URLStreamHandlers can modify URL fields. URLs are
   584      * otherwise constant.
   585      *
   586      * @param protocol the name of the protocol to use
   587      * @param host the name of the host
   588        @param port the port number on the host
   589      * @param file the file on the host
   590      * @param ref the internal reference in the URL
   591      */
   592     protected void set(String protocol, String host,
   593                        int port, String file, String ref) {
   594         synchronized (this) {
   595             this.protocol = protocol;
   596             this.host = host;
   597             authority = port == -1 ? host : host + ":" + port;
   598             this.port = port;
   599             this.file = file;
   600             this.ref = ref;
   601             /* This is very important. We must recompute this after the
   602              * URL has been changed. */
   603             hashCode = -1;
   604             int q = file.lastIndexOf('?');
   605             if (q != -1) {
   606                 query = file.substring(q+1);
   607                 path = file.substring(0, q);
   608             } else
   609                 path = file;
   610         }
   611     }
   612 
   613     /**
   614      * Sets the specified 8 fields of the URL. This is not a public method so
   615      * that only URLStreamHandlers can modify URL fields. URLs are otherwise
   616      * constant.
   617      *
   618      * @param protocol the name of the protocol to use
   619      * @param host the name of the host
   620      * @param port the port number on the host
   621      * @param authority the authority part for the url
   622      * @param userInfo the username and password
   623      * @param path the file on the host
   624      * @param ref the internal reference in the URL
   625      * @param query the query part of this URL
   626      * @since 1.3
   627      */
   628     protected void set(String protocol, String host, int port,
   629                        String authority, String userInfo, String path,
   630                        String query, String ref) {
   631         synchronized (this) {
   632             this.protocol = protocol;
   633             this.host = host;
   634             this.port = port;
   635             this.file = query == null ? path : path + "?" + query;
   636             this.userInfo = userInfo;
   637             this.path = path;
   638             this.ref = ref;
   639             /* This is very important. We must recompute this after the
   640              * URL has been changed. */
   641             hashCode = -1;
   642             this.query = query;
   643             this.authority = authority;
   644         }
   645     }
   646 
   647     /**
   648      * Gets the query part of this <code>URL</code>.
   649      *
   650      * @return  the query part of this <code>URL</code>,
   651      * or <CODE>null</CODE> if one does not exist
   652      * @since 1.3
   653      */
   654     public String getQuery() {
   655         return query;
   656     }
   657 
   658     /**
   659      * Gets the path part of this <code>URL</code>.
   660      *
   661      * @return  the path part of this <code>URL</code>, or an
   662      * empty string if one does not exist
   663      * @since 1.3
   664      */
   665     public String getPath() {
   666         return path;
   667     }
   668 
   669     /**
   670      * Gets the userInfo part of this <code>URL</code>.
   671      *
   672      * @return  the userInfo part of this <code>URL</code>, or
   673      * <CODE>null</CODE> if one does not exist
   674      * @since 1.3
   675      */
   676     public String getUserInfo() {
   677         return userInfo;
   678     }
   679 
   680     /**
   681      * Gets the authority part of this <code>URL</code>.
   682      *
   683      * @return  the authority part of this <code>URL</code>
   684      * @since 1.3
   685      */
   686     public String getAuthority() {
   687         return authority;
   688     }
   689 
   690     /**
   691      * Gets the port number of this <code>URL</code>.
   692      *
   693      * @return  the port number, or -1 if the port is not set
   694      */
   695     public int getPort() {
   696         return port;
   697     }
   698 
   699     /**
   700      * Gets the protocol name of this <code>URL</code>.
   701      *
   702      * @return  the protocol of this <code>URL</code>.
   703      */
   704     public String getProtocol() {
   705         return protocol;
   706     }
   707 
   708     /**
   709      * Gets the host name of this <code>URL</code>, if applicable.
   710      * The format of the host conforms to RFC 2732, i.e. for a
   711      * literal IPv6 address, this method will return the IPv6 address
   712      * enclosed in square brackets (<tt>'['</tt> and <tt>']'</tt>).
   713      *
   714      * @return  the host name of this <code>URL</code>.
   715      */
   716     public String getHost() {
   717         return host;
   718     }
   719 
   720     /**
   721      * Gets the file name of this <code>URL</code>.
   722      * The returned file portion will be
   723      * the same as <CODE>getPath()</CODE>, plus the concatenation of
   724      * the value of <CODE>getQuery()</CODE>, if any. If there is
   725      * no query portion, this method and <CODE>getPath()</CODE> will
   726      * return identical results.
   727      *
   728      * @return  the file name of this <code>URL</code>,
   729      * or an empty string if one does not exist
   730      */
   731     public String getFile() {
   732         return file;
   733     }
   734 
   735     /**
   736      * Gets the anchor (also known as the "reference") of this
   737      * <code>URL</code>.
   738      *
   739      * @return  the anchor (also known as the "reference") of this
   740      *          <code>URL</code>, or <CODE>null</CODE> if one does not exist
   741      */
   742     public String getRef() {
   743         return ref;
   744     }
   745 
   746     /**
   747      * Compares this URL for equality with another object.<p>
   748      *
   749      * If the given object is not a URL then this method immediately returns
   750      * <code>false</code>.<p>
   751      *
   752      * Two URL objects are equal if they have the same protocol, reference
   753      * equivalent hosts, have the same port number on the host, and the same
   754      * file and fragment of the file.<p>
   755      *
   756      * Two hosts are considered equivalent if both host names can be resolved
   757      * into the same IP addresses; else if either host name can't be
   758      * resolved, the host names must be equal without regard to case; or both
   759      * host names equal to null.<p>
   760      *
   761      * Since hosts comparison requires name resolution, this operation is a
   762      * blocking operation. <p>
   763      *
   764      * Note: The defined behavior for <code>equals</code> is known to
   765      * be inconsistent with virtual hosting in HTTP.
   766      *
   767      * @param   obj   the URL to compare against.
   768      * @return  <code>true</code> if the objects are the same;
   769      *          <code>false</code> otherwise.
   770      */
   771     public boolean equals(Object obj) {
   772         if (!(obj instanceof URL))
   773             return false;
   774         URL u2 = (URL)obj;
   775 
   776      //   return handler.equals(this, u2);
   777         return u2 == this;
   778     }
   779 
   780     /**
   781      * Creates an integer suitable for hash table indexing.<p>
   782      *
   783      * The hash code is based upon all the URL components relevant for URL
   784      * comparison. As such, this operation is a blocking operation.<p>
   785      *
   786      * @return  a hash code for this <code>URL</code>.
   787      */
   788     public synchronized int hashCode() {
   789         if (hashCode != -1)
   790             return hashCode;
   791 
   792      //   hashCode = handler.hashCode(this);
   793         return hashCode;
   794     }
   795 
   796     /**
   797      * Compares two URLs, excluding the fragment component.<p>
   798      *
   799      * Returns <code>true</code> if this <code>URL</code> and the
   800      * <code>other</code> argument are equal without taking the
   801      * fragment component into consideration.
   802      *
   803      * @param   other   the <code>URL</code> to compare against.
   804      * @return  <code>true</code> if they reference the same remote object;
   805      *          <code>false</code> otherwise.
   806      */
   807     public boolean sameFile(URL other) {
   808 //        return handler.sameFile(this, other);
   809         throw new UnsupportedOperationException();
   810     }
   811 
   812     /**
   813      * Constructs a string representation of this <code>URL</code>. The
   814      * string is created by calling the <code>toExternalForm</code>
   815      * method of the stream protocol handler for this object.
   816      *
   817      * @return  a string representation of this object.
   818      * @see     java.net.URL#URL(java.lang.String, java.lang.String, int,
   819      *                  java.lang.String)
   820      * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
   821      */
   822     public String toString() {
   823         return toExternalForm();
   824     }
   825 
   826     /**
   827      * Constructs a string representation of this <code>URL</code>. The
   828      * string is created by calling the <code>toExternalForm</code>
   829      * method of the stream protocol handler for this object.
   830      *
   831      * @return  a string representation of this object.
   832      * @see     java.net.URL#URL(java.lang.String, java.lang.String,
   833      *                  int, java.lang.String)
   834      * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
   835      */
   836     public String toExternalForm() {
   837         throw new UnsupportedOperationException();
   838 //        return handler.toExternalForm(this);
   839     }
   840 
   841     /**
   842      * Returns a {@link java.net.URLConnection URLConnection} instance that
   843      * represents a connection to the remote object referred to by the
   844      * {@code URL}.
   845      *
   846      * <P>A new instance of {@linkplain java.net.URLConnection URLConnection} is
   847      * created every time when invoking the
   848      * {@linkplain java.net.URLStreamHandler#openConnection(URL)
   849      * URLStreamHandler.openConnection(URL)} method of the protocol handler for
   850      * this URL.</P>
   851      *
   852      * <P>It should be noted that a URLConnection instance does not establish
   853      * the actual network connection on creation. This will happen only when
   854      * calling {@linkplain java.net.URLConnection#connect() URLConnection.connect()}.</P>
   855      *
   856      * <P>If for the URL's protocol (such as HTTP or JAR), there
   857      * exists a public, specialized URLConnection subclass belonging
   858      * to one of the following packages or one of their subpackages:
   859      * java.lang, java.io, java.util, java.net, the connection
   860      * returned will be of that subclass. For example, for HTTP an
   861      * HttpURLConnection will be returned, and for JAR a
   862      * JarURLConnection will be returned.</P>
   863      *
   864      * @return     a {@link java.net.URLConnection URLConnection} linking
   865      *             to the URL.
   866      * @exception  IOException  if an I/O exception occurs.
   867      * @see        java.net.URL#URL(java.lang.String, java.lang.String,
   868      *             int, java.lang.String)
   869      */
   870 //    public URLConnection openConnection() throws java.io.IOException {
   871 //        return handler.openConnection(this);
   872 //    }
   873 
   874 
   875     /**
   876      * Opens a connection to this <code>URL</code> and returns an
   877      * <code>InputStream</code> for reading from that connection. This
   878      * method is a shorthand for:
   879      * <blockquote><pre>
   880      *     openConnection().getInputStream()
   881      * </pre></blockquote>
   882      *
   883      * @return     an input stream for reading from the URL connection.
   884      * @exception  IOException  if an I/O exception occurs.
   885      * @see        java.net.URL#openConnection()
   886      * @see        java.net.URLConnection#getInputStream()
   887      */
   888     public final InputStream openStream() throws java.io.IOException {
   889         throw new IOException();
   890 //        return openConnection().getInputStream();
   891     }
   892 
   893     /**
   894      * Gets the contents of this URL. This method is a shorthand for:
   895      * <blockquote><pre>
   896      *     openConnection().getContent()
   897      * </pre></blockquote>
   898      *
   899      * @return     the contents of this URL.
   900      * @exception  IOException  if an I/O exception occurs.
   901      * @see        java.net.URLConnection#getContent()
   902      */
   903     public final Object getContent() throws java.io.IOException {
   904         throw new IOException();
   905 //        return openConnection().getContent();
   906     }
   907 
   908     /**
   909      * Gets the contents of this URL. This method is a shorthand for:
   910      * <blockquote><pre>
   911      *     openConnection().getContent(Class[])
   912      * </pre></blockquote>
   913      *
   914      * @param classes an array of Java types
   915      * @return     the content object of this URL that is the first match of
   916      *               the types specified in the classes array.
   917      *               null if none of the requested types are supported.
   918      * @exception  IOException  if an I/O exception occurs.
   919      * @see        java.net.URLConnection#getContent(Class[])
   920      * @since 1.3
   921      */
   922     public final Object getContent(Class[] classes)
   923     throws java.io.IOException {
   924         throw new IOException();
   925 //        return openConnection().getContent(classes);
   926     }
   927 
   928 
   929 }
   930 
   931 class Parts {
   932     String path, query, ref;
   933 
   934     Parts(String file) {
   935         int ind = file.indexOf('#');
   936         ref = ind < 0 ? null: file.substring(ind + 1);
   937         file = ind < 0 ? file: file.substring(0, ind);
   938         int q = file.lastIndexOf('?');
   939         if (q != -1) {
   940             query = file.substring(q+1);
   941             path = file.substring(0, q);
   942         } else {
   943             path = file;
   944         }
   945     }
   946 
   947     String getPath() {
   948         return path;
   949     }
   950 
   951     String getQuery() {
   952         return query;
   953     }
   954 
   955     String getRef() {
   956         return ref;
   957     }
   958 }