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