| // URL.java - A Uniform Resource Locator. |
| |
| /* Copyright (C) 1999, 2000 Free Software Foundation |
| |
| This file is part of libgcj. |
| |
| This software is copyrighted work licensed under the terms of the |
| Libgcj License. Please consult the file "LIBGCJ_LICENSE" for |
| details. */ |
| |
| package java.net; |
| |
| import java.io.*; |
| import java.util.Hashtable; |
| import java.util.StringTokenizer; |
| |
| /** |
| * @author Warren Levy <warrenl@cygnus.com> |
| * @date March 4, 1999. |
| */ |
| |
| /** |
| * Written using on-line Java Platform 1.2 API Specification, as well |
| * as "The Java Class Libraries", 2nd edition (Addison-Wesley, 1998). |
| * Status: Believed complete and correct. |
| */ |
| |
| public final class URL implements Serializable |
| { |
| private String protocol; |
| private String host; |
| private int port = -1; // Initialize for constructor using context. |
| private String file; |
| private String ref; |
| private int hashCode = 0; |
| transient private URLStreamHandler handler; |
| private static Hashtable handlers = new Hashtable(); |
| private static URLStreamHandlerFactory factory; |
| |
| private static final long serialVersionUID = -7627629688361524110L; |
| |
| public URL(String protocol, String host, int port, String file) |
| throws MalformedURLException |
| { |
| this(protocol, host, port, file, null); |
| } |
| |
| public URL(String protocol, String host, String file) |
| throws MalformedURLException |
| { |
| this(protocol, host, -1, file, null); |
| } |
| |
| // JDK1.2 |
| public URL(String protocol, String host, int port, String file, |
| URLStreamHandler handler) throws MalformedURLException |
| { |
| if (protocol == null) |
| throw new MalformedURLException("null protocol"); |
| this.protocol = protocol; |
| |
| if (handler != null) |
| { |
| // TODO12: Need SecurityManager.checkPermission and |
| // TODO12: java.net.NetPermission from JDK 1.2 to be implemented. |
| // Throw an exception if an extant security mgr precludes |
| // specifying a StreamHandler. |
| // |
| // SecurityManager s = System.getSecurityManager(); |
| // if (s != null) |
| // s.checkPermission(NetPermission("specifyStreamHandler")); |
| |
| this.handler = handler; |
| } |
| else |
| this.handler = setURLStreamHandler(protocol); |
| |
| if (this.handler == null) |
| throw new MalformedURLException("Handler for protocol not found"); |
| |
| this.host = host; |
| |
| this.port = port; |
| |
| int hashAt = file.indexOf('#'); |
| if (hashAt < 0) |
| { |
| this.file = file; |
| this.ref = null; |
| } |
| else |
| { |
| this.file = file.substring(0, hashAt); |
| this.ref = file.substring(hashAt + 1); |
| } |
| hashCode = hashCode(); // Used for serialization. |
| } |
| |
| public URL(String spec) throws MalformedURLException |
| { |
| this((URL) null, spec, (URLStreamHandler) null); |
| } |
| |
| public URL(URL context, String spec) throws MalformedURLException |
| { |
| this(context, spec, (URLStreamHandler) null); |
| } |
| |
| // JDK1.2 |
| public URL(URL context, String spec, URLStreamHandler handler) |
| throws MalformedURLException |
| { |
| /* A protocol is defined by the doc as the substring before a ':' |
| * as long as the ':' occurs before any '/'. |
| * |
| * If context is null, then spec must be an absolute URL. |
| * |
| * The relative URL need not specify all the components of a URL. |
| * If the protocol, host name, or port number is missing, the value |
| * is inherited from the context. A bare file component is appended |
| * to the context's file. The optional anchor is not inherited. |
| */ |
| |
| // If this is an absolute URL, then ignore context completely. |
| // An absolute URL must have chars prior to "://" but cannot have a colon |
| // right after the "://". The second colon is for an optional port value |
| // and implies that the host from the context is used if available. |
| int colon; |
| if ((colon = spec.indexOf("://", 1)) > 0 && |
| ! spec.regionMatches(colon, "://:", 0, 4)) |
| context = null; |
| |
| int slash; |
| if ((colon = spec.indexOf(':')) > 0 && |
| (colon < (slash = spec.indexOf('/')) || slash < 0)) |
| { |
| // Protocol specified in spec string. |
| protocol = spec.substring(0, colon); |
| if (context != null && context.protocol.equals(protocol)) |
| { |
| // The 1.2 doc specifically says these are copied to the new URL. |
| host = context.host; |
| port = context.port; |
| file = context.file; |
| } |
| } |
| else if (context != null) |
| { |
| // Protocol NOT specified in spec string. |
| // Use context fields (except ref) as a foundation for relative URLs. |
| colon = -1; |
| protocol = context.protocol; |
| host = context.host; |
| port = context.port; |
| file = context.file; |
| } |
| else // Protocol NOT specified in spec. and no context available. |
| throw new |
| MalformedURLException("Absolute URL required with null context"); |
| |
| if (handler != null) |
| { |
| // TODO12: Need SecurityManager.checkPermission and |
| // TODO12: java.net.NetPermission from JDK 1.2 to be implemented. |
| // Throw an exception if an extant security mgr precludes |
| // specifying a StreamHandler. |
| // |
| // SecurityManager s = System.getSecurityManager(); |
| // if (s != null) |
| // s.checkPermission(NetPermission("specifyStreamHandler")); |
| |
| this.handler = handler; |
| } |
| else |
| this.handler = setURLStreamHandler(protocol); |
| |
| if (this.handler == null) |
| throw new MalformedURLException("Handler for protocol not found"); |
| |
| // JDK 1.2 doc for parseURL specifically states that any '#' ref |
| // is to be excluded by passing the 'limit' as the indexOf the '#' |
| // if one exists, otherwise pass the end of the string. |
| int hashAt = spec.indexOf('#', colon + 1); |
| this.handler.parseURL(this, spec, colon + 1, |
| hashAt < 0 ? spec.length() : hashAt); |
| if (hashAt >= 0) |
| ref = spec.substring(hashAt + 1); |
| |
| hashCode = hashCode(); // Used for serialization. |
| } |
| |
| public boolean equals(Object obj) |
| { |
| if (obj == null || ! (obj instanceof URL)) |
| return false; |
| |
| URL uObj = (URL) obj; |
| |
| // This comparison is very conservative. It assumes that any |
| // field can be null. |
| return (port == uObj.port |
| && ((protocol == null && uObj.protocol == null) |
| || (protocol != null && protocol.equals(uObj.protocol))) |
| && ((host == null && uObj.host == null) |
| || (host != null && host.equals(uObj.host))) |
| && ((file == null && uObj.file == null) |
| || (file != null && file.equals(uObj.file))) |
| && ((ref == null && uObj.ref == null) |
| || (ref != null && ref.equals(uObj.ref)))); |
| } |
| |
| public final Object getContent() throws IOException |
| { |
| return openConnection().getContent(); |
| } |
| |
| public String getFile() |
| { |
| return file; |
| } |
| |
| public String getHost() |
| { |
| return host; |
| } |
| |
| public int getPort() |
| { |
| return port; |
| } |
| |
| public String getProtocol() |
| { |
| return protocol; |
| } |
| |
| public String getRef() |
| { |
| return ref; |
| } |
| |
| public int hashCode() |
| { |
| // JCL book says this is computed using (only) the hashcodes of the |
| // protocol, host and file fields. Empirical evidence indicates this |
| // is probably XOR in JDK 1.1. In JDK 1.2 it seems to be a sum including |
| // the port. |
| // |
| // JDK 1.2 online doc infers that host could be null because it |
| // explicitly states that file cannot be null but is silent on host. |
| // A simple example with protocol "http" (hashcode 3213448), host null, |
| // file "/" (hashcode 47) produced a hashcode (3213494) which appeared |
| // to be the sum of the two hashcodes plus the port. Another example |
| // using "/index.html" for file bore this out; as well as "#" for file |
| // (which was reduced to "" with a hashcode of zero). A "" host also |
| // causes the port number and the two hashcodes to be summed. |
| |
| if (hashCode != 0) |
| return hashCode; // Use cached value if available. |
| else |
| return (protocol.hashCode() + ((host == null) ? 0 : host.hashCode()) + |
| port + file.hashCode()); |
| } |
| |
| public URLConnection openConnection() throws IOException |
| { |
| return handler.openConnection(this); |
| } |
| |
| public final InputStream openStream() throws IOException |
| { |
| return openConnection().getInputStream(); |
| } |
| |
| public boolean sameFile(URL other) |
| { |
| // This comparison is very conservative. It assumes that any |
| // field can be null. |
| return (other != null |
| && port == other.port |
| && ((protocol == null && other.protocol == null) |
| || (protocol != null && protocol.equals(other.protocol))) |
| && ((host == null && other.host == null) |
| || (host != null && host.equals(other.host))) |
| && ((file == null && other.file == null) |
| || (file != null && file.equals(other.file)))); |
| } |
| |
| protected void set(String protocol, String host, int port, String file, |
| String ref) |
| { |
| // TBD: Theoretically, a poorly written StreamHandler could pass an |
| // invalid protocol. It will cause the handler to be set to null |
| // thus overriding a valid handler. Callers of this method should |
| // be aware of this. |
| this.handler = setURLStreamHandler(protocol); |
| this.protocol = protocol; |
| this.port = port; |
| this.host = host; |
| this.file = file; |
| this.ref = ref; |
| hashCode = hashCode(); // Used for serialization. |
| } |
| |
| public static synchronized void |
| setURLStreamHandlerFactory(URLStreamHandlerFactory fac) |
| { |
| if (factory != null) |
| throw new Error("URLStreamHandlerFactory already set"); |
| |
| // Throw an exception if an extant security mgr precludes |
| // setting the factory. |
| SecurityManager s = System.getSecurityManager(); |
| if (s != null) |
| s.checkSetFactory(); |
| factory = fac; |
| } |
| |
| public String toExternalForm() |
| { |
| // Identical to toString(). |
| return handler.toExternalForm(this); |
| } |
| |
| public String toString() |
| { |
| // Identical to toExternalForm(). |
| return handler.toExternalForm(this); |
| } |
| |
| private URLStreamHandler setURLStreamHandler(String protocol) |
| { |
| URLStreamHandler handler; |
| |
| // See if a handler has been cached for this protocol. |
| if ((handler = (URLStreamHandler) handlers.get(protocol)) != null) |
| return handler; |
| |
| // If a non-default factory has been set, use it to find the protocol. |
| if (factory != null) |
| handler = factory.createURLStreamHandler(protocol); |
| else if (protocol.equals ("file")) |
| { |
| // This is an interesting case. It's tempting to think that we |
| // could call Class.forName ("gnu.gcj.protocol.file.Handler") to |
| // get the appropriate class. Unfortunately, if we do that the |
| // program will never terminate, because setURLStreamHandler is |
| // eventually called by Class.forName. |
| // |
| // Treating "file" as a special case is the minimum that will |
| // fix this problem. If other protocols are required in a |
| // statically linked application they will need to be handled in |
| // the same way as "file". |
| handler = new gnu.gcj.protocol.file.Handler (); |
| } |
| |
| // Non-default factory may have returned null or a factory wasn't set. |
| // Use the default search algorithm to find a handler for this protocol. |
| if (handler == null) |
| { |
| // Get the list of packages to check and append our default handler |
| // to it, along with the JDK specified default as a last resort. |
| // Except in very unusual environments the JDK specified one shouldn't |
| // ever be needed (or available). |
| String propVal = System.getProperty("java.protocol.handler.pkgs"); |
| propVal = (propVal == null) ? "" : (propVal + "|"); |
| propVal = propVal + "gnu.gcj.protocol|sun.net.www.protocol"; |
| |
| StringTokenizer pkgPrefix = new StringTokenizer(propVal, "|"); |
| do |
| { |
| String facName = pkgPrefix.nextToken() + "." + protocol + |
| ".Handler"; |
| try |
| { |
| handler = |
| (URLStreamHandler) Class.forName(facName).newInstance(); |
| } |
| catch (Exception e) |
| { |
| // Can't instantiate; handler still null, go on to next element. |
| } |
| } while ((handler == null || |
| ! (handler instanceof URLStreamHandler)) && |
| pkgPrefix.hasMoreTokens()); |
| } |
| |
| // Update the hashtable with the new protocol handler. |
| if (handler != null) |
| if (handler instanceof URLStreamHandler) |
| handlers.put(protocol, handler); |
| else |
| handler = null; |
| |
| return handler; |
| } |
| |
| private void readObject(ObjectInputStream ois) |
| throws IOException, ClassNotFoundException |
| { |
| ois.defaultReadObject(); |
| this.handler = setURLStreamHandler(protocol); |
| if (this.handler == null) |
| throw new IOException("Handler for protocol " + protocol + " not found"); |
| } |
| |
| private void writeObject(ObjectOutputStream oos) throws IOException |
| { |
| oos.defaultWriteObject(); |
| } |
| } |