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 }