Home | History | Annotate | Download | only in net
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.net;
     19 
     20 import java.io.IOException;
     21 import java.io.InputStream;
     22 import java.io.ObjectOutputStream;
     23 import java.security.AccessController;
     24 import java.util.Hashtable;
     25 import java.util.StringTokenizer;
     26 import org.apache.harmony.luni.util.PriviAction;
     27 import org.apache.harmony.luni.util.Util;
     28 
     29 /**
     30  * A URL instance specifies the location of a resource on the internet as
     31  * specified by RFC 1738. Such a resource can be a simple file or a service
     32  * which generates the output dynamically. A URL is divided in its parts
     33  * protocol, host name, port, path, file, user-info, query, reference and
     34  * authority. However, not each of this parts has to be defined.
     35  */
     36 public final class URL implements java.io.Serializable {
     37     private static final long serialVersionUID = -7627629688361524110L;
     38 
     39     private static final NetPermission specifyStreamHandlerPermission = new NetPermission(
     40             "specifyStreamHandler");
     41 
     42     private int hashCode;
     43 
     44     /**
     45      * The receiver's filename.
     46      *
     47      * @serial the file of this URL
     48      *
     49      */
     50     private String file;
     51 
     52     /**
     53      * The receiver's protocol identifier.
     54      *
     55      * @serial the protocol of this URL (http, file)
     56      *
     57      */
     58     private String protocol = null;
     59 
     60     /**
     61      * The receiver's host name.
     62      *
     63      * @serial the host of this URL
     64      *
     65      */
     66     private String host;
     67 
     68     /**
     69      * The receiver's port number.
     70      *
     71      * @serial the port of this URL
     72      *
     73      */
     74     private int port = -1;
     75 
     76     /**
     77      * The receiver's authority.
     78      *
     79      * @serial the authority of this URL
     80      *
     81      */
     82     private String authority = null;
     83 
     84     /**
     85      * The receiver's userInfo.
     86      */
     87     private transient String userInfo = null;
     88 
     89     /**
     90      * The receiver's path.
     91      */
     92     private transient String path = null;
     93 
     94     /**
     95      * The receiver's query.
     96      */
     97     private transient String query = null;
     98 
     99     /**
    100      * The receiver's reference.
    101      *
    102      * @serial the reference of this URL
    103      *
    104      */
    105     private String ref = null;
    106 
    107     /**
    108      * Cache for storing protocol handler
    109      */
    110     private static Hashtable<String, URLStreamHandler> streamHandlers = new Hashtable<String, URLStreamHandler>();
    111 
    112     /**
    113      * The URL Stream (protocol) Handler
    114      */
    115     transient URLStreamHandler strmHandler;
    116 
    117     /**
    118      * The factory responsible for producing URL Stream (protocol) Handler
    119      */
    120     private static URLStreamHandlerFactory streamHandlerFactory;
    121 
    122     /**
    123      * Sets the {@code URLStreamHandlerFactory} which creates protocol specific
    124      * stream handlers. This method can be invoked only once during an
    125      * application's lifetime. If the {@code URLStreamHandlerFactory} is already
    126      * set an {@link Error} will be thrown.
    127      * <p>
    128      * A security check is performed to verify whether the current policy allows
    129      * to set the stream handler factory.
    130      *
    131      * @param streamFactory
    132      *            the factory to be used for creating stream protocol handlers.
    133      */
    134     public static synchronized void setURLStreamHandlerFactory(
    135             URLStreamHandlerFactory streamFactory) {
    136         if (streamHandlerFactory != null) {
    137             throw new Error("Factory already set");
    138         }
    139         SecurityManager sm = System.getSecurityManager();
    140         if (sm != null) {
    141             sm.checkSetFactory();
    142         }
    143         streamHandlers.clear();
    144         streamHandlerFactory = streamFactory;
    145     }
    146 
    147     /**
    148      * Creates a new URL instance by parsing the string {@code spec}.
    149      *
    150      * @param spec
    151      *            the URL string representation which has to be parsed.
    152      * @throws MalformedURLException
    153      *             if the given string {@code spec} could not be parsed as a
    154      *             URL.
    155      */
    156     public URL(String spec) throws MalformedURLException {
    157         this((URL) null, spec, (URLStreamHandler) null);
    158     }
    159 
    160     /**
    161      * Creates a new URL to the specified resource {@code spec}. This URL is
    162      * relative to the given {@code context}. If the protocol of the parsed URL
    163      * does not match with the protocol of the context URL, then the newly
    164      * created URL is absolute and bases only on the given URL represented by
    165      * {@code spec}. Otherwise the protocol is defined by the context URL.
    166      *
    167      * @param context
    168      *            the URL which is used as the context.
    169      * @param spec
    170      *            the URL string representation which has to be parsed.
    171      * @throws MalformedURLException
    172      *             if the given string {@code spec} could not be parsed as a URL
    173      *             or an invalid protocol has been found.
    174      */
    175     public URL(URL context, String spec) throws MalformedURLException {
    176         this(context, spec, (URLStreamHandler) null);
    177     }
    178 
    179     /**
    180      * Creates a new URL to the specified resource {@code spec}. This URL is
    181      * relative to the given {@code context}. The {@code handler} will be used
    182      * to parse the URL string representation. If this argument is {@code null}
    183      * the default {@code URLStreamHandler} will be used. If the protocol of the
    184      * parsed URL does not match with the protocol of the context URL, then the
    185      * newly created URL is absolute and bases only on the given URL represented
    186      * by {@code spec}. Otherwise the protocol is defined by the context URL.
    187      *
    188      * @param context
    189      *            the URL which is used as the context.
    190      * @param spec
    191      *            the URL string representation which has to be parsed.
    192      * @param handler
    193      *            the specific stream handler to be used by this URL.
    194      * @throws MalformedURLException
    195      *             if the given string {@code spec} could not be parsed as a URL
    196      *             or an invalid protocol has been found.
    197      */
    198     public URL(URL context, String spec, URLStreamHandler handler)
    199             throws MalformedURLException {
    200         if (handler != null) {
    201             SecurityManager sm = System.getSecurityManager();
    202             if (sm != null) {
    203                 sm.checkPermission(specifyStreamHandlerPermission);
    204             }
    205             strmHandler = handler;
    206         }
    207 
    208         if (spec == null) {
    209             throw new MalformedURLException();
    210         }
    211         spec = spec.trim();
    212 
    213         // The spec includes a protocol if it includes a colon character
    214         // before the first occurrence of a slash character. Note that,
    215         // "protocol" is the field which holds this URLs protocol.
    216         int index;
    217         try {
    218             index = spec.indexOf(':');
    219         } catch (NullPointerException e) {
    220             throw new MalformedURLException(e.toString());
    221         }
    222         int startIPv6Addr = spec.indexOf('[');
    223         if (index >= 0) {
    224             if ((startIPv6Addr == -1) || (index < startIPv6Addr)) {
    225                 protocol = spec.substring(0, index);
    226                 // According to RFC 2396 scheme part should match
    227                 // the following expression:
    228                 // alpha *( alpha | digit | "+" | "-" | "." )
    229                 char c = protocol.charAt(0);
    230                 boolean valid = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
    231                 for (int i = 1; valid && (i < protocol.length()); i++) {
    232                     c = protocol.charAt(i);
    233                     valid = ('a' <= c && c <= 'z') ||
    234                             ('A' <= c && c <= 'Z') ||
    235                             ('0' <= c && c <= '9') ||
    236                             (c == '+') ||
    237                             (c == '-') ||
    238                             (c == '.');
    239                 }
    240                 if (!valid) {
    241                     protocol = null;
    242                     index = -1;
    243                 } else {
    244                     // Ignore case in protocol names.
    245                     // Scheme is defined by ASCII characters.
    246                     protocol = Util.toASCIILowerCase(protocol);
    247                 }
    248             }
    249         }
    250 
    251         if (protocol != null) {
    252             // If the context was specified, and it had the same protocol
    253             // as the spec, then fill in the receiver's slots from the values
    254             // in the context but still allow them to be over-ridden later
    255             // by the values in the spec.
    256             if (context != null && protocol.equals(context.getProtocol())) {
    257                 String cPath = context.getPath();
    258                 if (cPath != null && cPath.startsWith("/")) {
    259                     set(protocol, context.getHost(), context.getPort(), context
    260                             .getAuthority(), context.getUserInfo(), cPath,
    261                             context.getQuery(), null);
    262                 }
    263                 if (strmHandler == null) {
    264                     strmHandler = context.strmHandler;
    265                 }
    266             }
    267         } else {
    268             // If the spec did not include a protocol, then the context
    269             // *must* be specified. Fill in the receiver's slots from the
    270             // values in the context, but still allow them to be over-ridden
    271             // by the values in the ("relative") spec.
    272             if (context == null) {
    273                 throw new MalformedURLException("Protocol not found: " + spec);
    274             }
    275             set(context.getProtocol(), context.getHost(), context.getPort(),
    276                     context.getAuthority(), context.getUserInfo(), context
    277                             .getPath(), context.getQuery(), null);
    278             if (strmHandler == null) {
    279                 strmHandler = context.strmHandler;
    280             }
    281         }
    282 
    283         // If the stream handler has not been determined, set it
    284         // to the default for the specified protocol.
    285         if (strmHandler == null) {
    286             setupStreamHandler();
    287             if (strmHandler == null) {
    288                 throw new MalformedURLException("Unknown protocol: " + protocol);
    289             }
    290         }
    291 
    292         // Let the handler parse the URL. If the handler throws
    293         // any exception, throw MalformedURLException instead.
    294         //
    295         // Note: We want "index" to be the index of the start of the scheme
    296         // specific part of the URL. At this point, it will be either
    297         // -1 or the index of the colon after the protocol, so we
    298         // increment it to point at either character 0 or the character
    299         // after the colon.
    300         try {
    301             strmHandler.parseURL(this, spec, ++index, spec.length());
    302         } catch (Exception e) {
    303             throw new MalformedURLException(e.toString());
    304         }
    305 
    306         if (port < -1) {
    307             throw new MalformedURLException("Port out of range: " + port);
    308         }
    309     }
    310 
    311     /**
    312      * Creates a new URL instance using the given arguments. The URL uses the
    313      * default port for the specified protocol.
    314      *
    315      * @param protocol
    316      *            the protocol of the new URL.
    317      * @param host
    318      *            the host name or IP address of the new URL.
    319      * @param file
    320      *            the name of the resource.
    321      * @throws MalformedURLException
    322      *             if the combination of all arguments do not represent a valid
    323      *             URL or the protocol is invalid.
    324      */
    325     public URL(String protocol, String host, String file)
    326             throws MalformedURLException {
    327         this(protocol, host, -1, file, (URLStreamHandler) null);
    328     }
    329 
    330     /**
    331      * Creates a new URL instance using the given arguments. The URL uses the
    332      * specified port instead of the default port for the given protocol.
    333      *
    334      * @param protocol
    335      *            the protocol of the new URL.
    336      * @param host
    337      *            the host name or IP address of the new URL.
    338      * @param port
    339      *            the specific port number of the URL. {@code -1} represents the
    340      *            default port of the protocol.
    341      * @param file
    342      *            the name of the resource.
    343      * @throws MalformedURLException
    344      *             if the combination of all arguments do not represent a valid
    345      *             URL or the protocol is invalid.
    346      */
    347     public URL(String protocol, String host, int port, String file)
    348             throws MalformedURLException {
    349         this(protocol, host, port, file, (URLStreamHandler) null);
    350     }
    351 
    352     /**
    353      * Creates a new URL instance using the given arguments. The URL uses the
    354      * specified port instead of the default port for the given protocol.
    355      *
    356      * @param protocol
    357      *            the protocol of the new URL.
    358      * @param host
    359      *            the host name or IP address of the new URL.
    360      * @param port
    361      *            the specific port number of the URL. {@code -1} represents the
    362      *            default port of the protocol.
    363      * @param file
    364      *            the name of the resource.
    365      * @param handler
    366      *            the stream handler to be used by this URL.
    367      * @throws MalformedURLException
    368      *             if the combination of all arguments do not represent a valid
    369      *             URL or the protocol is invalid.
    370      * @throws SecurityException
    371      *             if {@code handler} is non-{@code null}, and a security
    372      *             manager is installed that disallows user-defined protocol
    373      *             handlers.
    374      */
    375     public URL(String protocol, String host, int port, String file,
    376             URLStreamHandler handler) throws MalformedURLException {
    377         if (port < -1) {
    378             throw new MalformedURLException("Port out of range: " + port);
    379         }
    380 
    381         if (host != null && host.indexOf(":") != -1 && host.charAt(0) != '[') {
    382             host = "[" + host + "]";
    383         }
    384 
    385         if (protocol == null) {
    386             throw new NullPointerException("Unknown protocol: null");
    387         }
    388 
    389         this.protocol = protocol;
    390         this.host = host;
    391         this.port = port;
    392 
    393         // Set the fields from the arguments. Handle the case where the
    394         // passed in "file" includes both a file and a reference part.
    395         int index = -1;
    396         index = file.indexOf("#", file.lastIndexOf("/"));
    397         if (index >= 0) {
    398             this.file = file.substring(0, index);
    399             ref = file.substring(index + 1);
    400         } else {
    401             this.file = file;
    402         }
    403         fixURL(false);
    404 
    405         // Set the stream handler for the URL either to the handler
    406         // argument if it was specified, or to the default for the
    407         // receiver's protocol if the handler was null.
    408         if (handler == null) {
    409             setupStreamHandler();
    410             if (strmHandler == null) {
    411                 throw new MalformedURLException("Unknown protocol: " + protocol);
    412             }
    413         } else {
    414             SecurityManager sm = System.getSecurityManager();
    415             if (sm != null) {
    416                 sm.checkPermission(specifyStreamHandlerPermission);
    417             }
    418             strmHandler = handler;
    419         }
    420     }
    421 
    422     void fixURL(boolean fixHost) {
    423         int index;
    424         if (host != null && host.length() > 0) {
    425             authority = host;
    426             if (port != -1) {
    427                 authority = authority + ":" + port;
    428             }
    429         }
    430         if (fixHost) {
    431             if (host != null && (index = host.lastIndexOf('@')) > -1) {
    432                 userInfo = host.substring(0, index);
    433                 host = host.substring(index + 1);
    434             } else {
    435                 userInfo = null;
    436             }
    437         }
    438         if (file != null && (index = file.indexOf('?')) > -1) {
    439             query = file.substring(index + 1);
    440             path = file.substring(0, index);
    441         } else {
    442             query = null;
    443             path = file;
    444         }
    445     }
    446 
    447     /**
    448      * Sets the properties of this URL using the provided arguments. Only a
    449      * {@code URLStreamHandler} can use this method to set fields of the
    450      * existing URL instance. A URL is generally constant.
    451      *
    452      * @param protocol
    453      *            the protocol to be set.
    454      * @param host
    455      *            the host name to be set.
    456      * @param port
    457      *            the port number to be set.
    458      * @param file
    459      *            the file to be set.
    460      * @param ref
    461      *            the reference to be set.
    462      */
    463     protected void set(String protocol, String host, int port, String file,
    464             String ref) {
    465         if (this.protocol == null) {
    466             this.protocol = protocol;
    467         }
    468         this.host = host;
    469         this.file = file;
    470         this.port = port;
    471         this.ref = ref;
    472         hashCode = 0;
    473         fixURL(true);
    474     }
    475 
    476     /**
    477      * Compares this URL instance with the given argument {@code o} and
    478      * determines if both are equal. Two URL instances are equal if all single
    479      * parts are identical in their meaning. Compares the argument to the
    480      * receiver, and returns true if they represent the same URL. Two URLs are
    481      * equal if they have the same file, host, port, protocol, and reference
    482      * components.
    483      *
    484      * @param o
    485      *            the URL this instance has to be compared with.
    486      * @return {@code true} if both instances represents the same URL, {@code
    487      *         false} otherwise.
    488      * @see #hashCode()
    489      */
    490     @Override
    491     public boolean equals(Object o) {
    492         if (o == null) {
    493             return false;
    494         }
    495         if (this == o) {
    496             return true;
    497         }
    498         if (this.getClass() != o.getClass()) {
    499             return false;
    500         }
    501         return strmHandler.equals(this, (URL) o);
    502     }
    503 
    504     /**
    505      * Returns whether this URL refers to the same resource as the given
    506      * argument {@code otherURL}. All URL components except the reference field
    507      * are compared.
    508      *
    509      * @param otherURL
    510      *            the URL to compare against.
    511      * @return {@code true} if both instances refer to the same resource,
    512      *         {@code false} otherwise.
    513      */
    514     public boolean sameFile(URL otherURL) {
    515         return strmHandler.sameFile(this, otherURL);
    516     }
    517 
    518     /**
    519      * Gets the hashcode value of this URL instance.
    520      *
    521      * @return the appropriate hashcode value.
    522      */
    523     @Override
    524     public int hashCode() {
    525         if (hashCode == 0) {
    526             hashCode = strmHandler.hashCode(this);
    527         }
    528         return hashCode;
    529     }
    530 
    531     /**
    532      * Sets the receiver's stream handler to one which is appropriate for its
    533      * protocol. Throws a MalformedURLException if no reasonable handler is
    534      * available.
    535      * <p>
    536      * Note that this will overwrite any existing stream handler with the new
    537      * one. Senders must check if the strmHandler is null before calling the
    538      * method if they do not want this behavior (a speed optimization).
    539      */
    540     void setupStreamHandler() {
    541         // Check for a cached (previously looked up) handler for
    542         // the requested protocol.
    543         strmHandler = streamHandlers.get(protocol);
    544         if (strmHandler != null) {
    545             return;
    546         }
    547 
    548         // If there is a stream handler factory, then attempt to
    549         // use it to create the handler.
    550         if (streamHandlerFactory != null) {
    551             strmHandler = streamHandlerFactory.createURLStreamHandler(protocol);
    552             if (strmHandler != null) {
    553                 streamHandlers.put(protocol, strmHandler);
    554                 return;
    555             }
    556         }
    557 
    558         // Check if there is a list of packages which can provide handlers.
    559         // If so, then walk this list looking for an applicable one.
    560         String packageList = AccessController
    561                 .doPrivileged(new PriviAction<String>(
    562                         "java.protocol.handler.pkgs"));
    563         if (packageList != null) {
    564             StringTokenizer st = new StringTokenizer(packageList, "|");
    565             while (st.hasMoreTokens()) {
    566                 String className = st.nextToken() + "." + protocol + ".Handler";
    567 
    568                 try {
    569                     strmHandler = (URLStreamHandler) Class.forName(className,
    570                             true, ClassLoader.getSystemClassLoader())
    571                             .newInstance();
    572                     if (strmHandler != null) {
    573                         streamHandlers.put(protocol, strmHandler);
    574                     }
    575                     return;
    576                 } catch (IllegalAccessException e) {
    577                 } catch (InstantiationException e) {
    578                 } catch (ClassNotFoundException e) {
    579                 }
    580             }
    581         }
    582 
    583         // No one else has provided a handler, so try our internal one.
    584 
    585         String className = "org.apache.harmony.luni.internal.net.www.protocol." + protocol
    586                 + ".Handler";
    587         try {
    588             strmHandler = (URLStreamHandler) Class.forName(className)
    589                     .newInstance();
    590         } catch (IllegalAccessException e) {
    591         } catch (InstantiationException e) {
    592         } catch (ClassNotFoundException e) {
    593         }
    594         if (strmHandler != null) {
    595             streamHandlers.put(protocol, strmHandler);
    596         }
    597 
    598     }
    599 
    600     /**
    601      * Gets the content of the resource which is referred by this URL. By
    602      * default one of the following object types will be returned:
    603      * <p>
    604      * <li>Image for pictures</li>
    605      * <li>AudioClip for audio sequences</li>
    606      * <li>{@link InputStream} for all other data</li>
    607      *
    608      * @return the content of the referred resource.
    609      * @throws IOException
    610      *             if an error occurs obtaining the content.
    611      */
    612     public final Object getContent() throws IOException {
    613         return openConnection().getContent();
    614     }
    615 
    616     /**
    617      * Gets the content of the resource which is referred by this URL. The
    618      * argument {@code types} is an array of allowed or expected object types.
    619      * {@code null} will be returned if the obtained object type does not match
    620      * with one from this list. Otherwise the first type that matches will be
    621      * used.
    622      *
    623      * @param types
    624      *            the list of allowed or expected object types.
    625      * @return the object representing the resource referred by this URL,
    626      *         {@code null} if the content does not match to a specified content
    627      *         type.
    628      * @throws IOException
    629      *             if an error occurs obtaining the content.
    630      */
    631     // Param not generic in spec
    632     @SuppressWarnings("unchecked")
    633     public final Object getContent(Class[] types) throws IOException {
    634         return openConnection().getContent(types);
    635     }
    636 
    637     /**
    638      * Opens an InputStream to read the resource referred by this URL.
    639      *
    640      * @return the stream which allows to read the resource.
    641      * @throws IOException
    642      *             if an error occurs while opening the InputStream.
    643      */
    644     public final InputStream openStream() throws java.io.IOException {
    645         return openConnection().getInputStream();
    646     }
    647 
    648     /**
    649      * Opens a connection to the remote resource specified by this URL. This
    650      * connection allows bidirectional data transfer.
    651      *
    652      * @return the connection to this URL.
    653      * @throws IOException
    654      *             if an error occurs while opening the connection.
    655      */
    656     public URLConnection openConnection() throws IOException {
    657         return strmHandler.openConnection(this);
    658     }
    659 
    660     /**
    661      * Converts this URL instance into an equivalent URI object.
    662      *
    663      * @return the URI instance that represents this URL.
    664      * @throws URISyntaxException
    665      *             if this URL cannot be converted into a URI.
    666      */
    667     public URI toURI() throws URISyntaxException {
    668         return new URI(toExternalForm());
    669     }
    670 
    671     /**
    672      * Opens a connection to the remote resource specified by this URL. The
    673      * connection will be established through the given proxy and allows
    674      * bidirectional data transfer.
    675      *
    676      * @param proxy
    677      *            the proxy through which the connection will be established.
    678      * @return the appropriate URLconnection instance representing the
    679      *         connection to this URL.
    680      * @throws IOException
    681      *             if an I/O error occurs while opening the connection.
    682      * @throws SecurityException
    683      *             if a security manager is installed and it denies to connect
    684      *             to the proxy.
    685      * @throws IllegalArgumentException
    686      *             if the argument proxy is {@code null} or is an invalid type.
    687      * @throws UnsupportedOperationException
    688      *             if the protocol handler does not support opening connections
    689      *             through proxies.
    690      */
    691     public URLConnection openConnection(Proxy proxy) throws IOException {
    692         if (proxy == null) {
    693             throw new IllegalArgumentException("proxy == null");
    694         }
    695 
    696         SecurityManager sm = System.getSecurityManager();
    697         if (sm != null && proxy.type() != Proxy.Type.DIRECT) {
    698             InetSocketAddress pAddress = (InetSocketAddress) proxy.address();
    699             String pHostName = pAddress.isUnresolved() ? pAddress.getHostName()
    700                     : pAddress.getAddress().getHostAddress();
    701             sm.checkConnect(pHostName, pAddress.getPort());
    702         }
    703 
    704         return strmHandler.openConnection(this, proxy);
    705     }
    706 
    707     /**
    708      * Returns a string containing a concise, human-readable representation of
    709      * this URL. The returned string is the same as the result of the method
    710      * {@code toExternalForm()}.
    711      *
    712      * @return the string representation of this URL.
    713      */
    714     @Override
    715     public String toString() {
    716         return toExternalForm();
    717     }
    718 
    719     /**
    720      * Returns a string containing a concise, human-readable representation of
    721      * this URL.
    722      *
    723      * @return the string representation of this URL.
    724      */
    725     public String toExternalForm() {
    726         if (strmHandler == null) {
    727             return "unknown protocol(" + protocol + ")://" + host + file;
    728         }
    729         return strmHandler.toExternalForm(this);
    730     }
    731 
    732     /**
    733      * This method is called to restore the state of a URL object that has been
    734      * serialized. The stream handler is determined from the URL's protocol.
    735      *
    736      * @param stream
    737      *            the stream to read from.
    738      *
    739      * @throws IOException
    740      *             if an IO Exception occurs while reading the stream or the
    741      *             handler can not be found.
    742      */
    743     private void readObject(java.io.ObjectInputStream stream)
    744             throws java.io.IOException {
    745         try {
    746             stream.defaultReadObject();
    747             if (host != null && authority == null) {
    748                 fixURL(true);
    749             } else if (authority != null) {
    750                 int index;
    751                 if ((index = authority.lastIndexOf('@')) > -1) {
    752                     userInfo = authority.substring(0, index);
    753                 }
    754                 if (file != null && (index = file.indexOf('?')) > -1) {
    755                     query = file.substring(index + 1);
    756                     path = file.substring(0, index);
    757                 } else {
    758                     path = file;
    759                 }
    760             }
    761             setupStreamHandler();
    762             if (strmHandler == null) {
    763                 throw new IOException("Unknown protocol: " + protocol);
    764             }
    765         } catch (ClassNotFoundException e) {
    766             throw new IOException(e.toString());
    767         }
    768     }
    769 
    770     /**
    771      * This method is called to write any non-transient, non-static variables
    772      * into the output stream.
    773      * <p>
    774      * Note that, we really only need the readObject method but the spec that
    775      * says readObject will be ignored if no writeObject is present.
    776      *
    777      * @param s
    778      *            the stream to write on.
    779      * @throws IOException
    780      *             if an IO Exception occurs during the write.
    781      */
    782     private void writeObject(ObjectOutputStream s) throws IOException {
    783         s.defaultWriteObject();
    784     }
    785 
    786     /**
    787      * Gets the value of the file part of this URL.
    788      *
    789      * @return the file name this URL refers to or an empty string if the file
    790      *         part is not set.
    791      */
    792     public String getFile() {
    793         return file;
    794     }
    795 
    796     /**
    797      * Gets the value of the host part of this URL.
    798      *
    799      * @return the host name or IP address of this URL.
    800      */
    801     public String getHost() {
    802         return host;
    803     }
    804 
    805     /**
    806      * Gets the port number of this URL or {@code -1} if the port is not set.
    807      *
    808      * @return the port number of this URL.
    809      */
    810     public int getPort() {
    811         return port;
    812     }
    813 
    814     /** @hide */
    815     public int getEffectivePort() {
    816         return URI.getEffectivePort(protocol, port);
    817     }
    818 
    819     /**
    820      * Gets the protocol of this URL.
    821      *
    822      * @return the protocol type of this URL.
    823      */
    824     public String getProtocol() {
    825         return protocol;
    826     }
    827 
    828     /**
    829      * Gets the value of the reference part of this URL.
    830      *
    831      * @return the reference part of this URL.
    832      */
    833     public String getRef() {
    834         return ref;
    835     }
    836 
    837     /**
    838      * Gets the value of the query part of this URL.
    839      *
    840      * @return the query part of this URL.
    841      */
    842     public String getQuery() {
    843         return query;
    844     }
    845 
    846     /**
    847      * Gets the value of the path part of this URL.
    848      *
    849      * @return the path part of this URL.
    850      */
    851     public String getPath() {
    852         return path;
    853     }
    854 
    855     /**
    856      * Gets the value of the user-info part of this URL.
    857      *
    858      * @return the user-info part of this URL.
    859      */
    860     public String getUserInfo() {
    861         return userInfo;
    862     }
    863 
    864     /**
    865      * Gets the value of the authority part of this URL.
    866      *
    867      * @return the authority part of this URL.
    868      */
    869     public String getAuthority() {
    870         return authority;
    871     }
    872 
    873     /**
    874      * Sets the properties of this URL using the provided arguments. Only a
    875      * {@code URLStreamHandler} can use this method to set fields of the
    876      * existing URL instance. A URL is generally constant.
    877      *
    878      * @param protocol
    879      *            the protocol to be set.
    880      * @param host
    881      *            the host name to be set.
    882      * @param port
    883      *            the port number to be set.
    884      * @param authority
    885      *            the authority to be set.
    886      * @param userInfo
    887      *            the user-info to be set.
    888      * @param path
    889      *            the path to be set.
    890      * @param query
    891      *            the query to be set.
    892      * @param ref
    893      *            the reference to be set.
    894      */
    895     protected void set(String protocol, String host, int port,
    896             String authority, String userInfo, String path, String query,
    897             String ref) {
    898         String filePart = path;
    899         if (query != null && !query.isEmpty()) {
    900             if (filePart != null) {
    901                 filePart = filePart + "?" + query;
    902             } else {
    903                 filePart = "?" + query;
    904             }
    905         }
    906         set(protocol, host, port, filePart, ref);
    907         this.authority = authority;
    908         this.userInfo = userInfo;
    909         this.path = path;
    910         this.query = query;
    911     }
    912 
    913     /**
    914      * Gets the default port number of the protocol used by this URL. If no
    915      * default port is defined by the protocol or the {@code URLStreamHandler},
    916      * {@code -1} will be returned.
    917      *
    918      * @return the default port number according to the protocol of this URL.
    919      * @see URLStreamHandler#getDefaultPort
    920      */
    921     public int getDefaultPort() {
    922         return strmHandler.getDefaultPort();
    923     }
    924 }
    925