8147462: URI.toURL could be more efficient for most non-opaque URIs

Reviewed-by: alanb, chegar
This commit is contained in:
Claes Redestad 2016-01-29 11:35:56 +01:00
parent 37ceaefd4b
commit 7ae729a77f
3 changed files with 128 additions and 15 deletions

View File

@ -1080,11 +1080,8 @@ public final class URI
* If a protocol handler for the URL could not be found,
* or if some other error occurred while constructing the URL
*/
public URL toURL()
throws MalformedURLException {
if (!isAbsolute())
throw new IllegalArgumentException("URI is not absolute");
return new URL(toString());
public URL toURL() throws MalformedURLException {
return URL.fromURI(this);
}
// -- Component access methods --

View File

@ -659,6 +659,42 @@ public final class URL implements java.io.Serializable {
}
}
/**
* Creates a URL from a URI, as if by invoking {@code uri.toURL()}.
*
* @see java.net.URI#toURL()
*/
static URL fromURI(URI uri) throws MalformedURLException {
if (!uri.isAbsolute()) {
throw new IllegalArgumentException("URI is not absolute");
}
String protocol = uri.getScheme();
if (!uri.isOpaque() && uri.getRawFragment() == null &&
!isOverrideable(protocol)) {
// non-opaque URIs will have already validated the components,
// so using the component-based URL constructor here is safe.
//
// All URL constructors will properly check if the scheme
// maps to a valid protocol handler
String query = uri.getRawQuery();
String path = uri.getRawPath();
String file = (query == null) ? path : path + "?" + query;
// URL represent undefined host as empty string while URI use null
String host = uri.getHost();
if (host == null) {
host = "";
}
int port = uri.getPort();
return new URL(protocol, host, port, file, null);
} else {
return new URL((URL)null, uri.toString(), null);
}
}
/*
* Returns true if specified string is a valid protocol name.
*/
@ -1275,11 +1311,28 @@ public final class URL implements java.io.Serializable {
}
}
private static final String[] NON_OVERRIDEABLE_PROTOCOLS = {"file", "jrt"};
private static boolean isOverrideable(String protocol) {
for (String p : NON_OVERRIDEABLE_PROTOCOLS)
if (protocol.equalsIgnoreCase(p))
/**
* Non-overrideable protocols: "jrt" and "file"
*
* Character-based comparison for performance reasons; also ensures
* case-insensitive comparison in a locale-independent fashion.
*/
static boolean isOverrideable(String protocol) {
if (protocol.length() == 3) {
if ((Character.toLowerCase(protocol.charAt(0)) == 'j') &&
(Character.toLowerCase(protocol.charAt(1)) == 'r') &&
(Character.toLowerCase(protocol.charAt(2)) == 't')) {
return false;
}
} else if (protocol.length() == 4) {
if ((Character.toLowerCase(protocol.charAt(0)) == 'f') &&
(Character.toLowerCase(protocol.charAt(1)) == 'i') &&
(Character.toLowerCase(protocol.charAt(2)) == 'l') &&
(Character.toLowerCase(protocol.charAt(3)) == 'e')) {
return false;
}
}
return true;
}

View File

@ -23,13 +23,16 @@
/**
* @test
* @bug 4768755 4677045
* @summary URL.equal(URL) is inconsistant for opaque URI.toURL()
* and new URL(URI.toString)
* @bug 4768755 4677045 8147462
* @summary URL.equal(URL) is inconsistent for opaque URI.toURL()
* and new URL(URI.toString)
* URI.toURL() does not always work as specified
* Ensure URIs representing invalid/malformed URLs throw similar
* exception with new URL(URI.toString()) and URI.toURL()
*/
import java.net.*;
import java.util.Objects;
public class URItoURLTest {
@ -39,19 +42,43 @@ public class URItoURLTest {
URL classUrl = testClass.getClass().
getResource("/java/lang/Object.class");
String[] uris = { "mailto:xyz@abc.de",
String[] uris = {
"mailto:xyz@abc.de",
"file:xyz#ab",
"http:abc/xyz/pqr",
"http:abc/xyz/pqr?id=x%0a&ca=true",
"file:/C:/v700/dev/unitTesting/tests/apiUtil/uri",
"http:///p",
"file:/C:/v700/dev/unitTesting/tests/apiUtil/uri",
"file:/C:/v700/dev%20src/unitTesting/tests/apiUtil/uri",
"file:/C:/v700/dev%20src/./unitTesting/./tests/apiUtil/uri",
"http://localhost:80/abc/./xyz/../pqr?id=x%0a&ca=true",
"file:./test/./x",
"file:./././%20#i=3",
"file:?hmm",
"file:.#hmm",
classUrl.toExternalForm(),
};
// Strings that represent valid URIs but invalid URLs that should throw
// MalformedURLException both when calling toURL and new URL(String)
String[] malformedUrls = {
"test:/test",
"fiel:test",
};
// Non-absolute URIs should throw IAE when calling toURL but will throw
// MalformedURLException when calling new URL
String[] illegalUris = {
"./test",
"/test",
};
boolean isTestFailed = false;
boolean isURLFailed = false;
for (int i = 0; i < uris.length; i++) {
URI uri = URI.create(uris[i]);
for (String uriString : uris) {
URI uri = URI.create(uriString);
URL url1 = new URL(uri.toString());
URL url2 = uri.toURL();
@ -107,6 +134,42 @@ public class URItoURLTest {
System.out.println();
isURLFailed = false;
}
for (String malformedUrl : malformedUrls) {
Exception toURLEx = null;
Exception newURLEx = null;
try {
new URI(malformedUrl).toURL();
} catch (Exception e) {
// expected
toURLEx = e;
}
try {
new URL(new URI(malformedUrl).toString());
} catch (Exception e) {
// expected
newURLEx = e;
}
if (!(toURLEx instanceof MalformedURLException) ||
!(newURLEx instanceof MalformedURLException) ||
!toURLEx.getMessage().equals(newURLEx.getMessage())) {
isTestFailed = true;
System.out.println("Expected the same MalformedURLException: " +
newURLEx + " vs " + toURLEx);
}
}
for (String illegalUri : illegalUris) {
try {
new URI(illegalUri).toURL();
} catch (IllegalArgumentException e) {
// pass
}
try {
new URL(illegalUri);
} catch (MalformedURLException e) {
// pass
}
}
if (isTestFailed) {
throw new Exception("URI.toURL() test failed");
}