| 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 | 3089 | 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 | 3152 | Object value = map.get(key); |
| 166 | 3152 | if ((value instanceof String) && (!isDelimiterParsingDisabled())) |
| 167 | { | |
| 168 | 3089 | List<String> list = PropertyConverter.split((String) value, getListDelimiter(), !isTrimmingDisabled()); |
| 169 | 3089 | 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 | 60 | 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 | } |