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