    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.configuration2.io;
    018
    019import java.io.File;
    020import java.net.MalformedURLException;
    021import java.net.URI;
    022import java.net.URL;
    023import java.util.Arrays;
    024import java.util.Map;
    025
    026import org.apache.commons.configuration2.ex.ConfigurationException;
    027import org.apache.commons.lang3.ObjectUtils;
    028import org.apache.commons.lang3.StringUtils;
    029import org.apache.commons.logging.Log;
    030import org.apache.commons.logging.LogFactory;
    031
    032/**
    033 * <p>
    034 * A utility class providing helper methods related to locating files.
    035 * </p>
    036 * <p>
    037 * The methods of this class are used behind the scenes when retrieving
    038 * configuration files based on different criteria, e.g. URLs, files, or more
    039 * complex search strategies. They also implement functionality required by the
    040 * default {@link FileSystem} implementations. Most methods are intended to be
    041 * used internally only by other classes in the {@code io} package.
    042 * </p>
    043 *
    044 * @version $Id: FileLocatorUtils.java 1842194 2018-09-27 22:24:23Z ggregory $
    045 * @since 2.0
    046 */
    047public final class FileLocatorUtils
    048{
    049    /**
    050     * Constant for the default {@code FileSystem}. This file system is used by
    051     * operations of this class if no specific file system is provided. An
    052     * instance of {@link DefaultFileSystem} is used.
    053     */
    054    public static final FileSystem DEFAULT_FILE_SYSTEM =
    055            new DefaultFileSystem();
    056
    057    /**
    058     * Constant for the default {@code FileLocationStrategy}. This strategy is
    059     * used by the {@code locate()} method if the passed in {@code FileLocator}
    060     * does not define its own location strategy. The default location strategy
    061     * is roughly equivalent to the search algorithm used in version 1.x of
    062     * <em>Commons Configuration</em> (there it was hard-coded though). It
    063     * behaves in the following way when passed a {@code FileLocator}:
    064     * <ul>
    065     * <li>If the {@code FileLocator} has a defined URL, this URL is used as the
    066     * file's URL (without any further checks).</li>
    067     * <li>Otherwise, base path and file name stored in the {@code FileLocator}
    068     * are passed to the current {@code FileSystem}'s {@code locateFromURL()}
    069     * method. If this results in a URL, it is returned.</li>
    070     * <li>Otherwise, if the locator's file name is an absolute path to an
    071     * existing file, the URL of this file is returned.</li>
    072     * <li>Otherwise, the concatenation of base path and file name is
    073     * constructed. If this path points to an existing file, its URL is
    074     * returned.</li>
    075     * <li>Otherwise, a sub directory of the current user's home directory as
    076     * defined by the base path is searched for the referenced file. If the file
    077     * can be found there, its URL is returned.</li>
    078     * <li>Otherwise, the base path is ignored, and the file name is searched in
    079     * the current user's home directory. If the file can be found there, its
    080     * URL is returned.</li>
    081     * <li>Otherwise, a resource with the name of the locator's file name is
    082     * searched in the classpath. If it can be found, its URL is returned.</li>
    083     * <li>Otherwise, the strategy gives up and returns <b>null</b> indicating
    084     * that the file cannot be resolved.</li>
    085     * </ul>
    086     */
    087    public static final FileLocationStrategy DEFAULT_LOCATION_STRATEGY =
    088            initDefaultLocationStrategy();
    089
    090    /** Constant for the file URL protocol */
    091    private static final String FILE_SCHEME = "file:";
    092
    093    /** The logger.*/
    094    private static final Log LOG = LogFactory.getLog(FileLocatorUtils.class);
    095
    096    /** Property key for the base path. */
    097    private static final String PROP_BASE_PATH = "basePath";
    098
    099    /** Property key for the encoding. */
    100    private static final String PROP_ENCODING = "encoding";
    101
    102    /** Property key for the file name. */
    103    private static final String PROP_FILE_NAME = "fileName";
    104
    105    /** Property key for the file system. */
    106    private static final String PROP_FILE_SYSTEM = "fileSystem";
    107
    108    /** Property key for the location strategy. */
    109    private static final String PROP_STRATEGY = "locationStrategy";
    110
    111    /** Property key for the source URL. */
    112    private static final String PROP_SOURCE_URL = "sourceURL";
    113
    114    /**
    115     * Private constructor so that no instances can be created.
    116     */
    117    private FileLocatorUtils()
    118    {
    119    }
    120
    121    /**
    122     * Tries to convert the specified URL to a file object. If this fails,
    123     * <b>null</b> is returned.
    124     *
    125     * @param url the URL
    126     * @return the resulting file object
    127     */
    128    public static File fileFromURL(final URL url)
    129    {
    130        return FileUtils.toFile(url);
    131    }
    132
    133    /**
    134     * Returns an uninitialized {@code FileLocatorBuilder} which can be used
    135     * for the creation of a {@code FileLocator} object. This method provides
    136     * a convenient way to create file locators using a fluent API as in the
    137     * following example:
    138     * <pre>
    139     * FileLocator locator = FileLocatorUtils.fileLocator()
    140     *     .basePath(myBasePath)
    141     *     .fileName("test.xml")
    142     *     .create();
    143     * </pre>
    144     * @return a builder object for defining a {@code FileLocator}
    145     */
    146    public static FileLocator.FileLocatorBuilder fileLocator()
    147    {
    148        return fileLocator(null);
    149    }
    150
    151    /**
    152     * Returns a {@code FileLocatorBuilder} which is already initialized with
    153     * the properties of the passed in {@code FileLocator}. This builder can
    154     * be used to create a {@code FileLocator} object which shares properties
    155     * of the original locator (e.g. the {@code FileSystem} or the encoding),
    156     * but points to a different file. An example use case is as follows:
    157     * <pre>
    158     * FileLocator loc1 = ...
    159     * FileLocator loc2 = FileLocatorUtils.fileLocator(loc1)
    160     *     .setFileName("anotherTest.xml")
    161     *     .create();
    162     * </pre>
    163     * @param src the source {@code FileLocator} (may be <b>null</b>)
    164     * @return an initialized builder object for defining a {@code FileLocator}
    165     */
    166    public static FileLocator.FileLocatorBuilder fileLocator(final FileLocator src)
    167    {
    168        return new FileLocator.FileLocatorBuilder(src);
    169    }
    170
    171    /**
    172     * Creates a new {@code FileLocator} object with the properties defined in
    173     * the given map. The map must be conform to the structure generated by the
    174     * {@link #put(FileLocator, Map)} method; unexpected data can cause
    175     * {@code ClassCastException} exceptions. The map can be <b>null</b>, then
    176     * an uninitialized {@code FileLocator} is returned.
    177     *
    178     * @param map the map
    179     * @return the new {@code FileLocator}
    180     * @throws ClassCastException if the map contains invalid data
    181     */
    182    public static FileLocator fromMap(final Map<String, ?> map)
    183    {
    184        final FileLocator.FileLocatorBuilder builder = fileLocator();
    185        if (map != null)
    186        {
    187            builder.basePath((String) map.get(PROP_BASE_PATH))
    188                    .encoding((String) map.get(PROP_ENCODING))
    189                    .fileName((String) map.get(PROP_FILE_NAME))
    190                    .fileSystem((FileSystem) map.get(PROP_FILE_SYSTEM))
    191                    .locationStrategy(
    192                            (FileLocationStrategy) map.get(PROP_STRATEGY))
    193                    .sourceURL((URL) map.get(PROP_SOURCE_URL));
    194        }
    195        return builder.create();
    196    }
    197
    198    /**
    199     * Stores the specified {@code FileLocator} in the given map. With the
    200     * {@link #fromMap(Map)} method a new {@code FileLocator} with the same
    201     * properties as the original one can be created.
    202     *
    203     * @param locator the {@code FileLocator} to be stored
    204     * @param map the map in which to store the {@code FileLocator} (must not be
    205     *        <b>null</b>)
    206     * @throws IllegalArgumentException if the map is <b>null</b>
    207     */
    208    public static void put(final FileLocator locator, final Map<String, Object> map)
    209    {
    210        if (map == null)
    211        {
    212            throw new IllegalArgumentException("Map must not be null!");
    213        }
    214
    215        if (locator != null)
    216        {
    217            map.put(PROP_BASE_PATH, locator.getBasePath());
    218            map.put(PROP_ENCODING, locator.getEncoding());
    219            map.put(PROP_FILE_NAME, locator.getFileName());
    220            map.put(PROP_FILE_SYSTEM, locator.getFileSystem());
    221            map.put(PROP_SOURCE_URL, locator.getSourceURL());
    222            map.put(PROP_STRATEGY, locator.getLocationStrategy());
    223        }
    224    }
    225
    226    /**
    227     * Checks whether the specified {@code FileLocator} contains enough
    228     * information to locate a file. This is the case if a file name or a URL is
    229     * defined. If the passed in {@code FileLocator} is <b>null</b>, result is
    230     * <b>false</b>.
    231     *
    232     * @param locator the {@code FileLocator} to check
    233     * @return a flag whether a file location is defined by this
    234     *         {@code FileLocator}
    235     */
    236    public static boolean isLocationDefined(final FileLocator locator)
    237    {
    238        return (locator != null)
    239                && (locator.getFileName() != null || locator.getSourceURL() != null);
    240    }
    241
    242    /**
    243     * Returns a flag whether all components of the given {@code FileLocator}
    244     * describing the referenced file are defined. In order to reference a file,
    245     * it is not necessary that all components are filled in (for instance, the
    246     * URL alone is sufficient). For some use cases however, it might be of
    247     * interest to have different methods for accessing the referenced file.
    248     * Also, depending on the filled out properties, there is a subtle
    249     * difference how the file is accessed: If only the file name is set (and
    250     * optionally the base path), each time the file is accessed a
    251     * {@code locate()} operation has to be performed to uniquely identify the
    252     * file. If however the URL is determined once based on the other components
    253     * and stored in a fully defined {@code FileLocator}, it can be used
    254     * directly to identify the file. If the passed in {@code FileLocator} is
    255     * <b>null</b>, result is <b>false</b>.
    256     *
    257     * @param locator the {@code FileLocator} to be checked (may be <b>null</b>)
    258     * @return a flag whether all components describing the referenced file are
    259     *         initialized
    260     */
    261    public static boolean isFullyInitialized(final FileLocator locator)
    262    {
    263        if (locator == null)
    264        {
    265            return false;
    266        }
    267        return locator.getBasePath() != null && locator.getFileName() != null
    268                && locator.getSourceURL() != null;
    269    }
    270
    271    /**
    272     * Returns a {@code FileLocator} object based on the passed in one whose
    273     * location is fully defined. This method ensures that all components of the
    274     * {@code FileLocator} pointing to the file are set in a consistent way. In
    275     * detail it behaves as follows:
    276     * <ul>
    277     * <li>If the {@code FileLocator} has already all components set which
    278     * define the file, it is returned unchanged. <em>Note:</em> It is not
    279     * checked whether all components are really consistent!</li>
    280     * <li>{@link #locate(FileLocator)} is called to determine a unique URL
    281     * pointing to the referenced file. If this is successful, a new
    282     * {@code FileLocator} is created as a copy of the passed in one, but with
    283     * all components pointing to the file derived from this URL.</li>
    284     * <li>Otherwise, result is <b>null</b>.</li>
    285     * </ul>
    286     *
    287     * @param locator the {@code FileLocator} to be completed
    288     * @return a {@code FileLocator} with a fully initialized location if
    289     *         possible or <b>null</b>
    290     */
    291    public static FileLocator fullyInitializedLocator(final FileLocator locator)
    292    {
    293        if (isFullyInitialized(locator))
    294        {
    295            // already fully initialized
    296            return locator;
    297        }
    298
    299        final URL url = locate(locator);
    300        return (url != null) ? createFullyInitializedLocatorFromURL(locator,
    301                url) : null;
    302    }
    303
    304    /**
    305     * Locates the provided {@code FileLocator}, returning a URL for accessing
    306     * the referenced file. This method uses a {@link FileLocationStrategy} to
    307     * locate the file the passed in {@code FileLocator} points to. If the
    308     * {@code FileLocator} contains itself a {@code FileLocationStrategy}, it is
    309     * used. Otherwise, the default {@code FileLocationStrategy} is applied. The
    310     * strategy is passed the locator and a {@code FileSystem}. The resulting
    311     * URL is returned. If the {@code FileLocator} is <b>null</b>, result is
    312     * <b>null</b>.
    313     *
    314     * @param locator the {@code FileLocator} to be resolved
    315     * @return the URL pointing to the referenced file or <b>null</b> if the
    316     *         {@code FileLocator} could not be resolved
    317     * @see #DEFAULT_LOCATION_STRATEGY
    318     */
    319    public static URL locate(final FileLocator locator)
    320    {
    321        if (locator == null)
    322        {
    323            return null;
    324        }
    325
    326        return obtainLocationStrategy(locator).locate(
    327                obtainFileSystem(locator), locator);
    328    }
    329
    330    /**
    331     * Tries to locate the file referenced by the passed in {@code FileLocator}.
    332     * If this fails, an exception is thrown. This method works like
    333     * {@link #locate(FileLocator)}; however, in case of a failed location
    334     * attempt an exception is thrown.
    335     *
    336     * @param locator the {@code FileLocator} to be resolved
    337     * @return the URL pointing to the referenced file
    338     * @throws ConfigurationException if the file cannot be resolved
    339     */
    340    public static URL locateOrThrow(final FileLocator locator)
    341            throws ConfigurationException
    342    {
    343        final URL url = locate(locator);
    344        if (url == null)
    345        {
    346            throw new ConfigurationException("Could not locate: " + locator);
    347        }
    348        return url;
    349    }
    350
    351    /**
    352     * Return the path without the file name, for example http://xyz.net/foo/bar.xml
    353     * results in http://xyz.net/foo/
    354     *
    355     * @param url the URL from which to extract the path
    356     * @return the path component of the passed in URL
    357     */
    358    static String getBasePath(final URL url)
    359    {
    360        if (url == null)
    361        {
    362            return null;
    363        }
    364
    365        String s = url.toString();
    366        if (s.startsWith(FILE_SCHEME) && !s.startsWith("file://"))
    367        {
    368            s = "file://" + s.substring(FILE_SCHEME.length());
    369        }
    370
    371        if (s.endsWith("/") || StringUtils.isEmpty(url.getPath()))
    372        {
    373            return s;
    374        }
    375        return s.substring(0, s.lastIndexOf("/") + 1);
    376    }
    377
    378    /**
    379     * Extract the file name from the specified URL.
    380     *
    381     * @param url the URL from which to extract the file name
    382     * @return the extracted file name
    383     */
    384    static String getFileName(final URL url)
    385    {
    386        if (url == null)
    387        {
    388            return null;
    389        }
    390
    391        final String path = url.getPath();
    392
    393        if (path.endsWith("/") || StringUtils.isEmpty(path))
    394        {
    395            return null;
    396        }
    397        return path.substring(path.lastIndexOf("/") + 1);
    398    }
    399
    400    /**
    401     * Tries to convert the specified base path and file name into a file object.
    402     * This method is called e.g. by the save() methods of file based
    403     * configurations. The parameter strings can be relative files, absolute
    404     * files and URLs as well. This implementation checks first whether the passed in
    405     * file name is absolute. If this is the case, it is returned. Otherwise
    406     * further checks are performed whether the base path and file name can be
    407     * combined to a valid URL or a valid file name. <em>Note:</em> The test
    408     * if the passed in file name is absolute is performed using
    409     * {@code java.io.File.isAbsolute()}. If the file name starts with a
    410     * slash, this method will return <b>true</b> on Unix, but <b>false</b> on
    411     * Windows. So to ensure correct behavior for relative file names on all
    412     * platforms you should never let relative paths start with a slash. E.g.
    413     * in a configuration definition file do not use something like that:
    414     * <pre>
    415     * &lt;properties fileName="/subdir/my.properties"/&gt;
    416     * </pre>
    417     * Under Windows this path would be resolved relative to the configuration
    418     * definition file. Under Unix this would be treated as an absolute path
    419     * name.
    420     *
    421     * @param basePath the base path
    422     * @param fileName the file name (must not be <b>null</b>)
    423     * @return the file object (<b>null</b> if no file can be obtained)
    424     */
    425    static File getFile(final String basePath, final String fileName)
    426    {
    427        // Check if the file name is absolute
    428        final File f = new File(fileName);
    429        if (f.isAbsolute())
    430        {
    431            return f;
    432        }
    433
    434        // Check if URLs are involved
    435        URL url;
    436        try
    437        {
    438            url = new URL(new URL(basePath), fileName);
    439        }
    440        catch (final MalformedURLException mex1)
    441        {
    442            try
    443            {
    444                url = new URL(fileName);
    445            }
    446            catch (final MalformedURLException mex2)
    447            {
    448                url = null;
    449            }
    450        }
    451
    452        if (url != null)
    453        {
    454            return fileFromURL(url);
    455        }
    456
    457        return constructFile(basePath, fileName);
    458    }
    459
    460    /**
    461     * Convert the specified file into an URL. This method is equivalent
    462     * to file.toURI().toURL(). It was used to work around a bug in the JDK
    463     * preventing the transformation of a file into an URL if the file name
    464     * contains a '#' character. See the issue CONFIGURATION-300 for
    465     * more details. Now that we switched to JDK 1.4 we can directly use
    466     * file.toURI().toURL().
    467     *
    468     * @param file the file to be converted into an URL
    469     */
    470    static URL toURL(final File file) throws MalformedURLException
    471    {
    472        return file.toURI().toURL();
    473    }
    474
    475    /**
    476     * Tries to convert the specified URI to a URL. If this causes an exception,
    477     * result is <b>null</b>.
    478     *
    479     * @param uri the URI to be converted
    480     * @return the resulting URL or <b>null</b>
    481     */
    482    static URL convertURIToURL(final URI uri)
    483    {
    484        try
    485        {
    486            return uri.toURL();
    487        }
    488        catch (final MalformedURLException e)
    489        {
    490            return null;
    491        }
    492    }
    493
    494    /**
    495     * Tries to convert the specified file to a URL. If this causes an
    496     * exception, result is <b>null</b>.
    497     *
    498     * @param file the file to be converted
    499     * @return the resulting URL or <b>null</b>
    500     */
    501    static URL convertFileToURL(final File file)
    502    {
    503        return convertURIToURL(file.toURI());
    504    }
    505
    506    /**
    507     * Tries to find a resource with the given name in the classpath.
    508     *
    509     * @param resourceName the name of the resource
    510     * @return the URL to the found resource or <b>null</b> if the resource
    511     *         cannot be found
    512     */
    513    static URL locateFromClasspath(final String resourceName)
    514    {
    515        URL url = null;
    516        // attempt to load from the context classpath
    517        final ClassLoader loader = Thread.currentThread().getContextClassLoader();
    518        if (loader != null)
    519        {
    520            url = loader.getResource(resourceName);
    521
    522            if (url != null)
    523            {
    524                LOG.debug("Loading configuration from the context classpath (" + resourceName + ")");
    525            }
    526        }
    527
    528        // attempt to load from the system classpath
    529        if (url == null)
    530        {
    531            url = ClassLoader.getSystemResource(resourceName);
    532
    533            if (url != null)
    534            {
    535                LOG.debug("Loading configuration from the system classpath (" + resourceName + ")");
    536            }
    537        }
    538        return url;
    539    }
    540
    541    /**
    542     * Helper method for constructing a file object from a base path and a
    543     * file name. This method is called if the base path passed to
    544     * {@code getURL()} does not seem to be a valid URL.
    545     *
    546     * @param basePath the base path
    547     * @param fileName the file name (must not be <b>null</b>)
    548     * @return the resulting file
    549     */
    550    static File constructFile(final String basePath, final String fileName)
    551    {
    552        File file;
    553
    554        final File absolute = new File(fileName);
    555        if (StringUtils.isEmpty(basePath) || absolute.isAbsolute())
    556        {
    557            file = absolute;
    558        }
    559        else
    560        {
    561            file = new File(appendPath(basePath, fileName));
    562        }
    563
    564        return file;
    565    }
    566
    567    /**
    568     * Extends a path by another component. The given extension is added to the
    569     * already existing path adding a separator if necessary.
    570     *
    571     * @param path the path to be extended
    572     * @param ext the extension of the path
    573     * @return the extended path
    574     */
    575    static String appendPath(final String path, final String ext)
    576    {
    577        final StringBuilder fName = new StringBuilder();
    578        fName.append(path);
    579
    580        // My best friend. Paranoia.
    581        if (!path.endsWith(File.separator))
    582        {
    583            fName.append(File.separator);
    584        }
    585
    586        //
    587        // We have a relative path, and we have
    588        // two possible forms here. If we have the
    589        // "./" form then just strip that off first
    590        // before continuing.
    591        //
    592        if (ext.startsWith("." + File.separator))
    593        {
    594            fName.append(ext.substring(2));
    595        }
    596        else
    597        {
    598            fName.append(ext);
    599        }
    600        return fName.toString();
    601    }
    602
    603    /**
    604     * Obtains a non-<b>null</b> {@code FileSystem} object from the passed in
    605     * {@code FileLocator}. If the passed in {@code FileLocator} has a
    606     * {@code FileSystem} object, it is returned. Otherwise, result is the
    607     * default {@code FileSystem}.
    608     *
    609     * @param locator the {@code FileLocator} (may be <b>null</b>)
    610     * @return the {@code FileSystem} to be used for this {@code FileLocator}
    611     */
    612    static FileSystem obtainFileSystem(final FileLocator locator)
    613    {
    614        return (locator != null) ? ObjectUtils.defaultIfNull(
    615                locator.getFileSystem(), DEFAULT_FILE_SYSTEM)
    616                : DEFAULT_FILE_SYSTEM;
    617    }
    618
    619    /**
    620     * Obtains a non <b>null</b> {@code FileLocationStrategy} object from the
    621     * passed in {@code FileLocator}. If the {@code FileLocator} is not
    622     * <b>null</b> and has a {@code FileLocationStrategy} defined, this strategy
    623     * is returned. Otherwise, result is the default
    624     * {@code FileLocationStrategy}.
    625     *
    626     * @param locator the {@code FileLocator}
    627     * @return the {@code FileLocationStrategy} for this {@code FileLocator}
    628     */
    629    static FileLocationStrategy obtainLocationStrategy(final FileLocator locator)
    630    {
    631        return (locator != null) ? ObjectUtils.defaultIfNull(
    632                locator.getLocationStrategy(), DEFAULT_LOCATION_STRATEGY)
    633                : DEFAULT_LOCATION_STRATEGY;
    634    }
    635
    636    /**
    637     * Creates a fully initialized {@code FileLocator} based on the specified
    638     * URL.
    639     *
    640     * @param src the source {@code FileLocator}
    641     * @param url the URL
    642     * @return the fully initialized {@code FileLocator}
    643     */
    644    private static FileLocator createFullyInitializedLocatorFromURL(final FileLocator src,
    645            final URL url)
    646    {
    647        final FileLocator.FileLocatorBuilder fileLocatorBuilder = fileLocator(src);
    648        if (src.getSourceURL() == null)
    649        {
    650            fileLocatorBuilder.sourceURL(url);
    651        }
    652        if (StringUtils.isBlank(src.getFileName()))
    653        {
    654            fileLocatorBuilder.fileName(getFileName(url));
    655        }
    656        if (StringUtils.isBlank(src.getBasePath()))
    657        {
    658            fileLocatorBuilder.basePath(getBasePath(url));
    659        }
    660        return fileLocatorBuilder.create();
    661    }
    662
    663    /**
    664     * Creates the default location strategy. This method creates a combined
    665     * location strategy as described in the comment of the
    666     * {@link #DEFAULT_LOCATION_STRATEGY} member field.
    667     *
    668     * @return the default {@code FileLocationStrategy}
    669     */
    670    private static FileLocationStrategy initDefaultLocationStrategy()
    671    {
    672        final FileLocationStrategy[] subStrategies =
    673                new FileLocationStrategy[] {
    674                        new ProvidedURLLocationStrategy(),
    675                        new FileSystemLocationStrategy(),
    676                        new AbsoluteNameLocationStrategy(),
    677                        new BasePathLocationStrategy(),
    678                        new HomeDirectoryLocationStrategy(true),
    679                        new HomeDirectoryLocationStrategy(false),
    680                        new ClasspathLocationStrategy()
    681                };
    682        return new CombinedLocationStrategy(Arrays.asList(subStrategies));
    683    }
    684}