Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
MapConfiguration |
|
| 1.8461538461538463;1,846 |
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 | * http://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 | ||
18 | package org.apache.commons.configuration; | |
19 | ||
20 | import java.util.ArrayList; | |
21 | import java.util.HashMap; | |
22 | import java.util.Iterator; | |
23 | import java.util.List; | |
24 | import java.util.Map; | |
25 | import java.util.Properties; | |
26 | ||
27 | /** | |
28 | * <p> | |
29 | * A Map based Configuration. | |
30 | * </p> | |
31 | * <p> | |
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: | |
36 | * </p> | |
37 | * <p> | |
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. | |
44 | * </p> | |
45 | * <p> | |
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. | |
54 | * </p> | |
55 | * <p> | |
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. | |
66 | * </p> | |
67 | * <p> | |
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. | |
71 | * </p> | |
72 | * <p> | |
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. | |
76 | * </p> | |
77 | * <p> | |
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. | |
84 | * </p> | |
85 | * | |
86 | * @author Emmanuel Bourg | |
87 | * @version $Id: MapConfiguration.java 1210171 2011-12-04 18:32:07Z oheger $ | |
88 | * @since 1.1 | |
89 | */ | |
90 | public class MapConfiguration extends AbstractConfiguration implements Cloneable | |
91 | { | |
92 | /** The Map decorated by this configuration. */ | |
93 | protected Map<String, Object> map; | |
94 | ||
95 | /** A flag whether trimming of property values should be disabled.*/ | |
96 | private boolean trimmingDisabled; | |
97 | ||
98 | /** | |
99 | * Create a Configuration decorator around the specified Map. The map is | |
100 | * used to store the configuration properties, any change will also affect | |
101 | * the Map. | |
102 | * | |
103 | * @param map the map | |
104 | */ | |
105 | public MapConfiguration(Map<String, Object> map) | |
106 | 50 | { |
107 | 50 | this.map = map; |
108 | 50 | } |
109 | ||
110 | /** | |
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). | |
115 | * | |
116 | * @param props the {@code Properties} object defining the content of this | |
117 | * configuration | |
118 | * @throws NullPointerException if the {@code Properties} object is | |
119 | * <b>null</b> | |
120 | * @since 1.8 | |
121 | */ | |
122 | public MapConfiguration(Properties props) | |
123 | 38 | { |
124 | 38 | map = convertPropertiesToMap(props); |
125 | 38 | } |
126 | ||
127 | /** | |
128 | * Return the Map decorated by this configuration. | |
129 | * | |
130 | * @return the map this configuration is based onto | |
131 | */ | |
132 | public Map<String, Object> getMap() | |
133 | { | |
134 | 6 | return map; |
135 | } | |
136 | ||
137 | /** | |
138 | * Returns the flag whether trimming of property values is disabled. | |
139 | * | |
140 | * @return <b>true</b> if trimming of property values is disabled; | |
141 | * <b>false</b> otherwise | |
142 | * @since 1.7 | |
143 | */ | |
144 | public boolean isTrimmingDisabled() | |
145 | { | |
146 | 3081 | return trimmingDisabled; |
147 | } | |
148 | ||
149 | /** | |
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. | |
153 | * | |
154 | * @param trimmingDisabled a flag whether trimming of property values should | |
155 | * be disabled | |
156 | * @since 1.7 | |
157 | */ | |
158 | public void setTrimmingDisabled(boolean trimmingDisabled) | |
159 | { | |
160 | 1 | this.trimmingDisabled = trimmingDisabled; |
161 | 1 | } |
162 | ||
163 | public Object getProperty(String key) | |
164 | { | |
165 | 3144 | Object value = map.get(key); |
166 | 3144 | if ((value instanceof String) && (!isDelimiterParsingDisabled())) |
167 | { | |
168 | 3081 | List<String> list = PropertyConverter.split((String) value, getListDelimiter(), !isTrimmingDisabled()); |
169 | 3081 | return list.size() > 1 ? list : list.get(0); |
170 | } | |
171 | else | |
172 | { | |
173 | 63 | return value; |
174 | } | |
175 | } | |
176 | ||
177 | @Override | |
178 | protected void addPropertyDirect(String key, Object value) | |
179 | { | |
180 | 30 | Object previousValue = getProperty(key); |
181 | ||
182 | 30 | if (previousValue == null) |
183 | { | |
184 | 26 | map.put(key, value); |
185 | } | |
186 | 4 | else if (previousValue instanceof List) |
187 | { | |
188 | // the value is added to the existing list | |
189 | // Note: This is problematic. See header comment! | |
190 | 2 | ((List<Object>) previousValue).add(value); |
191 | } | |
192 | else | |
193 | { | |
194 | // the previous value is replaced by a list containing the previous value and the new value | |
195 | 2 | List<Object> list = new ArrayList<Object>(); |
196 | 2 | list.add(previousValue); |
197 | 2 | list.add(value); |
198 | ||
199 | 2 | map.put(key, list); |
200 | } | |
201 | 30 | } |
202 | ||
203 | public boolean isEmpty() | |
204 | { | |
205 | 4 | return map.isEmpty(); |
206 | } | |
207 | ||
208 | public boolean containsKey(String key) | |
209 | { | |
210 | 59 | return map.containsKey(key); |
211 | } | |
212 | ||
213 | @Override | |
214 | protected void clearPropertyDirect(String key) | |
215 | { | |
216 | 10 | map.remove(key); |
217 | 10 | } |
218 | ||
219 | public Iterator<String> getKeys() | |
220 | { | |
221 | 55 | return map.keySet().iterator(); |
222 | } | |
223 | ||
224 | /** | |
225 | * Returns a copy of this object. The returned configuration will contain | |
226 | * the same properties as the original. Event listeners are not cloned. | |
227 | * | |
228 | * @return the copy | |
229 | * @since 1.3 | |
230 | */ | |
231 | @Override | |
232 | public Object clone() | |
233 | { | |
234 | try | |
235 | { | |
236 | 2 | MapConfiguration copy = (MapConfiguration) super.clone(); |
237 | 2 | copy.clearConfigurationListeners(); |
238 | // Safe because ConfigurationUtils returns a map of the same types. | |
239 | @SuppressWarnings("unchecked") | |
240 | 2 | Map<String, Object> clonedMap = (Map<String, Object>) ConfigurationUtils.clone(map); |
241 | 2 | copy.map = clonedMap; |
242 | 2 | return copy; |
243 | } | |
244 | 0 | catch (CloneNotSupportedException cex) |
245 | { | |
246 | // cannot happen | |
247 | 0 | throw new ConfigurationRuntimeException(cex); |
248 | } | |
249 | } | |
250 | ||
251 | /** | |
252 | * Helper method for copying all string keys from the given | |
253 | * {@code Properties} object to a newly created map. | |
254 | * | |
255 | * @param props the {@code Properties} to be copied | |
256 | * @return a newly created map with all string keys of the properties | |
257 | */ | |
258 | private static Map<String, Object> convertPropertiesToMap(Properties props) | |
259 | { | |
260 | 38 | Map<String, Object> map = new HashMap<String, Object>(); |
261 | 38 | for (Map.Entry<Object, Object> e : props.entrySet()) |
262 | { | |
263 | 2025 | if (e.getKey() instanceof String) |
264 | { | |
265 | 2025 | map.put((String) e.getKey(), e.getValue()); |
266 | } | |
267 | } | |
268 | 38 | return map; |
269 | } | |
270 | } |