    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.io.Reader;
    020    import java.io.Writer;
    021    import java.math.BigDecimal;
    022    import java.math.BigInteger;
    023    import java.util.Collection;
    024    import java.util.Iterator;
    025    import java.util.List;
    026    import java.util.Properties;
    027
    028    import org.apache.commons.configuration.event.ConfigurationErrorListener;
    029    import org.apache.commons.configuration.event.ConfigurationListener;
    030    import org.apache.commons.configuration.tree.ConfigurationNode;
    031    import org.apache.commons.configuration.tree.ExpressionEngine;
    032
    033    /**
    034     * Wraps a HierarchicalConfiguration and allows subtrees to be access via a configured path with
    035     * replaceable tokens derived from the ConfigurationInterpolator. When used with injection frameworks
    036     * such as Spring it allows components to be injected with subtrees of the configuration.
    037     * @since 1.6
    038     * @author <a
    039     * href="http://commons.apache.org/configuration/team-list.html">Commons
    040     * Configuration team</a>
    041     * @version $Id: PatternSubtreeConfigurationWrapper.java 1210175 2011-12-04 18:49:51Z oheger $
    042     */
    043    public class PatternSubtreeConfigurationWrapper extends AbstractHierarchicalFileConfiguration
    044    {
    045        /**
    046         * Prevent recursion while resolving unprefixed properties.
    047         */
    048        private static ThreadLocal<Boolean> recursive = new ThreadLocal<Boolean>()
    049        {
    050            @Override
    051            protected synchronized Boolean initialValue()
    052            {
    053                return Boolean.FALSE;
    054            }
    055        };
    056
    057        /** The wrapped configuration */
    058        private final AbstractHierarchicalFileConfiguration config;
    059
    060        /** The path to the subtree */
    061        private final String path;
    062
    063        /** True if the path ends with '/', false otherwise */
    064        private final boolean trailing;
    065
    066        /** True if the constructor has finished */
    067        private boolean init;
    068
    069        /**
    070         * Constructor
    071         * @param config The Configuration to be wrapped.
    072         * @param path The base path pattern.
    073         */
    074        public PatternSubtreeConfigurationWrapper(AbstractHierarchicalFileConfiguration config, String path)
    075        {
    076            this.config = config;
    077            this.path = path;
    078            this.trailing = path.endsWith("/");
    079            this.init = true;
    080        }
    081
    082        @Override
    083        public Object getReloadLock()
    084        {
    085            return config.getReloadLock();
    086        }
    087
    088        @Override
    089        public void addProperty(String key, Object value)
    090        {
    091            config.addProperty(makePath(key), value);
    092        }
    093
    094        @Override
    095        public void clear()
    096        {
    097            getConfig().clear();
    098        }
    099
    100        @Override
    101        public void clearProperty(String key)
    102        {
    103            config.clearProperty(makePath(key));
    104        }
    105
    106        @Override
    107        public boolean containsKey(String key)
    108        {
    109            return config.containsKey(makePath(key));
    110        }
    111
    112        @Override
    113        public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
    114        {
    115            return config.getBigDecimal(makePath(key), defaultValue);
    116        }
    117
    118        @Override
    119        public BigDecimal getBigDecimal(String key)
    120        {
    121            return config.getBigDecimal(makePath(key));
    122        }
    123
    124        @Override
    125        public BigInteger getBigInteger(String key, BigInteger defaultValue)
    126        {
    127            return config.getBigInteger(makePath(key), defaultValue);
    128        }
    129
    130        @Override
    131        public BigInteger getBigInteger(String key)
    132        {
    133            return config.getBigInteger(makePath(key));
    134        }
    135
    136        @Override
    137        public boolean getBoolean(String key, boolean defaultValue)
    138        {
    139            return config.getBoolean(makePath(key), defaultValue);
    140        }
    141
    142        @Override
    143        public Boolean getBoolean(String key, Boolean defaultValue)
    144        {
    145            return config.getBoolean(makePath(key), defaultValue);
    146        }
    147
    148        @Override
    149        public boolean getBoolean(String key)
    150        {
    151            return config.getBoolean(makePath(key));
    152        }
    153
    154        @Override
    155        public byte getByte(String key, byte defaultValue)
    156        {
    157            return config.getByte(makePath(key), defaultValue);
    158        }
    159
    160        @Override
    161        public Byte getByte(String key, Byte defaultValue)
    162        {
    163            return config.getByte(makePath(key), defaultValue);
    164        }
    165
    166        @Override
    167        public byte getByte(String key)
    168        {
    169            return config.getByte(makePath(key));
    170        }
    171
    172        @Override
    173        public double getDouble(String key, double defaultValue)
    174        {
    175            return config.getDouble(makePath(key), defaultValue);
    176        }
    177
    178        @Override
    179        public Double getDouble(String key, Double defaultValue)
    180        {
    181            return config.getDouble(makePath(key), defaultValue);
    182        }
    183
    184        @Override
    185        public double getDouble(String key)
    186        {
    187            return config.getDouble(makePath(key));
    188        }
    189
    190        @Override
    191        public float getFloat(String key, float defaultValue)
    192        {
    193            return config.getFloat(makePath(key), defaultValue);
    194        }
    195
    196        @Override
    197        public Float getFloat(String key, Float defaultValue)
    198        {
    199            return config.getFloat(makePath(key), defaultValue);
    200        }
    201
    202        @Override
    203        public float getFloat(String key)
    204        {
    205            return config.getFloat(makePath(key));
    206        }
    207
    208        @Override
    209        public int getInt(String key, int defaultValue)
    210        {
    211            return config.getInt(makePath(key), defaultValue);
    212        }
    213
    214        @Override
    215        public int getInt(String key)
    216        {
    217            return config.getInt(makePath(key));
    218        }
    219
    220        @Override
    221        public Integer getInteger(String key, Integer defaultValue)
    222        {
    223            return config.getInteger(makePath(key), defaultValue);
    224        }
    225
    226        @Override
    227        public Iterator<String> getKeys()
    228        {
    229            return config.getKeys(makePath());
    230        }
    231
    232        @Override
    233        public Iterator<String> getKeys(String prefix)
    234        {
    235            return config.getKeys(makePath(prefix));
    236        }
    237
    238        @Override
    239        public List<Object> getList(String key, List<Object> defaultValue)
    240        {
    241            return config.getList(makePath(key), defaultValue);
    242        }
    243
    244        @Override
    245        public List<Object> getList(String key)
    246        {
    247            return config.getList(makePath(key));
    248        }
    249
    250        @Override
    251        public long getLong(String key, long defaultValue)
    252        {
    253            return config.getLong(makePath(key), defaultValue);
    254        }
    255
    256        @Override
    257        public Long getLong(String key, Long defaultValue)
    258        {
    259            return config.getLong(makePath(key), defaultValue);
    260        }
    261
    262        @Override
    263        public long getLong(String key)
    264        {
    265            return config.getLong(makePath(key));
    266        }
    267
    268        @Override
    269        public Properties getProperties(String key)
    270        {
    271            return config.getProperties(makePath(key));
    272        }
    273
    274        @Override
    275        public Object getProperty(String key)
    276        {
    277            return config.getProperty(makePath(key));
    278        }
    279
    280        @Override
    281        public short getShort(String key, short defaultValue)
    282        {
    283            return config.getShort(makePath(key), defaultValue);
    284        }
    285
    286        @Override
    287        public Short getShort(String key, Short defaultValue)
    288        {
    289            return config.getShort(makePath(key), defaultValue);
    290        }
    291
    292        @Override
    293        public short getShort(String key)
    294        {
    295            return config.getShort(makePath(key));
    296        }
    297
    298        @Override
    299        public String getString(String key, String defaultValue)
    300        {
    301            return config.getString(makePath(key), defaultValue);
    302        }
    303
    304        @Override
    305        public String getString(String key)
    306        {
    307            return config.getString(makePath(key));
    308        }
    309
    310        @Override
    311        public String[] getStringArray(String key)
    312        {
    313            return config.getStringArray(makePath(key));
    314        }
    315
    316        @Override
    317        public boolean isEmpty()
    318        {
    319            return getConfig().isEmpty();
    320        }
    321
    322        @Override
    323        public void setProperty(String key, Object value)
    324        {
    325            getConfig().setProperty(key, value);
    326        }
    327
    328        @Override
    329        public Configuration subset(String prefix)
    330        {
    331            return getConfig().subset(prefix);
    332        }
    333
    334        @Override
    335        public Node getRoot()
    336        {
    337            return getConfig().getRoot();
    338        }
    339
    340        @Override
    341        public void setRoot(Node node)
    342        {
    343            if (init)
    344            {
    345                getConfig().setRoot(node);
    346            }
    347            else
    348            {
    349                super.setRoot(node);
    350            }
    351        }
    352
    353        @Override
    354        public ConfigurationNode getRootNode()
    355        {
    356            return getConfig().getRootNode();
    357        }
    358
    359        @Override
    360        public void setRootNode(ConfigurationNode rootNode)
    361        {
    362            if (init)
    363            {
    364                getConfig().setRootNode(rootNode);
    365            }
    366            else
    367            {
    368                super.setRootNode(rootNode);
    369            }
    370        }
    371
    372        @Override
    373        public ExpressionEngine getExpressionEngine()
    374        {
    375            return config.getExpressionEngine();
    376        }
    377
    378        @Override
    379        public void setExpressionEngine(ExpressionEngine expressionEngine)
    380        {
    381            if (init)
    382            {
    383                config.setExpressionEngine(expressionEngine);
    384            }
    385            else
    386            {
    387                super.setExpressionEngine(expressionEngine);
    388            }
    389        }
    390
    391        @Override
    392        public void addNodes(String key, Collection<? extends ConfigurationNode> nodes)
    393        {
    394            getConfig().addNodes(key, nodes);
    395        }
    396
    397        @Override
    398        public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
    399        {
    400            return config.configurationAt(makePath(key), supportUpdates);
    401        }
    402
    403        @Override
    404        public SubnodeConfiguration configurationAt(String key)
    405        {
    406            return config.configurationAt(makePath(key));
    407        }
    408
    409        @Override
    410        public List<HierarchicalConfiguration> configurationsAt(String key)
    411        {
    412            return config.configurationsAt(makePath(key));
    413        }
    414
    415        @Override
    416        public void clearTree(String key)
    417        {
    418            config.clearTree(makePath(key));
    419        }
    420
    421        @Override
    422        public int getMaxIndex(String key)
    423        {
    424            return config.getMaxIndex(makePath(key));
    425        }
    426
    427        @Override
    428        public Configuration interpolatedConfiguration()
    429        {
    430            return getConfig().interpolatedConfiguration();
    431        }
    432
    433        @Override
    434        public void addConfigurationListener(ConfigurationListener l)
    435        {
    436            getConfig().addConfigurationListener(l);
    437        }
    438
    439        @Override
    440        public boolean removeConfigurationListener(ConfigurationListener l)
    441        {
    442            return getConfig().removeConfigurationListener(l);
    443        }
    444
    445        @Override
    446        public Collection<ConfigurationListener> getConfigurationListeners()
    447        {
    448            return getConfig().getConfigurationListeners();
    449        }
    450
    451        @Override
    452        public void clearConfigurationListeners()
    453        {
    454            getConfig().clearConfigurationListeners();
    455        }
    456
    457        @Override
    458        public void addErrorListener(ConfigurationErrorListener l)
    459        {
    460            getConfig().addErrorListener(l);
    461        }
    462
    463        @Override
    464        public boolean removeErrorListener(ConfigurationErrorListener l)
    465        {
    466            return getConfig().removeErrorListener(l);
    467        }
    468
    469        @Override
    470        public void clearErrorListeners()
    471        {
    472            getConfig().clearErrorListeners();
    473        }
    474
    475        public void save(Writer writer) throws ConfigurationException
    476        {
    477            config.save(writer);
    478        }
    479
    480        public void load(Reader reader) throws ConfigurationException
    481        {
    482            config.load(reader);
    483        }
    484
    485        @Override
    486        public Collection<ConfigurationErrorListener> getErrorListeners()
    487        {
    488            return getConfig().getErrorListeners();
    489        }
    490
    491        @Override
    492        protected Object resolveContainerStore(String key)
    493        {
    494            if (recursive.get().booleanValue())
    495            {
    496                return null;
    497            }
    498            recursive.set(Boolean.TRUE);
    499            try
    500            {
    501                return super.resolveContainerStore(key);
    502            }
    503            finally
    504            {
    505                recursive.set(Boolean.FALSE);
    506            }
    507        }
    508
    509        private HierarchicalConfiguration getConfig()
    510        {
    511            return config.configurationAt(makePath());
    512        }
    513
    514        private String makePath()
    515        {
    516            String pathPattern = trailing ? path.substring(0, path.length() - 1) : path;
    517            return getSubstitutor().replace(pathPattern);
    518        }
    519
    520        /*
    521         * Resolve the root expression and then add the item being retrieved. Insert a
    522         * separator character as required.
    523         */
    524        private String makePath(String item)
    525        {
    526            String pathPattern;
    527            if ((item.length() == 0 || item.startsWith("/")) && trailing)
    528            {
    529                pathPattern = path.substring(0, path.length() - 1);
    530            }
    531            else  if (!item.startsWith("/") || !trailing)
    532            {
    533                pathPattern = path + "/";
    534            }
    535            else
    536            {
    537                pathPattern = path;
    538            }
    539            return getSubstitutor().replace(pathPattern) + item;
    540        }
    541    }