The spec for for the method java.net.URI.toURL asserts:
This convenience method works as if invoking it were equivalent to
evaluating the expression
new URL(this.toString())
after first checking that this URI is absolute.
But that equivalence assertion is currently violated for "file:" URLs,
in 1.4.0 and Hopper today. The two approaches produce URL instances
that are not equal to each other-- the difference is whether the URL's
host value is null or empty string.
More generally:
Currently in 1.4.0 and Hopper, the following means of creating "file:"
URLs create a URL with a host value of empty string:
a) new URL("file:/tmp/")
b) (new File("/tmp")).toURL()
but these other means create a URL with a host value of null:
c) new URI("file:/tmp/").toURL()
d) (new File("/tmp")).toURI().toURL()
Thus, a URL created by (a) or (b) will not be equal (by URL.equals) to
a URL created by (c) of (d).
It would seem highly desirable for URL instances created by all of (a),
(b), (c), and (d)-- which all have the same external string forms-- to
be considered equal by URL.equals.
Put slightly differently, it would seem highly desirable to make the
expression "u.equals(new URL(u.toExternalForm())" return true for as
many kinds of URL instances "u" as reasonably possible.
The behaviors of (c) and (d) do seem justified, because these URIs
really do not have host components, and null is the value for not
having a given component-- see 4496251, which is marked fixed and
integrated in merlin-beta3, but was actually backed out because of
4506291 (also for merlin-beta3).
The seemingly reasonable equivalence assertion in the spec for
URI.toURL, which claims that (c) should produce the same result as (a),
is currently violated-- given that (c) seems to be doing the right
thing, this would seem to indicate that (a) should be fixed (again--
see Comments section), but then to keep 4506291 fixed as well, (b)
would then have to be fixed also, so that all of these expressions would
produce equivalent URL instances, with null host values.
But would such a change in the behaviors or (a) or (b) raise any other
compatibility issues?
Below is a test program that demonstrates the above issues and example output of the program with 1.4.0 and the latest Hopper promoted build.
import java.io.File;
import java.net.URI;
import java.net.URL;
public class FileURL {
public static void main(String[] args) throws Exception {
String pathString = args[0];
String pathURL = (new File(pathString)).toURL().toExternalForm();
println();
println("URL a = new URL(\"" + pathURL + "\")");
println();
URL a = new URL(pathURL);
dumpURL(a);
println();
println("URL b = (new File(\"" + pathString + "\")).toURL()");
println();
URL b = (new File(pathString)).toURL();
dumpURL(b);
println();
println("URL c = new URI(\"" + pathURL + "\").toURL()");
println();
URL c = new URI(pathURL).toURL();
dumpURL(c);
println();
println("URL d = (new File(\"" + pathString + "\")).toURI().toURL()");
println();
URL d = (new File(pathString)).toURI().toURL();
dumpURL(d);
println();
println("a.equals(b) returns " + a.equals(b));
println("a.equals(c) returns " + a.equals(c));
println("a.equals(d) returns " + a.equals(d));
println("b.equals(a) returns " + b.equals(a));
println("b.equals(c) returns " + b.equals(c));
println("b.equals(d) returns " + b.equals(d));
println("c.equals(a) returns " + c.equals(a));
println("c.equals(b) returns " + c.equals(b));
println("c.equals(d) returns " + c.equals(d));
println("d.equals(a) returns " + d.equals(a));
println("d.equals(b) returns " + d.equals(b));
println("d.equals(c) returns " + d.equals(c));
}
private static void dumpURL(URL u) {
println("external form: " + u.toExternalForm());
println("\tprotocol: " + u.getProtocol());
println("\tauthority: " + u.getAuthority());
println("\tuser info: " + u.getUserInfo());
println("\thost: " + u.getHost());
println("\tport: " + u.getPort());
println("\tpath: " + u.getPath());
println("\tfile: " + u.getFile());
println("\tquery: " + u.getQuery());
println("\tref: " + u.getRef());
}
private static void println() { System.err.println(); }
private static void println(String s) { System.err.println(s); }
}
[terrier] 71 % java -version
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)
[terrier] 72 % java FileURL /tmp
URL a = new URL("file:/tmp/")
external form: file:/tmp/
protocol: file
authority: null
user info: null
host:
port: -1
path: /tmp/
file: /tmp/
query: null
ref: null
URL b = (new File("/tmp")).toURL()
external form: file:/tmp/
protocol: file
authority:
user info: null
host:
port: -1
path: /tmp/
file: /tmp/
query: null
ref: null
URL c = new URI("file:/tmp/").toURL()
external form: file:/tmp/
protocol: file
authority: null
user info: null
host: null
port: -1
path: /tmp/
file: /tmp/
query: null
ref: null
URL d = (new File("/tmp")).toURI().toURL()
external form: file:/tmp/
protocol: file
authority: null
user info: null
host: null
port: -1
path: /tmp/
file: /tmp/
query: null
ref: null
a.equals(b) returns true
a.equals(c) returns false
a.equals(d) returns false
b.equals(a) returns true
b.equals(c) returns false
b.equals(d) returns false
c.equals(a) returns false
c.equals(b) returns false
c.equals(d) returns true
d.equals(a) returns false
d.equals(b) returns false
d.equals(c) returns true
[terrier] 74 % java -version
java version "1.4.1-beta"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-beta-b10)
Java HotSpot(TM) Client VM (build 1.4.1-beta-b10, mixed mode)
[terrier] 75 % java FileURL /tmp
URL a = new URL("file:/tmp/")
external form: file:/tmp/
protocol: file
authority: null
user info: null
host:
port: -1
path: /tmp/
file: /tmp/
query: null
ref: null
URL b = (new File("/tmp")).toURL()
external form: file:/tmp/
protocol: file
authority:
user info: null
host:
port: -1
path: /tmp/
file: /tmp/
query: null
ref: null
URL c = new URI("file:/tmp/").toURL()
external form: file:/tmp/
protocol: file
authority: null
user info: null
host: null
port: -1
path: /tmp/
file: /tmp/
query: null
ref: null
URL d = (new File("/tmp")).toURI().toURL()
external form: file:/tmp/
protocol: file
authority: null
user info: null
host: null
port: -1
path: /tmp/
file: /tmp/
query: null
ref: null
a.equals(b) returns true
a.equals(c) returns false
a.equals(d) returns false
b.equals(a) returns true
b.equals(c) returns false
b.equals(d) returns false
c.equals(a) returns false
c.equals(b) returns false
c.equals(d) returns true
d.equals(a) returns false
d.equals(b) returns false
d.equals(c) returns true