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

org.apache.commons.configuration2.io.FileLocatorUtils Maven / Gradle / Ivy

Go to download

Tools to assist in the reading of configuration/preferences files in various formats

There is a newer version: 2.11.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.commons.configuration2.io;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;

import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 

* A utility class providing helper methods related to locating files. *

*

* The methods of this class are used behind the scenes when retrieving * configuration files based on different criteria, e.g. URLs, files, or more * complex search strategies. They also implement functionality required by the * default {@link FileSystem} implementations. Most methods are intended to be * used internally only by other classes in the {@code io} package. *

* * @version $Id: FileLocatorUtils.java 1750126 2016-06-24 15:49:12Z oheger $ * @since 2.0 */ public final class FileLocatorUtils { /** * Constant for the default {@code FileSystem}. This file system is used by * operations of this class if no specific file system is provided. An * instance of {@link DefaultFileSystem} is used. */ public static final FileSystem DEFAULT_FILE_SYSTEM = new DefaultFileSystem(); /** * Constant for the default {@code FileLocationStrategy}. This strategy is * used by the {@code locate()} method if the passed in {@code FileLocator} * does not define its own location strategy. The default location strategy * is roughly equivalent to the search algorithm used in version 1.x of * Commons Configuration (there it was hard-coded though). It * behaves in the following way when passed a {@code FileLocator}: *
    *
  • If the {@code FileLocator} has a defined URL, this URL is used as the * file's URL (without any further checks).
  • *
  • Otherwise, base path and file name stored in the {@code FileLocator} * are passed to the current {@code FileSystem}'s {@code locateFromURL()} * method. If this results in a URL, it is returned.
  • *
  • Otherwise, if the locator's file name is an absolute path to an * existing file, the URL of this file is returned.
  • *
  • Otherwise, the concatenation of base path and file name is * constructed. If this path points to an existing file, its URL is * returned.
  • *
  • Otherwise, a sub directory of the current user's home directory as * defined by the base path is searched for the referenced file. If the file * can be found there, its URL is returned.
  • *
  • Otherwise, the base path is ignored, and the file name is searched in * the current user's home directory. If the file can be found there, its * URL is returned.
  • *
  • Otherwise, a resource with the name of the locator's file name is * searched in the classpath. If it can be found, its URL is returned.
  • *
  • Otherwise, the strategy gives up and returns null indicating * that the file cannot be resolved.
  • *
*/ public static final FileLocationStrategy DEFAULT_LOCATION_STRATEGY = initDefaultLocationStrategy(); /** Constant for the file URL protocol */ private static final String FILE_SCHEME = "file:"; /** The logger.*/ private static final Log LOG = LogFactory.getLog(FileLocatorUtils.class); /** Property key for the base path. */ private static final String PROP_BASE_PATH = "basePath"; /** Property key for the encoding. */ private static final String PROP_ENCODING = "encoding"; /** Property key for the file name. */ private static final String PROP_FILE_NAME = "fileName"; /** Property key for the file system. */ private static final String PROP_FILE_SYSTEM = "fileSystem"; /** Property key for the location strategy. */ private static final String PROP_STRATEGY = "locationStrategy"; /** Property key for the source URL. */ private static final String PROP_SOURCE_URL = "sourceURL"; /** * Private constructor so that no instances can be created. */ private FileLocatorUtils() { } /** * Tries to convert the specified URL to a file object. If this fails, * null is returned. * * @param url the URL * @return the resulting file object */ public static File fileFromURL(URL url) { return FileUtils.toFile(url); } /** * Returns an uninitialized {@code FileLocatorBuilder} which can be used * for the creation of a {@code FileLocator} object. This method provides * a convenient way to create file locators using a fluent API as in the * following example: *
     * FileLocator locator = FileLocatorUtils.fileLocator()
     *     .basePath(myBasePath)
     *     .fileName("test.xml")
     *     .create();
     * 
* @return a builder object for defining a {@code FileLocator} */ public static FileLocator.FileLocatorBuilder fileLocator() { return fileLocator(null); } /** * Returns a {@code FileLocatorBuilder} which is already initialized with * the properties of the passed in {@code FileLocator}. This builder can * be used to create a {@code FileLocator} object which shares properties * of the original locator (e.g. the {@code FileSystem} or the encoding), * but points to a different file. An example use case is as follows: *
     * FileLocator loc1 = ...
     * FileLocator loc2 = FileLocatorUtils.fileLocator(loc1)
     *     .setFileName("anotherTest.xml")
     *     .create();
     * 
* @param src the source {@code FileLocator} (may be null) * @return an initialized builder object for defining a {@code FileLocator} */ public static FileLocator.FileLocatorBuilder fileLocator(FileLocator src) { return new FileLocator.FileLocatorBuilder(src); } /** * Creates a new {@code FileLocator} object with the properties defined in * the given map. The map must be conform to the structure generated by the * {@link #put(FileLocator, Map)} method; unexpected data can cause * {@code ClassCastException} exceptions. The map can be null, then * an uninitialized {@code FileLocator} is returned. * * @param map the map * @return the new {@code FileLocator} * @throws ClassCastException if the map contains invalid data */ public static FileLocator fromMap(Map map) { FileLocator.FileLocatorBuilder builder = fileLocator(); if (map != null) { builder.basePath((String) map.get(PROP_BASE_PATH)) .encoding((String) map.get(PROP_ENCODING)) .fileName((String) map.get(PROP_FILE_NAME)) .fileSystem((FileSystem) map.get(PROP_FILE_SYSTEM)) .locationStrategy( (FileLocationStrategy) map.get(PROP_STRATEGY)) .sourceURL((URL) map.get(PROP_SOURCE_URL)); } return builder.create(); } /** * Stores the specified {@code FileLocator} in the given map. With the * {@link #fromMap(Map)} method a new {@code FileLocator} with the same * properties as the original one can be created. * * @param locator the {@code FileLocator} to be stored * @param map the map in which to store the {@code FileLocator} (must not be * null) * @throws IllegalArgumentException if the map is null */ public static void put(FileLocator locator, Map map) { if (map == null) { throw new IllegalArgumentException("Map must not be null!"); } if (locator != null) { map.put(PROP_BASE_PATH, locator.getBasePath()); map.put(PROP_ENCODING, locator.getEncoding()); map.put(PROP_FILE_NAME, locator.getFileName()); map.put(PROP_FILE_SYSTEM, locator.getFileSystem()); map.put(PROP_SOURCE_URL, locator.getSourceURL()); map.put(PROP_STRATEGY, locator.getLocationStrategy()); } } /** * Checks whether the specified {@code FileLocator} contains enough * information to locate a file. This is the case if a file name or a URL is * defined. If the passed in {@code FileLocator} is null, result is * false. * * @param locator the {@code FileLocator} to check * @return a flag whether a file location is defined by this * {@code FileLocator} */ public static boolean isLocationDefined(FileLocator locator) { return (locator != null) && (locator.getFileName() != null || locator.getSourceURL() != null); } /** * Returns a flag whether all components of the given {@code FileLocator} * describing the referenced file are defined. In order to reference a file, * it is not necessary that all components are filled in (for instance, the * URL alone is sufficient). For some use cases however, it might be of * interest to have different methods for accessing the referenced file. * Also, depending on the filled out properties, there is a subtle * difference how the file is accessed: If only the file name is set (and * optionally the base path), each time the file is accessed a * {@code locate()} operation has to be performed to uniquely identify the * file. If however the URL is determined once based on the other components * and stored in a fully defined {@code FileLocator}, it can be used * directly to identify the file. If the passed in {@code FileLocator} is * null, result is false. * * @param locator the {@code FileLocator} to be checked (may be null) * @return a flag whether all components describing the referenced file are * initialized */ public static boolean isFullyInitialized(FileLocator locator) { if (locator == null) { return false; } return locator.getBasePath() != null && locator.getFileName() != null && locator.getSourceURL() != null; } /** * Returns a {@code FileLocator} object based on the passed in one whose * location is fully defined. This method ensures that all components of the * {@code FileLocator} pointing to the file are set in a consistent way. In * detail it behaves as follows: *
    *
  • If the {@code FileLocator} has already all components set which * define the file, it is returned unchanged. Note: It is not * checked whether all components are really consistent!
  • *
  • {@link #locate(FileLocator)} is called to determine a unique URL * pointing to the referenced file. If this is successful, a new * {@code FileLocator} is created as a copy of the passed in one, but with * all components pointing to the file derived from this URL.
  • *
  • Otherwise, result is null.
  • *
* * @param locator the {@code FileLocator} to be completed * @return a {@code FileLocator} with a fully initialized location if * possible or null */ public static FileLocator fullyInitializedLocator(FileLocator locator) { if (isFullyInitialized(locator)) { // already fully initialized return locator; } URL url = locate(locator); return (url != null) ? createFullyInitializedLocatorFromURL(locator, url) : null; } /** * Locates the provided {@code FileLocator}, returning a URL for accessing * the referenced file. This method uses a {@link FileLocationStrategy} to * locate the file the passed in {@code FileLocator} points to. If the * {@code FileLocator} contains itself a {@code FileLocationStrategy}, it is * used. Otherwise, the default {@code FileLocationStrategy} is applied. The * strategy is passed the locator and a {@code FileSystem}. The resulting * URL is returned. If the {@code FileLocator} is null, result is * null. * * @param locator the {@code FileLocator} to be resolved * @return the URL pointing to the referenced file or null if the * {@code FileLocator} could not be resolved * @see #DEFAULT_LOCATION_STRATEGY */ public static URL locate(FileLocator locator) { if (locator == null) { return null; } return obtainLocationStrategy(locator).locate( obtainFileSystem(locator), locator); } /** * Tries to locate the file referenced by the passed in {@code FileLocator}. * If this fails, an exception is thrown. This method works like * {@link #locate(FileLocator)}; however, in case of a failed location * attempt an exception is thrown. * * @param locator the {@code FileLocator} to be resolved * @return the URL pointing to the referenced file * @throws ConfigurationException if the file cannot be resolved */ public static URL locateOrThrow(FileLocator locator) throws ConfigurationException { URL url = locate(locator); if (url == null) { throw new ConfigurationException("Could not locate: " + locator); } return url; } /** * Return the path without the file name, for example http://xyz.net/foo/bar.xml * results in http://xyz.net/foo/ * * @param url the URL from which to extract the path * @return the path component of the passed in URL */ static String getBasePath(URL url) { if (url == null) { return null; } String s = url.toString(); if (s.startsWith(FILE_SCHEME) && !s.startsWith("file://")) { s = "file://" + s.substring(FILE_SCHEME.length()); } if (s.endsWith("/") || StringUtils.isEmpty(url.getPath())) { return s; } else { return s.substring(0, s.lastIndexOf("/") + 1); } } /** * Extract the file name from the specified URL. * * @param url the URL from which to extract the file name * @return the extracted file name */ static String getFileName(URL url) { if (url == null) { return null; } String path = url.getPath(); if (path.endsWith("/") || StringUtils.isEmpty(path)) { return null; } else { return path.substring(path.lastIndexOf("/") + 1); } } /** * Tries to convert the specified base path and file name into a file object. * This method is called e.g. by the save() methods of file based * configurations. The parameter strings can be relative files, absolute * files and URLs as well. This implementation checks first whether the passed in * file name is absolute. If this is the case, it is returned. Otherwise * further checks are performed whether the base path and file name can be * combined to a valid URL or a valid file name. Note: The test * if the passed in file name is absolute is performed using * {@code java.io.File.isAbsolute()}. If the file name starts with a * slash, this method will return true on Unix, but false on * Windows. So to ensure correct behavior for relative file names on all * platforms you should never let relative paths start with a slash. E.g. * in a configuration definition file do not use something like that: *
     * <properties fileName="/subdir/my.properties"/>
     * 
* Under Windows this path would be resolved relative to the configuration * definition file. Under Unix this would be treated as an absolute path * name. * * @param basePath the base path * @param fileName the file name (must not be null) * @return the file object (null if no file can be obtained) */ static File getFile(String basePath, String fileName) { // Check if the file name is absolute File f = new File(fileName); if (f.isAbsolute()) { return f; } // Check if URLs are involved URL url; try { url = new URL(new URL(basePath), fileName); } catch (MalformedURLException mex1) { try { url = new URL(fileName); } catch (MalformedURLException mex2) { url = null; } } if (url != null) { return fileFromURL(url); } return constructFile(basePath, fileName); } /** * Convert the specified file into an URL. This method is equivalent * to file.toURI().toURL(). It was used to work around a bug in the JDK * preventing the transformation of a file into an URL if the file name * contains a '#' character. See the issue CONFIGURATION-300 for * more details. Now that we switched to JDK 1.4 we can directly use * file.toURI().toURL(). * * @param file the file to be converted into an URL */ static URL toURL(File file) throws MalformedURLException { return file.toURI().toURL(); } /** * Tries to convert the specified URI to a URL. If this causes an exception, * result is null. * * @param uri the URI to be converted * @return the resulting URL or null */ static URL convertURIToURL(URI uri) { try { return uri.toURL(); } catch (MalformedURLException e) { return null; } } /** * Tries to convert the specified file to a URL. If this causes an * exception, result is null. * * @param file the file to be converted * @return the resulting URL or null */ static URL convertFileToURL(File file) { return convertURIToURL(file.toURI()); } /** * Tries to find a resource with the given name in the classpath. * * @param resourceName the name of the resource * @return the URL to the found resource or null if the resource * cannot be found */ static URL locateFromClasspath(String resourceName) { URL url = null; // attempt to load from the context classpath ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader != null) { url = loader.getResource(resourceName); if (url != null) { LOG.debug("Loading configuration from the context classpath (" + resourceName + ")"); } } // attempt to load from the system classpath if (url == null) { url = ClassLoader.getSystemResource(resourceName); if (url != null) { LOG.debug("Loading configuration from the system classpath (" + resourceName + ")"); } } return url; } /** * Helper method for constructing a file object from a base path and a * file name. This method is called if the base path passed to * {@code getURL()} does not seem to be a valid URL. * * @param basePath the base path * @param fileName the file name (must not be null) * @return the resulting file */ static File constructFile(String basePath, String fileName) { File file; File absolute = new File(fileName); if (StringUtils.isEmpty(basePath) || absolute.isAbsolute()) { file = absolute; } else { file = new File(appendPath(basePath, fileName)); } return file; } /** * Extends a path by another component. The given extension is added to the * already existing path adding a separator if necessary. * * @param path the path to be extended * @param ext the extension of the path * @return the extended path */ static String appendPath(String path, String ext) { StringBuilder fName = new StringBuilder(); fName.append(path); // My best friend. Paranoia. if (!path.endsWith(File.separator)) { fName.append(File.separator); } // // We have a relative path, and we have // two possible forms here. If we have the // "./" form then just strip that off first // before continuing. // if (ext.startsWith("." + File.separator)) { fName.append(ext.substring(2)); } else { fName.append(ext); } return fName.toString(); } /** * Obtains a non-null {@code FileSystem} object from the passed in * {@code FileLocator}. If the passed in {@code FileLocator} has a * {@code FileSystem} object, it is returned. Otherwise, result is the * default {@code FileSystem}. * * @param locator the {@code FileLocator} (may be null) * @return the {@code FileSystem} to be used for this {@code FileLocator} */ static FileSystem obtainFileSystem(FileLocator locator) { return (locator != null) ? ObjectUtils.defaultIfNull( locator.getFileSystem(), DEFAULT_FILE_SYSTEM) : DEFAULT_FILE_SYSTEM; } /** * Obtains a non null {@code FileLocationStrategy} object from the * passed in {@code FileLocator}. If the {@code FileLocator} is not * null and has a {@code FileLocationStrategy} defined, this strategy * is returned. Otherwise, result is the default * {@code FileLocationStrategy}. * * @param locator the {@code FileLocator} * @return the {@code FileLocationStrategy} for this {@code FileLocator} */ static FileLocationStrategy obtainLocationStrategy(FileLocator locator) { return (locator != null) ? ObjectUtils.defaultIfNull( locator.getLocationStrategy(), DEFAULT_LOCATION_STRATEGY) : DEFAULT_LOCATION_STRATEGY; } /** * Creates a fully initialized {@code FileLocator} based on the specified * URL. * * @param src the source {@code FileLocator} * @param url the URL * @return the fully initialized {@code FileLocator} */ private static FileLocator createFullyInitializedLocatorFromURL(FileLocator src, URL url) { FileLocator.FileLocatorBuilder fileLocatorBuilder = fileLocator(src); if (src.getSourceURL() == null) { fileLocatorBuilder.sourceURL(url); } if (StringUtils.isBlank(src.getFileName())) { fileLocatorBuilder.fileName(getFileName(url)); } if (StringUtils.isBlank(src.getBasePath())) { fileLocatorBuilder.basePath(getBasePath(url)); } return fileLocatorBuilder.create(); } /** * Creates the default location strategy. This method creates a combined * location strategy as described in the comment of the * {@link #DEFAULT_LOCATION_STRATEGY} member field. * * @return the default {@code FileLocationStrategy} */ private static FileLocationStrategy initDefaultLocationStrategy() { FileLocationStrategy[] subStrategies = new FileLocationStrategy[] { new ProvidedURLLocationStrategy(), new FileSystemLocationStrategy(), new AbsoluteNameLocationStrategy(), new BasePathLocationStrategy(), new HomeDirectoryLocationStrategy(true), new HomeDirectoryLocationStrategy(false), new ClasspathLocationStrategy() }; return new CombinedLocationStrategy(Arrays.asList(subStrategies)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy