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
9
* http://www.apache.org/licenses/LICENSE-2.0
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.
18
package org.apache.commons.configuration;
20
import java.util.ArrayList;
21
import java.util.HashMap;
22
import java.util.Iterator;
23
import java.util.List;
25
import java.util.Properties;
29
* A Map based Configuration.
32
* This implementation of the {@code Configuration} interface is
33
* initialized with a {@code java.util.Map}. The methods of the
34
* {@code Configuration} interface are implemented on top of the content of
35
* this map. The following storage scheme is used:
38
* Property keys are directly mapped to map keys, i.e. the
39
* {@code getProperty()} method directly performs a {@code get()} on
40
* the map. Analogously, {@code setProperty()} or
41
* {@code addProperty()} operations write new data into the map. If a value
42
* is added to an existing property, a {@code java.util.List} is created,
43
* which stores the values of this property.
46
* An important use case of this class is to treat a map as a
47
* {@code Configuration} allowing access to its data through the richer
48
* interface. This can be a bit problematic in some cases because the map may
49
* contain values that need not adhere to the default storage scheme used by
50
* typical configuration implementations, e.g. regarding lists. In such cases
51
* care must be taken when manipulating the data through the
52
* {@code Configuration} interface, e.g. by calling
53
* {@code addProperty()}; results may be different than expected.
56
* An important point is the handling of list delimiters: If delimiter parsing
57
* is enabled (which it is per default), {@code getProperty()} checks
58
* whether the value of a property is a string and whether it contains the list
59
* delimiter character. If this is the case, the value is split at the delimiter
60
* resulting in a list. This split operation typically also involves trimming
61
* the single values as the list delimiter character may be surrounded by
62
* whitespace. Trimming can be disabled with the
63
* {@link #setTrimmingDisabled(boolean)} method. The whole list splitting
64
* behavior can be disabled using the
65
* {@link #setDelimiterParsingDisabled(boolean)} method.
68
* Notice that list splitting is only performed for single string values. If a
69
* property has multiple values, the single values are not split even if they
70
* contain the list delimiter character.
73
* As the underlying {@code Map} is directly used as store of the property
74
* values, the thread-safety of this {@code Configuration} implementation
75
* depends on the map passed to the constructor.
78
* Notes about type safety: For properties with multiple values this implementation
79
* creates lists of type {@code Object} and stores them. If a property is assigned
80
* another value, the value is added to the list. This can cause problems if the
81
* map passed to the constructor already contains lists of other types. This
82
* should be avoided, otherwise it cannot be guaranteed that the application
83
* might throw {@code ClassCastException} exceptions later.
86
* @author Emmanuel Bourg
87
* @version $Id: MapConfiguration.java 1210171 2011-12-04 18:32:07Z oheger $
90
public class MapConfiguration extends AbstractConfiguration implements Cloneable
92
/** The Map decorated by this configuration. */
93
protected Map<String, Object> map;
95
/** A flag whether trimming of property values should be disabled.*/
96
private boolean trimmingDisabled;
99
* Create a Configuration decorator around the specified Map. The map is
100
* used to store the configuration properties, any change will also affect
105
public MapConfiguration(Map<String, Object> map)
111
* Creates a new instance of {@code MapConfiguration} and initializes its
112
* content from the specified {@code Properties} object. The resulting
113
* configuration is not connected to the {@code Properties} object, but all
114
* keys which are strings are copied (keys of other types are ignored).
116
* @param props the {@code Properties} object defining the content of this
118
* @throws NullPointerException if the {@code Properties} object is
122
public MapConfiguration(Properties props)
124
map = convertPropertiesToMap(props);
128
* Return the Map decorated by this configuration.
130
* @return the map this configuration is based onto
132
public Map<String, Object> getMap()
138
* Returns the flag whether trimming of property values is disabled.
140
* @return <b>true</b> if trimming of property values is disabled;
141
* <b>false</b> otherwise
144
public boolean isTrimmingDisabled()
146
return trimmingDisabled;
150
* Sets a flag whether trimming of property values is disabled. This flag is
151
* only evaluated if list splitting is enabled. Refer to the header comment
152
* for more information about list splitting and trimming.
154
* @param trimmingDisabled a flag whether trimming of property values should
158
public void setTrimmingDisabled(boolean trimmingDisabled)
160
this.trimmingDisabled = trimmingDisabled;
163
public Object getProperty(String key)
165
Object value = map.get(key);
166
if ((value instanceof String) && (!isDelimiterParsingDisabled()))
168
List<String> list = PropertyConverter.split((String) value, getListDelimiter(), !isTrimmingDisabled());
169
return list.size() > 1 ? list : list.get(0);
178
protected void addPropertyDirect(String key, Object value)
180
Object previousValue = getProperty(key);
182
if (previousValue == null)
186
else if (previousValue instanceof List)
188
// the value is added to the existing list
189
// Note: This is problematic. See header comment!
190
((List<Object>) previousValue).add(value);
194
// the previous value is replaced by a list containing the previous value and the new value
195
List<Object> list = new ArrayList<Object>();
196
list.add(previousValue);
203
public boolean isEmpty()
205
return map.isEmpty();
208
public boolean containsKey(String key)
210
return map.containsKey(key);
214
protected void clearPropertyDirect(String key)
219
public Iterator<String> getKeys()
221
return map.keySet().iterator();
225
* Returns a copy of this object. The returned configuration will contain
226
* the same properties as the original. Event listeners are not cloned.
232
public Object clone()
236
MapConfiguration copy = (MapConfiguration) super.clone();
237
copy.clearConfigurationListeners();
238
// Safe because ConfigurationUtils returns a map of the same types.
239
@SuppressWarnings("unchecked")
240
Map<String, Object> clonedMap = (Map<String, Object>) ConfigurationUtils.clone(map);
241
copy.map = clonedMap;
244
catch (CloneNotSupportedException cex)
247
throw new ConfigurationRuntimeException(cex);
252
* Helper method for copying all string keys from the given
253
* {@code Properties} object to a newly created map.
255
* @param props the {@code Properties} to be copied
256
* @return a newly created map with all string keys of the properties
258
private static Map<String, Object> convertPropertiesToMap(Properties props)
260
Map<String, Object> map = new HashMap<String, Object>();
261
for (Map.Entry<Object, Object> e : props.entrySet())
263
if (e.getKey() instanceof String)
265
map.put((String) e.getKey(), e.getValue());