Copyright (c) 2008, 2011 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2008, 2011 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.core.runtime; import java.io.File; import java.net.*;
A utility class for manipulating URIs. This class works around some of the undesirable behavior of the URI class, and provides additional path manipulation methods that are not available on the URI class.
@noinstantiateThis class is not intended to be instantiated by clients.
Since:org.eclipse.equinox.common 3.5
/** * A utility class for manipulating URIs. This class works around some of the * undesirable behavior of the {@link java.net.URI} class, and provides additional * path manipulation methods that are not available on the URI class. * * @noinstantiate This class is not intended to be instantiated by clients. * @since org.eclipse.equinox.common 3.5 */
public final class URIUtil { private static final String JAR_SUFFIX = "!/"; //$NON-NLS-1$ private static final String UNC_PREFIX = "//"; //$NON-NLS-1$ private static final String SCHEME_FILE = "file"; //$NON-NLS-1$ private static final String SCHEME_JAR = "jar"; //$NON-NLS-1$ private static final boolean decodeResolved; static { decodeResolved = URI.create("foo:/a%20b/").resolve("c").getSchemeSpecificPart().indexOf('%') > 0; //$NON-NLS-1$ //$NON-NLS-2$ } private URIUtil() { // prevent instantiation }
Returns a new URI with all the same components as the given base URI, but with a path component created by appending the given extension to the base URI's path.

The important difference between this method and URI.resolve(String) is in the treatment of the final segment. The URI resolve method drops the last segment if there is no trailing slash as specified in section 5.2 of RFC 2396. This leads to unpredictable behaviour when working with file: URIs, because the existence of the trailing slash depends on the existence of a local file on disk. This method operates like a traditional path append and always preserves all segments of the base path.

Params:
  • base – The base URI to append to
  • extension – The unencoded path extension to be added
Returns:The appended URI
/** * Returns a new URI with all the same components as the given base URI, * but with a path component created by appending the given extension to the * base URI's path. * <p> * The important difference between this method * and {@link java.net.URI#resolve(String)} is in the treatment of the final segment. * The URI resolve method drops the last segment if there is no trailing slash as * specified in section 5.2 of RFC 2396. This leads to unpredictable behaviour * when working with file: URIs, because the existence of the trailing slash * depends on the existence of a local file on disk. This method operates * like a traditional path append and always preserves all segments of the base path. * * @param base The base URI to append to * @param extension The unencoded path extension to be added * @return The appended URI */
public static URI append(URI base, String extension) { try { String path = base.getPath(); if (path == null) return appendOpaque(base, extension); //if the base is already a directory then resolve will just do the right thing URI result; if (path.endsWith("/")) {//$NON-NLS-1$ result = base.resolve(new URI(null, null, extension, null)); if (decodeResolved) { //see bug 267219 - Java 1.4 implementation of URI#resolve incorrectly encoded the ssp result = new URI(toUnencodedString(result)); } } else { path = path + '/' + extension; result = new URI(base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), path, base.getQuery(), base.getFragment()); } result = result.normalize(); //Fix UNC paths that are incorrectly normalized by URI#resolve (see Java bug 4723726) String resultPath = result.getPath(); if (isFileURI(base) && path != null && path.startsWith(UNC_PREFIX) && (resultPath == null || !resultPath.startsWith(UNC_PREFIX))) result = new URI(result.getScheme(), ensureUNCPath(result.getSchemeSpecificPart()), result.getFragment()); return result; } catch (URISyntaxException e) { //shouldn't happen because we started from a valid URI throw new RuntimeException(e); } }
Special case of appending to an opaque URI. Since opaque URIs have no path segment the best we can do is append to the scheme-specific part
/** * Special case of appending to an opaque URI. Since opaque URIs * have no path segment the best we can do is append to the scheme-specific part */
private static URI appendOpaque(URI base, String extension) throws URISyntaxException { String ssp = base.getSchemeSpecificPart(); if (ssp.endsWith("/")) //$NON-NLS-1$ ssp += extension; else ssp = ssp + "/" + extension; //$NON-NLS-1$ return new URI(base.getScheme(), ssp, base.getFragment()); }
Ensures the given path string starts with exactly four leading slashes.
/** * Ensures the given path string starts with exactly four leading slashes. */
private static String ensureUNCPath(String path) { int len = path.length(); StringBuilder result = new StringBuilder(len); for (int i = 0; i < 4; i++) { // if we have hit the first non-slash character, add another leading slash if (i >= len || result.length() > 0 || path.charAt(i) != '/') result.append('/'); } result.append(path); return result.toString(); }
Returns a URI corresponding to the given unencoded string. This method will take care of encoding any characters that must be encoded according to the URI specification. This method must not be called with a string that already contains an encoded URI, since this will result in the URI escape character ('%') being escaped itself.
Params:
  • uriString – An unencoded URI string
Throws:
Returns:A URI corresponding to the given string
/** * Returns a URI corresponding to the given unencoded string. This method * will take care of encoding any characters that must be encoded according * to the URI specification. This method must not be called with a string that * already contains an encoded URI, since this will result in the URI escape character ('%') * being escaped itself. * * @param uriString An unencoded URI string * @return A URI corresponding to the given string * @throws URISyntaxException If the string cannot be formed into a valid URI */
public static URI fromString(String uriString) throws URISyntaxException { int colon = uriString.indexOf(':'); int hash = uriString.lastIndexOf('#'); boolean noHash = hash < 0; if (noHash) hash = uriString.length(); String scheme = colon < 0 ? null : uriString.substring(0, colon); String ssp = uriString.substring(colon + 1, hash); String fragment = noHash ? null : uriString.substring(hash + 1); //use java.io.File for constructing file: URIs if (scheme != null && scheme.equals(SCHEME_FILE)) { //handle relative URI string with scheme (produced by java.net.URL) File file = new File(ssp); if (file.isAbsolute()) return file.toURI(); if (File.separatorChar != '/') ssp = ssp.replace(File.separatorChar, '/'); //relative URIs have a null scheme. if (!ssp.startsWith("/"))//$NON-NLS-1$ scheme = null; } return new URI(scheme, ssp, fragment); }
Returns whether the given URI refers to a local file system URI.
Params:
  • uri – The URI to check
Returns:true if the URI is a local file system location, and false otherwise
/** * Returns whether the given URI refers to a local file system URI. * @param uri The URI to check * @return <code>true</code> if the URI is a local file system location, and <code>false</code> otherwise */
public static boolean isFileURI(URI uri) { return SCHEME_FILE.equalsIgnoreCase(uri.getScheme()); }
Returns the last segment of the given URI. For a hierarchical URL this returns the last segment of the path. For opaque URIs this treats the scheme-specific part as a path and returns the last segment. Returns null for a hierarchical URI with an empty path, and for opaque URIs whose scheme-specific part cannot be interpreted as a path.
/** * Returns the last segment of the given URI. For a hierarchical URL this returns * the last segment of the path. For opaque URIs this treats the scheme-specific * part as a path and returns the last segment. Returns <code>null</code> for * a hierarchical URI with an empty path, and for opaque URIs whose scheme-specific * part cannot be interpreted as a path. */
public static String lastSegment(URI location) { String path = location.getPath(); if (path == null) return new Path(location.getSchemeSpecificPart()).lastSegment(); return new Path(path).lastSegment(); }
Returns a new URI which is the same as this URI but with the file extension removed from the path part. If this URI does not have an extension, this path is returned.

The file extension portion is defined as the string following the last period (".") character in the last segment. If there is no period in the last segment, the path has no file extension portion. If the last segment ends in a period, the file extension portion is the empty string.

Returns:the new URI
/** * Returns a new URI which is the same as this URI but with the file extension removed * from the path part. If this URI does not have an extension, this path is returned. * <p> * The file extension portion is defined as the string * following the last period (".") character in the last segment. * If there is no period in the last segment, the path has no * file extension portion. If the last segment ends in a period, * the file extension portion is the empty string. * </p> * * @return the new URI */
public static URI removeFileExtension(URI uri) { String lastSegment = lastSegment(uri); if (lastSegment == null) return uri; int lastIndex = lastSegment.lastIndexOf('.'); if (lastIndex == -1) return uri; String uriString = uri.toString(); lastIndex = uriString.lastIndexOf('.'); uriString = uriString.substring(0, lastIndex); return URI.create(uriString); }
Returns true if the two URIs are equal. URIs are considered equal if URI.equals(Object) returns true, if the string representation of the URIs is equal, or if they URIs are represent the same local file.
Params:
  • uri1 – The first URI to compare
  • uri2 – The second URI to compare
Returns:true if the URIs are the same, and false otherwise.
/** * Returns true if the two URIs are equal. URIs are considered equal if * {@link URI#equals(Object)} returns true, if the string representation * of the URIs is equal, or if they URIs are represent the same local file. * @param uri1 The first URI to compare * @param uri2 The second URI to compare * @return <code>true</code> if the URIs are the same, and <code>false</code> otherwise. */
public static boolean sameURI(URI uri1, URI uri2) { if (uri1 == uri2) return true; if (uri1 == null || uri2 == null) return false; if (uri1.equals(uri2)) return true; if (sameString(uri1.getScheme(), uri2.getScheme()) && sameString(uri1.getSchemeSpecificPart(), uri2.getSchemeSpecificPart()) && sameString(uri1.getFragment(), uri2.getFragment())) return true; if (uri1.isAbsolute() != uri2.isAbsolute()) return false; // check if we have two local file references that are case variants File file1 = toFile(uri1); return file1 == null ? false : file1.equals(toFile(uri2)); } private static boolean sameString(String s1, String s2) { return (s1 == s2) || s1 != null && s1.equals(s2); }
Returns the URI as a local file, or null if the given URI does not represent a local file.
Params:
  • uri – The URI to return the file for
Returns:The local file corresponding to the given URI, or null
/** * Returns the URI as a local file, or <code>null</code> if the given * URI does not represent a local file. * @param uri The URI to return the file for * @return The local file corresponding to the given URI, or <code>null</code> */
public static File toFile(URI uri) { if (!isFileURI(uri)) return null; //assume all illegal characters have been properly encoded, so use URI class to unencode return new File(uri.getSchemeSpecificPart()); }
Returns a Java ARchive (JAR) URI for an entry in a jar or zip file. The given input URI should represent a zip or jar file, but this method will not check for existence or validity of a file at the given URI.

The entry path parameter can optionally be used to obtain the URI of an entry in a zip or jar file. If an entry path of null is provided, the resulting URI will represent the jar file itself.

Params:
  • uri – The URI of a zip or jar file
  • entryPath – The path of a file inside the jar, or null to obtain the URI for the jar file itself.
See Also:
Returns:A URI with the "jar" scheme for the given input URI and entry path
/** * Returns a Java ARchive (JAR) URI for an entry in a jar or zip file. The given input URI * should represent a zip or jar file, but this method will not check for existence or * validity of a file at the given URI. * <p> * The entry path parameter can optionally be used to obtain the URI of an entry * in a zip or jar file. If an entry path of <code>null</code> is provided, the resulting * URI will represent the jar file itself. * </p> * * @param uri The URI of a zip or jar file * @param entryPath The path of a file inside the jar, or <code>null</code> to * obtain the URI for the jar file itself. * @return A URI with the "jar" scheme for the given input URI and entry path * @see JarURLConnection */
public static URI toJarURI(URI uri, IPath entryPath) { try { if (entryPath == null) entryPath = Path.EMPTY; //must deconstruct the input URI to obtain unencoded strings, and then pass to URI constructor that will encode the entry path return new URI(SCHEME_JAR, uri.getScheme() + ':' + uri.getSchemeSpecificPart() + JAR_SUFFIX + entryPath.toString(), null); } catch (URISyntaxException e) { //should never happen throw new RuntimeException(e); } }
Returns the URL as a URI. This method will handle URLs that are not properly encoded (for example they contain unencoded space characters).
Params:
  • url – The URL to convert into a URI
Returns:A URI representing the given URL
/** * Returns the URL as a URI. This method will handle URLs that are * not properly encoded (for example they contain unencoded space characters). * * @param url The URL to convert into a URI * @return A URI representing the given URL */
public static URI toURI(URL url) throws URISyntaxException { //URL behaves differently across platforms so for file: URLs we parse from string form if (SCHEME_FILE.equals(url.getProtocol())) { String pathString = url.toExternalForm().substring(5); //ensure there is a leading slash to handle common malformed URLs such as file:c:/tmp if (pathString.indexOf('/') != 0) pathString = '/' + pathString; else if (pathString.startsWith(UNC_PREFIX) && !pathString.startsWith(UNC_PREFIX, 2)) { //URL encodes UNC path with two slashes, but URI uses four (see bug 207103) pathString = ensureUNCPath(pathString); } return new URI(SCHEME_FILE, null, pathString, null); } try { return new URI(url.toExternalForm()); } catch (URISyntaxException e) { //try multi-argument URI constructor to perform encoding return new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); } }
Returns a URI as a URL.

Better use URI.toURL() instead.

/** * Returns a URI as a URL. * * <p>Better use {@link URI#toURL()} instead.</p> */
public static URL toURL(URI uri) throws MalformedURLException { return new URL(uri.toString()); }
Returns a string representation of the given URI that doesn't have illegal characters encoded. This string is suitable for later passing to fromString(String).
Params:
  • uri – The URI to convert to string format
Returns:An unencoded string representation of the URI
/** * Returns a string representation of the given URI that doesn't have illegal * characters encoded. This string is suitable for later passing to {@link #fromString(String)}. * @param uri The URI to convert to string format * @return An unencoded string representation of the URI */
public static String toUnencodedString(URI uri) { StringBuilder result = new StringBuilder(); String scheme = uri.getScheme(); if (scheme != null) result.append(scheme).append(':'); //there is always a ssp result.append(uri.getSchemeSpecificPart()); String fragment = uri.getFragment(); if (fragment != null) result.append('#').append(fragment); return result.toString(); }
Returns an absolute URI that is created by appending the given relative URI to the given base. If the relative URI is already absolute it is simply returned.

This method is guaranteed to be the inverse of makeRelative(URI, URI). That is, if R = makeRelative(O, B), then makeAbsolute(R, B), will return the original URI O.

Params:
  • relative – the relative URI
  • baseURI – the base URI
Returns:an absolute URI
/** * Returns an absolute URI that is created by appending the given relative URI to * the given base. If the <code>relative</code> URI is already absolute it is simply returned. * <p> * This method is guaranteed to be the inverse of {@link #makeRelative(URI, URI)}. * That is, if R = makeRelative(O, B), then makeAbsolute(R, B), will return the original * URI O. * </p> * * @param relative the relative URI * @param baseURI the base URI * @return an absolute URI */
public static URI makeAbsolute(URI relative, URI baseURI) { if (relative.isAbsolute()) return relative; return append(baseURI, toUnencodedString(relative)); }
Returns a URI equivalent to the given original URI, but relative to the given base URI if possible.

This method is equivalent to URI.relativize, except for its handling of file URIs. For file URIs, this method handles file system path devices. If the URIs are not on the same device, then the original URI is returned.

Params:
  • original – the original URI
  • baseURI – the base URI
Returns:a relative URI
/** * Returns a URI equivalent to the given original URI, but relative to the given base * URI if possible. * <p> * This method is equivalent to {@link java.net.URI#relativize}, except for its * handling of file URIs. For file URIs, this method handles file system path devices. * If the URIs are not on the same device, then the original URI is returned. * * @param original the original URI * @param baseURI the base URI * @return a relative URI */
public static URI makeRelative(URI original, URI baseURI) { // for non-local URIs just use the built in relativize method if (!SCHEME_FILE.equals(original.getScheme()) || !SCHEME_FILE.equals(baseURI.getScheme())) return baseURI.relativize(original); IPath originalPath = new Path(original.getSchemeSpecificPart()); IPath basePath = new Path(baseURI.getSchemeSpecificPart()); // make sure we have an absolute path to start if (!basePath.isAbsolute()) return original; IPath relativePath = originalPath.makeRelativeTo(basePath); //if we could not make it relative, just return the original URI if (relativePath == originalPath) return original; try { return new URI(null, null, relativePath.toString(), original.getFragment()); } catch (URISyntaxException e) { //cannot make a relative path, just return the original return original; } } }