View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.configuration2.builder.combined;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.HashMap;
23  import java.util.LinkedList;
24  import java.util.Map;
25  
26  import org.apache.commons.configuration2.ConfigurationUtils;
27  import org.apache.commons.configuration2.HierarchicalConfiguration;
28  import org.apache.commons.configuration2.builder.BasicBuilderParameters;
29  import org.apache.commons.configuration2.builder.BuilderParameters;
30  import org.apache.commons.configuration2.builder.ConfigurationBuilder;
31  import org.apache.commons.configuration2.builder.DefaultParametersHandler;
32  import org.apache.commons.configuration2.builder.DefaultParametersManager;
33  
34  /**
35   * <p>
36   * A specialized parameters object for a {@link CombinedConfigurationBuilder}.
37   * </p>
38   * <p>
39   * This class defines methods for setting properties for customizing a builder for combined configurations. Note that
40   * some of these properties can also be set in the configuration definition file. If this is the case, the settings in
41   * the definition file override the content of this object.
42   * </p>
43   * <p>
44   * This class is not thread-safe. It is intended that an instance is constructed and initialized by a single thread
45   * during configuration of a {@code ConfigurationBuilder}.
46   * </p>
47   *
48   * @since 2.0
49   */
50  public class CombinedBuilderParametersImpl extends BasicBuilderParameters implements CombinedBuilderProperties<CombinedBuilderParametersImpl> {
51      /** Constant for the key in the parameters map used by this class. */
52      private static final String PARAM_KEY = RESERVED_PARAMETER_PREFIX + CombinedBuilderParametersImpl.class.getName();
53  
54      /** The definition configuration builder. */
55      private ConfigurationBuilder<? extends HierarchicalConfiguration<?>> definitionBuilder;
56  
57      /** A parameters object for the definition configuration builder. */
58      private BuilderParameters definitionBuilderParameters;
59  
60      /** A map with registered configuration builder providers. */
61      private final Map<String, ConfigurationBuilderProvider> providers;
62  
63      /** A list with default parameters for child configuration sources. */
64      private final Collection<BuilderParameters> childParameters;
65  
66      /** The manager for default handlers. */
67      private DefaultParametersManager childDefaultParametersManager;
68  
69      /** The base path for configuration sources to be loaded. */
70      private String basePath;
71  
72      /** A flag whether settings should be inherited by child builders. */
73      private boolean inheritSettings;
74  
75      /**
76       * Creates a new instance of {@code CombinedBuilderParametersImpl}.
77       */
78      public CombinedBuilderParametersImpl() {
79          providers = new HashMap<>();
80          childParameters = new LinkedList<>();
81          inheritSettings = true;
82      }
83  
84      /**
85       * Looks up an instance of this class in the specified parameters map. This is equivalent to
86       * {@code fromParameters(params, false);}
87       *
88       * @param params the map with parameters (must not be <b>null</b>
89       * @return the instance obtained from the map or <b>null</b>
90       * @throws NullPointerException if the map is <b>null</b>
91       */
92      public static CombinedBuilderParametersImpl fromParameters(final Map<String, ?> params) {
93          return fromParameters(params, false);
94      }
95  
96      /**
97       * Looks up an instance of this class in the specified parameters map and optionally creates a new one if none is found.
98       * This method can be used to obtain an instance of this class which has been stored in a parameters map. It is
99       * compatible with the {@code getParameters()} method.
100      *
101      * @param params the map with parameters (must not be <b>null</b>
102      * @param createIfMissing determines the behavior if no instance is found in the map; if <b>true</b>, a new instance
103      *        with default settings is created; if <b>false</b>, <b>null</b> is returned
104      * @return the instance obtained from the map or <b>null</b>
105      * @throws NullPointerException if the map is <b>null</b>
106      */
107     public static CombinedBuilderParametersImpl fromParameters(final Map<String, ?> params, final boolean createIfMissing) {
108         CombinedBuilderParametersImpl result = (CombinedBuilderParametersImpl) params.get(PARAM_KEY);
109         if (result == null && createIfMissing) {
110             result = new CombinedBuilderParametersImpl();
111         }
112         return result;
113     }
114 
115     /**
116      * {@inheritDoc} This implementation additionally copies some properties defined by this class.
117      */
118     @Override
119     public void inheritFrom(final Map<String, ?> source) {
120         super.inheritFrom(source);
121 
122         final CombinedBuilderParametersImpl srcParams = fromParameters(source);
123         if (srcParams != null) {
124             setChildDefaultParametersManager(srcParams.getChildDefaultParametersManager());
125             setInheritSettings(srcParams.isInheritSettings());
126         }
127     }
128 
129     /**
130      * Returns the current value of the flag that controls whether the settings of the parent combined configuration builder
131      * should be inherited by its child configurations.
132      *
133      * @return the flag whether settings should be inherited by child configurations
134      */
135     public boolean isInheritSettings() {
136         return inheritSettings;
137     }
138 
139     @Override
140     public CombinedBuilderParametersImpl setInheritSettings(final boolean inheritSettings) {
141         this.inheritSettings = inheritSettings;
142         return this;
143     }
144 
145     /**
146      * Gets the {@code ConfigurationBuilder} object for obtaining the definition configuration.
147      *
148      * @return the definition {@code ConfigurationBuilder}
149      */
150     public ConfigurationBuilder<? extends HierarchicalConfiguration<?>> getDefinitionBuilder() {
151         return definitionBuilder;
152     }
153 
154     /**
155      * Sets the {@code ConfigurationBuilder} for the definition configuration. This is the configuration which contains the
156      * configuration sources that form the combined configuration.
157      *
158      * @param builder the definition {@code ConfigurationBuilder}
159      * @return a reference to this object for method chaining
160      */
161     @Override
162     public CombinedBuilderParametersImpl setDefinitionBuilder(final ConfigurationBuilder<? extends HierarchicalConfiguration<?>> builder) {
163         definitionBuilder = builder;
164         return this;
165     }
166 
167     /**
168      * Registers the given {@code ConfigurationBuilderProvider} for the specified tag name. This means that whenever this
169      * tag is encountered in a configuration definition file, the corresponding builder provider is invoked.
170      *
171      * @param tagName the name of the tag (must not be <b>null</b>)
172      * @param provider the {@code ConfigurationBuilderProvider} (must not be <b>null</b>)
173      * @return a reference to this object for method chaining
174      * @throws IllegalArgumentException if a required parameter is missing
175      */
176     @Override
177     public CombinedBuilderParametersImpl registerProvider(final String tagName, final ConfigurationBuilderProvider provider) {
178         if (tagName == null) {
179             throw new IllegalArgumentException("Tag name must not be null!");
180         }
181         if (provider == null) {
182             throw new IllegalArgumentException("Provider must not be null!");
183         }
184 
185         providers.put(tagName, provider);
186         return this;
187     }
188 
189     /**
190      * Registers all {@code ConfigurationBuilderProvider}s in the given map to this object which have not yet been
191      * registered. This method is mainly used for internal purposes: a {@code CombinedConfigurationBuilder} takes the
192      * providers contained in a parameters object and adds all standard providers. This way it is possible to override a
193      * standard provider by registering a provider object for the same tag name at the parameters object.
194      *
195      * @param providers a map with tag names and corresponding providers (must not be <b>null</b> or contain <b>null</b>
196      *        entries)
197      * @return a reference to this object for method chaining
198      * @throws IllegalArgumentException if the map with providers is <b>null</b> or contains <b>null</b> entries
199      */
200     public CombinedBuilderParametersImpl registerMissingProviders(final Map<String, ConfigurationBuilderProvider> providers) {
201         if (providers == null) {
202             throw new IllegalArgumentException("Map with providers must not be null!");
203         }
204         providers.forEach((k, v) -> {
205             if (!this.providers.containsKey(k)) {
206                 registerProvider(k, v);
207             }
208         });
209         return this;
210     }
211 
212     /**
213      * Registers all {@code ConfigurationBuilderProvider}s in the given parameters object which have not yet been
214      * registered. This method works like the method with the same name, but the map with providers is obtained from the
215      * passed in parameters object.
216      *
217      * @param params the parameters object from which to copy providers(must not be <b>null</b>)
218      * @return a reference to this object for method chaining
219      * @throws IllegalArgumentException if the source parameters object is <b>null</b>
220      */
221     public CombinedBuilderParametersImpl registerMissingProviders(final CombinedBuilderParametersImpl params) {
222         if (params == null) {
223             throw new IllegalArgumentException("Source parameters must not be null!");
224         }
225         return registerMissingProviders(params.getProviders());
226     }
227 
228     /**
229      * Gets an (unmodifiable) map with the currently registered {@code ConfigurationBuilderProvider} objects.
230      *
231      * @return the map with {@code ConfigurationBuilderProvider} objects (the keys are the tag names)
232      */
233     public Map<String, ConfigurationBuilderProvider> getProviders() {
234         return Collections.unmodifiableMap(providers);
235     }
236 
237     /**
238      * Returns the {@code ConfigurationBuilderProvider} which is registered for the specified tag name or <b>null</b> if
239      * there is no registration for this tag.
240      *
241      * @param tagName the tag name
242      * @return the provider registered for this tag or <b>null</b>
243      */
244     public ConfigurationBuilderProvider providerForTag(final String tagName) {
245         return providers.get(tagName);
246     }
247 
248     /**
249      * Gets the base path for relative names of configuration sources. Result may be <b>null</b> if no base path has been
250      * set.
251      *
252      * @return the base path for resolving relative file names
253      */
254     public String getBasePath() {
255         return basePath;
256     }
257 
258     /**
259      * Sets the base path for this combined configuration builder. Normally it it not necessary to set the base path
260      * explicitly. Per default, relative file names of configuration sources are resolved based on the location of the
261      * definition file. If this is not desired or if the definition configuration is loaded by a different means, the base
262      * path for relative file names can be specified using this method.
263      *
264      * @param path the base path for resolving relative file names
265      * @return a reference to this object for method chaining
266      */
267     @Override
268     public CombinedBuilderParametersImpl setBasePath(final String path) {
269         basePath = path;
270         return this;
271     }
272 
273     /**
274      * Gets the parameters object for the definition configuration builder if present.
275      *
276      * @return the parameters object for the definition configuration builder or <b>null</b>
277      */
278     public BuilderParameters getDefinitionBuilderParameters() {
279         return definitionBuilderParameters;
280     }
281 
282     /**
283      * Sets the parameters object for the definition configuration builder. This property is evaluated only if the
284      * definition configuration builder is not set explicitly (using the {@link #setDefinitionBuilder(ConfigurationBuilder)}
285      * method). In this case, a builder for an XML configuration is created and configured with this parameters object.
286      *
287      * @param params the parameters object for the definition configuration builder
288      * @return a reference to this object for method chaining
289      */
290     @Override
291     public CombinedBuilderParametersImpl setDefinitionBuilderParameters(final BuilderParameters params) {
292         definitionBuilderParameters = params;
293         return this;
294     }
295 
296     /**
297      * Gets a collection with default parameter objects for child configuration sources. This collection contains the
298      * same objects (in the same order) that were passed to {@code addChildParameters()}. The returned collection is a
299      * defensive copy; it can be modified, but this has no effect on the parameters stored in this object.
300      *
301      * @return a map with default parameters for child sources
302      */
303     public Collection<? extends BuilderParameters> getDefaultChildParameters() {
304         return new ArrayList<>(childParameters);
305     }
306 
307     /**
308      * Gets the {@code DefaultParametersManager} object for initializing parameter objects for child configuration
309      * sources. This method never returns <b>null</b>. If no manager was set, a new instance is created right now.
310      *
311      * @return the {@code DefaultParametersManager} for child configuration sources
312      */
313     public DefaultParametersManager getChildDefaultParametersManager() {
314         if (childDefaultParametersManager == null) {
315             childDefaultParametersManager = new DefaultParametersManager();
316         }
317         return childDefaultParametersManager;
318     }
319 
320     /**
321      * {@inheritDoc} This implementation stores the passed in manager object. An already existing manager object (either
322      * explicitly set or created on demand) is overridden. This also removes all default handlers registered before!
323      */
324     @Override
325     public CombinedBuilderParametersImpl setChildDefaultParametersManager(final DefaultParametersManager manager) {
326         childDefaultParametersManager = manager;
327         return this;
328     }
329 
330     /**
331      * {@inheritDoc} This implementation registers the passed in handler at an internal {@link DefaultParametersManager}
332      * instance. If none was set, a new instance is created now.
333      */
334     @Override
335     public <D> CombinedBuilderParametersImpl registerChildDefaultsHandler(final Class<D> paramClass, final DefaultParametersHandler<? super D> handler) {
336         getChildDefaultParametersManager().registerDefaultsHandler(paramClass, handler);
337         return this;
338     }
339 
340     /**
341      * {@inheritDoc} This implementation registers the passed in handler at an internal {@link DefaultParametersManager}
342      * instance. If none was set, a new instance is created now.
343      */
344     @Override
345     public <D> CombinedBuilderParametersImpl registerChildDefaultsHandler(final Class<D> paramClass, final DefaultParametersHandler<? super D> handler,
346         final Class<?> startClass) {
347         getChildDefaultParametersManager().registerDefaultsHandler(paramClass, handler, startClass);
348         return this;
349     }
350 
351     /**
352      * {@inheritDoc} This implementation returns a map which contains this object itself under a specific key. The static
353      * {@code fromParameters()} method can be used to extract an instance from a parameters map.
354      */
355     @Override
356     public Map<String, Object> getParameters() {
357         final Map<String, Object> params = super.getParameters();
358         params.put(PARAM_KEY, this);
359         return params;
360     }
361 
362     /**
363      * {@inheritDoc} This implementation also clones the parameters object for the definition builder if possible.
364      */
365     @Override
366     public CombinedBuilderParametersImpl clone() {
367         final CombinedBuilderParametersImpl copy = (CombinedBuilderParametersImpl) super.clone();
368         copy.setDefinitionBuilderParameters((BuilderParameters) ConfigurationUtils.cloneIfPossible(getDefinitionBuilderParameters()));
369         return copy;
370     }
371 }