/***************************************************************************** * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. *****************************************************************************/ package org.eclipse.buckminster.runtime; import java.io.File; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** * @author Thomas Hallgren */ public abstract class URLUtils { /** * Append <code>path</code> to <code>url</code> while preserving all other * characteristics of the <code>url</code>. If <code>path</code> is * absolute, it will become the new path of the <code>url</code> else, if * <code>url</code> doesn't end with a trailing slash, it is appended prior * to appending the <code>path</code>. * * @param url * The url to use as root. * @param path * The path to append * * @return The url with the new path. */ public static URL appendPath(URL url, IPath path) { if (path == null || path.segmentCount() == 0) return url; try { URI u = url.toURI(); String urlPath; path = path.setDevice(null); if (path.isAbsolute()) urlPath = path.toPortableString(); else { urlPath = u.getPath(); if (urlPath == null || urlPath.length() == 0) urlPath = path.makeAbsolute().toPortableString(); else { StringBuilder bld = new StringBuilder(); bld.append(urlPath); if (!urlPath.endsWith("/")) //$NON-NLS-1$ bld.append('/'); bld.append(path.toPortableString()); urlPath = bld.toString(); } } url = new URI(u.getScheme(), u.getAuthority(), urlPath, u.getQuery(), u.getFragment()).toURL(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } return url; } /** * Appends a trailing slash to <code>url</code> and returns the result. If * the <code>url</code> already has a trailing slash, the argument is * returned without modification. * * @param url * The <code>url</code> that should receive the trailing slash. * Cannot be <code>null</code>. * @return A <code>url</code> that has a trailing slash */ public static URL appendTrailingSlash(URL url) { if (!url.getPath().endsWith("/")) //$NON-NLS-1$ { try { URI u = url.toURI(); url = new URI(u.getScheme(), u.getAuthority(), u.getPath() + '/', u.getQuery(), u.getFragment()).toURL(); } catch (RuntimeException e) { throw e; } catch (Exception e) { // Not very likely since original was a URL. // throw new RuntimeException(e); } } return url; } public static String[] decodeToQueryPairs(String query) { if (query == null || query.length() == 0) return Trivial.EMPTY_STRING_ARRAY; // split on a solitary '&' // String[] pairs = query.split("(?<!&)&(?!&)"); //$NON-NLS-1$ int idx = pairs.length; if (idx == 0) return Trivial.EMPTY_STRING_ARRAY; while (--idx >= 0) pairs[idx] = pairs[idx].replace("&&", "&"); //$NON-NLS-1$ //$NON-NLS-2$ return pairs; } public static String encodeFromQueryPairs(List<String> pairs) { if (pairs == null || pairs.size() == 0) return null; StringBuilder query = new StringBuilder(); for (String pair : pairs) { // if k/v pairs have already been added, add a single delimiter // if (query.length() > 0) query.append('&'); appendQueryItem(query, pair); } return query.toString(); } public static String encodeFromQueryPairs(Map<String, String> map) { if (map == null || map.size() == 0) return null; StringBuilder query = new StringBuilder(); for (Map.Entry<String, String> entry : map.entrySet()) { // if k/v pairs have already been added, add a single delimiter // if (query.length() > 0) query.append('&'); appendQueryItem(query, entry.getKey()); query.append('='); appendQueryItem(query, entry.getValue()); } return query.toString(); } public static URI getParentURI(URI uri) { if (uri == null) return uri; IPath uriPath = Path.fromPortableString(uri.getPath()); if (uriPath.segmentCount() == 0) return null; uriPath = uriPath.removeLastSegments(1).addTrailingSeparator(); try { return new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), uriPath.toPortableString(), uri.getQuery(), uri.getFragment()); } catch (URISyntaxException e) { // Shouldn't happen since we started with a valid URI // return null; } } public static URL getParentURL(URL url) { if (url == null) return null; try { return getParentURI(url.toURI()).toURL(); } catch (MalformedURLException e) { return null; } catch (URISyntaxException e) { return null; } } public static boolean isLocalURL(URL url) { String proto = url.getProtocol(); if (proto.equals("jar") || proto.equals("reference")) //$NON-NLS-1$ //$NON-NLS-2$ { String spec = url.getFile(); int sepIdx = spec.indexOf(':'); if (sepIdx == -1) return false; proto = spec.substring(0, sepIdx); } return "file".equals(proto) || "platform".equals(proto) || proto.startsWith("bundle"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } public static URI normalizeToURI(String pathOrURI, boolean asFolder) throws CoreException { if (pathOrURI == null || pathOrURI.length() == 0) return null; // If we have a protocol part that consists of one single letter, then // we assume // that this is a windows path. // pathOrURI = pathOrURI.replace('\\', '/'); if (pathOrURI.length() > 1 && pathOrURI.charAt(1) == ':') { char c = pathOrURI.charAt(0); if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) pathOrURI = "file:/" + pathOrURI; //$NON-NLS-1$ } URI uri; try { uri = new URI(pathOrURI); } catch (URISyntaxException e) { if (pathOrURI.indexOf(' ') < 0) throw BuckminsterException.wrap(e); try { uri = new URI(pathOrURI.replaceAll("\\s", "%20")); //$NON-NLS-1$ //$NON-NLS-2$ } catch (URISyntaxException e2) { throw BuckminsterException.wrap(e2); } } boolean change = false; String path = uri.getPath(); if (asFolder && path != null && !path.endsWith("/")) //$NON-NLS-1$ { path += "/"; //$NON-NLS-1$ change = true; } String scheme = uri.getScheme(); if (scheme == null) { scheme = "file"; //$NON-NLS-1$ // Attempt to create an absolute path try { IPath absPath = Path.fromPortableString(path); if (!absPath.isAbsolute()) { File file = new File(absPath.toOSString()); absPath = Path.fromOSString(file.getAbsolutePath()); path = absPath.toPortableString(); } } catch (Exception e) { Buckminster.getLogger().debug(e, "Unable to convert relative path %s to absolute", path); //$NON-NLS-1$ } change = true; } try { if (change) uri = new URI(scheme, uri.getAuthority(), path, uri.getQuery(), uri.getFragment()); return uri; } catch (URISyntaxException e) { throw BuckminsterException.wrap(e); } } public static URL normalizeToURL(String surl) throws MalformedURLException { if (surl == null) return null; try { return new URL(surl); } catch (MalformedURLException e) { // Do a space check. // if (surl.indexOf(' ') > 0) { try { return new URL(surl.replaceAll("\\s", "%20")); //$NON-NLS-1$ //$NON-NLS-2$ } catch (MalformedURLException me1) { } } try { return new File(surl).toURI().toURL(); } catch (MalformedURLException me2) { // Throw the original exception // throw e; } } } public static Map<String, String> queryAsParameters(String query) { if (query == null) return Collections.emptyMap(); String[] pairs = decodeToQueryPairs(query); int top = pairs.length; if (top == 0) return Collections.emptyMap(); Map<String, String> p = new HashMap<String, String>(top); while (--top >= 0) { // now split the pair on the first '=' only // (one '=' is required to be there, even if the value is blank) // String[] kv = pairs[top].split("=", 2); //$NON-NLS-1$ if (kv.length == 2) p.put(kv[0], kv[1]); } return p; } public static URL resolveURL(URL contextURL, String url) { if (url == null) return null; try { if (contextURL == null) return normalizeToURL(url); return new URL(contextURL, url); } catch (MalformedURLException e) { return null; } } private static void appendQueryItem(StringBuilder query, String item) { int top = item.length(); for (int idx = 0; idx < top; ++idx) { char c = item.charAt(idx); if (c == '&') query.append(c); query.append(c); } } }