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 1790899 2017-04-10 21:56:46Z 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            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            Map<String, ?> params, 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(Map<String, ?> source)
136    {
137        super.inheritFrom(source);
138
139        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            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            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(String tagName,
210            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            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 (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            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(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(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            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            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            Class<D> paramClass, 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            Class<D> paramClass, DefaultParametersHandler<? super D> handler,
437            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        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        CombinedBuilderParametersImpl copy =
465                (CombinedBuilderParametersImpl) super.clone();
466        copy.setDefinitionBuilderParameters((BuilderParameters) ConfigurationUtils
467                .cloneIfPossible(getDefinitionBuilderParameters()));
468        return copy;
469    }
470}