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 provide any load or save functions. If you want 031 * to load your Configuration from a file use PropertiesConfiguration or XmlConfiguration. 032 * 033 * This class extends normal Java properties by adding the possibility to use the same key many times concatenating the 034 * value strings instead of overwriting them. 035 */ 036public class BaseConfiguration extends AbstractConfiguration implements Cloneable { 037 /** Stores the configuration key-value pairs */ 038 private Map<String, Object> store = new LinkedHashMap<>(); 039 040 /** 041 * Adds a key/value pair to the map. This routine does no magic morphing. It ensures the keylist is maintained 042 * 043 * @param key key to use for mapping 044 * @param value object to store 045 */ 046 @Override 047 protected void addPropertyDirect(final String key, final Object value) { 048 final Object previousValue = getPropertyInternal(key); 049 050 if (previousValue == null) { 051 store.put(key, value); 052 } else if (previousValue instanceof List) { 053 // safe to case because we have created the lists ourselves 054 @SuppressWarnings("unchecked") 055 final List<Object> valueList = (List<Object>) previousValue; 056 // the value is added to the existing list 057 valueList.add(value); 058 } else { 059 // the previous value is replaced by a list containing the previous value and the new value 060 final List<Object> list = new ArrayList<>(); 061 list.add(previousValue); 062 list.add(value); 063 064 store.put(key, list); 065 } 066 } 067 068 @Override 069 protected void clearInternal() { 070 store.clear(); 071 } 072 073 /** 074 * Clear a property in the configuration. 075 * 076 * @param key the key to remove along with corresponding value. 077 */ 078 @Override 079 protected void clearPropertyDirect(final String key) { 080 store.remove(key); 081 } 082 083 /** 084 * Creates a copy of this object. This implementation will create a deep clone, i.e. the map that stores the properties 085 * is cloned, too. So changes performed at the copy won't affect the original and vice versa. 086 * 087 * @return the copy 088 * @since 1.3 089 */ 090 @Override 091 public Object clone() { 092 try { 093 final BaseConfiguration copy = (BaseConfiguration) super.clone(); 094 cloneStore(copy); 095 copy.cloneInterpolator(this); 096 097 return copy; 098 } catch (final CloneNotSupportedException cex) { 099 // 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 * 132 * @return {@code true} if Configuration contain given key, {@code false} otherwise. 133 */ 134 @Override 135 protected boolean containsKeyInternal(final String key) { 136 return store.containsKey(key); 137 } 138 139 /** 140 * Tests whether this configuration contains one or more matches to this value. This operation stops at first 141 * match but may be more expensive than the containsKey method. 142 * @since 2.11.0 143 */ 144 @Override 145 protected boolean containsValueInternal(final Object value) { 146 return store.containsValue(value); 147 } 148 149 /** 150 * Gets the list of the keys contained in the configuration repository. 151 * 152 * @return An Iterator. 153 */ 154 @Override 155 protected Iterator<String> getKeysInternal() { 156 return store.keySet().iterator(); 157 } 158 159 /** 160 * Read property from underlying map. 161 * 162 * @param key key to use for mapping 163 * 164 * @return object associated with the given configuration key. 165 */ 166 @Override 167 protected Object getPropertyInternal(final String key) { 168 return store.get(key); 169 } 170 171 /** 172 * Check if the configuration is empty 173 * 174 * @return {@code true} if Configuration is empty, {@code false} otherwise. 175 */ 176 @Override 177 protected boolean isEmptyInternal() { 178 return store.isEmpty(); 179 } 180 181 /** 182 * {@inheritDoc} This implementation obtains the size directly from the map used as data store. So this is a rather 183 * efficient implementation. 184 */ 185 @Override 186 protected int sizeInternal() { 187 return store.size(); 188 } 189}