| // 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("Protocol handler not found: " + protocol); | |
| 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("Protocol handler not found: " + protocol); | |
| // 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 ("core")) | |
| { | |
| handler = new gnu.gcj.protocol.core.Handler (); | |
| } | |
| 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(); | |
| } | |
| } |