All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.core.runtime.URIUtil Maven / Gradle / Ivy

/*******************************************************************************
 * 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 {@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 {@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 = toURI(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 */ 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 that UNC-Paths have at least four leading slashes. * * URI returns UNC-paths with two slashes, but needs four, when passing them as * path. See Java bug 4723726 or Eclipse bug 207103 for details */ private static String fixUNCPath(String path) { // URI needs an UNC-Path with four slashes. if (!path.startsWith(UNC_PREFIX) || path.startsWith(UNC_PREFIX, 2)) return path; return ensureUNCPath(path); } /** * Ensures the given path string starts with at least 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. * * @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 - unless UNC: URIs if (scheme != null && scheme.equals(SCHEME_FILE) && !ssp.startsWith(UNC_PREFIX)) { //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 toURI(scheme, ssp, fragment); } /** * Returns whether the given URI refers to a local file system URI. * @param uri The URI to check * @return true if the URI is a local file system location, and false 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. */ public static String lastSegment(URI location) { String path = location.getPath(); if (path == null) { path = location.getSchemeSpecificPart(); } return IPath.fromOSString(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. *

* * @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 * {@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 true if the URIs are the same, and false 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. * @param uri The URI to return the file for * @return The local file corresponding to the given URI, or null */ 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. *

* * @param uri The URI of a zip or jar file * @param entryPath The path of a file inside the jar, or null 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 = IPath.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). * * @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; return toURI(SCHEME_FILE, null, pathString, null); } try { return new URI(url.toExternalForm()); } catch (URISyntaxException e) { //try multi-argument URI constructor to perform encoding return toURI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); } } /** * An UNC-safe factory the the URI-ctor * * @param scheme Scheme name * @param ssp Scheme-specific part * @param fragment Fragment * @return The new URI from the given components * @throws URISyntaxException If the URI string constructed from the given * components violates RFC 2396 * @see java.net.URI#URI(String, String, String) * @since 3.17 */ public static URI toURI(String scheme, String ssp, String fragment) throws URISyntaxException { if (scheme != null && !scheme.equals(SCHEME_FILE)) { return new URI(scheme, ssp, fragment); } return new URI(scheme, fixUNCPath(ssp), fragment); } /** * An UNC-safe factory the the URI-ctor * * @param scheme Scheme name * @param userInfo User name and authorization information * @param host Host name * @param port Port number * @param path Path * @param query Query * @param fragment Fragment * @return The new hierarchical URI from the given components. * @throws URISyntaxException If both a scheme and a path are given but the path * is relative, if the URI string constructed from * the given components violates RFC 2396, or if the * authority component of the string is present but * cannot be parsed as a server-based authority * @see java.net.URI#URI(String, String, String, int, String, String, String) * @since 3.17 */ public static URI toURI(String scheme, String userInfo, String host, int port, String path, String query, String fragment) throws URISyntaxException { if (scheme != null && !scheme.equals(SCHEME_FILE)) { return new URI(scheme, userInfo, host, port, path, query, fragment); } return new URI(scheme, userInfo, host, port, fixUNCPath(path), query, fragment); } /** * An UNC-safe factory the the URI-ctor * * @param scheme Scheme name * @param host Host name * @param path Path * @param fragment Fragment * @return The new hierarchical URI from the given components * @throws URISyntaxException If the URI string constructed from the given * components violates RFC 2396 * @see java.net.URI#URI(String, String, String, String) * @since 3.17 */ public static URI toURI(String scheme, String host, String path, String fragment) throws URISyntaxException { if (scheme != null && !scheme.equals(SCHEME_FILE)) { return new URI(scheme, host, path, fragment); } return new URI(scheme, host, fixUNCPath(path), fragment); } /** * An UNC-safe factory the the URI-ctor * * @param scheme Scheme name * @param authority Authority * @param path Path * @param query Query * @param fragment Fragment * @return The new hierarchical URI from the given components * @throws URISyntaxException If both a scheme and a path are given but the path * is relative, if the URI string constructed from * the given components violates RFC 2396, or if the * authority component of the string is present but * cannot be parsed as a server-based authority * @see java.net.URI#URI(String, String, String, String, String) * @since 3.17 */ public static URI toURI(String scheme, String authority, String path, String query, String fragment) throws URISyntaxException { if (scheme != null && !scheme.equals(SCHEME_FILE)) { return new URI(scheme, authority, path, query, fragment); } return new URI(scheme, authority, fixUNCPath(path), query, fragment); } /** * Returns a URI as a URL. * *

Better use {@link URI#toURL()} instead.

*/ 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 {@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 {@link #makeRelative(URI, URI)}. * That is, if R = makeRelative(O, B), then makeAbsolute(R, B), will return the original * URI O. *

* * @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 {@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 = IPath.fromOSString(original.getSchemeSpecificPart()); IPath basePath = IPath.fromOSString(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; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy