001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.configuration2; 019 020import java.util.ArrayList; 021import java.util.Collection; 022import java.util.Iterator; 023import java.util.LinkedHashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.commons.configuration2.ex.ConfigurationRuntimeException; 028 029/** 030 * Basic configuration class. Stores the configuration data but does not 031 * provide any load or save functions. If you want to load your Configuration 032 * from a file use PropertiesConfiguration or XmlConfiguration. 033 * 034 * This class extends normal Java properties by adding the possibility 035 * to use the same key many times concatenating the value strings 036 * instead of overwriting them. 037 * 038 * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a> 039 * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a> 040 * @author <a href="mailto:daveb@miceda-data">Dave Bryson</a> 041 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> 042 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a> 043 * @author <a href="mailto:kjohnson@transparent.com">Kent Johnson</a> 044 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> 045 * @author <a href="mailto:ipriha@surfeu.fi">Ilkka Priha</a> 046 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> 047 * @author <a href="mailto:mpoeschl@marmot.at">Martin Poeschl</a> 048 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> 049 * @author <a href="mailto:ksh@scand.com">Konstantin Shaposhnikov</a> 050 * @version $Id: BaseConfiguration.java 1842194 2018-09-27 22:24:23Z ggregory $ 051 */ 052public class BaseConfiguration extends AbstractConfiguration implements Cloneable 053{ 054 /** stores the configuration key-value pairs */ 055 private Map<String, Object> store = new LinkedHashMap<>(); 056 057 /** 058 * Adds a key/value pair to the map. This routine does no magic morphing. 059 * It ensures the keylist is maintained 060 * 061 * @param key key to use for mapping 062 * @param value object to store 063 */ 064 @Override 065 protected void addPropertyDirect(final String key, final Object value) 066 { 067 final Object previousValue = getPropertyInternal(key); 068 069 if (previousValue == null) 070 { 071 store.put(key, value); 072 } 073 else if (previousValue instanceof List) 074 { 075 // safe to case because we have created the lists ourselves 076 @SuppressWarnings("unchecked") 077 final 078 List<Object> valueList = (List<Object>) previousValue; 079 // the value is added to the existing list 080 valueList.add(value); 081 } 082 else 083 { 084 // the previous value is replaced by a list containing the previous value and the new value 085 final List<Object> list = new ArrayList<>(); 086 list.add(previousValue); 087 list.add(value); 088 089 store.put(key, list); 090 } 091 } 092 093 /** 094 * Read property from underlying map. 095 * 096 * @param key key to use for mapping 097 * 098 * @return object associated with the given configuration key. 099 */ 100 @Override 101 protected Object getPropertyInternal(final String key) 102 { 103 return store.get(key); 104 } 105 106 /** 107 * Check if the configuration is empty 108 * 109 * @return {@code true} if Configuration is empty, 110 * {@code false} otherwise. 111 */ 112 @Override 113 protected boolean isEmptyInternal() 114 { 115 return store.isEmpty(); 116 } 117 118 /** 119 * check if the configuration contains the key 120 * 121 * @param key the configuration key 122 * 123 * @return {@code true} if Configuration contain given key, 124 * {@code false} otherwise. 125 */ 126 @Override 127 protected boolean containsKeyInternal(final String key) 128 { 129 return store.containsKey(key); 130 } 131 132 /** 133 * Clear a property in the configuration. 134 * 135 * @param key the key to remove along with corresponding value. 136 */ 137 @Override 138 protected void clearPropertyDirect(final String key) 139 { 140 store.remove(key); 141 } 142 143 @Override 144 protected void clearInternal() 145 { 146 store.clear(); 147 } 148 149 /** 150 * {@inheritDoc} This implementation obtains the size directly from the map 151 * used as data store. So this is a rather efficient implementation. 152 */ 153 @Override 154 protected int sizeInternal() 155 { 156 return store.size(); 157 } 158 159 /** 160 * Get the list of the keys contained in the configuration 161 * repository. 162 * 163 * @return An Iterator. 164 */ 165 @Override 166 protected Iterator<String> getKeysInternal() 167 { 168 return store.keySet().iterator(); 169 } 170 171 /** 172 * Creates a copy of this object. This implementation will create a deep 173 * clone, i.e. the map that stores the properties is cloned, too. So changes 174 * performed at the copy won't affect the original and vice versa. 175 * 176 * @return the copy 177 * @since 1.3 178 */ 179 @Override 180 public Object clone() 181 { 182 try 183 { 184 final BaseConfiguration copy = (BaseConfiguration) super.clone(); 185 cloneStore(copy); 186 copy.cloneInterpolator(this); 187 188 return copy; 189 } 190 catch (final CloneNotSupportedException cex) 191 { 192 // should not happen 193 throw new ConfigurationRuntimeException(cex); 194 } 195 } 196 197 /** 198 * Clones the internal map with the data of this configuration. 199 * 200 * @param copy the copy created by the {@code clone()} method 201 * @throws CloneNotSupportedException if the map cannot be cloned 202 */ 203 private void cloneStore(final BaseConfiguration copy) 204 throws CloneNotSupportedException 205 { 206 // This is safe because the type of the map is known 207 @SuppressWarnings("unchecked") 208 final 209 Map<String, Object> clonedStore = (Map<String, Object>) ConfigurationUtils.clone(store); 210 copy.store = clonedStore; 211 212 // Handle collections in the map; they have to be cloned, too 213 for (final Map.Entry<String, Object> e : store.entrySet()) 214 { 215 if (e.getValue() instanceof Collection) 216 { 217 // This is safe because the collections were created by ourselves 218 @SuppressWarnings("unchecked") 219 final 220 Collection<String> strList = (Collection<String>) e.getValue(); 221 copy.store.put(e.getKey(), new ArrayList<>(strList)); 222 } 223 } 224 } 225}