UrlResourceUtil.java

/* 
 * Copyright 2015 Development Entropy (deventropy.org) Contributors
 * Copyright 2002-2014 the original author or authors at Spring Framework.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.deventropy.shared.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * A utility class to provide uniform resource loading capability from real and <em>pseudo</em> URLs, like classpath.
 * 
 * <p>This utility can be used to allow consumers the same interface to load resources from classpaths, file system,
 * HTTP, etc. This class is inspired by the Spring Framework
 * <a href="http://tinyurl.com/gp4eagg">DefaultResourceLoader</a>.
 * 
 * <p>Supported formats or URLs by this util:
 * <ul>
 * <li><em>Classpath</em> Example: <code><b>classpath:</b>/org/deventropy/junithelper/utils/file.ext</code></li>
 * <li><em>File</em> Example: <code><b>file:</b>/path/to/file/file.ext</code></li>
 * <li><em>HTTP</em> Example: <code><b>http://</b>example.com/file.ext</code></li>
 * <li><em>HTTPS</em> Example: <code><b>https://</b>example.com/file.ext</code></li>
 * </ul>
 * 
 * @author Bindul Bhowmik
 */
public final class UrlResourceUtil {
	
	/**
	 * Classpath URL prefix.
	 */
	public static final String URL_PROTOCOL_CLASSPATH = "classpath:";
	
	/**
	 * File url prefix.
	 */
	public static final String URL_PROTOCOL_FILE = "file:";
	
	/**
	 * HTTP url prefix.
	 */
	public static final String URL_PROTOCOL_HTTP = "http://";
	
	/**
	 * HTTPS url prefix.
	 */
	public static final String URL_PROTOCOL_HTTPS = "https://";
	
	/**
	 * known protocols.
	 */
	public static final Set<String> KNOWN_PROTOCOLS;
	
	static {
		final Set<String> knownProtocols = new HashSet<String> ();
		knownProtocols.add(URL_PROTOCOL_FILE);
		knownProtocols.add(URL_PROTOCOL_CLASSPATH);
		knownProtocols.add(URL_PROTOCOL_HTTP);
		knownProtocols.add(URL_PROTOCOL_HTTPS);

		KNOWN_PROTOCOLS = Collections.unmodifiableSet(knownProtocols);
	}
	
	private UrlResourceUtil () {
		// Util class
	}
	
	/**
	 * @param resource Resource to checkl
	 * @param cl Classpath to use
	 * @return A URL format
	 * @throws MalformedURLException Malformed URL
	 * @throws IOException Resource not found
	 */
	public static URL getUrl (final String resource, final ClassLoader cl)
			throws MalformedURLException, IOException {

		ArgumentCheck.notNull(resource, "Resource cannot be null");
		URL url = null;

		if (resource.startsWith(URL_PROTOCOL_CLASSPATH)) {
			String resourcePart = resource.substring(URL_PROTOCOL_CLASSPATH.length());
	
			// System and non system classloaders behave different with the starting '/', so try both
			if (resourcePart.length() >= 1 && resourcePart.charAt(0) != '/') {
				resourcePart = "/" + resourcePart;
			}
			url = cl.getResource(resourcePart);
	
			if (null == url) {
				url = cl.getResource(resourcePart.substring(1)); // Try without the '/'
			}
	
			if (null == url) {
				throw new FileNotFoundException("The requested classpath resource " + resource
						+ " does not exist");
			}
		} else {
	
			try {
				url = new URL(resource);
			} catch (MalformedURLException e) {
				// Try to resolve it as a file
				url = new File(resource).toURI().toURL();
			}
		}

		return url;
	}
	
	/**
	 * @param resource Resource to check
	 * @return URL format
	 * @throws IOException Exception getting the URL
	 */
	public static URL getUrl (final String resource) throws IOException {
		return getUrl(resource, ClassUtil.getApplicableClassloader(null));
	}
	
	/**
	 * @param resource If the resource is a file
	 * @return <code>true</code> if file on mounted drive
	 */
	public static boolean isFile (final String resource) {
		return resource.startsWith(URL_PROTOCOL_FILE) || new File(resource).exists();
	}
	
	/**
	 * @param resource OPen resource as a file to write
	 * @return File if the resource is a file and can write
	 * @throws IOException Exception trying to open a file
	 */
	public static File getFileForWrite (final String resource) throws IOException {
		if (!isFile(resource)) {
			throw new IOException("Not a file");
		}
		if (resource.startsWith(URL_PROTOCOL_FILE)) {
			return new File(resource.substring(URL_PROTOCOL_FILE.length()));
		}
		return new File(resource);
	}

}