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