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