| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.configuration2.builder; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import org.apache.commons.configuration2.ConfigurationDecoder; |
| import org.apache.commons.configuration2.io.ConfigurationLogger; |
| import org.apache.commons.configuration2.beanutils.BeanHelper; |
| import org.apache.commons.configuration2.convert.ConversionHandler; |
| import org.apache.commons.configuration2.convert.ListDelimiterHandler; |
| import org.apache.commons.configuration2.interpol.ConfigurationInterpolator; |
| import org.apache.commons.configuration2.interpol.InterpolatorSpecification; |
| import org.apache.commons.configuration2.interpol.Lookup; |
| import org.apache.commons.configuration2.sync.Synchronizer; |
| |
| /** |
| * <p> |
| * An implementation of {@code BuilderParameters} which handles the parameters |
| * of a {@link ConfigurationBuilder} common to all concrete |
| * {@code Configuration} implementations. |
| * </p> |
| * <p> |
| * This class provides methods for setting standard properties supported by the |
| * {@code AbstractConfiguration} base class. A fluent interface can be used to |
| * set property values. |
| * </p> |
| * <p> |
| * This class is not thread-safe. It is intended that an instance is constructed |
| * and initialized by a single thread during configuration of a |
| * {@code ConfigurationBuilder}. |
| * </p> |
| * |
| * @version $Id$ |
| * @since 2.0 |
| */ |
| public class BasicBuilderParameters implements Cloneable, BuilderParameters, |
| BasicBuilderProperties<BasicBuilderParameters> |
| { |
| /** The key of the <em>throwExceptionOnMissing</em> property. */ |
| private static final String PROP_THROW_EXCEPTION_ON_MISSING = |
| "throwExceptionOnMissing"; |
| |
| /** The key of the <em>listDelimiterHandler</em> property. */ |
| private static final String PROP_LIST_DELIMITER_HANDLER = "listDelimiterHandler"; |
| |
| /** The key of the <em>logger</em> property. */ |
| private static final String PROP_LOGGER = "logger"; |
| |
| /** The key for the <em>interpolator</em> property. */ |
| private static final String PROP_INTERPOLATOR = "interpolator"; |
| |
| /** The key for the <em>prefixLookups</em> property. */ |
| private static final String PROP_PREFIX_LOOKUPS = "prefixLookups"; |
| |
| /** The key for the <em>defaultLookups</em> property. */ |
| private static final String PROP_DEFAULT_LOOKUPS = "defaultLookups"; |
| |
| /** The key for the <em>parentInterpolator</em> property. */ |
| private static final String PROP_PARENT_INTERPOLATOR = "parentInterpolator"; |
| |
| /** The key for the <em>synchronizer</em> property. */ |
| private static final String PROP_SYNCHRONIZER = "synchronizer"; |
| |
| /** The key for the <em>conversionHandler</em> property. */ |
| private static final String PROP_CONVERSION_HANDLER = "conversionHandler"; |
| |
| /** The key for the <em>configurationDecoder</em> property. */ |
| private static final String PROP_CONFIGURATION_DECODER = "configurationDecoder"; |
| |
| /** The key for the {@code BeanHelper}. */ |
| private static final String PROP_BEAN_HELPER = RESERVED_PARAMETER_PREFIX |
| + "BeanHelper"; |
| |
| /** The map for storing the current property values. */ |
| private Map<String, Object> properties; |
| |
| /** |
| * Creates a new instance of {@code BasicBuilderParameters}. |
| */ |
| public BasicBuilderParameters() |
| { |
| properties = new HashMap<String, Object>(); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation returns a copy of the internal |
| * parameters map with the values set so far. Collection structures |
| * (e.g. for lookup objects) are stored as defensive copies, so the |
| * original data cannot be modified. |
| */ |
| @Override |
| public Map<String, Object> getParameters() |
| { |
| HashMap<String, Object> result = |
| new HashMap<String, Object>(properties); |
| if (result.containsKey(PROP_INTERPOLATOR)) |
| { |
| // A custom ConfigurationInterpolator overrides lookups |
| result.remove(PROP_PREFIX_LOOKUPS); |
| result.remove(PROP_DEFAULT_LOOKUPS); |
| result.remove(PROP_PARENT_INTERPOLATOR); |
| } |
| |
| createDefensiveCopies(result); |
| return result; |
| } |
| |
| /** |
| * Sets the <em>logger</em> property. With this property a concrete |
| * {@code Log} object can be set for the configuration. Thus logging |
| * behavior can be controlled. |
| * |
| * @param log the {@code Log} for the configuration produced by this builder |
| * @return a reference to this object for method chaining |
| */ |
| @Override |
| public BasicBuilderParameters setLogger(ConfigurationLogger log) |
| { |
| return setProperty(PROP_LOGGER, log); |
| } |
| |
| /** |
| * Sets the value of the <em>throwExceptionOnMissing</em> property. This |
| * property controls the configuration's behavior if missing properties are |
| * queried: a value of <b>true</b> causes the configuration to throw an |
| * exception, for a value of <b>false</b> it will return <b>null</b> values. |
| * (Note: Methods returning a primitive data type will always throw an |
| * exception if the property is not defined.) |
| * |
| * @param b the value of the property |
| * @return a reference to this object for method chaining |
| */ |
| @Override |
| public BasicBuilderParameters setThrowExceptionOnMissing(boolean b) |
| { |
| return setProperty(PROP_THROW_EXCEPTION_ON_MISSING, Boolean.valueOf(b)); |
| } |
| |
| /** |
| * Sets the value of the <em>listDelimiterHandler</em> property. This |
| * property defines the object responsible for dealing with list delimiter |
| * and escaping characters. Note: |
| * {@link org.apache.commons.configuration2.AbstractConfiguration AbstractConfiguration} |
| * does not allow setting this property to <b>null</b>. If the default |
| * {@code ListDelimiterHandler} is to be used, do not call this method. |
| * |
| * @param handler the {@code ListDelimiterHandler} |
| * @return a reference to this object for method chaining |
| */ |
| @Override |
| public BasicBuilderParameters setListDelimiterHandler( |
| ListDelimiterHandler handler) |
| { |
| return setProperty(PROP_LIST_DELIMITER_HANDLER, handler); |
| } |
| |
| /** |
| * {@inheritDoc} The passed in {@code ConfigurationInterpolator} is set |
| * without modifications. |
| */ |
| @Override |
| public BasicBuilderParameters setInterpolator(ConfigurationInterpolator ci) |
| { |
| return setProperty(PROP_INTERPOLATOR, ci); |
| } |
| |
| /** |
| * {@inheritDoc} A defensive copy of the passed in map is created. A |
| * <b>null</b> argument causes all prefix lookups to be removed from the |
| * internal parameters map. |
| */ |
| @Override |
| public BasicBuilderParameters setPrefixLookups( |
| Map<String, ? extends Lookup> lookups) |
| { |
| if (lookups == null) |
| { |
| properties.remove(PROP_PREFIX_LOOKUPS); |
| return this; |
| } |
| else |
| { |
| return setProperty(PROP_PREFIX_LOOKUPS, |
| new HashMap<String, Lookup>(lookups)); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} A defensive copy of the passed in collection is created. A |
| * <b>null</b> argument causes all default lookups to be removed from the |
| * internal parameters map. |
| */ |
| @Override |
| public BasicBuilderParameters setDefaultLookups( |
| Collection<? extends Lookup> lookups) |
| { |
| if (lookups == null) |
| { |
| properties.remove(PROP_DEFAULT_LOOKUPS); |
| return this; |
| } |
| else |
| { |
| return setProperty(PROP_DEFAULT_LOOKUPS, new ArrayList<Lookup>( |
| lookups)); |
| } |
| } |
| |
| /** |
| * {@inheritDoc} This implementation stores the passed in |
| * {@code ConfigurationInterpolator} object in the internal parameters map. |
| */ |
| @Override |
| public BasicBuilderParameters setParentInterpolator( |
| ConfigurationInterpolator parent) |
| { |
| return setProperty(PROP_PARENT_INTERPOLATOR, parent); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation stores the passed in |
| * {@code Synchronizer} object in the internal parameters map. |
| */ |
| @Override |
| public BasicBuilderParameters setSynchronizer(Synchronizer sync) |
| { |
| return setProperty(PROP_SYNCHRONIZER, sync); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation stores the passed in |
| * {@code ConversionHandler} object in the internal parameters map. |
| */ |
| @Override |
| public BasicBuilderParameters setConversionHandler(ConversionHandler handler) |
| { |
| return setProperty(PROP_CONVERSION_HANDLER, handler); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation stores the passed in {@code BeanHelper} |
| * object in the internal parameters map, but uses a reserved key, so that |
| * it is not used for the initialization of properties of the managed |
| * configuration object. The {@code fetchBeanHelper()} method can be used to |
| * obtain the {@code BeanHelper} instance from a parameters map. |
| */ |
| @Override |
| public BasicBuilderParameters setBeanHelper(BeanHelper beanHelper) |
| { |
| return setProperty(PROP_BEAN_HELPER, beanHelper); |
| } |
| |
| /** |
| * {@inheritDoc} This implementation stores the passed in |
| * {@code ConfigurationDecoder} object in the internal parameters map. |
| */ |
| @Override |
| public BasicBuilderParameters setConfigurationDecoder( |
| ConfigurationDecoder decoder) |
| { |
| return setProperty(PROP_CONFIGURATION_DECODER, decoder); |
| } |
| |
| /** |
| * Merges this object with the given parameters object. This method adds all |
| * property values defined by the passed in parameters object to the |
| * internal storage which are not already in. So properties already defined |
| * in this object take precedence. Property names starting with the reserved |
| * parameter prefix are ignored. |
| * |
| * @param p the object whose properties should be merged (must not be |
| * <b>null</b>) |
| * @throws IllegalArgumentException if the passed in object is <b>null</b> |
| */ |
| public void merge(BuilderParameters p) |
| { |
| if (p == null) |
| { |
| throw new IllegalArgumentException( |
| "Parameters to merge must not be null!"); |
| } |
| |
| for (Map.Entry<String, Object> e : p.getParameters().entrySet()) |
| { |
| if (!properties.containsKey(e.getKey()) |
| && !e.getKey().startsWith(RESERVED_PARAMETER_PREFIX)) |
| { |
| storeProperty(e.getKey(), e.getValue()); |
| } |
| } |
| } |
| |
| /** |
| * Inherits properties from the specified map. This can be used for instance |
| * to reuse parameters from one builder in another builder - also in |
| * parent-child relations in which a parent builder creates child builders. |
| * The purpose of this method is to let a concrete implementation decide |
| * which properties can be inherited. Because parameters are basically |
| * organized as a map it would be possible to simply copy over all |
| * properties from the source object. However, this is not appropriate in |
| * all cases. For instance, some properties - like a |
| * {@code ConfigurationInterpolator} - are tightly connected to a |
| * configuration and cannot be reused in a different context. For other |
| * properties, e.g. a file name, it does not make sense to copy it. |
| * Therefore, an implementation has to be explicit in the properties it |
| * wants to take over. |
| * |
| * @param source the source properties to inherit from |
| * @throws IllegalArgumentException if the source map is <b>null</b> |
| */ |
| public void inheritFrom(Map<String, ?> source) |
| { |
| if (source == null) |
| { |
| throw new IllegalArgumentException( |
| "Source properties must not be null!"); |
| } |
| copyPropertiesFrom(source, PROP_BEAN_HELPER, PROP_CONFIGURATION_DECODER, |
| PROP_CONVERSION_HANDLER, PROP_LIST_DELIMITER_HANDLER, |
| PROP_LOGGER, PROP_SYNCHRONIZER, |
| PROP_THROW_EXCEPTION_ON_MISSING); |
| } |
| |
| /** |
| * Obtains a specification for a {@link ConfigurationInterpolator} from the |
| * specified map with parameters. All properties related to interpolation |
| * are evaluated and added to the specification object. |
| * |
| * @param params the map with parameters (must not be <b>null</b>) |
| * @return an {@code InterpolatorSpecification} object constructed with data |
| * from the map |
| * @throws IllegalArgumentException if the map is <b>null</b> or contains |
| * invalid data |
| */ |
| public static InterpolatorSpecification fetchInterpolatorSpecification( |
| Map<String, Object> params) |
| { |
| checkParameters(params); |
| return new InterpolatorSpecification.Builder() |
| .withInterpolator( |
| fetchParameter(params, PROP_INTERPOLATOR, |
| ConfigurationInterpolator.class)) |
| .withParentInterpolator( |
| fetchParameter(params, PROP_PARENT_INTERPOLATOR, |
| ConfigurationInterpolator.class)) |
| .withPrefixLookups(fetchAndCheckPrefixLookups(params)) |
| .withDefaultLookups(fetchAndCheckDefaultLookups(params)) |
| .create(); |
| } |
| |
| /** |
| * Obtains the {@code BeanHelper} object from the specified map with |
| * parameters. This method can be used to obtain an instance from a |
| * parameters map that has been set via the {@code setBeanHelper()} method. |
| * If no such instance is found, result is <b>null</b>. |
| * |
| * @param params the map with parameters (must not be <b>null</b>) |
| * @return the {@code BeanHelper} stored in this map or <b>null</b> |
| * @throws IllegalArgumentException if the map is <b>null</b> |
| */ |
| public static BeanHelper fetchBeanHelper(Map<String, Object> params) |
| { |
| checkParameters(params); |
| return (BeanHelper) params.get(PROP_BEAN_HELPER); |
| } |
| |
| /** |
| * Clones this object. This is useful because multiple builder instances may |
| * use a similar set of parameters. However, single instances of parameter |
| * objects must not assigned to multiple builders. Therefore, cloning a |
| * parameters object provides a solution for this use case. This method |
| * creates a new parameters object with the same content as this one. The |
| * internal map storing the parameter values is cloned, too, also collection |
| * structures contained in this map. However, no a full deep clone operation |
| * is performed. Objects like a {@code ConfigurationInterpolator} or |
| * {@code Lookup}s are shared between this and the newly created instance. |
| * |
| * @return a clone of this object |
| */ |
| @Override |
| public BasicBuilderParameters clone() |
| { |
| try |
| { |
| BasicBuilderParameters copy = |
| (BasicBuilderParameters) super.clone(); |
| copy.properties = getParameters(); |
| return copy; |
| } |
| catch (CloneNotSupportedException cnex) |
| { |
| // should not happen |
| throw new AssertionError(cnex); |
| } |
| } |
| |
| /** |
| * Sets a property for this parameters object. Properties are stored in an |
| * internal map. With this method a new entry can be added to this map. If |
| * the value is <b>null</b>, the key is removed from the internal map. This |
| * method can be used by sub classes which also store properties in a map. |
| * |
| * @param key the key of the property |
| * @param value the value of the property |
| */ |
| protected void storeProperty(String key, Object value) |
| { |
| if (value == null) |
| { |
| properties.remove(key); |
| } |
| else |
| { |
| properties.put(key, value); |
| } |
| } |
| |
| /** |
| * Obtains the value of the specified property from the internal map. This |
| * method can be used by derived classes if a specific property is to be |
| * accessed. If the given key is not found, result is <b>null</b>. |
| * |
| * @param key the key of the property in question |
| * @return the value of the property with this key or <b>null</b> |
| */ |
| protected Object fetchProperty(String key) |
| { |
| return properties.get(key); |
| } |
| |
| /** |
| * Copies a number of properties from the given map into this object. |
| * Properties are only copied if they are defined in the source map. |
| * |
| * @param source the source map |
| * @param keys the keys to be copied |
| */ |
| protected void copyPropertiesFrom(Map<String, ?> source, String... keys) |
| { |
| for (String key : keys) |
| { |
| Object value = source.get(key); |
| if (value != null) |
| { |
| storeProperty(key, value); |
| } |
| } |
| } |
| |
| /** |
| * Helper method for setting a property value. |
| * |
| * @param key the key of the property |
| * @param value the value of the property |
| * @return a reference to this object |
| */ |
| private BasicBuilderParameters setProperty(String key, Object value) |
| { |
| storeProperty(key, value); |
| return this; |
| } |
| |
| /** |
| * Creates defensive copies for collection structures when constructing the |
| * map with parameters. It should not be possible to modify this object's |
| * internal state when having access to the parameters map. |
| * |
| * @param params the map with parameters to be passed to the caller |
| */ |
| private static void createDefensiveCopies(HashMap<String, Object> params) |
| { |
| Map<String, ? extends Lookup> prefixLookups = |
| fetchPrefixLookups(params); |
| if (prefixLookups != null) |
| { |
| params.put(PROP_PREFIX_LOOKUPS, new HashMap<String, Lookup>( |
| prefixLookups)); |
| } |
| Collection<? extends Lookup> defLookups = fetchDefaultLookups(params); |
| if (defLookups != null) |
| { |
| params.put(PROP_DEFAULT_LOOKUPS, new ArrayList<Lookup>(defLookups)); |
| } |
| } |
| |
| /** |
| * Obtains the map with prefix lookups from the parameters map. |
| * |
| * @param params the map with parameters |
| * @return the map with prefix lookups (may be <b>null</b>) |
| */ |
| private static Map<String, ? extends Lookup> fetchPrefixLookups( |
| Map<String, Object> params) |
| { |
| // This is safe to cast because we either have full control over the map |
| // and thus know the types of the contained values or have checked |
| // the content before |
| @SuppressWarnings("unchecked") |
| Map<String, ? extends Lookup> prefixLookups = |
| (Map<String, ? extends Lookup>) params.get(PROP_PREFIX_LOOKUPS); |
| return prefixLookups; |
| } |
| |
| /** |
| * Tests whether the passed in map with parameters contains a map with |
| * prefix lookups. This method is used if the parameters map is from an |
| * insecure source and we cannot be sure that it contains valid data. |
| * Therefore, we have to map that the key for the prefix lookups actually |
| * points to a map containing keys and values of expected data types. |
| * |
| * @param params the parameters map |
| * @return the obtained map with prefix lookups |
| * @throws IllegalArgumentException if the map contains invalid data |
| */ |
| private static Map<String, ? extends Lookup> fetchAndCheckPrefixLookups( |
| Map<String, Object> params) |
| { |
| Map<?, ?> prefixes = |
| fetchParameter(params, PROP_PREFIX_LOOKUPS, Map.class); |
| if (prefixes == null) |
| { |
| return null; |
| } |
| |
| for (Map.Entry<?, ?> e : prefixes.entrySet()) |
| { |
| if (!(e.getKey() instanceof String) |
| || !(e.getValue() instanceof Lookup)) |
| { |
| throw new IllegalArgumentException( |
| "Map with prefix lookups contains invalid data: " |
| + prefixes); |
| } |
| } |
| return fetchPrefixLookups(params); |
| } |
| |
| /** |
| * Obtains the collection with default lookups from the parameters map. |
| * |
| * @param params the map with parameters |
| * @return the collection with default lookups (may be <b>null</b>) |
| */ |
| private static Collection<? extends Lookup> fetchDefaultLookups( |
| Map<String, Object> params) |
| { |
| // This is safe to cast because we either have full control over the map |
| // and thus know the types of the contained values or have checked |
| // the content before |
| @SuppressWarnings("unchecked") |
| Collection<? extends Lookup> defLookups = |
| (Collection<? extends Lookup>) params.get(PROP_DEFAULT_LOOKUPS); |
| return defLookups; |
| } |
| |
| /** |
| * Tests whether the passed in map with parameters contains a valid |
| * collection with default lookups. This method works like |
| * {@link #fetchAndCheckPrefixLookups(Map)}, but tests the default lookups |
| * collection. |
| * |
| * @param params the map with parameters |
| * @return the collection with default lookups (may be <b>null</b>) |
| * @throws IllegalArgumentException if invalid data is found |
| */ |
| private static Collection<? extends Lookup> fetchAndCheckDefaultLookups( |
| Map<String, Object> params) |
| { |
| Collection<?> col = |
| fetchParameter(params, PROP_DEFAULT_LOOKUPS, Collection.class); |
| if (col == null) |
| { |
| return null; |
| } |
| |
| for (Object o : col) |
| { |
| if (!(o instanceof Lookup)) |
| { |
| throw new IllegalArgumentException( |
| "Collection with default lookups contains invalid data: " |
| + col); |
| } |
| } |
| return fetchDefaultLookups(params); |
| } |
| |
| /** |
| * Obtains a parameter from a map and performs a type check. |
| * |
| * @param params the map with parameters |
| * @param key the key of the parameter |
| * @param expClass the expected class of the parameter value |
| * @param <T> the parameter type |
| * @return the value of the parameter in the correct data type |
| * @throws IllegalArgumentException if the parameter is not of the expected |
| * type |
| */ |
| private static <T> T fetchParameter(Map<String, Object> params, String key, |
| Class<T> expClass) |
| { |
| Object value = params.get(key); |
| if (value == null) |
| { |
| return null; |
| } |
| if (!expClass.isInstance(value)) |
| { |
| throw new IllegalArgumentException(String.format( |
| "Parameter %s is not of type %s!", key, |
| expClass.getSimpleName())); |
| } |
| return expClass.cast(value); |
| } |
| |
| /** |
| * Checks whether a map with parameters is present. Throws an exception if |
| * not. |
| * |
| * @param params the map with parameters to check |
| * @throws IllegalArgumentException if the map is <b>null</b> |
| */ |
| private static void checkParameters(Map<String, Object> params) |
| { |
| if (params == null) |
| { |
| throw new IllegalArgumentException( |
| "Parameters map must not be null!"); |
| } |
| } |
| } |