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