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  
52      /** Constant for the key in the parameters map used by this class. */
53      private static final String PARAM_KEY = RESERVED_PARAMETER_PREFIX + CombinedBuilderParametersImpl.class.getName();
54  
55      /**
56       * Looks up an instance of this class in the specified parameters map. This is equivalent to
57       * {@code fromParameters(params, false);}
58       *
59       * @param params the map with parameters (must not be <strong>null</strong>
60       * @return the instance obtained from the map or <strong>null</strong>
61       * @throws NullPointerException if the map is <strong>null</strong>
62       */
63      public static CombinedBuilderParametersImpl fromParameters(final Map<String, ?> params) {
64          return fromParameters(params, false);
65      }
66  
67      /**
68       * Looks up an instance of this class in the specified parameters map and optionally creates a new one if none is found.
69       * This method can be used to obtain an instance of this class which has been stored in a parameters map. It is
70       * compatible with the {@code getParameters()} method.
71       *
72       * @param params the map with parameters (must not be <strong>null</strong>
73       * @param createIfMissing determines the behavior if no instance is found in the map; if <strong>true</strong>, a new instance
74       *        with default settings is created; if <strong>false</strong>, <strong>null</strong> is returned
75       * @return the instance obtained from the map or <strong>null</strong>
76       * @throws NullPointerException if the map is <strong>null</strong>
77       */
78      public static CombinedBuilderParametersImpl fromParameters(final Map<String, ?> params, final boolean createIfMissing) {
79          CombinedBuilderParametersImpl result = (CombinedBuilderParametersImpl) params.get(PARAM_KEY);
80          if (result == null && createIfMissing) {
81              result = new CombinedBuilderParametersImpl();
82          }
83          return result;
84      }
85  
86      /** The definition configuration builder. */
87      private ConfigurationBuilder<? extends HierarchicalConfiguration<?>> definitionBuilder;
88  
89      /** A parameters object for the definition configuration builder. */
90      private BuilderParameters definitionBuilderParameters;
91  
92      /** A map with registered configuration builder providers. */
93      private final Map<String, ConfigurationBuilderProvider> providers;
94  
95      /** A list with default parameters for child configuration sources. */
96      private final Collection<BuilderParameters> childParameters;
97  
98      /** The manager for default handlers. */
99      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 }