/******************************************************************************* * Copyright (c) 2010-2014 SAP AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SAP AG - initial API and implementation *******************************************************************************/ package org.eclipse.skalli.commons; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.text.MessageFormat; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang.StringUtils; public class URLUtils { // no instances, please! private URLUtils() { } /** * Utility method to convert an enumeration of URLs to a corresponding list. * * @param u the enumeration to evaluate, or <code>null</code>. * @return a list if URLs, or an empty list. */ public static List<URL> asURLs(Enumeration<URL> u) { List<URL> ret = new LinkedList<URL>(); if (u != null) { while (u.hasMoreElements()) { URL url = u.nextElement(); ret.add(url); } } return ret; } /** * Composes a {@link URI} from a given web locator and path. * <p> * This method is equivalent to {@link #asURI(String, String, String) * asURI(webLocator, resourcePath, null)}. * * @param webLocator the web locator, e.g. <tt>http://localhost:8080</tt>. May be * <code>null</code> or blank. * @param resourcePath the absolute path of a resource, may be <code>null</code> or blank. * A blank path will be interpreted as root path "/". If the path is not beginning with * a slash it will be added on the fly. * * @return a URI, or <code>null</code> if the given resource path was <code>null</code>. */ public static URI asURI(String webLocator, String resourcePath) { return asURI(webLocator, resourcePath, null); } /** * Composes a {@link URI} from a given web locator, resource path and an optional query. * <p> * If the web locator is not specified, a <tt>file://</tt> URI will be returned. * Otherwise web locator, path and query are concatenated and converted to * an URI with {@link URI#URI(String)}. If that fails, the concatenated string is * first converted to an {@link URL} with {@link URL#URL(String)} and then to an * URI with {@link URI#URI(String, String, String, int, String, String, String)}. * This sanitizes some otherwise broken URIs and properly encodes the path segments. * * @param webLocator the web locator, e.g. <tt>http://localhost:8080</tt>. May be * <code>null</code> or blank. * @param resourcePath the absolute path of a resource, may be <code>null</code> or blank. * A blank path will be interpreted as root path "/". If the path is not beginning with * a slash it will be added on the fly. * @param query a query string, may be <code>null</code> or blank. * * @return a URI, or <code>null</code> if the given resource path was <code>null</code>. */ public static URI asURI(String webLocator, String resourcePath, String query) { String absolutePath = addSlashBegin(resourcePath); if (absolutePath == null) { return null; } if (StringUtils.isBlank(webLocator)) { try { return new URI("file", null, absolutePath, query, null); //$NON-NLS-1$ } catch (URISyntaxException e) { throw new IllegalArgumentException( MessageFormat.format("Not a valid file URI: ''{0}''", absolutePath), e); } } String uri = StringUtils.removeEnd(webLocator, "/") + absolutePath; //$NON-NLS-1$ if (StringUtils.isNotBlank(query)) { uri = uri + "?" + query; //$NON-NLS-1$ } try { return new URI(uri); } catch (URISyntaxException e) { URL url; try { url = new URL(uri); } catch (MalformedURLException ex) { throw new IllegalArgumentException( MessageFormat.format("Not a valid URL: ''{0}''", uri), ex); } try { return new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); } catch (URISyntaxException ex) { throw new IllegalArgumentException( MessageFormat.format("Not a valid URI: ''{0}''", uri), ex); } } } /** * Converts a given string into a corresponding URL. * <p> * Encodes path and/or query parts of the given string according to * {@link URI#URI(String, String, String, int, String, String, String)}. * For example, blanks in the path are converted to <tt>%20</tt>. * * @param s the string to convert to an URL. * @return an URL, or <code>null</code> if the string is <code>null</code>, empty or whitespace. * * @throws MalformedURLException if the given string is not a valid URL and cannot be * "sanitized" to yield a valid URL even after proper encoding of its parts. */ public static URL asURL(String s) throws MalformedURLException { if (StringUtils.isBlank(s)) { return null; } URI uri = null; try { uri = new URI(s); } catch (URISyntaxException e) { URL url = new URL(s); try { uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); } catch (URISyntaxException e1) { MalformedURLException e2 = new MalformedURLException(e1.getMessage()); e2.initCause(e1); throw e2; } } return new URL(uri.toASCIIString()); } /** * Ensures that the given string begins with a slash and removes * whitespace from both ends of the string. * * @param s the string, may be <code>null</code>. * * @return the string beginning with a slash, or <code>null</code> * if the string was <code>null</code>. */ @SuppressWarnings("nls") public static String addSlashBegin(String s) { String ret = StringUtils.trim(s); return ret.startsWith("/") ? ret : "/" + ret; } /** * Ensures that the given string ends with a slash and removes * whitespace from both ends of the string. * * @param s the string, may be <code>null</code>. * * @return the string ending with a slash, or <code>null</code> * if the string was <code>null</code>. */ @SuppressWarnings("nls") public static String addSlashEnd(String s) { String ret = StringUtils.trim(s); return ret == null || ret.endsWith("/") ? ret : ret + "/"; } /** * Removes trailing and leading slashes and whitespace from the given string. * * @param s the string to transform, may be <code>null</code>. * * @return the string without leading and trailing slashes and whitespace, * or <code>null</code> if the string was <code>null</code>. */ public static String removeSlashStartEnd(String s) { String ret = StringUtils.trim(s); if (!StringUtils.isEmpty(ret)) { while (ret.startsWith("/")){ //$NON-NLS-1$ ret = ret.substring(1); } while (ret.endsWith("/")) { //$NON-NLS-1$ ret = ret.substring(0, ret.length() - 1); } } return ret; } /** * Constructs an URL from a web locator (something like * <tt>"http://example.org:8080"</tt>) and given path segments. * <p> * This method handles leading and trailing slashes and whitespace * in all arguments. * * @param webLocator a web locator, or <code>null</code>. If no web locator * is specified, the returned string is just a path (without leading slash). * @param pathSegments the path segments to concatenate. If no path segements * are specified, the <code>webLocator</code> is returned. * * @return an URL, which may be an absolute URL with a web locator, * or just a (relative) path. The result may be the empty string, but * never <code>null</code>. */ public static String concat(String webLocator, Object... pathSegments) { StringBuilder ret = new StringBuilder(); if (webLocator != null) { ret.append(removeSlashStartEnd(webLocator)); } if (pathSegments != null) { for (Object next: pathSegments) { String pathSegment = next != null ? removeSlashStartEnd(next.toString()) : null; if (StringUtils.isNotBlank(pathSegment)) { if (ret.length() > 0) { ret.append('/'); } ret.append(pathSegment); } } } return ret.toString(); } }