~ubuntu-branches/ubuntu/saucy/commons-configuration/saucy

« back to all changes in this revision

Viewing changes to src/main/java/org/apache/commons/configuration/CompositeConfiguration.java

  • Committer: Package Import Robot
  • Author(s): Emmanuel Bourg
  • Date: 2013-07-01 16:29:44 UTC
  • mfrom: (1.1.4)
  • Revision ID: package-import@ubuntu.com-20130701162944-98waq5gogha5gpn1
Tags: 1.9-1
* New upstream release
* debian/control:
  - Updated Standards-Version to 3.9.4 (no changes)
  - Use canonical URLs for the Vcs-* fields
  - Added new build dependencies (libjavacc-maven-plugin-java, junit4)
  - Upgraded the dependency on the Servlet API (2.5 -> 3.0)
  - Removed the dependency on the Activation Framework (glassfish-activation)
  - Replaced the dependency on glassfish-mail with libgnumail-java
  - Removed the unused dependencies:
    liblog4j1.2-java-doc, libmaven-assembly-plugin-java
  - Replaced the dependency on libcommons-jexl-java by libcommons-jexl2-java
* debian/watch: Changed to point the official Apache distribution server
* Removed the obsolete file debian/ant.properties
* Installed the upstream changelog in the binary packages
* Added the report plugins to maven.ignoreRules
* Added the classpath attribute to the jar manifest

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.configuration;
 
19
 
 
20
import java.util.ArrayList;
 
21
import java.util.Collection;
 
22
import java.util.Iterator;
 
23
import java.util.LinkedHashSet;
 
24
import java.util.LinkedList;
 
25
import java.util.List;
 
26
import java.util.ListIterator;
 
27
import java.util.Set;
 
28
 
 
29
/**
 
30
 * <p>{@code CompositeConfiguration} allows you to add multiple {@code Configuration}
 
31
 * objects to an aggregated configuration. If you add Configuration1, and then Configuration2,
 
32
 * any properties shared will mean that the value defined by Configuration1
 
33
 * will be returned. If Configuration1 doesn't have the property, then
 
34
 * Configuration2 will be checked. You can add multiple different types or the
 
35
 * same type of properties file.</p>
 
36
 * <p>When querying properties the order in which child configurations have been
 
37
 * added is relevant. To deal with property updates, a so-called <em>in-memory
 
38
 * configuration</em> is used. Per default, such a configuration is created
 
39
 * automatically. All property writes target this special configuration. There
 
40
 * are constructors which allow you to provide a specific in-memory configuration.
 
41
 * If used that way, the in-memory configuration is always the last one in the
 
42
 * list of child configurations. This means that for query operations all other
 
43
 * configurations take precedence.</p>
 
44
 * <p>Alternatively it is possible to mark a child configuration as in-memory
 
45
 * configuration when it is added. In this case the treatment of the in-memory
 
46
 * configuration is slightly different: it remains in the list of child
 
47
 * configurations at the position it was added, i.e. its priority for property
 
48
 * queries can be defined by adding the child configurations in the correct
 
49
 * order.</p>
 
50
 *
 
51
 * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
 
52
 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
 
53
 * @version $Id: CompositeConfiguration.java 1233058 2012-01-18 20:49:12Z oheger $
 
54
 */
 
55
public class CompositeConfiguration extends AbstractConfiguration
 
56
implements Cloneable
 
57
{
 
58
    /** List holding all the configuration */
 
59
    private List<Configuration> configList = new LinkedList<Configuration>();
 
60
 
 
61
    /**
 
62
     * Configuration that holds in memory stuff.  Inserted as first so any
 
63
     * setProperty() override anything else added.
 
64
     */
 
65
    private Configuration inMemoryConfiguration;
 
66
 
 
67
    /**
 
68
     * Stores a flag whether the current in-memory configuration is also a
 
69
     * child configuration.
 
70
     */
 
71
    private boolean inMemoryConfigIsChild;
 
72
 
 
73
    /**
 
74
     * Creates an empty CompositeConfiguration object which can then
 
75
     * be added some other Configuration files
 
76
     */
 
77
    public CompositeConfiguration()
 
78
    {
 
79
        clear();
 
80
    }
 
81
 
 
82
    /**
 
83
     * Creates a CompositeConfiguration object with a specified <em>in-memory
 
84
     * configuration</em>. This configuration will store any changes made to the
 
85
     * {@code CompositeConfiguration}. Note: Use this constructor if you want to
 
86
     * set a special type of in-memory configuration. If you have a
 
87
     * configuration which should act as both a child configuration and as
 
88
     * in-memory configuration, use
 
89
     * {@link #addConfiguration(Configuration, boolean)} with a value of
 
90
     * <b>true</b> instead.
 
91
     *
 
92
     * @param inMemoryConfiguration the in memory configuration to use
 
93
     */
 
94
    public CompositeConfiguration(Configuration inMemoryConfiguration)
 
95
    {
 
96
        configList.clear();
 
97
        this.inMemoryConfiguration = inMemoryConfiguration;
 
98
        configList.add(inMemoryConfiguration);
 
99
    }
 
100
 
 
101
    /**
 
102
     * Create a CompositeConfiguration with an empty in memory configuration
 
103
     * and adds the collection of configurations specified.
 
104
     *
 
105
     * @param configurations the collection of configurations to add
 
106
     */
 
107
    public CompositeConfiguration(Collection<? extends Configuration> configurations)
 
108
    {
 
109
        this(new BaseConfiguration(), configurations);
 
110
    }
 
111
 
 
112
    /**
 
113
     * Creates a CompositeConfiguration with a specified <em>in-memory
 
114
     * configuration</em>, and then adds the given collection of configurations.
 
115
     *
 
116
     * @param inMemoryConfiguration the in memory configuration to use
 
117
     * @param configurations        the collection of configurations to add
 
118
     * @see #CompositeConfiguration(Configuration)
 
119
     */
 
120
    public CompositeConfiguration(Configuration inMemoryConfiguration,
 
121
            Collection<? extends Configuration> configurations)
 
122
    {
 
123
        this(inMemoryConfiguration);
 
124
 
 
125
        if (configurations != null)
 
126
        {
 
127
            for (Configuration c : configurations)
 
128
            {
 
129
                addConfiguration(c);
 
130
            }
 
131
        }
 
132
    }
 
133
 
 
134
    /**
 
135
     * Add a configuration.
 
136
     *
 
137
     * @param config the configuration to add
 
138
     */
 
139
    public void addConfiguration(Configuration config)
 
140
    {
 
141
        addConfiguration(config, false);
 
142
    }
 
143
 
 
144
    /**
 
145
     * Adds a child configuration and optionally makes it the <em>in-memory
 
146
     * configuration</em>. This means that all future property write operations
 
147
     * are executed on this configuration. Note that the current in-memory
 
148
     * configuration is replaced by the new one. If it was created automatically
 
149
     * or passed to the constructor, it is removed from the list of child
 
150
     * configurations! Otherwise, it stays in the list of child configurations
 
151
     * at its current position, but it passes its role as in-memory
 
152
     * configuration to the new one.
 
153
     *
 
154
     * @param config the configuration to be added
 
155
     * @param asInMemory <b>true</b> if this configuration becomes the new
 
156
     *        <em>in-memory</em> configuration, <b>false</b> otherwise
 
157
     * @since 1.8
 
158
     */
 
159
    public void addConfiguration(Configuration config, boolean asInMemory)
 
160
    {
 
161
        if (!configList.contains(config))
 
162
        {
 
163
            if (asInMemory)
 
164
            {
 
165
                replaceInMemoryConfiguration(config);
 
166
                inMemoryConfigIsChild = true;
 
167
            }
 
168
 
 
169
            if (!inMemoryConfigIsChild)
 
170
            {
 
171
                // As the inMemoryConfiguration contains all manually added
 
172
                // keys, we must make sure that it is always last. "Normal", non
 
173
                // composed configurations add their keys at the end of the
 
174
                // configuration and we want to mimic this behavior.
 
175
                configList.add(configList.indexOf(inMemoryConfiguration),
 
176
                        config);
 
177
            }
 
178
            else
 
179
            {
 
180
                // However, if the in-memory configuration is a regular child,
 
181
                // only the order in which child configurations are added is
 
182
                // relevant
 
183
                configList.add(config);
 
184
            }
 
185
 
 
186
            if (config instanceof AbstractConfiguration)
 
187
            {
 
188
                ((AbstractConfiguration) config)
 
189
                        .setThrowExceptionOnMissing(isThrowExceptionOnMissing());
 
190
            }
 
191
        }
 
192
    }
 
193
 
 
194
    /**
 
195
     * Remove a configuration. The in memory configuration cannot be removed.
 
196
     *
 
197
     * @param config The configuration to remove
 
198
     */
 
199
    public void removeConfiguration(Configuration config)
 
200
    {
 
201
        // Make sure that you can't remove the inMemoryConfiguration from
 
202
        // the CompositeConfiguration object
 
203
        if (!config.equals(inMemoryConfiguration))
 
204
        {
 
205
            configList.remove(config);
 
206
        }
 
207
    }
 
208
 
 
209
    /**
 
210
     * Return the number of configurations.
 
211
     *
 
212
     * @return the number of configuration
 
213
     */
 
214
    public int getNumberOfConfigurations()
 
215
    {
 
216
        return configList.size();
 
217
    }
 
218
 
 
219
    /**
 
220
     * Removes all child configurations and reinitializes the <em>in-memory
 
221
     * configuration</em>. <strong>Attention:</strong> A new in-memory
 
222
     * configuration is created; the old one is lost.
 
223
     */
 
224
    @Override
 
225
    public void clear()
 
226
    {
 
227
        configList.clear();
 
228
        // recreate the in memory configuration
 
229
        inMemoryConfiguration = new BaseConfiguration();
 
230
        ((BaseConfiguration) inMemoryConfiguration).setThrowExceptionOnMissing(isThrowExceptionOnMissing());
 
231
        ((BaseConfiguration) inMemoryConfiguration).setListDelimiter(getListDelimiter());
 
232
        ((BaseConfiguration) inMemoryConfiguration).setDelimiterParsingDisabled(isDelimiterParsingDisabled());
 
233
        configList.add(inMemoryConfiguration);
 
234
        inMemoryConfigIsChild = false;
 
235
    }
 
236
 
 
237
    /**
 
238
     * Add this property to the inmemory Configuration.
 
239
     *
 
240
     * @param key The Key to add the property to.
 
241
     * @param token The Value to add.
 
242
     */
 
243
    @Override
 
244
    protected void addPropertyDirect(String key, Object token)
 
245
    {
 
246
        inMemoryConfiguration.addProperty(key, token);
 
247
    }
 
248
 
 
249
    /**
 
250
     * Read property from underlying composite
 
251
     *
 
252
     * @param key key to use for mapping
 
253
     *
 
254
     * @return object associated with the given configuration key.
 
255
     */
 
256
    public Object getProperty(String key)
 
257
    {
 
258
        Configuration firstMatchingConfiguration = null;
 
259
        for (Configuration config : configList)
 
260
        {
 
261
            if (config.containsKey(key))
 
262
            {
 
263
                firstMatchingConfiguration = config;
 
264
                break;
 
265
            }
 
266
        }
 
267
 
 
268
        if (firstMatchingConfiguration != null)
 
269
        {
 
270
            return firstMatchingConfiguration.getProperty(key);
 
271
        }
 
272
        else
 
273
        {
 
274
            return null;
 
275
        }
 
276
    }
 
277
 
 
278
    public Iterator<String> getKeys()
 
279
    {
 
280
        Set<String> keys = new LinkedHashSet<String>();
 
281
        for (Configuration config : configList)
 
282
        {
 
283
            for (Iterator<String> it = config.getKeys(); it.hasNext();)
 
284
            {
 
285
                keys.add(it.next());
 
286
            }
 
287
        }
 
288
 
 
289
        return keys.iterator();
 
290
    }
 
291
 
 
292
    @Override
 
293
    public Iterator<String> getKeys(String key)
 
294
    {
 
295
        Set<String> keys = new LinkedHashSet<String>();
 
296
        for (Configuration config : configList)
 
297
        {
 
298
            for (Iterator<String> it = config.getKeys(key); it.hasNext();)
 
299
            {
 
300
                keys.add(it.next());
 
301
            }
 
302
        }
 
303
 
 
304
        return keys.iterator();
 
305
    }
 
306
 
 
307
    public boolean isEmpty()
 
308
    {
 
309
        for (Configuration config : configList)
 
310
        {
 
311
            if (!config.isEmpty())
 
312
            {
 
313
                return false;
 
314
            }
 
315
        }
 
316
 
 
317
        return true;
 
318
    }
 
319
 
 
320
    @Override
 
321
    protected void clearPropertyDirect(String key)
 
322
    {
 
323
        for (Configuration config : configList)
 
324
        {
 
325
            config.clearProperty(key);
 
326
        }
 
327
    }
 
328
 
 
329
    public boolean containsKey(String key)
 
330
    {
 
331
        for (Configuration config : configList)
 
332
        {
 
333
            if (config.containsKey(key))
 
334
            {
 
335
                return true;
 
336
            }
 
337
        }
 
338
        return false;
 
339
    }
 
340
 
 
341
    @Override
 
342
    public List<Object> getList(String key, List<Object> defaultValue)
 
343
    {
 
344
        List<Object> list = new ArrayList<Object>();
 
345
 
 
346
        // add all elements from the first configuration containing the requested key
 
347
        Iterator<Configuration> it = configList.iterator();
 
348
        while (it.hasNext() && list.isEmpty())
 
349
        {
 
350
            Configuration config = it.next();
 
351
            if (config != inMemoryConfiguration && config.containsKey(key))
 
352
            {
 
353
                appendListProperty(list, config, key);
 
354
            }
 
355
        }
 
356
 
 
357
        // add all elements from the in memory configuration
 
358
        appendListProperty(list, inMemoryConfiguration, key);
 
359
 
 
360
        if (list.isEmpty())
 
361
        {
 
362
            return defaultValue;
 
363
        }
 
364
 
 
365
        ListIterator<Object> lit = list.listIterator();
 
366
        while (lit.hasNext())
 
367
        {
 
368
            lit.set(interpolate(lit.next()));
 
369
        }
 
370
 
 
371
        return list;
 
372
    }
 
373
 
 
374
    @Override
 
375
    public String[] getStringArray(String key)
 
376
    {
 
377
        List<Object> list = getList(key);
 
378
 
 
379
        // transform property values into strings
 
380
        String[] tokens = new String[list.size()];
 
381
 
 
382
        for (int i = 0; i < tokens.length; i++)
 
383
        {
 
384
            tokens[i] = String.valueOf(list.get(i));
 
385
        }
 
386
 
 
387
        return tokens;
 
388
    }
 
389
 
 
390
    /**
 
391
     * Return the configuration at the specified index.
 
392
     *
 
393
     * @param index The index of the configuration to retrieve
 
394
     * @return the configuration at this index
 
395
     */
 
396
    public Configuration getConfiguration(int index)
 
397
    {
 
398
        return configList.get(index);
 
399
    }
 
400
 
 
401
    /**
 
402
     * Returns the &quot;in memory configuration&quot;. In this configuration
 
403
     * changes are stored.
 
404
     *
 
405
     * @return the in memory configuration
 
406
     */
 
407
    public Configuration getInMemoryConfiguration()
 
408
    {
 
409
        return inMemoryConfiguration;
 
410
    }
 
411
 
 
412
    /**
 
413
     * Returns a copy of this object. This implementation will create a deep
 
414
     * clone, i.e. all configurations contained in this composite will also be
 
415
     * cloned. This only works if all contained configurations support cloning;
 
416
     * otherwise a runtime exception will be thrown. Registered event handlers
 
417
     * won't get cloned.
 
418
     *
 
419
     * @return the copy
 
420
     * @since 1.3
 
421
     */
 
422
    @Override
 
423
    public Object clone()
 
424
    {
 
425
        try
 
426
        {
 
427
            CompositeConfiguration copy = (CompositeConfiguration) super
 
428
                    .clone();
 
429
            copy.clearConfigurationListeners();
 
430
            copy.configList = new LinkedList<Configuration>();
 
431
            copy.inMemoryConfiguration = ConfigurationUtils
 
432
                    .cloneConfiguration(getInMemoryConfiguration());
 
433
            copy.configList.add(copy.inMemoryConfiguration);
 
434
 
 
435
            for (Configuration config : configList)
 
436
            {
 
437
                if (config != getInMemoryConfiguration())
 
438
                {
 
439
                    copy.addConfiguration(ConfigurationUtils
 
440
                            .cloneConfiguration(config));
 
441
                }
 
442
            }
 
443
 
 
444
            return copy;
 
445
        }
 
446
        catch (CloneNotSupportedException cnex)
 
447
        {
 
448
            // cannot happen
 
449
            throw new ConfigurationRuntimeException(cnex);
 
450
        }
 
451
    }
 
452
 
 
453
    /**
 
454
     * Sets a flag whether added values for string properties should be checked
 
455
     * for the list delimiter. This implementation ensures that the in memory
 
456
     * configuration is correctly initialized.
 
457
     *
 
458
     * @param delimiterParsingDisabled the new value of the flag
 
459
     * @since 1.4
 
460
     */
 
461
    @Override
 
462
    public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled)
 
463
    {
 
464
        if (inMemoryConfiguration instanceof AbstractConfiguration)
 
465
        {
 
466
            ((AbstractConfiguration) inMemoryConfiguration)
 
467
                    .setDelimiterParsingDisabled(delimiterParsingDisabled);
 
468
        }
 
469
        super.setDelimiterParsingDisabled(delimiterParsingDisabled);
 
470
    }
 
471
 
 
472
    /**
 
473
     * Sets the character that is used as list delimiter. This implementation
 
474
     * ensures that the in memory configuration is correctly initialized.
 
475
     *
 
476
     * @param listDelimiter the new list delimiter character
 
477
     * @since 1.4
 
478
     */
 
479
    @Override
 
480
    public void setListDelimiter(char listDelimiter)
 
481
    {
 
482
        if (inMemoryConfiguration instanceof AbstractConfiguration)
 
483
        {
 
484
            ((AbstractConfiguration) inMemoryConfiguration)
 
485
                    .setListDelimiter(listDelimiter);
 
486
        }
 
487
        super.setListDelimiter(listDelimiter);
 
488
    }
 
489
 
 
490
    /**
 
491
     * Returns the configuration source, in which the specified key is defined.
 
492
     * This method will iterate over all existing child configurations and check
 
493
     * whether they contain the specified key. The following constellations are
 
494
     * possible:
 
495
     * <ul>
 
496
     * <li>If exactly one child configuration contains the key, this
 
497
     * configuration is returned as the source configuration. This may be the
 
498
     * <em>in memory configuration</em> (this has to be explicitly checked by
 
499
     * the calling application).</li>
 
500
     * <li>If none of the child configurations contain the key, <b>null</b> is
 
501
     * returned.</li>
 
502
     * <li>If the key is contained in multiple child configurations or if the
 
503
     * key is <b>null</b>, a {@code IllegalArgumentException} is thrown.
 
504
     * In this case the source configuration cannot be determined.</li>
 
505
     * </ul>
 
506
     *
 
507
     * @param key the key to be checked
 
508
     * @return the source configuration of this key
 
509
     * @throws IllegalArgumentException if the source configuration cannot be
 
510
     * determined
 
511
     * @since 1.5
 
512
     */
 
513
    public Configuration getSource(String key)
 
514
    {
 
515
        if (key == null)
 
516
        {
 
517
            throw new IllegalArgumentException("Key must not be null!");
 
518
        }
 
519
 
 
520
        Configuration source = null;
 
521
        for (Configuration conf : configList)
 
522
        {
 
523
            if (conf.containsKey(key))
 
524
            {
 
525
                if (source != null)
 
526
                {
 
527
                    throw new IllegalArgumentException("The key " + key
 
528
                            + " is defined by multiple sources!");
 
529
                }
 
530
                source = conf;
 
531
            }
 
532
        }
 
533
 
 
534
        return source;
 
535
    }
 
536
 
 
537
    /**
 
538
     * Replaces the current in-memory configuration by the given one.
 
539
     *
 
540
     * @param config the new in-memory configuration
 
541
     */
 
542
    private void replaceInMemoryConfiguration(Configuration config)
 
543
    {
 
544
        if (!inMemoryConfigIsChild)
 
545
        {
 
546
            // remove current in-memory configuration
 
547
            configList.remove(inMemoryConfiguration);
 
548
        }
 
549
        inMemoryConfiguration = config;
 
550
    }
 
551
 
 
552
    /**
 
553
     * Adds the value of a property to the given list. This method is used by
 
554
     * {@code getList()} for gathering property values from the child
 
555
     * configurations.
 
556
     *
 
557
     * @param dest the list for collecting the data
 
558
     * @param config the configuration to query
 
559
     * @param key the key of the property
 
560
     */
 
561
    private static void appendListProperty(List<Object> dest, Configuration config,
 
562
            String key)
 
563
    {
 
564
        Object value = config.getProperty(key);
 
565
        if (value != null)
 
566
        {
 
567
            if (value instanceof Collection)
 
568
            {
 
569
                Collection<?> col = (Collection<?>) value;
 
570
                dest.addAll(col);
 
571
            }
 
572
            else
 
573
            {
 
574
                dest.add(value);
 
575
            }
 
576
        }
 
577
    }
 
578
}