| // URL.java - A Uniform Resource Locator. |
| |
| /* Copyright (C) 1999 Cygnus Solutions |
| |
| 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 URLStreamHandler handler; |
| private static Hashtable handlers = new Hashtable(); |
| private static URLStreamHandlerFactory factory; |
| |
| 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); |
| } |
| } |
| |
| 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); |
| } |
| |
| public boolean equals(Object obj) |
| { |
| if (obj == null || ! (obj instanceof URL)) |
| return false; |
| |
| URL uObj = (URL) obj; |
| if (protocol != uObj.protocol || host != uObj.host || port != uObj.port || |
| file != uObj.file || ref != uObj.ref) |
| return false; |
| |
| return true; |
| } |
| |
| 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. |
| |
| 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) |
| { |
| if (other == null || protocol != other.protocol || host != other.host || |
| port != other.port || file != other.file) |
| return false; |
| |
| return true; |
| } |
| |
| 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; |
| } |
| |
| 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); |
| |
| // 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; |
| } |
| } |