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 */ 039public class BaseConfiguration extends AbstractConfiguration implements Cloneable 040{ 041 /** stores the configuration key-value pairs */ 042 private Map<String, Object> store = new LinkedHashMap<>(); 043 044 /** 045 * Adds a key/value pair to the map. This routine does no magic morphing. 046 * It ensures the keylist is maintained 047 * 048 * @param key key to use for mapping 049 * @param value object to store 050 */ 051 @Override 052 protected void addPropertyDirect(final String key, final Object value) 053 { 054 final Object previousValue = getPropertyInternal(key); 055 056 if (previousValue == null) 057 { 058 store.put(key, value); 059 } 060 else if (previousValue instanceof List) 061 { 062 // safe to case because we have created the lists ourselves 063 @SuppressWarnings("unchecked") 064 final 065 List<Object> valueList = (List<Object>) previousValue; 066 // the value is added to the existing list 067 valueList.add(value); 068 } 069 else 070 { 071 // the previous value is replaced by a list containing the previous value and the new value 072 final List<Object> list = new ArrayList<>(); 073 list.add(previousValue); 074 list.add(value); 075 076 store.put(key, list); 077 } 078 } 079 080 /** 081 * Read property from underlying map. 082 * 083 * @param key key to use for mapping 084 * 085 * @return object associated with the given configuration key. 086 */ 087 @Override 088 protected Object getPropertyInternal(final String key) 089 { 090 return store.get(key); 091 } 092 093 /** 094 * Check if the configuration is empty 095 * 096 * @return {@code true} if Configuration is empty, 097 * {@code false} otherwise. 098 */ 099 @Override 100 protected boolean isEmptyInternal() 101 { 102 return store.isEmpty(); 103 } 104 105 /** 106 * check if the configuration contains the key 107 * 108 * @param key the configuration key 109 * 110 * @return {@code true} if Configuration contain given key, 111 * {@code false} otherwise. 112 */ 113 @Override 114 protected boolean containsKeyInternal(final String key) 115 { 116 return store.containsKey(key); 117 } 118 119 /** 120 * Clear a property in the configuration. 121 * 122 * @param key the key to remove along with corresponding value. 123 */ 124 @Override 125 protected void clearPropertyDirect(final String key) 126 { 127 store.remove(key); 128 } 129 130 @Override 131 protected void clearInternal() 132 { 133 store.clear(); 134 } 135 136 /** 137 * {@inheritDoc} This implementation obtains the size directly from the map 138 * used as data store. So this is a rather efficient implementation. 139 */ 140 @Override 141 protected int sizeInternal() 142 { 143 return store.size(); 144 } 145 146 /** 147 * Get the list of the keys contained in the configuration 148 * repository. 149 * 150 * @return An Iterator. 151 */ 152 @Override 153 protected Iterator<String> getKeysInternal() 154 { 155 return store.keySet().iterator(); 156 } 157 158 /** 159 * Creates a copy of this object. This implementation will create a deep 160 * clone, i.e. the map that stores the properties is cloned, too. So changes 161 * performed at the copy won't affect the original and vice versa. 162 * 163 * @return the copy 164 * @since 1.3 165 */ 166 @Override 167 public Object clone() 168 { 169 try 170 { 171 final BaseConfiguration copy = (BaseConfiguration) super.clone(); 172 cloneStore(copy); 173 copy.cloneInterpolator(this); 174 175 return copy; 176 } 177 catch (final CloneNotSupportedException cex) 178 { 179 // should not happen 180 throw new ConfigurationRuntimeException(cex); 181 } 182 } 183 184 /** 185 * Clones the internal map with the data of this configuration. 186 * 187 * @param copy the copy created by the {@code clone()} method 188 * @throws CloneNotSupportedException if the map cannot be cloned 189 */ 190 private void cloneStore(final BaseConfiguration copy) 191 throws CloneNotSupportedException 192 { 193 // This is safe because the type of the map is known 194 @SuppressWarnings("unchecked") 195 final 196 Map<String, Object> clonedStore = (Map<String, Object>) ConfigurationUtils.clone(store); 197 copy.store = clonedStore; 198 199 // Handle collections in the map; they have to be cloned, too 200 for (final Map.Entry<String, Object> e : store.entrySet()) 201 { 202 if (e.getValue() instanceof Collection) 203 { 204 // This is safe because the collections were created by ourselves 205 @SuppressWarnings("unchecked") 206 final 207 Collection<String> strList = (Collection<String>) e.getValue(); 208 copy.store.put(e.getKey(), new ArrayList<>(strList)); 209 } 210 } 211 } 212}