home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / net / URL.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  35.1 KB  |  973 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)URL.java    1.79 98/10/07
  3.  *
  4.  * Copyright 1995-1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.net;
  16.  
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.io.OutputStream;
  20. import java.util.Hashtable;
  21. import java.util.StringTokenizer;
  22.  
  23. /**
  24.  * Class <code>URL</code> represents a Uniform Resource 
  25.  * Locator, a pointer to a "resource" on the World 
  26.  * Wide Web. A resource can be something as simple as a file or a 
  27.  * directory, or it can be a reference to a more complicated object, 
  28.  * such as a query to a database or to a search engine. More 
  29.  * information on the types of URLs and their formats can be found at:
  30.  * <blockquote><pre>
  31.  *     http://www.ncsa.uiuc.edu/demoweb/url-primer.html
  32.  * </pre></blockquote>
  33.  * <p>
  34.  * In general, a URL can be broken into several parts. The previous 
  35.  * example of a URL indicates that the protocol to use is 
  36.  * <code>http</code> (HyperText Transport Protocol) and that the 
  37.  * information resides on a host machine named 
  38.  * <code>www.ncsa.uiuc.edu</code>. The information on that host 
  39.  * machine is named <code>demoweb/url-primer.html</code>. The exact 
  40.  * meaning of this name on the host machine is both protocol 
  41.  * dependent and host dependent. The information normally resides in 
  42.  * a file, but it could be generated on the fly. This component of 
  43.  * the URL is called the <i>file</i> component, even though the 
  44.  * information is not necessarily in a file. 
  45.  * <p>
  46.  * A URL can optionally specify a "port", which is the 
  47.  * port number to which the TCP connection is made on the remote host 
  48.  * machine. If the port is not specified, the default port for 
  49.  * the protocol is used instead. For example, the default port for 
  50.  * <code>http</code> is <code>80</code>. An alternative port could be 
  51.  * specified as: 
  52.  * <blockquote><pre>
  53.  *     http://www.ncsa.uiuc.edu:8080/demoweb/url-primer.html
  54.  * </pre></blockquote>
  55.  * <p>
  56.  * A URL may have appended to it an "anchor", also known 
  57.  * as a "ref" or a "reference". The anchor is 
  58.  * indicated by the sharp sign character "#" followed by 
  59.  * more characters. For example, 
  60.  * <blockquote><pre>
  61.  *     http://java.sun.com/index.html#chapter1
  62.  * </pre></blockquote>
  63.  * <p>
  64.  * This anchor is not technically part of the URL. Rather, it 
  65.  * indicates that after the specified resource is retrieved, the 
  66.  * application is specifically interested in that part of the 
  67.  * document that has the tag <code>chapter1</code> attached to it. The 
  68.  * meaning of a tag is resource specific. 
  69.  * <p>
  70.  * An application can also specify a "relative URL", 
  71.  * which contains only enough information to reach the resource 
  72.  * relative to another URL. Relative URLs are frequently used within 
  73.  * HTML pages. For example, if the contents of the URL:
  74.  * <blockquote><pre>
  75.  *     http://java.sun.com/index.html
  76.  * </pre></blockquote>
  77.  * contained within it the relative URL:
  78.  * <blockquote><pre>
  79.  *     FAQ.html
  80.  * </pre></blockquote>
  81.  * it would be a shorthand for:
  82.  * <blockquote><pre>
  83.  *     http://java.sun.com/FAQ.html
  84.  * </pre></blockquote>
  85.  * <p>
  86.  * The relative URL need not specify all the components of a URL. If 
  87.  * the protocol, host name, or port number is missing, the value is 
  88.  * inherited from the fully specified URL. The file component must be 
  89.  * specified. The optional anchor is not inherited. 
  90.  *
  91.  * @author  James Gosling
  92.  * @version 1.61, 04/16/98
  93.  * @since   JDK1.0
  94.  */
  95. public final class URL implements java.io.Serializable {
  96.  
  97.     static final long serialVersionUID = -7627629688361524110L;
  98.  
  99.     /**
  100.      * The property which specifies the package prefix list to be scanned
  101.      * for protocol handlers.  The value of this property (if any) should
  102.      * be a vertical bar delimited list of package names to search through
  103.      * for a protocol handler to load.  The policy of this class is that
  104.      * all protocol handlers will be in a class called <protocolname>.Handler,
  105.      * and each package in the list is examined in turn for a matching
  106.      * handler.  If none are found (or the property is not specified), the
  107.      * default package prefix, sun.net.www.protocol, is used.  The search
  108.      * proceeds from the first package in the list to the last and stops
  109.      * when a match is found.
  110.      */
  111.     private static final String protocolPathProp = "java.protocol.handler.pkgs";
  112.  
  113.     /** 
  114.      * The protocol to use (ftp, http, nntp, ... etc.) . 
  115.      */
  116.     private String protocol;
  117.  
  118.     /** 
  119.      * The host name in which to connect to. 
  120.      */
  121.     private String host;
  122.  
  123.     /**
  124.      * The host's IP address, used in equals and hashCode.
  125.      * Computed on demand. An uninitialized or unknown hostAddress is null.
  126.      */
  127.     private transient InetAddress hostAddress;
  128.  
  129.     /** 
  130.      * The protocol port to connect to. 
  131.      */
  132.     private int port = -1;
  133.  
  134.     /** 
  135.      * The specified file name on that host. 
  136.      */
  137.     private String file;
  138.  
  139.     /** 
  140.      * # reference. 
  141.      */
  142.     private String ref;
  143.  
  144.     /**
  145.      * The URLStreamHandler for this URL.
  146.      */
  147.     transient URLStreamHandler handler;
  148.  
  149.     /* Our hash code. */
  150.     private int hashCode = -1;
  151.  
  152.     /** 
  153.      * Creates a <code>URL</code> object from the specified 
  154.      * <code>protocol</code>, <code>host</code>, <code>port</code> 
  155.      * number, and <code>file</code>. Specifying a <code>port</code> 
  156.      * number of <code>-1</code> indicates that the URL should use 
  157.      * the default port for the protocol. 
  158.      * <p>
  159.      * If this is the first URL object being created with the specified 
  160.      * protocol, a <i>stream protocol handler</i> object, an instance of 
  161.      * class <code>URLStreamHandler</code>, is created for that protocol:
  162.      * <ol>
  163.      * <li>If the application has previously set up an instance of
  164.      *     <code>URLStreamHandlerFactory</code> as the stream handler factory,
  165.      *     then the <code>createURLStreamHandler</code> method of that instance
  166.      *     is called with the protocol string as an argument to create the
  167.      *     stream protocol handler.
  168.      * <li>If no <code>URLStreamHandlerFactory</code> has yet been set up,
  169.      *     or if the factory's <code>createURLStreamHandler</code> method
  170.      *     returns <code>null</code>, then the constructor finds the 
  171.      *     value of the system property:
  172.      *     <blockquote><pre>
  173.      *         java.protocol.handler.pkgs
  174.      *     </pre></blockquote>
  175.      *     If the value of that system property is not <code>null</code>,
  176.      *     it is interpreted as a list of packages separated by a vertical
  177.      *     slash character '<code>|</code>'. The constructor tries to load 
  178.      *     the class named:
  179.      *     <blockquote><pre>
  180.      *         <<i>package</i>>.<<i>protocol</i>>.Handler
  181.      *     </pre></blockquote>
  182.      *     where <<i>package</i>> is replaced by the name of the package
  183.      *     and <<i>protocol</i>> is replaced by the name of the protocol.
  184.      *     If this class does not exist, or if the class exists but it is not
  185.      *     a subclass of <code>URLStreamHandler</code>, then the next package
  186.      *     in the list is tried.
  187.      * <li>If the previous step fails to find a protocol handler, then the
  188.      *     constructor tries to load the class named:
  189.      *     <blockquote><pre>
  190.      *         sun.net.www.protocol.<<i>protocol</i>>.Handler
  191.      *     </pre></blockquote>
  192.      *     If this class does not exist, or if the class exists but it is not a
  193.      *     subclass of <code>URLStreamHandler</code>, then a
  194.      *     <code>MalformedURLException</code> is thrown.
  195.      * </ol>
  196.      *
  197.      * @param      protocol   the name of the protocol.
  198.      * @param      host       the name of the host.
  199.      * @param      port       the port number.
  200.      * @param      file       the host file.
  201.      * @exception  MalformedURLException  if an unknown protocol is specified.
  202.      * @see        java.lang.System#getProperty(java.lang.String)
  203.      * @see        java.net.URL#setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory)
  204.      * @see        java.net.URLStreamHandler
  205.      * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(java.lang.String)
  206.      */
  207.     public URL(String protocol, String host, int port, String file)
  208.     throws MalformedURLException
  209.     {
  210.     this(protocol, host, port, file, null);
  211.     }
  212.  
  213.     /** 
  214.      * Creates an absolute URL from the specified <code>protocol</code> 
  215.      * name, <code>host</code> name, and <code>file</code> name. The 
  216.      * default port for the specified protocol is used. 
  217.      * <p>
  218.      * This method is equivalent to calling the four-argument 
  219.      * constructor with the arguments being <code>protocol</code>, 
  220.      * <code>host</code>, <code>-1</code>, and <code>file</code>. 
  221.      *
  222.      * @param      protocol   the protocol to use.
  223.      * @param      host       the host to connect to.
  224.      * @param      file       the file on that host.
  225.      * @exception  MalformedURLException  if an unknown protocol is specified.
  226.      * @see        java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
  227.      */
  228.     public URL(String protocol, String host, String file) throws MalformedURLException {
  229.     this(protocol, host, -1, file);
  230.     }
  231.  
  232.     /** 
  233.      * Creates a <code>URL</code> object from the specified 
  234.      * <code>protocol</code>, <code>host</code>, <code>port</code> 
  235.      * number, <code>file</code>, and <code>handler</code>. Specifying
  236.      * a <code>port</code> number of <code>-1</code> indicates that
  237.      * the URL should use the default port for the protocol. Specifying
  238.      * a <code>handler</code> of <code>null</code> indicates that the URL
  239.      * should use a default stream handler for the protocol, as outlined
  240.      * for:
  241.      * <blockquote><pre>
  242.      *     java.net.URL#URL(java.lang.String, java.lang.String, int,
  243.      *                      java.lang.String)
  244.      * </pre></blockquote>
  245.      * 
  246.      * <p>If the handler is not null and there is a security manager, 
  247.      * the security manager's <code>checkPermission</code> 
  248.      * method is called with a 
  249.      * <code>NetPermission("specifyStreamHandler")</code> permission.
  250.      * This may result in a SecurityException.
  251.      *
  252.      * @param      protocol   the name of the protocol.
  253.      * @param      host       the name of the host.
  254.      * @param      port       the port number.
  255.      * @param      file       the host file.
  256.      * @param       handler    the stream handler.
  257.      * @exception  MalformedURLException  if an unknown protocol is specified.
  258.      * @exception  SecurityException
  259.      *        if a security manager exists and its 
  260.      *        <code>checkPermission</code> method doesn't allow 
  261.      *        specifying a stream handler explicitly.
  262.      * @see        java.lang.System#getProperty(java.lang.String)
  263.      * @see        java.net.URL#setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory)
  264.      * @see        java.net.URLStreamHandler
  265.      * @see        java.net.URLStreamHandlerFactory#createURLStreamHandler(java.lang.String)
  266.      * @see        SecurityManager#checkPermission
  267.      * @see        java.net.NetPermission
  268.      */
  269.     public URL(String protocol, String host, int port, String file,
  270.            URLStreamHandler handler)
  271.     throws MalformedURLException
  272.     {
  273.     if (handler != null) {
  274.         SecurityManager sm = System.getSecurityManager();
  275.         if (sm != null) {
  276.         // check for permission to specify a handler
  277.         checkSpecifyHandler(sm);
  278.         }
  279.     }
  280.     this.protocol = protocol;
  281.     this.host = host;
  282.     this.port = port;
  283.     int ind = file.indexOf('#');
  284.     this.file = ind < 0 ? file: file.substring(0, ind);
  285.     this.ref = ind < 0 ? null: file.substring(ind + 1);
  286.     // Note: we don't do validation of the URL here. Too risky to change
  287.     // right now, but worth considering for future reference. -br
  288.     if (handler == null &&
  289.         (handler = getURLStreamHandler(protocol)) == null) {
  290.         throw new MalformedURLException("unknown protocol: " + protocol);
  291.     }
  292.     this.handler = handler;
  293.     }
  294.  
  295.     /**
  296.      * Creates a <code>URL</code> object from the <code>String</code> 
  297.      * representation. 
  298.      * <p>
  299.      * This constructor is equivalent to a call to the two-argument 
  300.      * constructor with a <code>null</code> first argument. 
  301.      *
  302.      * @param      spec   the <code>String</code> to parse as a URL.
  303.      * @exception  MalformedURLException  If the string specifies an
  304.      *               unknown protocol.
  305.      * @see        java.net.URL#URL(java.net.URL, java.lang.String)
  306.      */
  307.     public URL(String spec) throws MalformedURLException {
  308.     this(null, spec);
  309.     }
  310.  
  311.     /** 
  312.      * Creates a URL by parsing the specification <code>spec</code> 
  313.      * within a specified context. If the <code>context</code> argument 
  314.      * is not <code>null</code> and the <code>spec</code> argument is a 
  315.      * partial URL specification, then any of the strings missing 
  316.      * components are inherited from the <code>context</code> argument. 
  317.      * <p>
  318.      * The specification given by the <code>String</code> argument is 
  319.      * parsed to determine if it specifies a protocol. If the 
  320.      * <code>String</code> contains an ASCII colon '<code>:</code>'
  321.      * character before the first occurrence of an ASCII slash character 
  322.      * '<code>/</code>', then the characters before the colon comprise 
  323.      * the protocol. 
  324.      * <ul>
  325.      * <li>If the <code>spec</code> argument does not specify a protocol:
  326.      *     <ul>
  327.      *     <li>If the context argument is not <code>null</code>, then the
  328.      *         protocol is copied from the context argument.
  329.      *     <li>If the context argument is <code>null</code>, then a
  330.      *         <code>MalformedURLException</code> is thrown.
  331.      *     </ul>
  332.      * <li>If the <code>spec</code> argument does specify a protocol:
  333.      *     <ul>
  334.      *     <li>If the context argument is <code>null</code>, or specifies a
  335.      *         different protocol than the specification argument, the context
  336.      *         argument is ignored.
  337.      *     <li>If the context argument is not <code>null</code> and specifies
  338.      *         the same protocol as the specification, the <code>host</code>,
  339.      *         <code>port</code> number, and <code>file</code> are copied from
  340.      *         the context argument into the newly created <code>URL</code>.
  341.      *     </ul>
  342.      * </ul>
  343.      * <p>
  344.      * The constructor then searches for an appropriate stream protocol 
  345.      * handler of type <code>URLStreamHandler</code> as outlined for:
  346.      * <blockquote><pre>
  347.      *     java.net.URL#URL(java.lang.String, java.lang.String, int,
  348.      *                      java.lang.String)
  349.      * </pre></blockquote>
  350.      * The stream protocol handler's 
  351.      * <code>parseURL</code> method is called to parse the remaining 
  352.      * fields of the specification that override any defaults set by the 
  353.      * context argument. 
  354.  
  355.      * @param      context   the context in which to parse the specification.
  356.      * @param      spec      a <code>String</code> representation of a URL.
  357.      * @exception  MalformedURLException  if no protocol is specified, or an
  358.      *               unknown protocol is found.
  359.      * @see        java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
  360.      * @see        java.net.URLStreamHandler
  361.      * @see        java.net.URLStreamHandler#parseURL(java.net.URL, java.lang.String, int, int)
  362.      */
  363.     public URL(URL context, String spec) throws MalformedURLException {
  364.     this(context, spec, null);
  365.     }
  366.  
  367.     /** 
  368.      * Creates a URL by parsing the specification <code>spec</code> 
  369.      * within a specified context. If the <code>context</code> argument 
  370.      * is not <code>null</code> and the <code>spec</code> argument is a 
  371.      * partial URL specification, then any of the strings missing 
  372.      * components are inherited from the <code>context</code> argument. 
  373.      * <p>
  374.      * The specification given by the <code>String</code> argument is 
  375.      * parsed to determine if it specifies a protocol. If the 
  376.      * <code>String</code> contains an ASCII colon '<code>:</code>'
  377.      * character before the first occurrence of an ASCII slash character 
  378.      * '<code>/</code>', then the characters before the colon comprise 
  379.      * the protocol. 
  380.      * <ul>
  381.      * <li>If the <code>spec</code> argument does not specify a protocol:
  382.      *     <ul>
  383.      *     <li>If the context argument is not <code>null</code>, then the
  384.      *         protocol is copied from the context argument.
  385.      *     <li>If the context argument is <code>null</code>, then a
  386.      *         <code>MalformedURLException</code> is thrown.
  387.      *     </ul>
  388.      * <li>If the <code>spec</code> argument does specify a protocol:
  389.      *     <ul>
  390.      *     <li>If the context argument is <code>null</code>, or specifies a
  391.      *         different protocol than the specification argument, the context
  392.      *         argument is ignored.
  393.      *     <li>If the context argument is not <code>null</code> and specifies
  394.      *         the same protocol as the specification, the <code>host</code>,
  395.      *         <code>port</code> number, and <code>file</code> are copied from
  396.      *         the context argument into the newly created <code>URL</code>.
  397.      *     </ul>
  398.      * </ul>
  399.      * <p>
  400.      * If the argument <code>handler</code> is specified then it will be
  401.      * used as the stream handler for the URL and will override that of
  402.      * the context. Specifying a stream handler requires the NetPermission
  403.      * <code>"specifyStreamHandler"</code> or a <code>SecurityException</code>
  404.      * will be thrown.
  405.      * <p>Otherwise, if <code>handler</code> is null and the context is
  406.      * valid then the protocol handler of the context will be inherited.
  407.      * The stream protocol handler's 
  408.      * <code>parseURL</code> method is called to parse the remaining 
  409.      * fields of the specification that override any defaults set by the 
  410.      * context argument. 
  411.      *
  412.      * @param      context   the context in which to parse the specification.
  413.      * @param      spec      a <code>String</code> representation of a URL.
  414.      * @param       handler   the stream handler for the URL.
  415.      * @exception  MalformedURLException  if no protocol is specified, or an
  416.      *               unknown protocol is found.
  417.      * @exception  SecurityException
  418.      *        if a security manager exists and its 
  419.      *        <code>checkPermission</code> method doesn't allow 
  420.      *        specifying a stream handler.
  421.      * @see        java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
  422.      * @see        java.net.URLStreamHandler
  423.      * @see        java.net.URLStreamHandler#parseURL(java.net.URL, java.lang.String, int, int)
  424.      */
  425.     public URL(URL context, String spec, URLStreamHandler handler)
  426.     throws MalformedURLException
  427.     {
  428.     String original = spec;
  429.     int i, limit, c;
  430.     int start = 0;
  431.     String newProtocol = null;
  432.     boolean aRef=false;
  433.  
  434.     // Check for permission to specify a handler
  435.     if (handler != null) {
  436.         SecurityManager sm = System.getSecurityManager();
  437.         if (sm != null) {
  438.         checkSpecifyHandler(sm);
  439.         }
  440.     }
  441.  
  442.     try {
  443.         limit = spec.length();
  444.         while ((limit > 0) && (spec.charAt(limit - 1) <= ' ')) {
  445.         limit--;    //eliminate trailing whitespace
  446.         }
  447.         while ((start < limit) && (spec.charAt(start) <= ' ')) {
  448.         start++;    // eliminate leading whitespace
  449.         }
  450.  
  451.         if (spec.regionMatches(true, start, "url:", 0, 4)) {
  452.         start += 4;
  453.         }
  454.         if (start < spec.length() && spec.charAt(start) == '#') {
  455.         /* we're assuming this is a ref relative to the context URL.
  456.          * This means protocols cannot start w/ '#', but we must parse
  457.          * ref URL's like: "hello:there" w/ a ':' in them.
  458.          */
  459.         aRef=true;
  460.         }
  461.         for (i = start ; !aRef && (i < limit) && 
  462.              ((c = spec.charAt(i)) != '/') ; i++) {
  463.         if (c == ':') {
  464.             String s = spec.substring(start, i).toLowerCase();
  465.             if (isValidProtocol(s)) {
  466.             newProtocol = s;
  467.             start = i + 1;
  468.             }
  469.             break;
  470.         }
  471.         }
  472.  
  473.         // Only use our context if the protocols match.
  474.         if ((context != null) && ((newProtocol == null) ||
  475.                     newProtocol.equals(context.protocol))) {
  476.         protocol = context.protocol;
  477.         host = context.host;
  478.         port = context.port;
  479.         file = context.file;
  480.         // inherit the protocol handler as well from the context
  481.         // if not specified to the contructor
  482.         if (handler == null) {
  483.             handler = context.handler;
  484.         }
  485.         } else {
  486.         protocol = newProtocol;
  487.         }
  488.  
  489.         if (protocol == null) {
  490.         throw new MalformedURLException("no protocol: "+original);
  491.         }
  492.  
  493.         // Get the protocol handler if not specified or the protocol
  494.         // of the context could not be used
  495.         if (handler == null &&
  496.             (handler = getURLStreamHandler(protocol)) == null) {
  497.         throw new MalformedURLException("unknown protocol: "+protocol);
  498.         }
  499.  
  500.         this.handler = handler;
  501.  
  502.         i = spec.indexOf('#', start);
  503.         if (i >= 0) {
  504.         ref = spec.substring(i + 1, limit);
  505.         limit = i;
  506.         }
  507.         handler.parseURL(this, spec, start, limit);
  508.  
  509.     } catch(MalformedURLException e) {
  510.         throw e;
  511.     } catch(Exception e) {
  512.         throw new MalformedURLException(original + ": " + e);
  513.     }
  514.     }
  515.  
  516.     /*
  517.      * Returns true if specified string is a valid protocol name.
  518.      */
  519.     private boolean isValidProtocol(String protocol) {
  520.     int len = protocol.length();
  521.         if (len < 2)
  522.             return false;
  523.     for (int i = 0; i < len; i++) {
  524.         char c = protocol.charAt(i);
  525.         if (!Character.isLetterOrDigit(c) && c != '.' && c != '+' &&
  526.         c != '-') {
  527.         return false;
  528.         }
  529.     }
  530.     return true;
  531.     }
  532.  
  533.     /*
  534.      * Checks for permission to specify a stream handler.
  535.      */
  536.     private void checkSpecifyHandler(SecurityManager sm) {
  537.     if (specifyHandlerPerm == null) {
  538.         specifyHandlerPerm = new NetPermission("specifyStreamHandler");
  539.     }
  540.     sm.checkPermission(specifyHandlerPerm);
  541.     }
  542.  
  543.     private static NetPermission specifyHandlerPerm;
  544.  
  545.     /**
  546.      * Sets the fields of the URL. This is not a public method so that 
  547.      * only URLStreamHandlers can modify URL fields. URLs are 
  548.      * otherwise constant.
  549.      *
  550.      * @param protocol the protocol to use
  551.      * @param host the host name to connecto to
  552.        @param port the protocol port to connect to
  553.      * @param file the specified file name on that host
  554.      * @param ref the reference
  555.      */
  556.     protected void set(String protocol, String host, 
  557.                int port, String file, String ref) {
  558.     synchronized (this) {
  559.         this.protocol = protocol;
  560.         this.host = host;
  561.         this.port = port;
  562.         this.file = file;
  563.         this.ref = ref;
  564.         /* This is very important. We must recompute this after the
  565.          * URL has been changed. */
  566.         hashCode = -1;
  567.             hostAddress = null;
  568.     }
  569.     }
  570.  
  571.     /**
  572.      * Returns the port number of this <code>URL</code>.
  573.      * Returns -1 if the port is not set.
  574.      *
  575.      * @return  the port number
  576.      */
  577.     public int getPort() {
  578.     return port;
  579.     }
  580.  
  581.     /**
  582.      * Returns the protocol name this <code>URL</code>.
  583.      *
  584.      * @return  the protocol of this <code>URL</code>.
  585.      */
  586.     public String getProtocol() {
  587.     return protocol;
  588.     }
  589.  
  590.     /**
  591.      * Returns the host name of this <code>URL</code>, if applicable.
  592.      * For "<code>file</code>" protocol, this is an empty string.
  593.      *
  594.      * @return  the host name of this <code>URL</code>.
  595.      */
  596.     public String getHost() {
  597.     return host;
  598.     }
  599.  
  600.     /**
  601.      * Returns the file name of this <code>URL</code>.
  602.      *
  603.      * @return  the file name of this <code>URL</code>.
  604.      */
  605.     public String getFile() {
  606.     return file;
  607.     }
  608.  
  609.     /**
  610.      * Returns the anchor (also known as the "reference") of this
  611.      * <code>URL</code>.
  612.      *
  613.      * @return  the anchor (also known as the "reference") of this
  614.      *          <code>URL</code>.
  615.      */
  616.     public String getRef() {
  617.     return ref;
  618.     }
  619.  
  620.     /** 
  621.      * Compares two URLs.  The result is <code>true</code> if and
  622.      * only if the argument is not <code>null</code> and is a
  623.      * <code>URL</code> object that represents the same
  624.      * <code>URL</code> as this object. Two URL objects are equal if
  625.      * they have the same protocol and reference the same host, the
  626.      * same port number on the host, and the same file and anchor on
  627.      * the host.
  628.      *
  629.      * @param   obj   the URL to compare against.
  630.      * @return  <code>true</code> if the objects are the same;
  631.      *          <code>false</code> otherwise.
  632.      */
  633.     public boolean equals(Object obj) {
  634.         if (!(obj instanceof URL))
  635.             return false;
  636.     URL u2 = (URL)obj;
  637.  
  638.         return sameFile(u2) && 
  639.             (ref == u2.ref || (ref != null && ref.equals(u2.ref)));
  640.     }
  641.  
  642.     /** 
  643.      * Creates an integer suitable for hash table indexing.
  644.      *
  645.      * @return  a hash code for this <code>URL</code>.
  646.      */
  647.     public synchronized int hashCode() {
  648.     if (hashCode != -1)
  649.             return hashCode;
  650.         int h = 0;
  651.  
  652.         // Generate the protocol part.
  653.         if (protocol != null)
  654.         h += protocol.hashCode();
  655.  
  656.         // Generate the host part.
  657.     InetAddress addr = getHostAddress();
  658.     if (addr != null)
  659.         h += addr.hashCode();
  660.     else if (host != null)
  661.         h += host.toLowerCase().hashCode();
  662.  
  663.         // Generate the file part.
  664.     if (file != null)
  665.         h += file.hashCode();
  666.  
  667.         // Generate the port part.
  668.         h += port;
  669.  
  670.         // Generate the ref part.
  671.     if (ref != null)
  672.             h += ref.hashCode();
  673.  
  674.         hashCode = h;
  675.     return h;
  676.     }
  677.  
  678.     /**
  679.      * Get the IP address of our host. An empty host field or a DNS failure
  680.      * will result in a null return.
  681.      */
  682.     private synchronized InetAddress getHostAddress() {
  683.     if (hostAddress != null)
  684.             return hostAddress;
  685.     
  686.         if (host == null || host.equals("")) {
  687.             return null;
  688.         } else {
  689.             try {
  690.                 hostAddress = InetAddress.getByName(host);
  691.             } catch (UnknownHostException ex) {
  692.                 return null;
  693.             } catch (SecurityException se) {
  694.                 return null;
  695.             }
  696.         }
  697.     return hostAddress;
  698.     }
  699.  
  700.     /**
  701.      * Compares the host components of two URLs.
  702.      * @param h1 the URL of the first host to compare 
  703.      * @param h2 the URL of the second host to compare 
  704.      * @return    true if and only if they are equal, false otherwise.
  705.      * @exception UnknownHostException If an unknown host is found.
  706.      */
  707.     private static boolean hostsEqual(URL u1, URL u2) {
  708.     InetAddress a1 = u1.getHostAddress();
  709.         InetAddress a2 = u2.getHostAddress();
  710.     // if we have internet address for both, compare them
  711.     if (a1 != null && a2 != null) {
  712.         return a1.equals(a2);
  713.         // else, if both have host names, compare them
  714.     } else if (u1.host != null && u2.host != null) 
  715.             return u1.host.equalsIgnoreCase(u2.host);
  716.      else
  717.             return u1.host == null && u2.host == null;
  718.     }
  719.  
  720.     /**
  721.      * Compares two URLs, excluding the "ref" fields.
  722.      * Returns <code>true</code> if this <code>URL</code> and the 
  723.      * <code>other</code> argument both refer to the same resource.
  724.      * The two <code>URL</code>s might not both contain the same anchor. 
  725.      *
  726.      * @param   other   the <code>URL</code> to compare against.
  727.      * @return  <code>true</code> if they reference the same remote object;
  728.      *          <code>false</code> otherwise.
  729.      */
  730.     public boolean sameFile(URL other) {
  731.         // Compare the protocols.
  732.         if (!((other.protocol == protocol) ||
  733.               (protocol != null && protocol.equalsIgnoreCase(other.protocol))))
  734.             return false;
  735.  
  736.     // Compare the hosts.
  737.     if (!hostsEqual(this, other))
  738.             return false;
  739.  
  740.     // Compare the files.
  741.     if (!(file == other.file || (file != null && file.equals(other.file))))
  742.         return false;
  743.  
  744.     // Compare the ports.
  745.     if (port != other.port)
  746.         return false;
  747.  
  748.         return true;
  749.     }
  750.  
  751.     /**
  752.      * Constructs a string representation of this <code>URL</code>. The 
  753.      * string is created by calling the <code>toExternalForm</code> 
  754.      * method of the stream protocol handler for this object. 
  755.      *
  756.      * @return  a string representation of this object.
  757.      * @see     java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
  758.      * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
  759.      */
  760.     public String toString() {
  761.     return toExternalForm();
  762.     }
  763.  
  764.     /**
  765.      * Constructs a string representation of this <code>URL</code>. The 
  766.      * string is created by calling the <code>toExternalForm</code> 
  767.      * method of the stream protocol handler for this object. 
  768.      *
  769.      * @return  a string representation of this object.
  770.      * @see     java.net.URL#URL(java.lang.String, java.lang.String, int, java.lang.String)
  771.      * @see     java.net.URLStreamHandler#toExternalForm(java.net.URL)
  772.      */
  773.     public String toExternalForm() {
  774.     return handler.toExternalForm(this);
  775.     }
  776.  
  777.     /** 
  778.      * Returns a <code>URLConnection</code> object that represents a 
  779.      * connection to the remote object referred to by the <code>URL</code>.
  780.      *
  781.      * <p>A new connection is opened every time by calling the
  782.      * <code>openConnection</code> method of the protocol handler for
  783.      * this URL.
  784.      *
  785.      * <p>If for the URL's protocol (such as HTTP or JAR), there
  786.      * exists a public, specialized URLConnection subclass belonging
  787.      * to one of the following packages or one of their subpackages:
  788.      * java.lang, java.io, java.util, java.net, the connection
  789.      * returned will be of that subclass. For example, for HTTP an
  790.      * HttpURLConnection will be returned, and for JAR a
  791.      * JarURLConnection will be returned.
  792.      *
  793.      * @return     a <code>URLConnection</code> to the URL.
  794.      * @exception  IOException  if an I/O exception occurs.
  795.      * @see        java.net.URL#URL(java.lang.String, java.lang.String, 
  796.      *             int, java.lang.String)
  797.      * @see        java.net.URLConnection
  798.      * @see java.net.URLStreamHandler#openConnection(java.net.URL) 
  799.      */
  800.     public URLConnection openConnection() throws java.io.IOException {
  801.     return handler.openConnection(this);
  802.     }
  803.  
  804.     /**
  805.      * Opens a connection to this <code>URL</code> and returns an 
  806.      * <code>InputStream</code> for reading from that connection. This 
  807.      * method is a shorthand for:
  808.      * <blockquote><pre>
  809.      *     openConnection().getInputStream()
  810.      * </pre></blockquote>
  811.      *
  812.      * @return     an input stream for reading from the URL connection.
  813.      * @exception  IOException  if an I/O exception occurs.
  814.      * @see        java.net.URL#openConnection()
  815.      * @see        java.net.URLConnection#getInputStream()
  816.      */
  817.     public final InputStream openStream() throws java.io.IOException {
  818.     return openConnection().getInputStream();
  819.     }
  820.  
  821.     /**
  822.      * Returns the contents of this URL. This method is a shorthand for:
  823.      * <blockquote><pre>
  824.      *     openConnection().getContent()
  825.      * </pre></blockquote>
  826.      *
  827.      * @return     the contents of this URL.
  828.      * @exception  IOException  if an I/O exception occurs.
  829.      * @see        java.net.URLConnection#getContent()
  830.      */
  831.     public final Object getContent() throws java.io.IOException {
  832.     return openConnection().getContent();
  833.     }
  834.  
  835.     /**
  836.      * The URLStreamHandler factory.
  837.      */
  838.     static URLStreamHandlerFactory factory;
  839.  
  840.     /**
  841.      * Sets an application's <code>URLStreamHandlerFactory</code>.
  842.      * This method can be called at most once in a given Java Virtual
  843.      * Machine.
  844.      *
  845.      *<p> The <code>URLStreamHandlerFactory</code> instance is used to
  846.      *construct a stream protocol handler from a protocol name.
  847.      * 
  848.      * <p> If there is a security manager, this method first calls
  849.      * the security manager's <code>checkSetFactory</code> method 
  850.      * to ensure the operation is allowed. 
  851.      * This could result in a SecurityException.
  852.      *
  853.      * @param      fac   the desired factory.
  854.      * @exception  Error  if the application has already set a factory.
  855.      * @exception  SecurityException  if a security manager exists and its  
  856.      *             <code>checkSetFactory</code> method doesn't allow the operation.
  857.      * @see        java.net.URL#URL(java.lang.String, java.lang.String, 
  858.      *             int, java.lang.String)
  859.      * @see        java.net.URLStreamHandlerFactory
  860.      * @see        SecurityManager#checkSetFactory
  861.      */
  862.     public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory fac) {
  863.     if (factory != null) {
  864.         throw new Error("factory already defined");
  865.     }
  866.     SecurityManager security = System.getSecurityManager();
  867.     if (security != null) {
  868.         security.checkSetFactory();
  869.     }
  870.     handlers.clear();
  871.     factory = fac;
  872.     }
  873.  
  874.     /**
  875.      * A table of protocol handlers.
  876.      */
  877.     static Hashtable handlers = new Hashtable();
  878.  
  879.  
  880.     /**
  881.      * Returns the Stream Handler.
  882.      * @param protocol the protocol to use
  883.      */
  884.     static synchronized URLStreamHandler getURLStreamHandler(String protocol) {
  885.     URLStreamHandler handler = (URLStreamHandler)handlers.get(protocol);
  886.     if (handler == null) {
  887.         // Use the factory (if any)
  888.         if (factory != null) {
  889.         handler = factory.createURLStreamHandler(protocol);
  890.         }
  891.  
  892.         // Try java protocol handler
  893.         if (handler == null) {
  894.         String packagePrefixList = null;
  895.  
  896.         packagePrefixList
  897.             = (String) java.security.AccessController.doPrivileged(
  898.                     new sun.security.action.GetPropertyAction(protocolPathProp,""));
  899.         if (packagePrefixList != "") {
  900.             packagePrefixList += "|";
  901.         }
  902.  
  903.         // REMIND: decide whether to allow the "null" class prefix
  904.         // or not.
  905.         packagePrefixList += "sun.net.www.protocol";
  906.  
  907.         StringTokenizer packagePrefixIter =
  908.             new StringTokenizer(packagePrefixList, "|");
  909.  
  910.         while (handler == null && 
  911.                packagePrefixIter.hasMoreTokens()) {
  912.  
  913.             String packagePrefix = 
  914.               packagePrefixIter.nextToken().trim();
  915.             try {
  916.                 String clsName = packagePrefix + "." + protocol +
  917.               ".Handler";
  918.             Class cls = null;
  919.             try {
  920.                             cls = Class.forName(clsName);
  921.                         } catch (ClassNotFoundException e) {
  922.                 ClassLoader cl = ClassLoader.getSystemClassLoader();
  923.                 if (cl != null) {
  924.                     cls = cl.loadClass(clsName);
  925.                 }
  926.             }
  927.             if (cls != null) {
  928.                 handler  = 
  929.                   (URLStreamHandler)cls.newInstance();
  930.             }
  931.             } catch (Exception e) {
  932.             // any number of exceptions can get thrown here
  933.             }
  934.         }
  935.         }
  936.         if (handler != null) {
  937.         handlers.put(protocol, handler);
  938.         }
  939.     }
  940.     return handler;
  941.     }
  942.  
  943.     /** 
  944.      * WriteObject is called to save the state of the URL to an
  945.      * ObjectOutputStream. The handler is not saved since it is
  946.      * specific to this system.  
  947.      *
  948.      * @serialData the default write object value. When read back in,
  949.      * the reader must ensure that calling getURLStreamHandler with
  950.      * the protocol variable returns a valid URLStreamHandler and 
  951.      * throw an IOException if it does not.
  952.      */
  953.     private synchronized void writeObject(java.io.ObjectOutputStream s)
  954.         throws IOException
  955.     {
  956.     s.defaultWriteObject();    // write the fields
  957.     }
  958.  
  959.     /** 
  960.      * readObject is called to restore the state of the URL from the
  961.      * stream.  It reads the components of the URL and finds the local
  962.      * stream handler.  
  963.      */
  964.     private synchronized void readObject(java.io.ObjectInputStream s)
  965.          throws IOException, ClassNotFoundException
  966.     {
  967.     s.defaultReadObject();    // read the fields
  968.     if ((handler = getURLStreamHandler(protocol)) == null) {
  969.         throw new IOException("unknown protocol: " + protocol);
  970.     }
  971.     }
  972. }
  973.