/*
* 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
A Map based Configuration.
This implementation of the Configuration
interface is initialized with a java.util.Map
. The methods of the Configuration
interface are implemented on top of the content of this map. The following storage scheme is used:
Property keys are directly mapped to map keys, i.e. the getProperty()
method directly performs a get()
on the map. Analogously, setProperty()
or addProperty()
operations write new data into the map. If a value is added to an existing property, a java.util.List
is created, which stores the values of this property.
An important use case of this class is to treat a map as a Configuration
allowing access to its data through the richer interface. This can be a bit problematic in some cases because the map may contain values that need not adhere to the default storage scheme used by typical configuration implementations, e.g. regarding lists. In such cases care must be taken when manipulating the data through the Configuration
interface, e.g. by calling addProperty()
; results may be different than expected.
The handling of list delimiters is a bit different for this configuration implementation: When a property of type String is queried, it is passed to the current
ListDelimiterHandler
which may generate multiple values. Note that per default a list delimiter handler is set which does not do any list splitting, so this feature is disabled. It can be enabled by setting a properly configured ListDelimiterHandler
implementation, e.g. a
DefaultListDelimiterHandler
object.
Notice that list splitting is only performed for single string values. If a
property has multiple values, the single values are not split even if they
contain the list delimiter character.
As the underlying Map
is directly used as store of the property values, the thread-safety of this Configuration
implementation depends on the map passed to the constructor.
Notes about type safety: For properties with multiple values this implementation creates lists of type Object
and stores them. If a property is assigned another value, the value is added to the list. This can cause problems if the map passed to the constructor already contains lists of other types. This should be avoided, otherwise it cannot be guaranteed that the application might throw ClassCastException
exceptions later.
Since: 1.1
/**
* <p>
* A Map based Configuration.
* </p>
* <p>
* This implementation of the {@code Configuration} interface is
* initialized with a {@code java.util.Map}. The methods of the
* {@code Configuration} interface are implemented on top of the content of
* this map. The following storage scheme is used:
* </p>
* <p>
* Property keys are directly mapped to map keys, i.e. the
* {@code getProperty()} method directly performs a {@code get()} on
* the map. Analogously, {@code setProperty()} or
* {@code addProperty()} operations write new data into the map. If a value
* is added to an existing property, a {@code java.util.List} is created,
* which stores the values of this property.
* </p>
* <p>
* An important use case of this class is to treat a map as a
* {@code Configuration} allowing access to its data through the richer
* interface. This can be a bit problematic in some cases because the map may
* contain values that need not adhere to the default storage scheme used by
* typical configuration implementations, e.g. regarding lists. In such cases
* care must be taken when manipulating the data through the
* {@code Configuration} interface, e.g. by calling
* {@code addProperty()}; results may be different than expected.
* </p>
* <p>
* The handling of list delimiters is a bit different for this configuration
* implementation: When a property of type String is queried, it is passed to
* the current {@link org.apache.commons.configuration2.convert.ListDelimiterHandler
* ListDelimiterHandler} which may generate multiple values.
* Note that per default a list delimiter handler is set which does not do any
* list splitting, so this feature is disabled. It can be enabled by setting
* a properly configured {@code ListDelimiterHandler} implementation, e.g. a
* {@link org.apache.commons.configuration2.convert.DefaultListDelimiterHandler
* DefaultListDelimiterHandler} object.
* </p>
* <p>
* Notice that list splitting is only performed for single string values. If a
* property has multiple values, the single values are not split even if they
* contain the list delimiter character.
* </p>
* <p>
* As the underlying {@code Map} is directly used as store of the property
* values, the thread-safety of this {@code Configuration} implementation
* depends on the map passed to the constructor.
* </p>
* <p>
* Notes about type safety: For properties with multiple values this implementation
* creates lists of type {@code Object} and stores them. If a property is assigned
* another value, the value is added to the list. This can cause problems if the
* map passed to the constructor already contains lists of other types. This
* should be avoided, otherwise it cannot be guaranteed that the application
* might throw {@code ClassCastException} exceptions later.
* </p>
*
* @since 1.1
*/
public class MapConfiguration extends AbstractConfiguration implements Cloneable
{
The Map decorated by this configuration. /** The Map decorated by this configuration. */
protected Map<String, Object> map;
A flag whether trimming of property values should be disabled./** A flag whether trimming of property values should be disabled.*/
private boolean trimmingDisabled;
Create a Configuration decorator around the specified Map. The map is
used to store the configuration properties, any change will also affect
the Map.
Params: - map – the map
/**
* Create a Configuration decorator around the specified Map. The map is
* used to store the configuration properties, any change will also affect
* the Map.
*
* @param map the map
*/
public MapConfiguration(final Map<String, ?> map)
{
this.map = (Map<String, Object>) map;
}
Creates a new instance of MapConfiguration
which uses the specified Properties
object as its data store. All changes of this configuration affect the given Properties
object and vice versa. Note that while Properties
actually implements Map<Object, Object>
, we expect it to contain only string keys. Other key types will lead to ClassCastException
exceptions on certain methods. Params: - props – the
Properties
object defining the content of this configuration
Since: 1.8
/**
* Creates a new instance of {@code MapConfiguration} which uses the
* specified {@code Properties} object as its data store. All changes of
* this configuration affect the given {@code Properties} object and
* vice versa. Note that while {@code Properties} actually
* implements {@code Map<Object, Object>}, we expect it to contain only
* string keys. Other key types will lead to {@code ClassCastException}
* exceptions on certain methods.
*
* @param props the {@code Properties} object defining the content of this
* configuration
* @since 1.8
*/
public MapConfiguration(final Properties props)
{
map = convertPropertiesToMap(props);
}
Return the Map decorated by this configuration.
Returns: the map this configuration is based onto
/**
* Return the Map decorated by this configuration.
*
* @return the map this configuration is based onto
*/
public Map<String, Object> getMap()
{
return map;
}
Returns the flag whether trimming of property values is disabled.
Returns: true if trimming of property values is disabled;
false otherwise Since: 1.7
/**
* Returns the flag whether trimming of property values is disabled.
*
* @return <b>true</b> if trimming of property values is disabled;
* <b>false</b> otherwise
* @since 1.7
*/
public boolean isTrimmingDisabled()
{
return trimmingDisabled;
}
Sets a flag whether trimming of property values is disabled. This flag is
only evaluated if list splitting is enabled. Refer to the header comment
for more information about list splitting and trimming.
Params: - trimmingDisabled – a flag whether trimming of property values should
be disabled
Since: 1.7
/**
* Sets a flag whether trimming of property values is disabled. This flag is
* only evaluated if list splitting is enabled. Refer to the header comment
* for more information about list splitting and trimming.
*
* @param trimmingDisabled a flag whether trimming of property values should
* be disabled
* @since 1.7
*/
public void setTrimmingDisabled(final boolean trimmingDisabled)
{
this.trimmingDisabled = trimmingDisabled;
}
@Override
protected Object getPropertyInternal(final String key)
{
final Object value = map.get(key);
if (value instanceof String)
{
final Collection<String> list = getListDelimiterHandler().split((String) value, !isTrimmingDisabled());
return list.size() > 1 ? list : list.iterator().next();
}
return value;
}
@Override
protected void addPropertyDirect(final String key, final Object value)
{
final Object previousValue = getProperty(key);
if (previousValue == null)
{
map.put(key, value);
}
else if (previousValue instanceof List)
{
// the value is added to the existing list
// Note: This is problematic. See header comment!
((List<Object>) previousValue).add(value);
}
else
{
// the previous value is replaced by a list containing the previous value and the new value
final List<Object> list = new ArrayList<>();
list.add(previousValue);
list.add(value);
map.put(key, list);
}
}
@Override
protected boolean isEmptyInternal()
{
return map.isEmpty();
}
@Override
protected boolean containsKeyInternal(final String key)
{
return map.containsKey(key);
}
@Override
protected void clearPropertyDirect(final String key)
{
map.remove(key);
}
@Override
protected Iterator<String> getKeysInternal()
{
return map.keySet().iterator();
}
@Override
protected int sizeInternal()
{
return map.size();
}
Returns a copy of this object. The returned configuration will contain
the same properties as the original. Event listeners are not cloned.
Returns: the copy Since: 1.3
/**
* Returns a copy of this object. The returned configuration will contain
* the same properties as the original. Event listeners are not cloned.
*
* @return the copy
* @since 1.3
*/
@Override
public Object clone()
{
try
{
final MapConfiguration copy = (MapConfiguration) super.clone();
// Safe because ConfigurationUtils returns a map of the same types.
@SuppressWarnings("unchecked")
final
Map<String, Object> clonedMap = (Map<String, Object>) ConfigurationUtils.clone(map);
copy.map = clonedMap;
copy.cloneInterpolator(this);
return copy;
}
catch (final CloneNotSupportedException cex)
{
// cannot happen
throw new ConfigurationRuntimeException(cex);
}
}
Helper method for converting the type of the Properties
object to a supported map type. As stated by the comment of the constructor, we expect the Properties
object to contain only String key; therefore, it is safe to do this cast. Params: - props – the
Properties
to be copied
Returns: a newly created map with all string keys of the properties
/**
* Helper method for converting the type of the {@code Properties} object
* to a supported map type. As stated by the comment of the constructor,
* we expect the {@code Properties} object to contain only String key;
* therefore, it is safe to do this cast.
*
* @param props the {@code Properties} to be copied
* @return a newly created map with all string keys of the properties
*/
@SuppressWarnings("unchecked")
private static Map<String, Object> convertPropertiesToMap(final Properties props)
{
@SuppressWarnings("rawtypes")
final
Map map = props;
return map;
}
Converts this object to a String suitable for debugging and logging.
Since: 2.3
/**
* Converts this object to a String suitable for debugging and logging.
*
* @since 2.3
*/
@Override
public String toString()
{
return getClass().getSimpleName() + " [map=" + map + ", trimmingDisabled=" + trimmingDisabled + "]";
}
}