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 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    /** Constant for the file extension separator. */
047    private static final char EXT_SEPARATOR = '.';
048
049    /**
050     * Extracts the extension from the given file name. The name can be <b>null</b>.
051     *
052     * @param fileName the file name
053     * @return the extension (<b>null</b> if there is none)
054     */
055    private static String extractExtension(final String fileName) {
056        if (fileName == null) {
057            return null;
058        }
059
060        final int pos = fileName.lastIndexOf(EXT_SEPARATOR);
061        return pos < 0 ? null : fileName.substring(pos + 1);
062    }
063
064    /**
065     * Tries to obtain the current file name from the given list of parameter objects.
066     *
067     * @param params the parameter objects
068     * @return the file name or <b>null</b> if unspecified
069     */
070    private static String fetchCurrentFileName(final Collection<BuilderParameters> params) {
071        for (final BuilderParameters p : params) {
072            if (p instanceof FileBasedBuilderParametersImpl) {
073                final FileBasedBuilderParametersImpl fp = (FileBasedBuilderParametersImpl) p;
074                return fp.getFileHandler().getFileName();
075            }
076        }
077        return null;
078    }
079
080    /** The matching configuration class. */
081    private final String matchingConfigurationClass;
082
083    /** The file extension. */
084    private final String extension;
085
086    /**
087     * Creates a new instance of {@code FileExtensionConfigurationBuilderProvider}.
088     *
089     * @param bldrCls the name of the builder class
090     * @param reloadBldrCls the name of a builder class to be used if reloading support is required (<b>null</b> if
091     *        reloading is not supported)
092     * @param matchingConfigCls the name of the configuration class to be used if the provided file extension matches (must
093     *        not be <b>null</b>)
094     * @param defConfigClass the name of the configuration class to be used if the provided file extension does not match
095     *        (must not be <b>null</b>)
096     * @param ext the file extension to select the configuration class (must not be <b>null</b>)
097     * @param paramCls a collection with the names of parameters classes; an instance of a parameters object with basic
098     *        properties is created automatically and does not need to be contained in this list; the collection can be
099     *        <b>null</b> if no additional parameter objects are needed
100     * @throws IllegalArgumentException if a required parameter is missing
101     */
102    public FileExtensionConfigurationBuilderProvider(final String bldrCls, final String reloadBldrCls, final String matchingConfigCls,
103        final String defConfigClass, final String ext, final Collection<String> paramCls) {
104        super(bldrCls, reloadBldrCls, defConfigClass, paramCls);
105        if (matchingConfigCls == null) {
106            throw new IllegalArgumentException("Matching configuration class must not be null!");
107        }
108        if (ext == null) {
109            throw new IllegalArgumentException("File extension must not be null!");
110        }
111
112        matchingConfigurationClass = matchingConfigCls;
113        extension = ext;
114    }
115
116    /**
117     * {@inheritDoc} This implementation tries to find a {@link FileBasedBuilderParametersImpl} object in the parameter
118     * objects. If one is found, the extension of the file name is obtained and compared against the stored file extension.
119     * In case of a match, the matching configuration class is selected, otherwise the default one.
120     */
121    @Override
122    protected String determineConfigurationClass(final ConfigurationDeclaration decl, final Collection<BuilderParameters> params)
123        throws ConfigurationException {
124        final String currentExt = extractExtension(fetchCurrentFileName(params));
125        return getExtension().equalsIgnoreCase(currentExt) ? getMatchingConfigurationClass() : getConfigurationClass();
126    }
127
128    /**
129     * Gets the file extension of this provider.
130     *
131     * @return the file extension to match
132     */
133    public String getExtension() {
134        return extension;
135    }
136
137    /**
138     * Gets the name of the matching configuration class. This class is used if the file extension matches the extension
139     * of this provider.
140     *
141     * @return the matching configuration class
142     */
143    public String getMatchingConfigurationClass() {
144        return matchingConfigurationClass;
145    }
146}