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}