View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.configuration2;
19  
20  import java.util.ArrayList;
21  import java.util.Collection;
22  import java.util.Iterator;
23  import java.util.LinkedHashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.apache.commons.configuration2.ex.ConfigurationRuntimeException;
28  
29  /**
30   * Basic configuration class. Stores the configuration data but does not provide any load or save functions. If you want
31   * to load your Configuration from a file use PropertiesConfiguration or XmlConfiguration.
32   *
33   * This class extends normal Java properties by adding the possibility to use the same key many times concatenating the
34   * value strings instead of overwriting them.
35   */
36  public class BaseConfiguration extends AbstractConfiguration implements Cloneable {
37      /** Stores the configuration key-value pairs */
38      private Map<String, Object> store = new LinkedHashMap<>();
39  
40      /**
41       * Adds a key/value pair to the map. This routine does no magic morphing. It ensures the keylist is maintained
42       *
43       * @param key key to use for mapping
44       * @param value object to store
45       */
46      @Override
47      protected void addPropertyDirect(final String key, final Object value) {
48          final Object previousValue = getPropertyInternal(key);
49  
50          if (previousValue == null) {
51              store.put(key, value);
52          } else if (previousValue instanceof List) {
53              // safe to case because we have created the lists ourselves
54              @SuppressWarnings("unchecked")
55              final List<Object> valueList = (List<Object>) previousValue;
56              // the value is added to the existing list
57              valueList.add(value);
58          } else {
59              // the previous value is replaced by a list containing the previous value and the new value
60              final List<Object> list = new ArrayList<>();
61              list.add(previousValue);
62              list.add(value);
63  
64              store.put(key, list);
65          }
66      }
67  
68      @Override
69      protected void clearInternal() {
70          store.clear();
71      }
72  
73      /**
74       * Clear a property in the configuration.
75       *
76       * @param key the key to remove along with corresponding value.
77       */
78      @Override
79      protected void clearPropertyDirect(final String key) {
80          store.remove(key);
81      }
82  
83      /**
84       * Creates a copy of this object. This implementation will create a deep clone, i.e. the map that stores the properties
85       * is cloned, too. So changes performed at the copy won't affect the original and vice versa.
86       *
87       * @return the copy
88       * @since 1.3
89       */
90      @Override
91      public Object clone() {
92          try {
93              final BaseConfiguration copy = (BaseConfiguration) super.clone();
94              cloneStore(copy);
95              copy.cloneInterpolator(this);
96  
97              return copy;
98          } catch (final CloneNotSupportedException cex) {
99              // 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      * @return {@code true} if Configuration contain given key, {@code false} otherwise.
132      */
133     @Override
134     protected boolean containsKeyInternal(final String key) {
135         return store.containsKey(key);
136     }
137 
138     /**
139      * Tests whether this configuration contains one or more matches to this value. This operation stops at first
140      * match but may be more expensive than the containsKey method.
141      * @since 2.11.0
142      */
143     @Override
144     protected boolean containsValueInternal(final Object value) {
145         return store.containsValue(value);
146     }
147 
148     /**
149      * Gets the list of the keys contained in the configuration repository.
150      *
151      * @return An Iterator.
152      */
153     @Override
154     protected Iterator<String> getKeysInternal() {
155         return store.keySet().iterator();
156     }
157 
158     /**
159      * Reads property from underlying map.
160      *
161      * @param key key to use for mapping
162      * @return object associated with the given configuration key.
163      */
164     @Override
165     protected Object getPropertyInternal(final String key) {
166         return store.get(key);
167     }
168 
169     /**
170      * Check if the configuration is empty
171      *
172      * @return {@code true} if Configuration is empty, {@code false} otherwise.
173      */
174     @Override
175     protected boolean isEmptyInternal() {
176         return store.isEmpty();
177     }
178 
179     /**
180      * {@inheritDoc} This implementation obtains the size directly from the map used as data store. So this is a rather
181      * efficient implementation.
182      */
183     @Override
184     protected int sizeInternal() {
185         return store.size();
186     }
187 }