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