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