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