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