    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    package org.apache.commons.configuration;
    018
    019    import java.math.BigDecimal;
    020    import java.math.BigInteger;
    021    import java.util.ArrayList;
    022    import java.util.Collection;
    023    import java.util.HashMap;
    024    import java.util.Iterator;
    025    import java.util.List;
    026    import java.util.Map;
    027    import java.util.Properties;
    028    import java.util.Set;
    029
    030    import org.apache.commons.configuration.event.ConfigurationErrorListener;
    031    import org.apache.commons.configuration.event.ConfigurationListener;
    032    import org.apache.commons.configuration.interpol.ConfigurationInterpolator;
    033    import org.apache.commons.configuration.tree.ConfigurationNode;
    034    import org.apache.commons.configuration.tree.ExpressionEngine;
    035    import org.apache.commons.configuration.tree.NodeCombiner;
    036    import org.apache.commons.lang.text.StrSubstitutor;
    037    import org.apache.commons.logging.Log;
    038    import org.apache.commons.logging.LogFactory;
    039
    040    /**
    041     * DynamicCombinedConfiguration allows a set of CombinedConfigurations to be used. Each CombinedConfiguration
    042     * is referenced by a key that is dynamically constructed from a key pattern on each call. The key pattern
    043     * will be resolved using the configured ConfigurationInterpolator.
    044     * @since 1.6
    045     * @author <a
    046     * href="http://commons.apache.org/configuration/team-list.html">Commons
    047     * Configuration team</a>
    048     * @version $Id: DynamicCombinedConfiguration.java 1231721 2012-01-15 18:32:07Z oheger $
    049     */
    050    public class DynamicCombinedConfiguration extends CombinedConfiguration
    051    {
    052        /**
    053         * Prevent recursion while resolving unprefixed properties.
    054         */
    055        private static ThreadLocal<Boolean> recursive = new ThreadLocal<Boolean>()
    056        {
    057            @Override
    058            protected synchronized Boolean initialValue()
    059            {
    060                return Boolean.FALSE;
    061            }
    062        };
    063
    064        /** The CombinedConfigurations */
    065        private Map<String, CombinedConfiguration> configs =
    066                new HashMap<String, CombinedConfiguration>();
    067
    068        /** Stores a list with the contained configurations. */
    069        private List<ConfigData> configurations = new ArrayList<ConfigData>();
    070
    071        /** Stores a map with the named configurations. */
    072        private Map<String, AbstractConfiguration> namedConfigurations =
    073                new HashMap<String, AbstractConfiguration>();
    074
    075        /** The key pattern for the CombinedConfiguration map */
    076        private String keyPattern;
    077
    078        /** Stores the combiner. */
    079        private NodeCombiner nodeCombiner;
    080
    081        /** The name of the logger to use for each CombinedConfiguration */
    082        private String loggerName = DynamicCombinedConfiguration.class.getName();
    083
    084        /** The object for handling variable substitution in key patterns. */
    085        private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator());
    086
    087        /**
    088         * Creates a new instance of {@code DynamicCombinedConfiguration} and
    089         * initializes the combiner to be used.
    090         *
    091         * @param comb the node combiner (can be <b>null</b>, then a union combiner
    092         * is used as default)
    093         */
    094        public DynamicCombinedConfiguration(NodeCombiner comb)
    095        {
    096            super();
    097            setNodeCombiner(comb);
    098            setIgnoreReloadExceptions(false);
    099            setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
    100        }
    101
    102        /**
    103         * Creates a new instance of {@code DynamicCombinedConfiguration} that uses
    104         * a union combiner.
    105         *
    106         * @see org.apache.commons.configuration.tree.UnionCombiner
    107         */
    108        public DynamicCombinedConfiguration()
    109        {
    110            super();
    111            setIgnoreReloadExceptions(false);
    112            setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class));
    113        }
    114
    115        public void setKeyPattern(String pattern)
    116        {
    117            this.keyPattern = pattern;
    118        }
    119
    120        public String getKeyPattern()
    121        {
    122            return this.keyPattern;
    123        }
    124
    125        /**
    126         * Set the name of the Logger to use on each CombinedConfiguration.
    127         * @param name The Logger name.
    128         */
    129        public void setLoggerName(String name)
    130        {
    131            this.loggerName = name;
    132        }
    133
    134        /**
    135         * Returns the node combiner that is used for creating the combined node
    136         * structure.
    137         *
    138         * @return the node combiner
    139         */
    140        @Override
    141        public NodeCombiner getNodeCombiner()
    142        {
    143            return nodeCombiner;
    144        }
    145
    146        /**
    147         * Sets the node combiner. This object will be used when the combined node
    148         * structure is to be constructed. It must not be <b>null</b>, otherwise an
    149         * {@code IllegalArgumentException} exception is thrown. Changing the
    150         * node combiner causes an invalidation of this combined configuration, so
    151         * that the new combiner immediately takes effect.
    152         *
    153         * @param nodeCombiner the node combiner
    154         */
    155        @Override
    156        public void setNodeCombiner(NodeCombiner nodeCombiner)
    157        {
    158            if (nodeCombiner == null)
    159            {
    160                throw new IllegalArgumentException(
    161                        "Node combiner must not be null!");
    162            }
    163            this.nodeCombiner = nodeCombiner;
    164            invalidateAll();
    165        }
    166        /**
    167         * Adds a new configuration to this combined configuration. It is possible
    168         * (but not mandatory) to give the new configuration a name. This name must
    169         * be unique, otherwise a {@code ConfigurationRuntimeException} will
    170         * be thrown. With the optional {@code at} argument you can specify
    171         * where in the resulting node structure the content of the added
    172         * configuration should appear. This is a string that uses dots as property
    173         * delimiters (independent on the current expression engine). For instance
    174         * if you pass in the string {@code "database.tables"},
    175         * all properties of the added configuration will occur in this branch.
    176         *
    177         * @param config the configuration to add (must not be <b>null</b>)
    178         * @param name the name of this configuration (can be <b>null</b>)
    179         * @param at the position of this configuration in the combined tree (can be
    180         * <b>null</b>)
    181         */
    182        @Override
    183        public void addConfiguration(AbstractConfiguration config, String name,
    184                String at)
    185        {
    186            ConfigData cd = new ConfigData(config, name, at);
    187            configurations.add(cd);
    188            if (name != null)
    189            {
    190                namedConfigurations.put(name, config);
    191            }
    192        }
    193           /**
    194         * Returns the number of configurations that are contained in this combined
    195         * configuration.
    196         *
    197         * @return the number of contained configurations
    198         */
    199        @Override
    200        public int getNumberOfConfigurations()
    201        {
    202            return configurations.size();
    203        }
    204
    205        /**
    206         * Returns the configuration at the specified index. The contained
    207         * configurations are numbered in the order they were added to this combined
    208         * configuration. The index of the first configuration is 0.
    209         *
    210         * @param index the index
    211         * @return the configuration at this index
    212         */
    213        @Override
    214        public Configuration getConfiguration(int index)
    215        {
    216            ConfigData cd = configurations.get(index);
    217            return cd.getConfiguration();
    218        }
    219
    220        /**
    221         * Returns the configuration with the given name. This can be <b>null</b>
    222         * if no such configuration exists.
    223         *
    224         * @param name the name of the configuration
    225         * @return the configuration with this name
    226         */
    227        @Override
    228        public Configuration getConfiguration(String name)
    229        {
    230            return namedConfigurations.get(name);
    231        }
    232
    233        /**
    234         * Returns a set with the names of all configurations contained in this
    235         * combined configuration. Of course here are only these configurations
    236         * listed, for which a name was specified when they were added.
    237         *
    238         * @return a set with the names of the contained configurations (never
    239         * <b>null</b>)
    240         */
    241        @Override
    242        public Set<String> getConfigurationNames()
    243        {
    244            return namedConfigurations.keySet();
    245        }
    246
    247        /**
    248         * Removes the configuration with the specified name.
    249         *
    250         * @param name the name of the configuration to be removed
    251         * @return the removed configuration (<b>null</b> if this configuration
    252         * was not found)
    253         */
    254        @Override
    255        public Configuration removeConfiguration(String name)
    256        {
    257            Configuration conf = getConfiguration(name);
    258            if (conf != null)
    259            {
    260                removeConfiguration(conf);
    261            }
    262            return conf;
    263        }
    264
    265        /**
    266         * Removes the specified configuration from this combined configuration.
    267         *
    268         * @param config the configuration to be removed
    269         * @return a flag whether this configuration was found and could be removed
    270         */
    271        @Override
    272        public boolean removeConfiguration(Configuration config)
    273        {
    274            for (int index = 0; index < getNumberOfConfigurations(); index++)
    275            {
    276                if (configurations.get(index).getConfiguration() == config)
    277                {
    278                    removeConfigurationAt(index);
    279
    280                }
    281            }
    282
    283            return super.removeConfiguration(config);
    284        }
    285
    286        /**
    287         * Removes the configuration at the specified index.
    288         *
    289         * @param index the index
    290         * @return the removed configuration
    291         */
    292        @Override
    293        public Configuration removeConfigurationAt(int index)
    294        {
    295            ConfigData cd = configurations.remove(index);
    296            if (cd.getName() != null)
    297            {
    298                namedConfigurations.remove(cd.getName());
    299            }
    300            return super.removeConfigurationAt(index);
    301        }
    302        /**
    303         * Returns the configuration root node of this combined configuration. This
    304         * method will construct a combined node structure using the current node
    305         * combiner if necessary.
    306         *
    307         * @return the combined root node
    308         */
    309        @Override
    310        public ConfigurationNode getRootNode()
    311        {
    312            return getCurrentConfig().getRootNode();
    313        }
    314
    315        @Override
    316        public void setRootNode(ConfigurationNode rootNode)
    317        {
    318            if (configs != null)
    319            {
    320                this.getCurrentConfig().setRootNode(rootNode);
    321            }
    322            else
    323            {
    324                super.setRootNode(rootNode);
    325            }
    326        }
    327
    328        @Override
    329        public void addProperty(String key, Object value)
    330        {
    331            this.getCurrentConfig().addProperty(key, value);
    332        }
    333
    334        @Override
    335        public void clear()
    336        {
    337            if (configs != null)
    338            {
    339                this.getCurrentConfig().clear();
    340            }
    341        }
    342
    343        @Override
    344        public void clearProperty(String key)
    345        {
    346            this.getCurrentConfig().clearProperty(key);
    347        }
    348
    349        @Override
    350        public boolean containsKey(String key)
    351        {
    352            return this.getCurrentConfig().containsKey(key);
    353        }
    354
    355        @Override
    356        public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
    357        {
    358            return this.getCurrentConfig().getBigDecimal(key, defaultValue);
    359        }
    360
    361        @Override
    362        public BigDecimal getBigDecimal(String key)
    363        {
    364            return this.getCurrentConfig().getBigDecimal(key);
    365        }
    366
    367        @Override
    368        public BigInteger getBigInteger(String key, BigInteger defaultValue)
    369        {
    370            return this.getCurrentConfig().getBigInteger(key, defaultValue);
    371        }
    372
    373        @Override
    374        public BigInteger getBigInteger(String key)
    375        {
    376            return this.getCurrentConfig().getBigInteger(key);
    377        }
    378
    379        @Override
    380        public boolean getBoolean(String key, boolean defaultValue)
    381        {
    382            return this.getCurrentConfig().getBoolean(key, defaultValue);
    383        }
    384
    385        @Override
    386        public Boolean getBoolean(String key, Boolean defaultValue)
    387        {
    388            return this.getCurrentConfig().getBoolean(key, defaultValue);
    389        }
    390
    391        @Override
    392        public boolean getBoolean(String key)
    393        {
    394            return this.getCurrentConfig().getBoolean(key);
    395        }
    396
    397        @Override
    398        public byte getByte(String key, byte defaultValue)
    399        {
    400            return this.getCurrentConfig().getByte(key, defaultValue);
    401        }
    402
    403        @Override
    404        public Byte getByte(String key, Byte defaultValue)
    405        {
    406            return this.getCurrentConfig().getByte(key, defaultValue);
    407        }
    408
    409        @Override
    410        public byte getByte(String key)
    411        {
    412            return this.getCurrentConfig().getByte(key);
    413        }
    414
    415        @Override
    416        public double getDouble(String key, double defaultValue)
    417        {
    418            return this.getCurrentConfig().getDouble(key, defaultValue);
    419        }
    420
    421        @Override
    422        public Double getDouble(String key, Double defaultValue)
    423        {
    424            return this.getCurrentConfig().getDouble(key, defaultValue);
    425        }
    426
    427        @Override
    428        public double getDouble(String key)
    429        {
    430            return this.getCurrentConfig().getDouble(key);
    431        }
    432
    433        @Override
    434        public float getFloat(String key, float defaultValue)
    435        {
    436            return this.getCurrentConfig().getFloat(key, defaultValue);
    437        }
    438
    439        @Override
    440        public Float getFloat(String key, Float defaultValue)
    441        {
    442            return this.getCurrentConfig().getFloat(key, defaultValue);
    443        }
    444
    445        @Override
    446        public float getFloat(String key)
    447        {
    448            return this.getCurrentConfig().getFloat(key);
    449        }
    450
    451        @Override
    452        public int getInt(String key, int defaultValue)
    453        {
    454            return this.getCurrentConfig().getInt(key, defaultValue);
    455        }
    456
    457        @Override
    458        public int getInt(String key)
    459        {
    460            return this.getCurrentConfig().getInt(key);
    461        }
    462
    463        @Override
    464        public Integer getInteger(String key, Integer defaultValue)
    465        {
    466            return this.getCurrentConfig().getInteger(key, defaultValue);
    467        }
    468
    469        @Override
    470        public Iterator<String> getKeys()
    471        {
    472            return this.getCurrentConfig().getKeys();
    473        }
    474
    475        @Override
    476        public Iterator<String> getKeys(String prefix)
    477        {
    478            return this.getCurrentConfig().getKeys(prefix);
    479        }
    480
    481        @Override
    482        public List<Object> getList(String key, List<Object> defaultValue)
    483        {
    484            return this.getCurrentConfig().getList(key, defaultValue);
    485        }
    486
    487        @Override
    488        public List<Object> getList(String key)
    489        {
    490            return this.getCurrentConfig().getList(key);
    491        }
    492
    493        @Override
    494        public long getLong(String key, long defaultValue)
    495        {
    496            return this.getCurrentConfig().getLong(key, defaultValue);
    497        }
    498
    499        @Override
    500        public Long getLong(String key, Long defaultValue)
    501        {
    502            return this.getCurrentConfig().getLong(key, defaultValue);
    503        }
    504
    505        @Override
    506        public long getLong(String key)
    507        {
    508            return this.getCurrentConfig().getLong(key);
    509        }
    510
    511        @Override
    512        public Properties getProperties(String key)
    513        {
    514            return this.getCurrentConfig().getProperties(key);
    515        }
    516
    517        @Override
    518        public Object getProperty(String key)
    519        {
    520            return this.getCurrentConfig().getProperty(key);
    521        }
    522
    523        @Override
    524        public short getShort(String key, short defaultValue)
    525        {
    526            return this.getCurrentConfig().getShort(key, defaultValue);
    527        }
    528
    529        @Override
    530        public Short getShort(String key, Short defaultValue)
    531        {
    532            return this.getCurrentConfig().getShort(key, defaultValue);
    533        }
    534
    535        @Override
    536        public short getShort(String key)
    537        {
    538            return this.getCurrentConfig().getShort(key);
    539        }
    540
    541        @Override
    542        public String getString(String key, String defaultValue)
    543        {
    544            return this.getCurrentConfig().getString(key, defaultValue);
    545        }
    546
    547        @Override
    548        public String getString(String key)
    549        {
    550            return this.getCurrentConfig().getString(key);
    551        }
    552
    553        @Override
    554        public String[] getStringArray(String key)
    555        {
    556            return this.getCurrentConfig().getStringArray(key);
    557        }
    558
    559        @Override
    560        public boolean isEmpty()
    561        {
    562            return this.getCurrentConfig().isEmpty();
    563        }
    564
    565        @Override
    566        public void setProperty(String key, Object value)
    567        {
    568            if (configs != null)
    569            {
    570                this.getCurrentConfig().setProperty(key, value);
    571            }
    572        }
    573
    574        @Override
    575        public Configuration subset(String prefix)
    576        {
    577            return this.getCurrentConfig().subset(prefix);
    578        }
    579
    580        @Override
    581        public Node getRoot()
    582        {
    583            return this.getCurrentConfig().getRoot();
    584        }
    585
    586        @Override
    587        public void setRoot(Node node)
    588        {
    589            if (configs != null)
    590            {
    591                this.getCurrentConfig().setRoot(node);
    592            }
    593            else
    594            {
    595                super.setRoot(node);
    596            }
    597        }
    598
    599        @Override
    600        public ExpressionEngine getExpressionEngine()
    601        {
    602            return super.getExpressionEngine();
    603        }
    604
    605        @Override
    606        public void setExpressionEngine(ExpressionEngine expressionEngine)
    607        {
    608            super.setExpressionEngine(expressionEngine);
    609        }
    610
    611        @Override
    612        public void addNodes(String key, Collection<? extends ConfigurationNode> nodes)
    613        {
    614            this.getCurrentConfig().addNodes(key, nodes);
    615        }
    616
    617        @Override
    618        public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
    619        {
    620            return this.getCurrentConfig().configurationAt(key, supportUpdates);
    621        }
    622
    623        @Override
    624        public SubnodeConfiguration configurationAt(String key)
    625        {
    626            return this.getCurrentConfig().configurationAt(key);
    627        }
    628
    629        @Override
    630        public List<HierarchicalConfiguration> configurationsAt(String key)
    631        {
    632            return this.getCurrentConfig().configurationsAt(key);
    633        }
    634
    635        @Override
    636        public void clearTree(String key)
    637        {
    638            this.getCurrentConfig().clearTree(key);
    639        }
    640
    641        @Override
    642        public int getMaxIndex(String key)
    643        {
    644            return this.getCurrentConfig().getMaxIndex(key);
    645        }
    646
    647        @Override
    648        public Configuration interpolatedConfiguration()
    649        {
    650            return this.getCurrentConfig().interpolatedConfiguration();
    651        }
    652
    653
    654        /**
    655         * Returns the configuration source, in which the specified key is defined.
    656         * This method will determine the configuration node that is identified by
    657         * the given key. The following constellations are possible:
    658         * <ul>
    659         * <li>If no node object is found for this key, <b>null</b> is returned.</li>
    660         * <li>If the key maps to multiple nodes belonging to different
    661         * configuration sources, a {@code IllegalArgumentException} is
    662         * thrown (in this case no unique source can be determined).</li>
    663         * <li>If exactly one node is found for the key, the (child) configuration
    664         * object, to which the node belongs is determined and returned.</li>
    665         * <li>For keys that have been added directly to this combined
    666         * configuration and that do not belong to the namespaces defined by
    667         * existing child configurations this configuration will be returned.</li>
    668         * </ul>
    669         *
    670         * @param key the key of a configuration property
    671         * @return the configuration, to which this property belongs or <b>null</b>
    672         * if the key cannot be resolved
    673         * @throws IllegalArgumentException if the key maps to multiple properties
    674         * and the source cannot be determined, or if the key is <b>null</b>
    675         */
    676        @Override
    677        public Configuration getSource(String key)
    678        {
    679            if (key == null)
    680            {
    681                throw new IllegalArgumentException("Key must not be null!");
    682            }
    683            return getCurrentConfig().getSource(key);
    684        }
    685
    686        @Override
    687        public void addConfigurationListener(ConfigurationListener l)
    688        {
    689            super.addConfigurationListener(l);
    690
    691            for (CombinedConfiguration cc : configs.values())
    692            {
    693                cc.addConfigurationListener(l);
    694            }
    695        }
    696
    697        @Override
    698        public boolean removeConfigurationListener(ConfigurationListener l)
    699        {
    700            for (CombinedConfiguration cc : configs.values())
    701            {
    702                cc.removeConfigurationListener(l);
    703            }
    704            return super.removeConfigurationListener(l);
    705        }
    706
    707        @Override
    708        public Collection<ConfigurationListener> getConfigurationListeners()
    709        {
    710            return super.getConfigurationListeners();
    711        }
    712
    713        @Override
    714        public void clearConfigurationListeners()
    715        {
    716            for (CombinedConfiguration cc : configs.values())
    717            {
    718                cc.clearConfigurationListeners();
    719            }
    720            super.clearConfigurationListeners();
    721        }
    722
    723        @Override
    724        public void addErrorListener(ConfigurationErrorListener l)
    725        {
    726            for (CombinedConfiguration cc : configs.values())
    727            {
    728                cc.addErrorListener(l);
    729            }
    730            super.addErrorListener(l);
    731        }
    732
    733        @Override
    734        public boolean removeErrorListener(ConfigurationErrorListener l)
    735        {
    736            for (CombinedConfiguration cc : configs.values())
    737            {
    738                cc.removeErrorListener(l);
    739            }
    740            return super.removeErrorListener(l);
    741        }
    742
    743        @Override
    744        public void clearErrorListeners()
    745        {
    746            for (CombinedConfiguration cc : configs.values())
    747            {
    748                cc.clearErrorListeners();
    749            }
    750            super.clearErrorListeners();
    751        }
    752
    753        @Override
    754        public Collection<ConfigurationErrorListener> getErrorListeners()
    755        {
    756            return super.getErrorListeners();
    757        }
    758
    759        /**
    760         * Returns a copy of this object. This implementation performs a deep clone,
    761         * i.e. all contained configurations will be cloned, too. For this to work,
    762         * all contained configurations must be cloneable. Registered event
    763         * listeners won't be cloned. The clone will use the same node combiner than
    764         * the original.
    765         *
    766         * @return the copied object
    767         */
    768        @Override
    769        public Object clone()
    770        {
    771            return super.clone();
    772        }
    773
    774        /**
    775         * Invalidates the current combined configuration. This means that the next time a
    776         * property is accessed the combined node structure must be re-constructed.
    777         * Invalidation of a combined configuration also means that an event of type
    778         * {@code EVENT_COMBINED_INVALIDATE} is fired. Note that while other
    779         * events most times appear twice (once before and once after an update),
    780         * this event is only fired once (after update).
    781         */
    782        @Override
    783        public void invalidate()
    784        {
    785            getCurrentConfig().invalidate();
    786        }
    787
    788        public void invalidateAll()
    789        {
    790            if (configs == null)
    791            {
    792                return;
    793            }
    794            for (CombinedConfiguration cc : configs.values())
    795            {
    796                cc.invalidate();
    797            }
    798        }
    799
    800        /*
    801         * Don't allow resolveContainerStore to be called recursively.
    802         * @param key The key to resolve.
    803         * @return The value of the key.
    804         */
    805        @Override
    806        protected Object resolveContainerStore(String key)
    807        {
    808            if (recursive.get().booleanValue())
    809            {
    810                return null;
    811            }
    812            recursive.set(Boolean.TRUE);
    813            try
    814            {
    815                return super.resolveContainerStore(key);
    816            }
    817            finally
    818            {
    819                recursive.set(Boolean.FALSE);
    820            }
    821        }
    822
    823        private CombinedConfiguration getCurrentConfig()
    824        {
    825            String key = localSubst.replace(keyPattern);
    826            CombinedConfiguration config;
    827            synchronized (getNodeCombiner())
    828            {
    829                config = configs.get(key);
    830                if (config == null)
    831                {
    832                    config = new CombinedConfiguration(getNodeCombiner());
    833                    if (loggerName != null)
    834                    {
    835                        Log log = LogFactory.getLog(loggerName);
    836                        if (log != null)
    837                        {
    838                            config.setLogger(log);
    839                        }
    840                    }
    841                    config.setIgnoreReloadExceptions(isIgnoreReloadExceptions());
    842                    config.setExpressionEngine(this.getExpressionEngine());
    843                    config.setDelimiterParsingDisabled(isDelimiterParsingDisabled());
    844                    config.setConversionExpressionEngine(getConversionExpressionEngine());
    845                    config.setListDelimiter(getListDelimiter());
    846                    for (ConfigurationErrorListener listener : getErrorListeners())
    847                    {
    848                        config.addErrorListener(listener);
    849                    }
    850                    for (ConfigurationListener listener : getConfigurationListeners())
    851                    {
    852                        config.addConfigurationListener(listener);
    853                    }
    854                    config.setForceReloadCheck(isForceReloadCheck());
    855                    for (ConfigData data : configurations)
    856                    {
    857                        config.addConfiguration(data.getConfiguration(), data.getName(),
    858                                data.getAt());
    859                    }
    860                    configs.put(key, config);
    861                }
    862            }
    863            if (getLogger().isDebugEnabled())
    864            {
    865                getLogger().debug("Returning config for " + key + ": " + config);
    866            }
    867            return config;
    868        }
    869
    870        /**
    871         * Internal class that identifies each Configuration.
    872         */
    873        static class ConfigData
    874        {
    875            /** Stores a reference to the configuration. */
    876            private AbstractConfiguration configuration;
    877
    878            /** Stores the name under which the configuration is stored. */
    879            private String name;
    880
    881            /** Stores the at string.*/
    882            private String at;
    883
    884            /**
    885             * Creates a new instance of {@code ConfigData} and initializes
    886             * it.
    887             *
    888             * @param config the configuration
    889             * @param n the name
    890             * @param at the at position
    891             */
    892            public ConfigData(AbstractConfiguration config, String n, String at)
    893            {
    894                configuration = config;
    895                name = n;
    896                this.at = at;
    897            }
    898
    899            /**
    900             * Returns the stored configuration.
    901             *
    902             * @return the configuration
    903             */
    904            public AbstractConfiguration getConfiguration()
    905            {
    906                return configuration;
    907            }
    908
    909            /**
    910             * Returns the configuration's name.
    911             *
    912             * @return the name
    913             */
    914            public String getName()
    915            {
    916                return name;
    917            }
    918
    919            /**
    920             * Returns the at position of this configuration.
    921             *
    922             * @return the at position
    923             */
    924            public String getAt()
    925            {
    926                return at;
    927            }
    928
    929        }
    930    }