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 823891 2009-10-10 17:17:44Z rgoers $
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 Object getReloadLock()
082        {
083            return config.getReloadLock();
084        }
085    
086        public void addProperty(String key, Object value)
087        {
088            config.addProperty(makePath(key), value);
089        }
090    
091        public void clear()
092        {
093            getConfig().clear();
094        }
095    
096        public void clearProperty(String key)
097        {
098            config.clearProperty(makePath(key));
099        }
100    
101        public boolean containsKey(String key)
102        {
103            return config.containsKey(makePath(key));
104        }
105    
106        public BigDecimal getBigDecimal(String key, BigDecimal defaultValue)
107        {
108            return config.getBigDecimal(makePath(key), defaultValue);
109        }
110    
111        public BigDecimal getBigDecimal(String key)
112        {
113            return config.getBigDecimal(makePath(key));
114        }
115    
116        public BigInteger getBigInteger(String key, BigInteger defaultValue)
117        {
118            return config.getBigInteger(makePath(key), defaultValue);
119        }
120    
121        public BigInteger getBigInteger(String key)
122        {
123            return config.getBigInteger(makePath(key));
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, Boolean defaultValue)
132        {
133            return config.getBoolean(makePath(key), defaultValue);
134        }
135    
136        public boolean getBoolean(String key)
137        {
138            return config.getBoolean(makePath(key));
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, Byte defaultValue)
147        {
148            return config.getByte(makePath(key), defaultValue);
149        }
150    
151        public byte getByte(String key)
152        {
153            return config.getByte(makePath(key));
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, Double defaultValue)
162        {
163            return config.getDouble(makePath(key), defaultValue);
164        }
165    
166        public double getDouble(String key)
167        {
168            return config.getDouble(makePath(key));
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, Float defaultValue)
177        {
178            return config.getFloat(makePath(key), defaultValue);
179        }
180    
181        public float getFloat(String key)
182        {
183            return config.getFloat(makePath(key));
184        }
185    
186        public int getInt(String key, int defaultValue)
187        {
188            return config.getInt(makePath(key), defaultValue);
189        }
190    
191        public int getInt(String key)
192        {
193            return config.getInt(makePath(key));
194        }
195    
196        public Integer getInteger(String key, Integer defaultValue)
197        {
198            return config.getInteger(makePath(key), defaultValue);
199        }
200    
201        public Iterator getKeys()
202        {
203            return config.getKeys(makePath());
204        }
205    
206        public Iterator getKeys(String prefix)
207        {
208            return config.getKeys(makePath(prefix));
209        }
210    
211        public List getList(String key, List defaultValue)
212        {
213            return config.getList(makePath(key), defaultValue);
214        }
215    
216        public List getList(String key)
217        {
218            return config.getList(makePath(key));
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, Long defaultValue)
227        {
228            return config.getLong(makePath(key), defaultValue);
229        }
230    
231        public long getLong(String key)
232        {
233            return config.getLong(makePath(key));
234        }
235    
236        public Properties getProperties(String key)
237        {
238            return config.getProperties(makePath(key));
239        }
240    
241        public Object getProperty(String key)
242        {
243            return config.getProperty(makePath(key));
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, Short defaultValue)
252        {
253            return config.getShort(makePath(key), defaultValue);
254        }
255    
256        public short getShort(String key)
257        {
258            return config.getShort(makePath(key));
259        }
260    
261        public String getString(String key, String defaultValue)
262        {
263            return config.getString(makePath(key), defaultValue);
264        }
265    
266        public String getString(String key)
267        {
268            return config.getString(makePath(key));
269        }
270    
271        public String[] getStringArray(String key)
272        {
273            return config.getStringArray(makePath(key));
274        }
275    
276        public boolean isEmpty()
277        {
278            return getConfig().isEmpty();
279        }
280    
281        public void setProperty(String key, Object value)
282        {
283            getConfig().setProperty(key, value);
284        }
285    
286        public Configuration subset(String prefix)
287        {
288            return getConfig().subset(prefix);
289        }
290    
291        public Node getRoot()
292        {
293            return getConfig().getRoot();
294        }
295    
296        public void setRoot(Node node)
297        {
298            if (init)
299            {
300                getConfig().setRoot(node);
301            }
302            else
303            {
304                super.setRoot(node);
305            }
306        }
307    
308        public ConfigurationNode getRootNode()
309        {
310            return getConfig().getRootNode();
311        }
312    
313        public void setRootNode(ConfigurationNode rootNode)
314        {
315            if (init)
316            {
317                getConfig().setRootNode(rootNode);
318            }
319            else
320            {
321                super.setRootNode(rootNode);
322            }
323        }
324    
325        public ExpressionEngine getExpressionEngine()
326        {
327            return config.getExpressionEngine();
328        }
329    
330        public void setExpressionEngine(ExpressionEngine expressionEngine)
331        {
332            if (init)
333            {
334                config.setExpressionEngine(expressionEngine);
335            }
336            else
337            {
338                super.setExpressionEngine(expressionEngine);
339            }
340        }
341    
342        public void addNodes(String key, Collection nodes)
343        {
344            getConfig().addNodes(key, nodes);
345        }
346    
347        public SubnodeConfiguration configurationAt(String key, boolean supportUpdates)
348        {
349            return config.configurationAt(makePath(key), supportUpdates);
350        }
351    
352        public SubnodeConfiguration configurationAt(String key)
353        {
354            return config.configurationAt(makePath(key));
355        }
356    
357        public List configurationsAt(String key)
358        {
359            return config.configurationsAt(makePath(key));
360        }
361    
362        public void clearTree(String key)
363        {
364            config.clearTree(makePath(key));
365        }
366    
367        public int getMaxIndex(String key)
368        {
369            return config.getMaxIndex(makePath(key));
370        }
371    
372        public Configuration interpolatedConfiguration()
373        {
374            return getConfig().interpolatedConfiguration();
375        }
376    
377        public void addConfigurationListener(ConfigurationListener l)
378        {
379            getConfig().addConfigurationListener(l);
380        }
381    
382        public boolean removeConfigurationListener(ConfigurationListener l)
383        {
384            return getConfig().removeConfigurationListener(l);
385        }
386    
387        public Collection getConfigurationListeners()
388        {
389            return getConfig().getConfigurationListeners();
390        }
391    
392        public void clearConfigurationListeners()
393        {
394            getConfig().clearConfigurationListeners();
395        }
396    
397        public void addErrorListener(ConfigurationErrorListener l)
398        {
399            getConfig().addErrorListener(l);
400        }
401    
402        public boolean removeErrorListener(ConfigurationErrorListener l)
403        {
404            return getConfig().removeErrorListener(l);
405        }
406    
407        public void clearErrorListeners()
408        {
409            getConfig().clearErrorListeners();
410        }
411    
412        public void save(Writer writer) throws ConfigurationException
413        {
414            config.save(writer);
415        }
416    
417        public void load(Reader reader) throws ConfigurationException
418        {
419            config.load(reader);
420        }
421    
422        public Collection getErrorListeners()
423        {
424            return getConfig().getErrorListeners();
425        }
426    
427        protected Object resolveContainerStore(String key)
428        {
429            if (((Boolean) recursive.get()).booleanValue())
430            {
431                return null;
432            }
433            recursive.set(Boolean.TRUE);
434            try
435            {
436                return super.resolveContainerStore(key);
437            }
438            finally
439            {
440                recursive.set(Boolean.FALSE);
441            }
442        }
443    
444        private HierarchicalConfiguration getConfig()
445        {
446            return config.configurationAt(makePath());
447        }
448    
449        private String makePath()
450        {
451            String pathPattern = trailing ? path.substring(0, path.length() - 1) : path;
452            return getSubstitutor().replace(pathPattern);
453        }
454    
455        /*
456         * Resolve the root expression and then add the item being retrieved. Insert a
457         * separator character as required.
458         */
459        private String makePath(String item)
460        {
461            String pathPattern;
462            if ((item.length() == 0 || item.startsWith("/")) && trailing)
463            {
464                pathPattern = path.substring(0, path.length() - 1);
465            }
466            else  if (!item.startsWith("/") || !trailing)
467            {
468                pathPattern = path + "/";
469            }
470            else
471            {
472                pathPattern = path;
473            }
474            return getSubstitutor().replace(pathPattern) + item;
475        }
476    }