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