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