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