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