    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.ArrayList;
    020import java.util.Collection;
    021import java.util.Collections;
    022import java.util.HashMap;
    023import java.util.LinkedList;
    024import java.util.Map;
    025
    026import org.apache.commons.configuration2.ConfigurationUtils;
    027import org.apache.commons.configuration2.HierarchicalConfiguration;
    028import org.apache.commons.configuration2.builder.BasicBuilderParameters;
    029import org.apache.commons.configuration2.builder.BuilderParameters;
    030import org.apache.commons.configuration2.builder.ConfigurationBuilder;
    031import org.apache.commons.configuration2.builder.DefaultParametersHandler;
    032import org.apache.commons.configuration2.builder.DefaultParametersManager;
    033
    034/**
    035 * <p>
    036 * A specialized parameters object for a {@link CombinedConfigurationBuilder}.
    037 * </p>
    038 * <p>
    039 * This class defines methods for setting properties for customizing a builder for combined configurations. Note that
    040 * some of these properties can also be set in the configuration definition file. If this is the case, the settings in
    041 * the definition file override the content of this object.
    042 * </p>
    043 * <p>
    044 * This class is not thread-safe. It is intended that an instance is constructed and initialized by a single thread
    045 * during configuration of a {@code ConfigurationBuilder}.
    046 * </p>
    047 *
    048 * @since 2.0
    049 */
    050public class CombinedBuilderParametersImpl extends BasicBuilderParameters implements CombinedBuilderProperties<CombinedBuilderParametersImpl> {
    051
    052    /** Constant for the key in the parameters map used by this class. */
    053    private static final String PARAM_KEY = RESERVED_PARAMETER_PREFIX + CombinedBuilderParametersImpl.class.getName();
    054
    055    /**
    056     * Looks up an instance of this class in the specified parameters map. This is equivalent to
    057     * {@code fromParameters(params, false);}
    058     *
    059     * @param params the map with parameters (must not be <strong>null</strong>
    060     * @return the instance obtained from the map or <strong>null</strong>
    061     * @throws NullPointerException if the map is <strong>null</strong>
    062     */
    063    public static CombinedBuilderParametersImpl fromParameters(final Map<String, ?> params) {
    064        return fromParameters(params, false);
    065    }
    066
    067    /**
    068     * Looks up an instance of this class in the specified parameters map and optionally creates a new one if none is found.
    069     * This method can be used to obtain an instance of this class which has been stored in a parameters map. It is
    070     * compatible with the {@code getParameters()} method.
    071     *
    072     * @param params the map with parameters (must not be <strong>null</strong>
    073     * @param createIfMissing determines the behavior if no instance is found in the map; if <strong>true</strong>, a new instance
    074     *        with default settings is created; if <strong>false</strong>, <strong>null</strong> is returned
    075     * @return the instance obtained from the map or <strong>null</strong>
    076     * @throws NullPointerException if the map is <strong>null</strong>
    077     */
    078    public static CombinedBuilderParametersImpl fromParameters(final Map<String, ?> params, final boolean createIfMissing) {
    079        CombinedBuilderParametersImpl result = (CombinedBuilderParametersImpl) params.get(PARAM_KEY);
    080        if (result == null && createIfMissing) {
    081            result = new CombinedBuilderParametersImpl();
    082        }
    083        return result;
    084    }
    085
    086    /** The definition configuration builder. */
    087    private ConfigurationBuilder<? extends HierarchicalConfiguration<?>> definitionBuilder;
    088
    089    /** A parameters object for the definition configuration builder. */
    090    private BuilderParameters definitionBuilderParameters;
    091
    092    /** A map with registered configuration builder providers. */
    093    private final Map<String, ConfigurationBuilderProvider> providers;
    094
    095    /** A list with default parameters for child configuration sources. */
    096    private final Collection<BuilderParameters> childParameters;
    097
    098    /** The manager for default handlers. */
    099    private DefaultParametersManager childDefaultParametersManager;
    100
    101    /** The base path for configuration sources to be loaded. */
    102    private String basePath;
    103
    104    /** A flag whether settings should be inherited by child builders. */
    105    private boolean inheritSettings;
    106
    107    /**
    108     * Creates a new instance of {@code CombinedBuilderParametersImpl}.
    109     */
    110    public CombinedBuilderParametersImpl() {
    111        providers = new HashMap<>();
    112        childParameters = new LinkedList<>();
    113        inheritSettings = true;
    114    }
    115
    116    /**
    117     * {@inheritDoc} This implementation also clones the parameters object for the definition builder if possible.
    118     */
    119    @Override
    120    public CombinedBuilderParametersImpl clone() {
    121        final CombinedBuilderParametersImpl copy = (CombinedBuilderParametersImpl) super.clone();
    122        copy.setDefinitionBuilderParameters((BuilderParameters) ConfigurationUtils.cloneIfPossible(getDefinitionBuilderParameters()));
    123        return copy;
    124    }
    125
    126    /**
    127     * Gets the base path for relative names of configuration sources. Result may be <strong>null</strong> if no base path has been
    128     * set.
    129     *
    130     * @return the base path for resolving relative file names
    131     */
    132    public String getBasePath() {
    133        return basePath;
    134    }
    135
    136    /**
    137     * Gets the {@code DefaultParametersManager} object for initializing parameter objects for child configuration
    138     * sources. This method never returns <strong>null</strong>. If no manager was set, a new instance is created right now.
    139     *
    140     * @return the {@code DefaultParametersManager} for child configuration sources
    141     */
    142    public DefaultParametersManager getChildDefaultParametersManager() {
    143        if (childDefaultParametersManager == null) {
    144            childDefaultParametersManager = new DefaultParametersManager();
    145        }
    146        return childDefaultParametersManager;
    147    }
    148
    149    /**
    150     * Gets a collection with default parameter objects for child configuration sources. This collection contains the
    151     * same objects (in the same order) that were passed to {@code addChildParameters()}. The returned collection is a
    152     * defensive copy; it can be modified, but this has no effect on the parameters stored in this object.
    153     *
    154     * @return a map with default parameters for child sources
    155     */
    156    public Collection<? extends BuilderParameters> getDefaultChildParameters() {
    157        return new ArrayList<>(childParameters);
    158    }
    159
    160    /**
    161     * Gets the {@code ConfigurationBuilder} object for obtaining the definition configuration.
    162     *
    163     * @return the definition {@code ConfigurationBuilder}
    164     */
    165    public ConfigurationBuilder<? extends HierarchicalConfiguration<?>> getDefinitionBuilder() {
    166        return definitionBuilder;
    167    }
    168
    169    /**
    170     * Gets the parameters object for the definition configuration builder if present.
    171     *
    172     * @return the parameters object for the definition configuration builder or <strong>null</strong>
    173     */
    174    public BuilderParameters getDefinitionBuilderParameters() {
    175        return definitionBuilderParameters;
    176    }
    177
    178    /**
    179     * {@inheritDoc} This implementation returns a map which contains this object itself under a specific key. The static
    180     * {@code fromParameters()} method can be used to extract an instance from a parameters map.
    181     */
    182    @Override
    183    public Map<String, Object> getParameters() {
    184        final Map<String, Object> params = super.getParameters();
    185        params.put(PARAM_KEY, this);
    186        return params;
    187    }
    188
    189    /**
    190     * Gets an (unmodifiable) map with the currently registered {@code ConfigurationBuilderProvider} objects.
    191     *
    192     * @return the map with {@code ConfigurationBuilderProvider} objects (the keys are the tag names)
    193     */
    194    public Map<String, ConfigurationBuilderProvider> getProviders() {
    195        return Collections.unmodifiableMap(providers);
    196    }
    197
    198    /**
    199     * {@inheritDoc} This implementation additionally copies some properties defined by this class.
    200     */
    201    @Override
    202    public void inheritFrom(final Map<String, ?> source) {
    203        super.inheritFrom(source);
    204
    205        final CombinedBuilderParametersImpl srcParams = fromParameters(source);
    206        if (srcParams != null) {
    207            setChildDefaultParametersManager(srcParams.getChildDefaultParametersManager());
    208            setInheritSettings(srcParams.isInheritSettings());
    209        }
    210    }
    211
    212    /**
    213     * Returns the current value of the flag that controls whether the settings of the parent combined configuration builder
    214     * should be inherited by its child configurations.
    215     *
    216     * @return the flag whether settings should be inherited by child configurations
    217     */
    218    public boolean isInheritSettings() {
    219        return inheritSettings;
    220    }
    221
    222    /**
    223     * Returns the {@code ConfigurationBuilderProvider} which is registered for the specified tag name or <strong>null</strong> if
    224     * there is no registration for this tag.
    225     *
    226     * @param tagName the tag name
    227     * @return the provider registered for this tag or <strong>null</strong>
    228     */
    229    public ConfigurationBuilderProvider providerForTag(final String tagName) {
    230        return providers.get(tagName);
    231    }
    232
    233    /**
    234     * {@inheritDoc} This implementation registers the passed in handler at an internal {@link DefaultParametersManager}
    235     * instance. If none was set, a new instance is created now.
    236     */
    237    @Override
    238    public <D> CombinedBuilderParametersImpl registerChildDefaultsHandler(final Class<D> paramClass, final DefaultParametersHandler<? super D> handler) {
    239        getChildDefaultParametersManager().registerDefaultsHandler(paramClass, handler);
    240        return this;
    241    }
    242
    243    /**
    244     * {@inheritDoc} This implementation registers the passed in handler at an internal {@link DefaultParametersManager}
    245     * instance. If none was set, a new instance is created now.
    246     */
    247    @Override
    248    public <D> CombinedBuilderParametersImpl registerChildDefaultsHandler(final Class<D> paramClass, final DefaultParametersHandler<? super D> handler,
    249        final Class<?> startClass) {
    250        getChildDefaultParametersManager().registerDefaultsHandler(paramClass, handler, startClass);
    251        return this;
    252    }
    253
    254    /**
    255     * Registers all {@code ConfigurationBuilderProvider}s in the given parameters object which have not yet been
    256     * registered. This method works like the method with the same name, but the map with providers is obtained from the
    257     * passed in parameters object.
    258     *
    259     * @param params the parameters object from which to copy providers(must not be <strong>null</strong>)
    260     * @return a reference to this object for method chaining
    261     * @throws IllegalArgumentException if the source parameters object is <strong>null</strong>
    262     */
    263    public CombinedBuilderParametersImpl registerMissingProviders(final CombinedBuilderParametersImpl params) {
    264        if (params == null) {
    265            throw new IllegalArgumentException("Source parameters must not be null.");
    266        }
    267        return registerMissingProviders(params.getProviders());
    268    }
    269
    270    /**
    271     * Registers all {@code ConfigurationBuilderProvider}s in the given map to this object which have not yet been
    272     * registered. This method is mainly used for internal purposes: a {@code CombinedConfigurationBuilder} takes the
    273     * providers contained in a parameters object and adds all standard providers. This way it is possible to override a
    274     * standard provider by registering a provider object for the same tag name at the parameters object.
    275     *
    276     * @param providers a map with tag names and corresponding providers (must not be <strong>null</strong> or contain <strong>null</strong>
    277     *        entries)
    278     * @return a reference to this object for method chaining
    279     * @throws IllegalArgumentException if the map with providers is <strong>null</strong> or contains <strong>null</strong> entries
    280     */
    281    public CombinedBuilderParametersImpl registerMissingProviders(final Map<String, ConfigurationBuilderProvider> providers) {
    282        if (providers == null) {
    283            throw new IllegalArgumentException("Map with providers must not be null.");
    284        }
    285        providers.forEach((k, v) -> {
    286            if (!this.providers.containsKey(k)) {
    287                registerProvider(k, v);
    288            }
    289        });
    290        return this;
    291    }
    292
    293    /**
    294     * Registers the given {@code ConfigurationBuilderProvider} for the specified tag name. This means that whenever this
    295     * tag is encountered in a configuration definition file, the corresponding builder provider is invoked.
    296     *
    297     * @param tagName the name of the tag (must not be <strong>null</strong>)
    298     * @param provider the {@code ConfigurationBuilderProvider} (must not be <strong>null</strong>)
    299     * @return a reference to this object for method chaining
    300     * @throws IllegalArgumentException if a required parameter is missing
    301     */
    302    @Override
    303    public CombinedBuilderParametersImpl registerProvider(final String tagName, final ConfigurationBuilderProvider provider) {
    304        if (tagName == null) {
    305            throw new IllegalArgumentException("Tag name must not be null.");
    306        }
    307        if (provider == null) {
    308            throw new IllegalArgumentException("Provider must not be null.");
    309        }
    310
    311        providers.put(tagName, provider);
    312        return this;
    313    }
    314
    315    /**
    316     * Sets the base path for this combined configuration builder. Normally it it not necessary to set the base path
    317     * explicitly. Per default, relative file names of configuration sources are resolved based on the location of the
    318     * definition file. If this is not desired or if the definition configuration is loaded by a different means, the base
    319     * path for relative file names can be specified using this method.
    320     *
    321     * @param path the base path for resolving relative file names
    322     * @return a reference to this object for method chaining
    323     */
    324    @Override
    325    public CombinedBuilderParametersImpl setBasePath(final String path) {
    326        basePath = path;
    327        return this;
    328    }
    329
    330    /**
    331     * {@inheritDoc} This implementation stores the passed in manager object. An already existing manager object (either
    332     * explicitly set or created on demand) is overridden. This also removes all default handlers registered before!
    333     */
    334    @Override
    335    public CombinedBuilderParametersImpl setChildDefaultParametersManager(final DefaultParametersManager manager) {
    336        childDefaultParametersManager = manager;
    337        return this;
    338    }
    339
    340    /**
    341     * Sets the {@code ConfigurationBuilder} for the definition configuration. This is the configuration which contains the
    342     * configuration sources that form the combined configuration.
    343     *
    344     * @param builder the definition {@code ConfigurationBuilder}
    345     * @return a reference to this object for method chaining
    346     */
    347    @Override
    348    public CombinedBuilderParametersImpl setDefinitionBuilder(final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> builder) {
    349        definitionBuilder = builder;
    350        return this;
    351    }
    352
    353    /**
    354     * Sets the parameters object for the definition configuration builder. This property is evaluated only if the
    355     * definition configuration builder is not set explicitly (using the {@link #setDefinitionBuilder(ConfigurationBuilder)}
    356     * method). In this case, a builder for an XML configuration is created and configured with this parameters object.
    357     *
    358     * @param params the parameters object for the definition configuration builder
    359     * @return a reference to this object for method chaining
    360     */
    361    @Override
    362    public CombinedBuilderParametersImpl setDefinitionBuilderParameters(final BuilderParameters params) {
    363        definitionBuilderParameters = params;
    364        return this;
    365    }
    366
    367    @Override
    368    public CombinedBuilderParametersImpl setInheritSettings(final boolean inheritSettings) {
    369        this.inheritSettings = inheritSettings;
    370        return this;
    371    }
    372}