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 /** 69 * Read property from underlying map. 70 * 71 * @param key key to use for mapping 72 * 73 * @return object associated with the given configuration key. 74 */ 75 @Override 76 protected Object getPropertyInternal(final String key) { 77 return store.get(key); 78 } 79 80 /** 81 * Check if the configuration is empty 82 * 83 * @return {@code true} if Configuration is empty, {@code false} otherwise. 84 */ 85 @Override 86 protected boolean isEmptyInternal() { 87 return store.isEmpty(); 88 } 89 90 /** 91 * check if the configuration contains the key 92 * 93 * @param key the configuration key 94 * 95 * @return {@code true} if Configuration contain given key, {@code false} otherwise. 96 */ 97 @Override 98 protected boolean containsKeyInternal(final String key) { 99 return store.containsKey(key); 100 } 101 102 /** 103 * Clear a property in the configuration. 104 * 105 * @param key the key to remove along with corresponding value. 106 */ 107 @Override 108 protected void clearPropertyDirect(final String key) { 109 store.remove(key); 110 } 111 112 @Override 113 protected void clearInternal() { 114 store.clear(); 115 } 116 117 /** 118 * {@inheritDoc} This implementation obtains the size directly from the map used as data store. So this is a rather 119 * efficient implementation. 120 */ 121 @Override 122 protected int sizeInternal() { 123 return store.size(); 124 } 125 126 /** 127 * Gets the list of the keys contained in the configuration repository. 128 * 129 * @return An Iterator. 130 */ 131 @Override 132 protected Iterator<String> getKeysInternal() { 133 return store.keySet().iterator(); 134 } 135 136 /** 137 * Creates a copy of this object. This implementation will create a deep clone, i.e. the map that stores the properties 138 * is cloned, too. So changes performed at the copy won't affect the original and vice versa. 139 * 140 * @return the copy 141 * @since 1.3 142 */ 143 @Override 144 public Object clone() { 145 try { 146 final BaseConfiguration copy = (BaseConfiguration) super.clone(); 147 cloneStore(copy); 148 copy.cloneInterpolator(this); 149 150 return copy; 151 } catch (final CloneNotSupportedException cex) { 152 // should not happen 153 throw new ConfigurationRuntimeException(cex); 154 } 155 } 156 157 /** 158 * Clones the internal map with the data of this configuration. 159 * 160 * @param copy the copy created by the {@code clone()} method 161 * @throws CloneNotSupportedException if the map cannot be cloned 162 */ 163 private void cloneStore(final BaseConfiguration copy) throws CloneNotSupportedException { 164 // This is safe because the type of the map is known 165 @SuppressWarnings("unchecked") 166 final Map<String, Object> clonedStore = (Map<String, Object>) ConfigurationUtils.clone(store); 167 copy.store = clonedStore; 168 169 // Handle collections in the map; they have to be cloned, too 170 store.forEach((k, v) -> { 171 if (v instanceof Collection) { 172 // This is safe because the collections were created by ourselves 173 @SuppressWarnings("unchecked") 174 final Collection<String> strList = (Collection<String>) v; 175 copy.store.put(k, new ArrayList<>(strList)); 176 } 177 }); 178 } 179 }