/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.collect.io; import static com.google.common.base.MoreObjects.firstNonNull; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; import org.joda.convert.FromString; import org.joda.convert.ToString; import com.google.common.io.ByteSource; import com.google.common.io.CharSource; import com.google.common.io.Files; import com.google.common.io.Resources; import com.opengamma.strata.collect.ArgChecker; /** * A locator for a resource, specified as a file, URL, path or classpath resource. * <p> * An instance of this class provides access to a resource, such as a configuration file. * The resource data is accessed using {@link CharSource} or {@link ByteSource}. */ public final class ResourceLocator { /** * The prefix for classpath resource locators. */ public static final String CLASSPATH_URL_PREFIX = "classpath:"; /** * The prefix for file resource locators. */ public static final String FILE_URL_PREFIX = "file:"; /** * The prefix for URL resource locators. */ public static final String URL_PREFIX = "url:"; /** * The resource locator. */ private final String locator; /** * The source. */ private final ByteSource source; //------------------------------------------------------------------------- /** * Creates a resource from a string locator. * <p> * This accepts locators starting with 'classpath:', 'url:' or 'file:'. * It also accepts unprefixed locators, treated as files. * * @param locator the string form of the resource locator * @return the resource locator */ @FromString public static ResourceLocator of(String locator) { ArgChecker.notNull(locator, "locator"); try { if (locator.startsWith(CLASSPATH_URL_PREFIX)) { String urlStr = locator.substring(CLASSPATH_URL_PREFIX.length()); return ofClasspath(urlStr); } else if (locator.startsWith(FILE_URL_PREFIX)) { String fileStr = locator.substring(FILE_URL_PREFIX.length()); return ofFile(new File(fileStr)); } else if (locator.startsWith(URL_PREFIX)) { String pathStr = locator.substring(URL_PREFIX.length()); return ofUrl(new URL(pathStr)); } else { return ofFile(new File(locator)); } } catch (Exception ex) { throw new IllegalArgumentException("Invalid resource locator: " + locator, ex); } } /** * Creates a resource from a {@code File}. * <p> * Windows separators are converted to UNIX style. * This takes no account of possible '/' characters in the name. * * @param file the file to wrap * @return the resource locator */ public static ResourceLocator ofFile(File file) { ArgChecker.notNull(file, "file"); String filename = file.toString(); // convert Windows separators to unix filename = (File.separatorChar == '\\' ? filename.replace('\\', '/') : filename); return new ResourceLocator(FILE_URL_PREFIX + filename, Files.asByteSource(file)); } /** * Creates a resource from a {@code Path}. * <p> * This will return either a file locator or a URL locator. * * @param path path to the file to wrap * @return the resource locator * @throws IllegalArgumentException if the path is neither a file nor a URL */ public static ResourceLocator ofPath(Path path) { ArgChecker.notNull(path, "path"); try { return ofFile(path.toFile()); } catch (UnsupportedOperationException ex) { try { return ofUrl(path.toUri().toURL()); } catch (MalformedURLException ex2) { throw new IllegalArgumentException("Path could not be converted to a File or URL: " + path); } } } /** * Creates a resource from a {@code URL}. * * @param url path to the file to wrap * @return the resource locator */ public static ResourceLocator ofUrl(URL url) { ArgChecker.notNull(url, "url"); String filename = url.toString(); return new ResourceLocator(URL_PREFIX + filename, Resources.asByteSource(url)); } /** * Creates a resource from a fully qualified resource name. * * @param resourceName the classpath resource name * @return the resource locator */ public static ResourceLocator ofClasspath(String resourceName) { ArgChecker.notNull(resourceName, "classpathLocator"); URL url = classLoader().getResource(resourceName); if (url == null) { throw new IllegalArgumentException("Resource not found: " + resourceName); } return ofClasspathUrl(url); } /** * Creates a resource locator for a classpath resource which is associated with a class. * <p> * The classpath is searched using the same method as {@code Class.getResource}. * <ul> * <li>If the resource name starts with '/' it is treated as an absolute path relative to the classpath root</li> * <li>Otherwise the resource name is treated as a path relative to the package containing the class</li> * </ul> * * @param cls the class * @param resourceName the resource name * @return the resource locator */ public static ResourceLocator ofClasspath(Class<?> cls, String resourceName) { ArgChecker.notNull(resourceName, "classpathLocator"); URL url = cls.getResource(resourceName); if (url == null) { throw new IllegalArgumentException("Resource not found: " + resourceName); } return ofClasspathUrl(url); } /** * Creates a resource from a {@code URL}. * * @param url the URL to wrap * @return the resource locator */ public static ResourceLocator ofClasspathUrl(URL url) { ArgChecker.notNull(url, "url"); String locator = CLASSPATH_URL_PREFIX + url.toString(); return new ResourceLocator(locator, Resources.asByteSource(url)); } /** * Selects a suitable class loader. * * @return the class loader */ static ClassLoader classLoader() { return firstNonNull(Thread.currentThread().getContextClassLoader(), ResourceConfig.class.getClassLoader()); } //------------------------------------------------------------------------- /** * Creates an instance of the locator. * * @param locator the locator * @param source the byte source */ private ResourceLocator(String locator, ByteSource source) { super(); this.locator = locator; this.source = source; } //------------------------------------------------------------------------- /** * Gets the string form of the locator. * <p> * The string form of the locator describes the location of the resource. * * @return the locator string */ public String getLocator() { return locator; } /** * Gets the byte source to access the resource. * <p> * A byte source is a supplier of data. * The source itself is neither opened nor closed. * * @return the byte source */ public ByteSource getByteSource() { return source; } /** * Gets the char source to access the resource using UTF-8. * <p> * A char source is a supplier of data. * The source itself is neither opened nor closed. * * @return the char source */ public CharSource getCharSource() { return getCharSource(StandardCharsets.UTF_8); } /** * Gets the char source to access the resource specifying the character set. * <p> * A char source is a supplier of data. * The source itself is neither opened nor closed. * * @param charset the character set to use * @return the char source */ public CharSource getCharSource(Charset charset) { return source.asCharSource(charset); } //------------------------------------------------------------------------- /** * Checks if this locator equals another locator. * <p> * The comparison checks the locator string. * * @param obj the other locator, null returns false * @return true if equal */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof ResourceLocator) { return locator.equals(((ResourceLocator) obj).locator); } return false; } /** * Returns a suitable hash code for the locator. * * @return the hash code */ @Override public int hashCode() { return locator.hashCode(); } /** * Returns a string describing the locator. * <p> * This can be parsed using {@link #of(String)}. * * @return the descriptive string */ @ToString @Override public String toString() { return locator; } }