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