9abe2e29b4
* java/net/URL.java (getPath): New JDK 1.3 method. * java/net/URLStreamHandler.java (parseURL): It is wrong to prepend '/' to the file part of a relative url. * java/net/URLStreamHandler.java (parseURL): Minor optizations - append '/' rather than "/". * java/net/URLStreamHandler.java (parseURL): Don't canonicalize "xx/.." or "./" URLs - JDK doesn't. We probably should canonicalize for a context-relative url, though. * java/net/URL.java (sameFile): Delegate to URLStreamHandler. * java/net/URLStreamHandler.java (canonicalizeFilename): New helper. (sameFile): New method. Uses canonicalizeFilename. From-SVN: r49980
413 lines
12 KiB
Java
413 lines
12 KiB
Java
// URL.java - A Uniform Resource Locator.
|
|
|
|
/* Copyright (C) 1999, 2000, 2002 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 getPath()
|
|
{
|
|
int quest = file.indexOf('?');
|
|
return quest < 0 ? file : file.substring(0, quest);
|
|
}
|
|
|
|
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)
|
|
{
|
|
return handler.sameFile(this, other);
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|