    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.builder.combined;
    018
    019import java.util.Collection;
    020
    021import org.apache.commons.configuration2.builder.BuilderParameters;
    022import org.apache.commons.configuration2.builder.FileBasedBuilderParametersImpl;
    023import org.apache.commons.configuration2.ex.ConfigurationException;
    024
    025/**
    026 * <p>
    027 * A specialized implementation of {@link ConfigurationBuilderProvider} which
    028 * determines the name of the result configuration class based on the extension
    029 * of the file to load.
    030 * </p>
    031 * <p>
    032 * This class works analogously to its base class {@link BaseConfigurationBuilderProvider};
    033 * especially, the resulting builder is created based on reflection. It extends
    034 * the super class's functionality by a specific mechanism for determining the
    035 * resulting configuration class: At construction time two configuration class
    036 * names and a file extension are passed in. If a file name is provided in the
    037 * builder's initialization parameters and this file name has the specified
    038 * extension, then the first configuration class name is used; otherwise the
    039 * default configuration class name is selected.
    040 * </p>
    041 * <p>
    042 * There are some tags for {@code CombinedConfigurationProvider} which can
    043 * produce different results depending on the configuration files they have to
    044 * load. This class can be used to implement this feature in a generic way.
    045 * </p>
    046 *
    047 * @version $Id: FileExtensionConfigurationBuilderProvider.java 1842194 2018-09-27 22:24:23Z ggregory $
    048 * @since 2.0
    049 */
    050public class FileExtensionConfigurationBuilderProvider extends
    051        BaseConfigurationBuilderProvider
    052{
    053    /** Constant for the file extension separator. */
    054    private static final char EXT_SEPARATOR = '.';
    055
    056    /** The matching configuration class. */
    057    private final String matchingConfigurationClass;
    058
    059    /** The file extension. */
    060    private final String extension;
    061
    062    /**
    063     * Creates a new instance of
    064     * {@code FileExtensionConfigurationBuilderProvider}.
    065     *
    066     * @param bldrCls the name of the builder class
    067     * @param reloadBldrCls the name of a builder class to be used if reloading
    068     *        support is required (<b>null</b> if reloading is not supported)
    069     * @param matchingConfigCls the name of the configuration class to be used
    070     *        if the provided file extension matches (must not be <b>null</b>)
    071     * @param defConfigClass the name of the configuration class to be used if
    072     *        the provided file extension does not match (must not be
    073     *        <b>null</b>)
    074     * @param ext the file extension to select the configuration class (must not
    075     *        be <b>null</b>)
    076     * @param paramCls a collection with the names of parameters classes; an
    077     *        instance of a parameters object with basic properties is created
    078     *        automatically and does not need to be contained in this list; the
    079     *        collection can be <b>null</b> if no additional parameter objects
    080     *        are needed
    081     * @throws IllegalArgumentException if a required parameter is missing
    082     */
    083    public FileExtensionConfigurationBuilderProvider(final String bldrCls,
    084            final String reloadBldrCls, final String matchingConfigCls,
    085            final String defConfigClass, final String ext, final Collection<String> paramCls)
    086    {
    087        super(bldrCls, reloadBldrCls, defConfigClass, paramCls);
    088        if (matchingConfigCls == null)
    089        {
    090            throw new IllegalArgumentException(
    091                    "Matching configuration class must not be null!");
    092        }
    093        if (ext == null)
    094        {
    095            throw new IllegalArgumentException(
    096                    "File extension must not be null!");
    097        }
    098
    099        matchingConfigurationClass = matchingConfigCls;
    100        extension = ext;
    101    }
    102
    103    /**
    104     * Returns the name of the matching configuration class. This class is used
    105     * if the file extension matches the extension of this provider.
    106     *
    107     * @return the matching configuration class
    108     */
    109    public String getMatchingConfigurationClass()
    110    {
    111        return matchingConfigurationClass;
    112    }
    113
    114    /**
    115     * Returns the file extension of this provider.
    116     *
    117     * @return the file extension to match
    118     */
    119    public String getExtension()
    120    {
    121        return extension;
    122    }
    123
    124    /**
    125     * {@inheritDoc} This implementation tries to find a
    126     * {@link FileBasedBuilderParametersImpl} object in the parameter objects. If
    127     * one is found, the extension of the file name is obtained and compared
    128     * against the stored file extension. In case of a match, the matching
    129     * configuration class is selected, otherwise the default one.
    130     */
    131    @Override
    132    protected String determineConfigurationClass(final ConfigurationDeclaration decl,
    133            final Collection<BuilderParameters> params) throws ConfigurationException
    134    {
    135        final String currentExt = extractExtension(fetchCurrentFileName(params));
    136        return getExtension().equalsIgnoreCase(currentExt) ? getMatchingConfigurationClass()
    137                : getConfigurationClass();
    138    }
    139
    140    /**
    141     * Tries to obtain the current file name from the given list of parameter
    142     * objects.
    143     *
    144     * @param params the parameter objects
    145     * @return the file name or <b>null</b> if unspecified
    146     */
    147    private static String fetchCurrentFileName(
    148            final Collection<BuilderParameters> params)
    149    {
    150        for (final BuilderParameters p : params)
    151        {
    152            if (p instanceof FileBasedBuilderParametersImpl)
    153            {
    154                final FileBasedBuilderParametersImpl fp = (FileBasedBuilderParametersImpl) p;
    155                return fp.getFileHandler().getFileName();
    156            }
    157        }
    158        return null;
    159    }
    160
    161    /**
    162     * Extracts the extension from the given file name. The name can be
    163     * <b>null</b>.
    164     *
    165     * @param fileName the file name
    166     * @return the extension (<b>null</b> if there is none)
    167     */
    168    private static String extractExtension(final String fileName)
    169    {
    170        if (fileName == null)
    171        {
    172            return null;
    173        }
    174
    175        final int pos = fileName.lastIndexOf(EXT_SEPARATOR);
    176        return (pos < 0) ? null : fileName.substring(pos + 1);
    177    }
    178}