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.configuration2; 19 20 import java.util.ArrayList; 21 import java.util.Collection; 22 import java.util.Iterator; 23 import java.util.LinkedHashMap; 24 import java.util.List; 25 import java.util.Map; 26 27 import org.apache.commons.configuration2.ex.ConfigurationRuntimeException; 28 29 /** 30 * Basic configuration class. Stores the configuration data but does not provide any load or save functions. If you want 31 * to load your Configuration from a file use PropertiesConfiguration or XmlConfiguration. 32 * 33 * This class extends normal Java properties by adding the possibility to use the same key many times concatenating the 34 * value strings instead of overwriting them. 35 */ 36 public class BaseConfiguration extends AbstractConfiguration implements Cloneable { 37 /** Stores the configuration key-value pairs */ 38 private Map<String, Object> store = new LinkedHashMap<>(); 39 40 /** 41 * Adds a key/value pair to the map. This routine does no magic morphing. It ensures the keylist is maintained 42 * 43 * @param key key to use for mapping 44 * @param value object to store 45 */ 46 @Override 47 protected void addPropertyDirect(final String key, final Object value) { 48 final Object previousValue = getPropertyInternal(key); 49 50 if (previousValue == null) { 51 store.put(key, value); 52 } else if (previousValue instanceof List) { 53 // safe to case because we have created the lists ourselves 54 @SuppressWarnings("unchecked") 55 final List<Object> valueList = (List<Object>) previousValue; 56 // the value is added to the existing list 57 valueList.add(value); 58 } else { 59 // the previous value is replaced by a list containing the previous value and the new value 60 final List<Object> list = new ArrayList<>(); 61 list.add(previousValue); 62 list.add(value); 63 64 store.put(key, list); 65 } 66 } 67 68 @Override 69 protected void clearInternal() { 70 store.clear(); 71 } 72 73 /** 74 * Clear a property in the configuration. 75 * 76 * @param key the key to remove along with corresponding value. 77 */ 78 @Override 79 protected void clearPropertyDirect(final String key) { 80 store.remove(key); 81 } 82 83 /** 84 * Creates a copy of this object. This implementation will create a deep clone, i.e. the map that stores the properties 85 * is cloned, too. So changes performed at the copy won't affect the original and vice versa. 86 * 87 * @return the copy 88 * @since 1.3 89 */ 90 @Override 91 public Object clone() { 92 try { 93 final BaseConfiguration copy = (BaseConfiguration) super.clone(); 94 cloneStore(copy); 95 copy.cloneInterpolator(this); 96 97 return copy; 98 } catch (final CloneNotSupportedException cex) { 99 // should not happen 100 throw new ConfigurationRuntimeException(cex); 101 } 102 } 103 104 /** 105 * Clones the internal map with the data of this configuration. 106 * 107 * @param copy the copy created by the {@code clone()} method 108 * @throws CloneNotSupportedException if the map cannot be cloned 109 */ 110 private void cloneStore(final BaseConfiguration copy) throws CloneNotSupportedException { 111 // This is safe because the type of the map is known 112 @SuppressWarnings("unchecked") 113 final Map<String, Object> clonedStore = (Map<String, Object>) ConfigurationUtils.clone(store); 114 copy.store = clonedStore; 115 116 // Handle collections in the map; they have to be cloned, too 117 store.forEach((k, v) -> { 118 if (v instanceof Collection) { 119 // This is safe because the collections were created by ourselves 120 @SuppressWarnings("unchecked") 121 final Collection<String> strList = (Collection<String>) v; 122 copy.store.put(k, new ArrayList<>(strList)); 123 } 124 }); 125 } 126 127 /** 128 * check if the configuration contains the key 129 * 130 * @param key the configuration key 131 * @return {@code true} if Configuration contain given key, {@code false} otherwise. 132 */ 133 @Override 134 protected boolean containsKeyInternal(final String key) { 135 return store.containsKey(key); 136 } 137 138 /** 139 * Tests whether this configuration contains one or more matches to this value. This operation stops at first 140 * match but may be more expensive than the containsKey method. 141 * @since 2.11.0 142 */ 143 @Override 144 protected boolean containsValueInternal(final Object value) { 145 return store.containsValue(value); 146 } 147 148 /** 149 * Gets the list of the keys contained in the configuration repository. 150 * 151 * @return An Iterator. 152 */ 153 @Override 154 protected Iterator<String> getKeysInternal() { 155 return store.keySet().iterator(); 156 } 157 158 /** 159 * Reads property from underlying map. 160 * 161 * @param key key to use for mapping 162 * @return object associated with the given configuration key. 163 */ 164 @Override 165 protected Object getPropertyInternal(final String key) { 166 return store.get(key); 167 } 168 169 /** 170 * Check if the configuration is empty 171 * 172 * @return {@code true} if Configuration is empty, {@code false} otherwise. 173 */ 174 @Override 175 protected boolean isEmptyInternal() { 176 return store.isEmpty(); 177 } 178 179 /** 180 * {@inheritDoc} This implementation obtains the size directly from the map used as data store. So this is a rather 181 * efficient implementation. 182 */ 183 @Override 184 protected int sizeInternal() { 185 return store.size(); 186 } 187 }