Return-Path: X-Original-To: apmail-commons-commits-archive@minotaur.apache.org Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id E0C4BE494 for ; Tue, 25 Dec 2012 20:15:35 +0000 (UTC) Received: (qmail 4340 invoked by uid 500); 25 Dec 2012 20:15:35 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 4291 invoked by uid 500); 25 Dec 2012 20:15:35 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 4284 invoked by uid 99); 25 Dec 2012 20:15:35 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 25 Dec 2012 20:15:35 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 25 Dec 2012 20:15:21 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id D0A6623888EA; Tue, 25 Dec 2012 20:14:58 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1425770 [1/2] - in /commons/proper/configuration/trunk/src: main/java/org/apache/commons/configuration/ main/java/org/apache/commons/configuration/interpol/ test/java/org/apache/commons/configuration/ test/java/org/apache/commons/configura... Date: Tue, 25 Dec 2012 20:14:57 -0000 To: commits@commons.apache.org From: oheger@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20121225201458.D0A6623888EA@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: oheger Date: Tue Dec 25 20:14:56 2012 New Revision: 1425770 URL: http://svn.apache.org/viewvc?rev=1425770&view=rev Log: [CONFIGURATION-518] Reworked ConfigurationInterpolator class. Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java (with props) commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java (with props) commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestConfigurationLookup.java (with props) Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDefaultConfigurationBuilder.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestDynamicCombinedConfiguration.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestPropertyConverter.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestSubnodeConfiguration.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestSubsetConfiguration.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestVFSConfigurationBuilder.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestWebdavConfigurationBuilder.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestConfigurationInterpolator.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestConstantLookup.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestEnvironmentLookup.java commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/interpol/TestExprLookup.java commons/proper/configuration/trunk/src/test/resources/testExpression.xml Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java Tue Dec 25 20:14:56 2012 @@ -24,8 +24,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; @@ -33,11 +35,11 @@ import org.apache.commons.configuration. import org.apache.commons.configuration.event.ConfigurationErrorEvent; import org.apache.commons.configuration.event.ConfigurationErrorListener; import org.apache.commons.configuration.interpol.ConfigurationInterpolator; +import org.apache.commons.configuration.interpol.DefaultLookups; +import org.apache.commons.configuration.interpol.Lookup; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.ClassUtils; import org.apache.commons.lang.ObjectUtils; -import org.apache.commons.lang.text.StrLookup; -import org.apache.commons.lang.text.StrSubstitutor; import org.apache.commons.logging.Log; import org.apache.commons.logging.impl.NoOpLog; @@ -142,8 +144,8 @@ public abstract class AbstractConfigurat */ private boolean throwExceptionOnMissing; - /** Stores a reference to the object that handles variable interpolation.*/ - private StrSubstitutor substitutor; + /** Stores a reference to the object that handles variable interpolation. */ + private volatile ConfigurationInterpolator interpolator; /** Stores the logger.*/ private Log log; @@ -154,6 +156,7 @@ public abstract class AbstractConfigurat public AbstractConfiguration() { setLogger(null); + installDefaultInterpolator(); } /** @@ -282,62 +285,63 @@ public abstract class AbstractConfigurat } /** - * Returns the object that is responsible for variable interpolation. + * Returns the {@code ConfigurationInterpolator} object that manages the + * lookup objects for resolving variables. * - * @return the object responsible for variable interpolation + * @return the {@code ConfigurationInterpolator} associated with this + * configuration * @since 1.4 */ - public synchronized StrSubstitutor getSubstitutor() + public ConfigurationInterpolator getInterpolator() { - if (substitutor == null) - { - substitutor = new StrSubstitutor(createInterpolator()); - } - return substitutor; + return interpolator; } /** - * Returns the {@code ConfigurationInterpolator} object that manages - * the lookup objects for resolving variables. Note: If this - * object is manipulated (e.g. new lookup objects added), synchronization - * has to be manually ensured. Because - * {@code ConfigurationInterpolator} is not thread-safe concurrent - * access to properties of this configuration instance (which causes the - * interpolator to be invoked) may cause race conditions. + * {@inheritDoc} This implementation sets the passed in object without + * further modifications. A null argument is allowed; this disables + * interpolation. * - * @return the {@code ConfigurationInterpolator} associated with this - * configuration - * @since 1.4 + * @since 2.0 */ - public ConfigurationInterpolator getInterpolator() + public final void setInterpolator(ConfigurationInterpolator ci) { - return (ConfigurationInterpolator) getSubstitutor() - .getVariableResolver(); + interpolator = ci; } /** - * Creates the interpolator object that is responsible for variable - * interpolation. This method is invoked on first access of the - * interpolation features. It creates a new instance of - * {@code ConfigurationInterpolator} and sets the default lookup - * object to an implementation that queries this configuration. + * {@inheritDoc} This implementation creates a new + * {@code ConfigurationInterpolator} instance and initializes it with the + * given {@code Lookup} objects. In addition, it adds a specialized default + * {@code Lookup} object which queries this {@code Configuration}. * - * @return the newly created interpolator object - * @since 1.4 + * @since 2.0 */ - protected ConfigurationInterpolator createInterpolator() + public final void installInterpolator( + Map prefixLookups, + Collection defLookups) { - ConfigurationInterpolator interpol = new ConfigurationInterpolator(); - interpol.setDefaultLookup(new StrLookup() + ConfigurationInterpolator ci = new ConfigurationInterpolator(); + ci.registerLookups(prefixLookups); + ci.addDefaultLookups(defLookups); + ci.addDefaultLookup(new ConfigurationLookup(this)); + setInterpolator(ci); + } + + /** + * Creates a default {@code ConfigurationInterpolator} which is initialized + * with all default {@code Lookup} objects. This method is called by the + * constructor. It ensures that default interpolation works for every new + * configuration instance. + */ + private void installDefaultInterpolator() + { + Map lookups = new HashMap(); + for (DefaultLookups l : DefaultLookups.values()) { - @Override - public String lookup(String var) - { - Object prop = resolveContainerStore(var); - return (prop != null) ? prop.toString() : null; - } - }); - return interpol; + lookups.put(l.getPrefix(), l.getLookup()); + } + installInterpolator(lookups, null); } /** @@ -438,37 +442,18 @@ public abstract class AbstractConfigurat } /** - * Returns the interpolated value. Non String values are returned without change. + * Returns the interpolated value. This implementation delegates to the + * current {@code ConfigurationInterpolator}. If no + * {@code ConfigurationInterpolator} is set, the passed in value is returned + * without changes. * * @param value the value to interpolate - * - * @return returns the value with variables substituted + * @return the value with variables substituted */ protected Object interpolate(Object value) { - return PropertyConverter.interpolate(value, this); - } - - /** - * Recursive handler for multple levels of interpolation. - * - * When called the first time, priorVariables should be null. - * - * @param base string with the ${key} variables - * @param priorVariables serves two purposes: to allow checking for loops, - * and creating a meaningful exception message should a loop occur. It's - * 0'th element will be set to the value of base from the first call. All - * subsequent interpolated variables are added afterward. - * - * @return the string with the interpolation taken care of - * @deprecated Interpolation is now handled by - * {@link PropertyConverter}; this method will no longer be - * called - */ - @Deprecated - protected String interpolateHelper(String base, List priorVariables) - { - return base; // just a dummy implementation + ConfigurationInterpolator ci = getInterpolator(); + return (ci != null) ? ci.interpolate(value) : value; } public Configuration subset(String prefix) Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/Configuration.java Tue Dec 25 20:14:56 2012 @@ -17,6 +17,12 @@ package org.apache.commons.configuration; +import java.util.Collection; +import java.util.Map; + +import org.apache.commons.configuration.interpol.ConfigurationInterpolator; +import org.apache.commons.configuration.interpol.Lookup; + /** *

The main Configuration interface.

@@ -124,4 +130,43 @@ public interface Configuration extends I * Remove all properties from the configuration. */ void clear(); + + /** + * Returns the {@code ConfigurationInterpolator} object used by this + * {@code Configuration}. This object is responsible for variable + * substitution. + * + * @return the {@code ConfigurationInterpolator} (can be null) + */ + ConfigurationInterpolator getInterpolator(); + + /** + * Sets the {@code ConfigurationInterpolator} object to be used by this + * {@code Configuration}. This object is invoked for each access of a string + * property in order to substitute variables which may be contained. The + * argument can be null to disable interpolation at all. + * + * @param ci the new {@code ConfigurationInterpolator} + */ + void setInterpolator(ConfigurationInterpolator ci); + + /** + * Creates and installs a new {@code ConfigurationInterpolator} for this + * {@code Configuration} based on the passed in arguments. This method + * creates a default {@code ConfigurationInterpolator} instance and + * initializes it with the passed in {@code Lookup} objects. It also adds a + * special default {@code Lookup} object that tries to resolve variables by + * matching them with properties contained in this {@code Configuration}. + * This is also the main difference to the + * {@link #setConfigurationInterpolator(ConfigurationInterpolator)} method + * which sets the passed in object as is without adding this special lookup. + * + * @param prefixLookups the map with {@code Lookup} objects associated with + * specific prefixes (can be null) + * @param defLookups a collection with default {@code Lookup} objects (can + * be null) + * @see ConfigurationInterpolator + */ + void installInterpolator(Map prefixLookups, + Collection defLookups); } Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java?rev=1425770&view=auto ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java (added) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java Tue Dec 25 20:14:56 2012 @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.configuration; + +import org.apache.commons.configuration.interpol.Lookup; + +/** + *

+ * A specialized implementation of the {@code Lookup} interface which uses a + * {@code Configuration} object to resolve variables. + *

+ *

+ * This class is passed an {@link ImmutableConfiguration} object at construction + * time. In its implementation of the {@code lookup()} method it simply queries + * this configuration for the passed in variable name. So the keys passed to + * {@code lookup()} are mapped directly to configuration properties. + *

+ * + * @version $Id$ + * @since 2.0 + */ +public class ConfigurationLookup implements Lookup +{ + /** The configuration to which lookups are delegated. */ + private final ImmutableConfiguration configuration; + + /** + * Creates a new instance of {@code ConfigurationLookup} and sets the + * associated {@code ImmutableConfiguration}. + * + * @param config the configuration to use for lookups (must not be + * null) + * @throws IllegalArgumentException if the configuration is null + */ + public ConfigurationLookup(ImmutableConfiguration config) + { + if (config == null) + { + throw new IllegalArgumentException( + "Configuration must not be null!"); + } + configuration = config; + } + + /** + * Returns the {@code ImmutableConfiguration} used by this object. + * + * @return the associated {@code ImmutableConfiguration} + */ + public ImmutableConfiguration getConfiguration() + { + return configuration; + } + + /** + * {@inheritDoc} This implementation calls {@code getProperty()} on the + * associated configuration. The return value is directly returned. Note + * that this may be a complex object, e.g. a collection or an array. + */ + public Object lookup(String variable) + { + return getConfiguration().getProperty(variable); + } +} Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/ConfigurationLookup.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DefaultConfigurationBuilder.java Tue Dec 25 20:14:56 2012 @@ -33,6 +33,7 @@ import org.apache.commons.configuration. import org.apache.commons.configuration.event.ConfigurationErrorListener; import org.apache.commons.configuration.event.ConfigurationListener; import org.apache.commons.configuration.interpol.ConfigurationInterpolator; +import org.apache.commons.configuration.interpol.Lookup; import org.apache.commons.configuration.resolver.CatalogResolver; import org.apache.commons.configuration.resolver.EntityRegistry; import org.apache.commons.configuration.resolver.EntityResolverSupport; @@ -40,7 +41,6 @@ import org.apache.commons.configuration. import org.apache.commons.configuration.tree.DefaultExpressionEngine; import org.apache.commons.configuration.tree.OverrideCombiner; import org.apache.commons.configuration.tree.UnionCombiner; -import org.apache.commons.lang.text.StrLookup; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.xml.sax.EntityResolver; @@ -189,7 +189,7 @@ import org.xml.sax.EntityResolver; * section. *
  * <lookups>
- *   <lookup config-prefix="prefix" config-class="StrLookup fully qualified class name"/>
+ *   <lookup config-prefix="prefix" config-class="Lookup fully qualified class name"/>
  * </lookups>
  * 
*

@@ -415,21 +415,18 @@ public class DefaultConfigurationBuilder private static final long serialVersionUID = -3113777854714492123L; /** - * A specialized {@code StrLookup} object which operates on the combined + * A specialized {@code Lookup} object which operates on the combined * configuration constructed by this builder. This object is used as * default lookup for {@code ConfigurationInterpolator} objects assigned to * newly created configuration objects. */ - private final StrLookup combinedConfigLookup = new StrLookup() + private final Lookup combinedConfigLookup = new Lookup() { - @Override - public String lookup(String key) + public Object lookup(String key) { if (constructedConfiguration != null) { - Object value = - constructedConfiguration.resolveContainerStore(key); - return (value != null) ? value.toString() : null; + return constructedConfiguration.resolveContainerStore(key); } return null; } @@ -657,6 +654,7 @@ public class DefaultConfigurationBuilder result.setNodeCombiner(new OverrideCombiner()); } + result.getInterpolator().registerLookups(getInterpolator().getLookups()); return result; } @@ -767,9 +765,8 @@ public class DefaultConfigurationBuilder { XMLBeanDeclaration decl = new XMLBeanDeclaration(config); String key = config.getString(KEY_LOOKUP_KEY); - StrLookup lookup = (StrLookup) BeanHelper.createBean(decl); + Lookup lookup = (Lookup) BeanHelper.createBean(decl); BeanHelper.setProperty(lookup, "configuration", this); - ConfigurationInterpolator.registerGlobalLookup(key, lookup); this.getInterpolator().registerLookup(key, lookup); } } @@ -813,7 +810,6 @@ public class DefaultConfigurationBuilder EntityResolver resolver = (EntityResolver) BeanHelper.createBean(decl, CatalogResolver.class); BeanHelper.setProperty(resolver, "fileSystem", getFileSystem()); BeanHelper.setProperty(resolver, "baseDir", getBasePath()); - BeanHelper.setProperty(resolver, "substitutor", getSubstitutor()); setEntityResolver(resolver); } } @@ -1387,7 +1383,7 @@ public class DefaultConfigurationBuilder AbstractConfiguration config) { ConfigurationInterpolator parent = new ConfigurationInterpolator(); - parent.setDefaultLookup(decl.getConfigurationBuilder().combinedConfigLookup); + parent.addDefaultLookup(decl.getConfigurationBuilder().combinedConfigLookup); config.getInterpolator().setParentInterpolator(parent); } } Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/DynamicCombinedConfiguration.java Tue Dec 25 20:14:56 2012 @@ -35,7 +35,6 @@ import org.apache.commons.configuration. import org.apache.commons.configuration.tree.ConfigurationNode; import org.apache.commons.configuration.tree.ExpressionEngine; import org.apache.commons.configuration.tree.NodeCombiner; -import org.apache.commons.lang.text.StrSubstitutor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -84,7 +83,7 @@ public class DynamicCombinedConfiguratio private String loggerName = DynamicCombinedConfiguration.class.getName(); /** The object for handling variable substitution in key patterns. */ - private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator()); + private final ConfigurationInterpolator localSubst; /** * Creates a new instance of {@code DynamicCombinedConfiguration} and @@ -99,6 +98,7 @@ public class DynamicCombinedConfiguratio setNodeCombiner(comb); setIgnoreReloadExceptions(false); setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class)); + localSubst = initLocalInterpolator(); } /** @@ -112,6 +112,7 @@ public class DynamicCombinedConfiguratio super(); setIgnoreReloadExceptions(false); setLogger(LogFactory.getLog(DynamicCombinedConfiguration.class)); + localSubst = initLocalInterpolator(); } public void setKeyPattern(String pattern) @@ -805,7 +806,7 @@ public class DynamicCombinedConfiguratio private CombinedConfiguration getCurrentConfig() { - String key = localSubst.replace(keyPattern); + String key = String.valueOf(localSubst.interpolate(keyPattern)); CombinedConfiguration config = configs.get(key); // The double-checked works here due to the Thread guarantees of ConcurrentMap. if (config == null) @@ -854,6 +855,19 @@ public class DynamicCombinedConfiguratio } /** + * Creates a {@code ConfigurationInterpolator} instance for performing local + * variable substitutions. + * + * @return the {@code ConfigurationInterpolator} + */ + private ConfigurationInterpolator initLocalInterpolator() + { + ConfigurationInterpolator ci = new ConfigurationInterpolator(); + ci.registerLookups(getInterpolator().getLookups()); + return ci; + } + + /** * Internal class that identifies each Configuration. */ static class ConfigData Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/MultiFileHierarchicalConfiguration.java Tue Dec 25 20:14:56 2012 @@ -41,7 +41,6 @@ import org.apache.commons.configuration. import org.apache.commons.configuration.resolver.EntityResolverSupport; import org.apache.commons.configuration.tree.ConfigurationNode; import org.apache.commons.configuration.tree.ExpressionEngine; -import org.apache.commons.lang.text.StrSubstitutor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.xml.sax.EntityResolver; @@ -106,7 +105,7 @@ public class MultiFileHierarchicalConfig private EntityResolver entityResolver; /** The internally used helper object for variable substitution. */ - private StrSubstitutor localSubst = new StrSubstitutor(new ConfigurationInterpolator()); + private final ConfigurationInterpolator localSubst; /** * Default Constructor. @@ -116,6 +115,7 @@ public class MultiFileHierarchicalConfig super(); this.init = true; setLogger(LogFactory.getLog(loggerName)); + localSubst = initLocalInterpolator(); } /** @@ -128,6 +128,7 @@ public class MultiFileHierarchicalConfig this.pattern = pathPattern; this.init = true; setLogger(LogFactory.getLog(loggerName)); + localSubst = initLocalInterpolator(); } public void setLoggerName(String name) @@ -730,7 +731,7 @@ public class MultiFileHierarchicalConfig */ public void removeConfiguration() { - String path = getSubstitutor().replace(pattern); + String path = String.valueOf(getInterpolator().interpolate(pattern)); configurationsMap.remove(path); } @@ -746,7 +747,7 @@ public class MultiFileHierarchicalConfig { throw new ConfigurationRuntimeException("File pattern must be defined"); } - String path = localSubst.replace(pattern); + String path = String.valueOf(localSubst.interpolate(pattern)); if (configurationsMap.containsKey(path)) { @@ -848,4 +849,16 @@ public class MultiFileHierarchicalConfig } } + /** + * Creates a {@code ConfigurationInterpolator} instance for performing local + * variable substitutions. + * + * @return the {@code ConfigurationInterpolator} + */ + private ConfigurationInterpolator initLocalInterpolator() + { + ConfigurationInterpolator ci = new ConfigurationInterpolator(); + ci.registerLookups(getInterpolator().getLookups()); + return ci; + } } Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PatternSubtreeConfigurationWrapper.java Tue Dec 25 20:14:56 2012 @@ -495,7 +495,7 @@ public class PatternSubtreeConfiguration private String makePath() { String pathPattern = trailing ? path.substring(0, path.length() - 1) : path; - return getSubstitutor().replace(pathPattern); + return substitute(pathPattern); } /* @@ -517,6 +517,19 @@ public class PatternSubtreeConfiguration { pathPattern = path; } - return getSubstitutor().replace(pathPattern) + item; + return substitute(pathPattern) + item; + } + + /** + * Uses this configuration's {@code ConfigurationInterpolator} to perform + * variable substitution on the given pattern string. + * + * @param pattern the pattern string + * @return the string with variables replaced + */ + private String substitute(String pattern) + { + Object value = getInterpolator().interpolate(pattern); + return (value != null) ? value.toString() : null; } } Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/PropertyConverter.java Tue Dec 25 20:14:56 2012 @@ -38,6 +38,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; +import org.apache.commons.configuration.interpol.ConfigurationInterpolator; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; @@ -994,25 +995,20 @@ public final class PropertyConverter } /** - * Performs interpolation of the specified value. This method checks if the - * given value contains variables of the form ${...}. If - * this is the case, all occurrences will be substituted by their current - * values. + * Performs interpolation of the specified value using the given + * {@code Configuration} object. This method checks if the given + * {@code Configuration} has a {@link ConfigurationInterpolator} object. If + * so, it is called to perform interpolation. Otherwise, the passed in value + * is return unchanged. * * @param value the value to be interpolated * @param config the current configuration object * @return the interpolated value */ - public static Object interpolate(Object value, AbstractConfiguration config) + public static Object interpolate(Object value, Configuration config) { - if (value instanceof String) - { - return config.getSubstitutor().replace((String) value); - } - else - { - return value; - } + ConfigurationInterpolator interpolator = config.getInterpolator(); + return (interpolator != null) ? interpolator.interpolate(value) : value; } /** Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubnodeConfiguration.java Tue Dec 25 20:14:56 2012 @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.apache.commons.configuration.interpol.ConfigurationInterpolator; import org.apache.commons.configuration.reloading.Reloadable; import org.apache.commons.configuration.tree.ConfigurationNode; @@ -152,6 +151,7 @@ public class SubnodeConfiguration extend setRootNode(root); this.parent = parent; initFromParent(parent); + initInterpolator(); } /** @@ -350,16 +350,12 @@ public class SubnodeConfiguration extend } /** - * Creates a ConfigurationInterpolator with a chain to the parent's - * interpolator. - * - * @return the new interpolator + * Initializes the {@code ConfigurationInterpolator} for this sub configuration. + * This is a standard {@code ConfigurationInterpolator} which also references + * the {@code ConfigurationInterpolator} of the parent configuration. */ - @Override - protected ConfigurationInterpolator createInterpolator() + private void initInterpolator() { - ConfigurationInterpolator interpolator = super.createInterpolator(); - interpolator.setParentInterpolator(getParent().getInterpolator()); - return interpolator; + getInterpolator().setParentInterpolator(getParent().getInterpolator()); } } Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/SubsetConfiguration.java Tue Dec 25 20:14:56 2012 @@ -19,8 +19,6 @@ package org.apache.commons.configuration import java.util.Iterator; -import org.apache.commons.configuration.interpol.ConfigurationInterpolator; - /** *

A subset of another configuration. The new Configuration object contains * every key from the parent Configuration that starts with prefix. The prefix @@ -46,27 +44,35 @@ public class SubsetConfiguration extends /** * Create a subset of the specified configuration * - * @param parent The parent configuration + * @param parent The parent configuration (must not be null) * @param prefix The prefix used to select the properties + * @throws IllegalArgumentException if the parent configuration is null */ public SubsetConfiguration(Configuration parent, String prefix) { - this.parent = parent; - this.prefix = prefix; + this(parent, prefix, null); } /** * Create a subset of the specified configuration * - * @param parent The parent configuration + * @param parent The parent configuration (must not be null) * @param prefix The prefix used to select the properties * @param delimiter The prefix delimiter + * @throws IllegalArgumentException if the parent configuration is null */ public SubsetConfiguration(Configuration parent, String prefix, String delimiter) { + if (parent == null) + { + throw new IllegalArgumentException( + "Parent configuration must not be null!"); + } + this.parent = parent; this.prefix = prefix; this.delimiter = delimiter; + initInterpolator(); } /** @@ -192,32 +198,6 @@ public class SubsetConfiguration extends return new SubsetIterator(parent.getKeys(prefix)); } - @Override - protected Object interpolate(Object base) - { - if (delimiter == null && "".equals(prefix)) - { - return super.interpolate(base); - } - else - { - SubsetConfiguration config = new SubsetConfiguration(parent, ""); - ConfigurationInterpolator interpolator = config.getInterpolator(); - getInterpolator().registerLocalLookups(interpolator); - if (parent instanceof AbstractConfiguration) - { - interpolator.setParentInterpolator(((AbstractConfiguration) parent).getInterpolator()); - } - return config.interpolate(base); - } - } - - @Override - protected String interpolate(String base) - { - return super.interpolate(base); - } - /** * {@inheritDoc} * @@ -328,6 +308,15 @@ public class SubsetConfiguration extends } } + /** + * Initializes the {@code ConfigurationInterpolator} for this sub configuration. + * This is a standard {@code ConfigurationInterpolator} which also references + * the {@code ConfigurationInterpolator} of the parent configuration. + */ + private void initInterpolator() + { + getInterpolator().setParentInterpolator(getParent().getInterpolator()); + } /** * A specialized iterator to be returned by the {@code getKeys()} Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConfigurationInterpolator.java Tue Dec 25 20:14:56 2012 @@ -16,11 +16,18 @@ */ package org.apache.commons.configuration.interpol; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; -import org.apache.commons.lang.text.StrLookup; +import org.apache.commons.lang3.text.StrLookup; +import org.apache.commons.lang3.text.StrSubstitutor; /** *

@@ -28,140 +35,109 @@ import org.apache.commons.lang.text.StrL * objects. *

*

- * Each instance of {@code AbstractConfiguration} is associated with an - * object of this class. All interpolation tasks are delegated to this object. + * Each instance of {@code AbstractConfiguration} is associated with an object + * of this class. All interpolation tasks are delegated to this object. *

*

- * {@code ConfigurationInterpolator} works together with the - * {@code StrSubstitutor} class from Commons Lang. By extending - * {@code StrLookup} it is able to provide values for variables that - * appear in expressions. + * {@code ConfigurationInterpolator} internally uses the {@code StrSubstitutor} + * class from Commons Lang. Thus it + * supports the same syntax of variable expressions. *

*

* The basic idea of this class is that it can maintain a set of primitive - * {@code StrLookup} objects, each of which is identified by a special - * prefix. The variables to be processed have the form - * ${prefix:name}. {@code ConfigurationInterpolator} will - * extract the prefix and determine, which primitive lookup object is registered - * for it. Then the name of the variable is passed to this object to obtain the - * actual value. It is also possible to define a default lookup object, which - * will be used for variables that do not have a prefix or that cannot be - * resolved by their associated lookup object. + * {@link Lookup} objects, each of which is identified by a special prefix. The + * variables to be processed have the form ${prefix:name}. + * {@code ConfigurationInterpolator} will extract the prefix and determine, + * which primitive lookup object is registered for it. Then the name of the + * variable is passed to this object to obtain the actual value. It is also + * possible to define an arbitrary number of default lookup objects, which are + * used for variables that do not have a prefix or that cannot be resolved by + * their associated lookup object. When adding default lookup objects their + * order matters; they are queried in this order, and the first non-null + * variable value is used. *

*

- * When a new instance of this class is created it is initialized with a default - * set of primitive lookup objects. This set can be customized using the static - * methods {@code registerGlobalLookup()} and - * {@code deregisterGlobalLookup()}. Per default it contains the - * following standard lookup objects: + * After an instance has been created it does not contain any {@code Lookup} + * objects. The current set of lookup objects can be modified using the + * {@code registerLookup()} and {@code deregisterLookup()} methods. Default + * lookup objects (that are invoked for variables without a prefix) can be added + * or removed with the {@code addDefaultLookup()} and + * {@code removeDefaultLookup()} methods respectively. (When a + * {@code ConfigurationInterpolator} instance is created by a configuration + * object, a default lookup object is added pointing to the configuration + * itself, so that variables are resolved using the configuration's properties.) *

*

- * - * - * - * - * - * - * - * - * - * - * - * - * - *
PrefixLookup object
sysWith this prefix a lookup object is associated that is able to resolve - * system properties.
constThe {@code const} prefix indicates that a variable is to be - * interpreted as a constant member field of a class (i.e. a field with the - * static final modifiers). The name of the variable must be of the form - * {@code .}, e.g. - * {@code org.apache.commons.configuration.interpol.ConfigurationInterpolator.PREFIX_CONSTANTS}. - *
+ * The default usage scenario is that on a fully initialized instance the + * {@code interpolate()} method is called. It is passed an object value which + * may contain variables. All these variables are substituted if they can be + * resolved. The result is the passed in value with variables replaced. + * Alternatively, the {@code resolve()} method can be called to obtain the + * values of specific variables without performing interpolation. *

*

- * After an instance has been created the current set of lookup objects can be - * modified using the {@code registerLookup()} and - * {@code deregisterLookup()} methods. The default lookup object (that is - * invoked for variables without a prefix) can be set with the - * {@code setDefaultLookup()} method. (If a - * {@code ConfigurationInterpolator} instance is created by a - * configuration object, this lookup points to the configuration itself, so that - * variables are resolved using the configuration's properties. This ensures - * backward compatibility to earlier version of Commons Configuration.) - *

- *

- * Implementation node: Instances of this class are not thread-safe related to - * modifications of their current set of registered lookup objects. It is - * intended that each instance is associated with a single - * {@code Configuration} object and used for its interpolation tasks. + * Implementation node: This class is thread-safe. Lookup objects can be added + * or removed at any time concurrent to interpolation operations. *

* * @version $Id$ * @since 1.4 * @author Commons - * Configuration team + * href="http://commons.apache.org/configuration/team-list.html">Commons + * Configuration team */ -public class ConfigurationInterpolator extends StrLookup +public class ConfigurationInterpolator { - /** - * Constant for the prefix of the standard lookup object for resolving - * system properties. - */ - public static final String PREFIX_SYSPROPERTIES = "sys"; - - /** - * Constant for the prefix of the standard lookup object for resolving - * constant values. - */ - public static final String PREFIX_CONSTANTS = "const"; - - /** - * Constant for the prefix of the standard lookup object for resolving - * environment properties. - * @since 1.7 - */ - public static final String PREFIX_ENVIRONMENT = "env"; - /** Constant for the prefix separator. */ private static final char PREFIX_SEPARATOR = ':'; - /** A map with the globally registered lookup objects. */ - private static Map globalLookups; + /** A map with the currently registered lookup objects. */ + private final Map prefixLookups; - /** A map with the locally registered lookup objects. */ - private Map localLookups; + /** Stores the default lookup objects. */ + private final List defaultLookups; - /** Stores the default lookup object. */ - private StrLookup defaultLookup; + /** The helper object performing variable substitution. */ + private final StrSubstitutor substitutor; /** Stores a parent interpolator objects if the interpolator is nested hierarchically. */ - private ConfigurationInterpolator parentInterpolator; + private volatile ConfigurationInterpolator parentInterpolator; /** * Creates a new instance of {@code ConfigurationInterpolator}. */ public ConfigurationInterpolator() { - synchronized (globalLookups) - { - localLookups = new HashMap(globalLookups); - } + prefixLookups = new ConcurrentHashMap(); + defaultLookups = new CopyOnWriteArrayList(); + substitutor = initSubstitutor(); } /** - * Registers the given lookup object for the specified prefix globally. This - * means that all instances that are created later will use this lookup - * object for this prefix. If for this prefix a lookup object is already - * registered, the new lookup object will replace the old one. Note that the - * lookup objects registered here will be shared between multiple clients. - * So they should be thread-safe. + * Returns a map with the currently registered {@code Lookup} objects and + * their prefixes. This is a snapshot copy of the internally used map. So + * modifications of this map do not effect this instance. + * + * @return a copy of the map with the currently registered {@code Lookup} + * objects + */ + public Map getLookups() + { + return new HashMap(prefixLookups); + } + + /** + * Registers the given {@code Lookup} object for the specified prefix at + * this instance. From now on this lookup object will be used for variables + * that have the specified prefix. * * @param prefix the variable prefix (must not be null) - * @param lookup the lookup object to be used for this prefix (must not be - * null) + * @param lookup the {@code Lookup} object to be used for this prefix (must + * not be null) + * @throws IllegalArgumentException if either the prefix or the + * {@code Lookup} object is null */ - public static void registerGlobalLookup(String prefix, StrLookup lookup) + public void registerLookup(String prefix, Lookup lookup) { if (prefix == null) { @@ -173,115 +149,191 @@ public class ConfigurationInterpolator e throw new IllegalArgumentException( "Lookup object must not be null!"); } - synchronized (globalLookups) + prefixLookups.put(prefix, lookup); + } + + /** + * Registers all {@code Lookup} objects in the given map with their prefixes + * at this {@code ConfigurationInterpolator}. Using this method multiple + * {@code Lookup} objects can be registered at once. If the passed in map is + * null, this method does not have any effect. + * + * @param lookups the map with lookups to register (may be null) + * @throws IllegalArgumentException if the map contains entries + */ + public void registerLookups(Map lookups) + { + if (lookups != null) { - globalLookups.put(prefix, lookup); + prefixLookups.putAll(lookups); } } /** - * Deregisters the global lookup object for the specified prefix. This means - * that this lookup object won't be available for later created instances - * any more. For already existing instances this operation does not have any - * impact. + * Deregisters the {@code Lookup} object for the specified prefix at this + * instance. It will be removed from this instance. * * @param prefix the variable prefix * @return a flag whether for this prefix a lookup object had been - * registered + * registered */ - public static boolean deregisterGlobalLookup(String prefix) + public boolean deregisterLookup(String prefix) { - synchronized (globalLookups) - { - return globalLookups.remove(prefix) != null; - } + return prefixLookups.remove(prefix) != null; } /** - * Registers the given lookup object for the specified prefix at this - * instance. From now on this lookup object will be used for variables that - * have the specified prefix. + * Returns an unmodifiable set with the prefixes, for which {@code Lookup} + * objects are registered at this instance. This means that variables with + * these prefixes can be processed. * - * @param prefix the variable prefix (must not be null) - * @param lookup the lookup object to be used for this prefix (must not be - * null) + * @return a set with the registered variable prefixes */ - public void registerLookup(String prefix, StrLookup lookup) + public Set prefixSet() { - if (prefix == null) - { - throw new IllegalArgumentException( - "Prefix for lookup object must not be null!"); - } - if (lookup == null) + return Collections.unmodifiableSet(prefixLookups.keySet()); + } + + /** + * Returns a collection with the default {@code Lookup} objects + * added to this {@code ConfigurationInterpolator}. These objects are not + * associated with a variable prefix. The returned list is a snapshot copy + * of the internal collection of default lookups; so manipulating it does + * not affect this instance. + * + * @return the default lookup objects + */ + public List getDefaultLookups() + { + return new ArrayList(defaultLookups); + } + + /** + * Adds a default {@code Lookup} object. Default {@code Lookup} objects are + * queried (in the order they were added) for all variables without a + * special prefix. If no default {@code Lookup} objects are present, such + * variables won't be processed. + * + * @param defaultLookup the default {@code Lookup} object to be added (must + * not be null) + * @throws IllegalArgumentException if the {@code Lookup} object is + * null + */ + public void addDefaultLookup(Lookup defaultLookup) + { + defaultLookups.add(defaultLookup); + } + + /** + * Adds all {@code Lookup} objects in the given collection as default + * lookups. The collection can be null, then this method has no + * effect. It must not contain null entries. + * + * @param lookups the {@code Lookup} objects to be added as default lookups + * @throws IllegalArgumentException if the collection contains a null + * entry + */ + public void addDefaultLookups(Collection lookups) + { + if (lookups != null) { - throw new IllegalArgumentException( - "Lookup object must not be null!"); + defaultLookups.addAll(lookups); } - localLookups.put(prefix, lookup); } /** - * Deregisters the lookup object for the specified prefix at this instance. - * It will be removed from this instance. + * Removes the specified {@code Lookup} object from the list of default + * {@code Lookup}s. * - * @param prefix the variable prefix - * @return a flag whether for this prefix a lookup object had been - * registered + * @param lookup the {@code Lookup} object to be removed + * @return a flag whether this {@code Lookup} object actually existed and + * was removed */ - public boolean deregisterLookup(String prefix) + public boolean removeDefaultLookup(Lookup lookup) { - return localLookups.remove(prefix) != null; + return defaultLookups.remove(lookup); } /** - * Returns a set with the prefixes, for which lookup objects are registered - * at this instance. This means that variables with these prefixes can be - * processed. + * Sets the parent {@code ConfigurationInterpolator}. This object is used if + * the {@code Lookup} objects registered at this object cannot resolve a + * variable. * - * @return a set with the registered variable prefixes + * @param parentInterpolator the parent {@code ConfigurationInterpolator} + * object (can be null) */ - public Set prefixSet() + public void setParentInterpolator( + ConfigurationInterpolator parentInterpolator) { - return localLookups.keySet(); + this.parentInterpolator = parentInterpolator; } /** - * Returns the default lookup object. + * Returns the parent {@code ConfigurationInterpolator}. * - * @return the default lookup object + * @return the parent {@code ConfigurationInterpolator} (can be null) */ - public StrLookup getDefaultLookup() + public ConfigurationInterpolator getParentInterpolator() { - return defaultLookup; + return this.parentInterpolator; } /** - * Sets the default lookup object. This lookup object will be used for all - * variables without a special prefix. If it is set to null, such - * variables won't be processed. + * Sets a flag that variable names can contain other variables. If enabled, + * variable substitution is also done in variable names. + * + * @return the substitution in variables flag + */ + public boolean isEnableSubstitutionInVariables() + { + return substitutor.isEnableSubstitutionInVariables(); + } + + /** + * Sets the flag whether variable names can contain other variables. This + * flag corresponds to the {@code enableSubstitutionInVariables} property of + * the underlying {@code StrSubstitutor} object. * - * @param defaultLookup the new default lookup object + * @param f the new value of the flag */ - public void setDefaultLookup(StrLookup defaultLookup) + public void setEnableSubstitutionInVariables(boolean f) { - this.defaultLookup = defaultLookup; + substitutor.setEnableSubstitutionInVariables(f); } /** - * Resolves the specified variable. This implementation will try to extract + * Performs interpolation of the passed in value. If the value is of type + * String, this method checks whether it contains variables. If so, all + * variables are replaced by their current values (if possible). For non + * string arguments, the value is returned without changes. + * + * @param value the value to be interpolated + * @return the interpolated value + */ + public Object interpolate(Object value) + { + if (value instanceof String) + { + return substitutor.replace((String) value); + } + return value; + } + + /** + * Resolves the specified variable. This implementation tries to extract * a variable prefix from the given variable name (the first colon (':') is * used as prefix separator). It then passes the name of the variable with * the prefix stripped to the lookup object registered for this prefix. If * no prefix can be found or if the associated lookup object cannot resolve - * this variable, the default lookup object will be used. + * this variable, the default lookup objects are used. If this is not + * successful either and a parent {@code ConfigurationInterpolator} is + * available, this object is asked to resolve the variable. * * @param var the name of the variable whose value is to be looked up * @return the value of this variable or null if it cannot be * resolved */ - @Override - public String lookup(String var) + public Object resolve(String var) { if (var == null) { @@ -293,35 +345,28 @@ public class ConfigurationInterpolator e { String prefix = var.substring(0, prefixPos); String name = var.substring(prefixPos + 1); - String value = fetchLookupForPrefix(prefix).lookup(name); - if (value == null && getParentInterpolator() != null) + Object value = fetchLookupForPrefix(prefix).lookup(name); + if (value != null) { - value = getParentInterpolator().lookup(name); + return value; } + } + + for (Lookup l : defaultLookups) + { + Object value = l.lookup(var); if (value != null) { return value; } } - String value = fetchNoPrefixLookup().lookup(var); - if (value == null && getParentInterpolator() != null) + + ConfigurationInterpolator parent = getParentInterpolator(); + if (parent != null) { - value = getParentInterpolator().lookup(var); + return getParentInterpolator().resolve(var); } - return value; - } - - /** - * Returns the lookup object to be used for variables without a prefix. This - * implementation will check whether a default lookup object was set. If - * this is the case, it will be returned. Otherwise a null lookup - * object will be returned (never null). - * - * @return the lookup object to be used for variables without a prefix - */ - protected StrLookup fetchNoPrefixLookup() - { - return (getDefaultLookup() != null) ? getDefaultLookup() : StrLookup.noneLookup(); + return null; } /** @@ -333,57 +378,34 @@ public class ConfigurationInterpolator e * @param prefix the prefix * @return the lookup object to be used for this prefix */ - protected StrLookup fetchLookupForPrefix(String prefix) + protected Lookup fetchLookupForPrefix(String prefix) { - StrLookup lookup = localLookups.get(prefix); + Lookup lookup = prefixLookups.get(prefix); if (lookup == null) { - lookup = StrLookup.noneLookup(); + lookup = DummyLookup.INSTANCE; } return lookup; } /** - * Registers the local lookup instances for the given interpolator. + * Creates and initializes a {@code StrSubstitutor} object which is used for + * variable substitution. This {@code StrSubstitutor} is assigned a + * specialized lookup object implementing the correct variable resolving + * algorithm. * - * @param interpolator the instance receiving the local lookups - * @since upcoming + * @return the {@code StrSubstitutor} used by this object */ - public void registerLocalLookups(ConfigurationInterpolator interpolator) + private StrSubstitutor initSubstitutor() { - interpolator.localLookups.putAll(localLookups); - } - - /** - * Sets the parent interpolator. This object is used if the interpolation is nested - * hierarchically and the current interpolation object cannot resolve a variable. - * - * @param parentInterpolator the parent interpolator object or null - * @since upcoming - */ - public void setParentInterpolator(ConfigurationInterpolator parentInterpolator) - { - this.parentInterpolator = parentInterpolator; - } - - /** - * Requests the parent interpolator. This object is used if the interpolation is nested - * hierarchically and the current interpolation - * - * @return the parent interpolator or null - * @since upcoming - */ - public ConfigurationInterpolator getParentInterpolator() - { - return this.parentInterpolator; - } - - // static initializer, sets up the map with the standard lookups - static - { - globalLookups = new HashMap(); - globalLookups.put(PREFIX_SYSPROPERTIES, StrLookup.systemPropertiesLookup()); - globalLookups.put(PREFIX_CONSTANTS, new ConstantLookup()); - globalLookups.put(PREFIX_ENVIRONMENT, new EnvironmentLookup()); + return new StrSubstitutor(new StrLookup() + { + @Override + public String lookup(String key) + { + Object result = resolve(key); + return (result != null) ? result.toString() : null; + } + }); } } Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ConstantLookup.java Tue Dec 25 20:14:56 2012 @@ -21,7 +21,6 @@ import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.ClassUtils; -import org.apache.commons.lang.text.StrLookup; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -52,13 +51,13 @@ import org.apache.commons.logging.LogFac * href="http://commons.apache.org/configuration/team-list.html">Commons * Configuration team */ -public class ConstantLookup extends StrLookup +public class ConstantLookup implements Lookup { /** Constant for the field separator. */ private static final char FIELD_SEPRATOR = '.'; /** An internally used cache for already retrieved values. */ - private static Map constantCache = new HashMap(); + private static Map constantCache = new HashMap(); /** The logger. */ private Log log = LogFactory.getLog(getClass()); @@ -75,15 +74,14 @@ public class ConstantLookup extends StrL * @return the value of this variable or null if it cannot be * resolved */ - @Override - public String lookup(String var) + public Object lookup(String var) { if (var == null) { return null; } - String result; + Object result; synchronized (constantCache) { result = constantCache.get(var); @@ -107,11 +105,11 @@ public class ConstantLookup extends StrL synchronized (constantCache) { // In worst case, the value will be fetched multiple times - // because of this lax synchronisation, but for constant + // because of this lax synchronization, but for constant // values this shouldn't be a problem. - constantCache.put(var, String.valueOf(value)); + constantCache.put(var, value); } - result = value.toString(); + result = value; } } catch (Exception ex) Added: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java?rev=1425770&view=auto ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java (added) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java Tue Dec 25 20:14:56 2012 @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.configuration.interpol; + +/** + *

+ * An enumeration class defining constants for the {@code Lookup} objects + * available for each {@code Configuration} object per default. + *

+ *

+ * When a new configuration object derived from {@code AbstractConfiguration} is + * created it installs a {@link ConfigurationInterpolator} with a default set of + * {@link Lookup} objects. These lookups are defined by this enumeration class. + *

+ *

+ * All the default {@code Lookup} classes are state-less, thus their instances + * can be shared between multiple configuration objects. Therefore, it makes + * sense to keep shared instances in this enumeration class. + *

+ * + * @version $Id$ + * @since 2.0 + */ +public enum DefaultLookups +{ + /** The lookup for system properties. */ + SYSTEM_PROPERTIES("sys", new SystemPropertiesLookup()), + + /** The lookup for environment properties. */ + ENVIRONMENT("env", new EnvironmentLookup()), + + /** The lookup for constants. */ + CONST("const", new ConstantLookup()); + + /** The prefix under which the associated lookup object is registered. */ + private final String prefix; + + /** The associated lookup instance. */ + private final Lookup lookup; + + /** + * Creates a new instance of {@code DefaultLookups} and sets the prefix and + * the associated lookup instance. + * + * @param prfx the prefix + * @param look the {@code Lookup} instance + */ + private DefaultLookups(String prfx, Lookup look) + { + prefix = prfx; + lookup = look; + } + + /** + * Returns the standard prefix for the lookup object of this kind. + * + * @return the prefix + */ + public String getPrefix() + { + return prefix; + } + + /** + * Returns the standard {@code Lookup} instance of this kind. + * + * @return the associated {@code Lookup} object + */ + public Lookup getLookup() + { + return lookup; + } +} Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Propchange: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/DefaultLookups.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/EnvironmentLookup.java Tue Dec 25 20:14:56 2012 @@ -16,8 +16,6 @@ */ package org.apache.commons.configuration.interpol; -import org.apache.commons.configuration.EnvironmentConfiguration; -import org.apache.commons.lang.text.StrLookup; /** *

@@ -25,7 +23,7 @@ import org.apache.commons.lang.text.StrL * variables. *

*

- * This implementation relies on {@link EnvironmentConfiguration} to resolve + * This implementation relies on {@link System.getenv()} to resolve * environment variables. It can be used for referencing environment variables * in configuration files in an easy way, for instance: * @@ -45,21 +43,17 @@ import org.apache.commons.lang.text.StrL * @since 1.7 * @version $Id$ */ -public class EnvironmentLookup extends StrLookup +public class EnvironmentLookup implements Lookup { - /** Stores the underlying {@code EnvironmentConfiguration}. */ - private final EnvironmentConfiguration environmentConfig = new EnvironmentConfiguration(); - /** * Performs a lookup for the specified variable. This implementation - * directly delegates to a {@code EnvironmentConfiguration}. + * directly delegates to a {@code System.getenv()}. * * @param key the key to lookup * @return the value of this key or null if it cannot be resolved */ - @Override public String lookup(String key) { - return environmentConfig.getString(key); + return System.getenv(key); } } Modified: commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java (original) +++ commons/proper/configuration/trunk/src/main/java/org/apache/commons/configuration/interpol/ExprLookup.java Tue Dec 25 20:14:56 2012 @@ -64,7 +64,7 @@ import org.apache.commons.lang.text.StrS * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team * @version $Id$ */ -public class ExprLookup extends StrLookup +public class ExprLookup implements Lookup { /** Prefix to identify a Java Class object */ private static final String CLASS = "Class:"; @@ -78,6 +78,9 @@ public class ExprLookup extends StrLooku /** Configuration being operated on */ private AbstractConfiguration configuration; + /** The StrSubstitutor for performing replace operations. */ + private StrSubstitutor substitutor; + /** The engine. */ private final JexlEngine engine = new JexlEngine(); @@ -166,6 +169,7 @@ public class ExprLookup extends StrLooku public void setConfiguration(AbstractConfiguration config) { this.configuration = config; + installSubstitutor(config); } /** @@ -173,15 +177,14 @@ public class ExprLookup extends StrLooku * @param var The expression. * @return The String result of the expression. */ - @Override public String lookup(String var) { - ConfigurationInterpolator interp = configuration.getInterpolator(); - StrSubstitutor subst = new StrSubstitutor(interp, prefixMatcher, suffixMatcher, - StrSubstitutor.DEFAULT_ESCAPE); - - String result = subst.replace(var); + if(substitutor == null) + { + return var; + } + String result = substitutor.replace(var); try { Expression exp = engine.createExpression(result); @@ -196,6 +199,38 @@ public class ExprLookup extends StrLooku } /** + * Creates a {@code StrSubstitutor} object which uses the + * {@code ConfigurationInterpolator} of the passed in configuration as + * lookup object. + * + * @param config the associated configuration + */ + private void installSubstitutor(AbstractConfiguration config) + { + final ConfigurationInterpolator interpolator = + (config == null) ? null : config.getInterpolator(); + if (interpolator == null) + { + substitutor = null; + } + else + { + StrLookup variableResolver = new StrLookup() + { + @Override + public String lookup(String key) + { + Object value = interpolator.resolve(key); + return (value != null) ? value.toString() : null; + } + }; + substitutor = + new StrSubstitutor(variableResolver, prefixMatcher, + suffixMatcher, StrSubstitutor.DEFAULT_ESCAPE); + } + } + + /** * Creates a new {@code JexlContext} and initializes it with the variables * managed by this Lookup object. * Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java (original) +++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/InterpolationTestHelper.java Tue Dec 25 20:14:56 2012 @@ -21,9 +21,10 @@ import static org.junit.Assert.fail; import java.awt.event.KeyEvent; import java.util.List; +import java.util.Map; import org.apache.commons.configuration.interpol.ConfigurationInterpolator; -import org.apache.commons.lang.text.StrLookup; +import org.apache.commons.configuration.interpol.Lookup; /** * A helper class that defines a bunch of tests related to variable @@ -63,7 +64,8 @@ public class InterpolationTestHelper config.addProperty("path", "/temp,C:\\Temp,/usr/local/tmp"); config.setProperty("path.current", "${path}"); assertEquals("Interpolation with multi-valued property", - "/temp", config.getString("path.current")); + String.valueOf(config.getProperty("path")), + config.getString("path.current")); } /** @@ -164,6 +166,26 @@ public class InterpolationTestHelper } /** + * Tests interpolation of environment properties. + * + * @param config the configuration to test + */ + public static void testInterpolationEnvironment(Configuration config) + { + Map env = System.getenv(); + for (Map.Entry e : env.entrySet()) + { + config.addProperty("prop" + e.getKey(), "${env:" + e.getKey() + "}"); + } + + for (Map.Entry e : env.entrySet()) + { + assertEquals("Wrong value for environment property " + e.getKey(), + e.getValue(), config.getString("prop" + e.getKey())); + } + } + + /** * Tests interpolation of constant values. * * @param config the configuration to test @@ -201,10 +223,9 @@ public class InterpolationTestHelper { config.addProperty("var", "${echo:testVar}"); ConfigurationInterpolator interpol = config.getInterpolator(); - interpol.registerLookup("echo", new StrLookup() + interpol.registerLookup("echo", new Lookup() { - @Override - public String lookup(String varName) + public Object lookup(String varName) { return "Value of variable " + varName; } Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java (original) +++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestAbstractConfigurationBasicFeatures.java Tue Dec 25 20:14:56 2012 @@ -17,7 +17,6 @@ package org.apache.commons.configuration; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.ArrayList; @@ -278,21 +277,7 @@ public class TestAbstractConfigurationBa { AbstractConfiguration config = new TestConfigurationImpl( new PropertiesConfiguration()); - EnvironmentConfiguration envConfig = new EnvironmentConfiguration(); - Map env = new HashMap(); - for (Iterator it = envConfig.getKeys(); it.hasNext();) - { - String key = it.next(); - String propKey = "envtest." + key; - env.put(propKey, envConfig.getString(key)); - config.addProperty(propKey, "${env:" + key + "}"); - } - assertFalse("No environment properties", env.isEmpty()); - for (Map.Entry e : env.entrySet()) - { - assertEquals("Wrong value for " + e.getKey(), e.getValue(), config - .getString(e.getKey())); - } + InterpolationTestHelper.testInterpolationEnvironment(config); } /** @@ -360,7 +345,7 @@ public class TestAbstractConfigurationBa public void testNestedVariableInterpolation() { BaseConfiguration config = new BaseConfiguration(); - config.getSubstitutor().setEnableSubstitutionInVariables(true); + config.getInterpolator().setEnableSubstitutionInVariables(true); config.addProperty("java.version", "1.4"); config.addProperty("jre-1.4", "C:\\java\\1.4"); config.addProperty("jre.path", "${jre-${java.version}}"); Modified: commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java?rev=1425770&r1=1425769&r2=1425770&view=diff ============================================================================== --- commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java (original) +++ commons/proper/configuration/trunk/src/test/java/org/apache/commons/configuration/TestBaseConfiguration.java Tue Dec 25 20:14:56 2012 @@ -20,15 +20,18 @@ package org.apache.commons.configuration import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; import java.util.Set; @@ -38,6 +41,9 @@ import junitx.framework.ListAssert; import org.apache.commons.configuration.event.ConfigurationEvent; import org.apache.commons.configuration.event.ConfigurationListener; +import org.apache.commons.configuration.interpol.ConfigurationInterpolator; +import org.apache.commons.configuration.interpol.Lookup; +import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; @@ -522,6 +528,16 @@ public class TestBaseConfiguration } /** + * Tests interpolation of environment properties. + */ + @Test + public void testInterpolationEnvironment() + { + config.setDelimiterParsingDisabled(true); + InterpolationTestHelper.testInterpolationEnvironment(config); + } + + /** * Tests interpolation of constant values. */ @Test @@ -559,6 +575,60 @@ public class TestBaseConfiguration InterpolationTestHelper.testInterpolatedConfiguration(config); } + /** + * Tests whether a {@code ConfigurationInterpolator} can be set. + */ + @Test + public void testSetInterpolator() + { + ConfigurationInterpolator interpolator = + EasyMock.createMock(ConfigurationInterpolator.class); + EasyMock.replay(interpolator); + config.setInterpolator(interpolator); + assertSame("Interpolator not set", interpolator, + config.getInterpolator()); + } + + /** + * Tests whether a {@code ConfigurationInterpolator} can be created and + * installed. + */ + @Test + public void testInstallInterpolator() + { + Lookup prefixLookup = EasyMock.createMock(Lookup.class); + Lookup defLookup = EasyMock.createMock(Lookup.class); + EasyMock.replay(prefixLookup, defLookup); + Map prefixLookups = new HashMap(); + prefixLookups.put("test", prefixLookup); + List defLookups = new ArrayList(); + defLookups.add(defLookup); + config.installInterpolator(prefixLookups, defLookups); + ConfigurationInterpolator interpolator = config.getInterpolator(); + assertEquals("Wrong prefix lookups", prefixLookups, + interpolator.getLookups()); + List defLookups2 = interpolator.getDefaultLookups(); + assertEquals("Wrong number of default lookups", 2, defLookups2.size()); + assertSame("Wrong default lookup 1", defLookup, defLookups2.get(0)); + String var = "testVariable"; + Object value = 42; + config.addProperty(var, value); + assertEquals("Wrong lookup result", value, + defLookups2.get(1).lookup(var)); + } + + /** + * Tests whether property access is possible without a + * {@code ConfigurationInterpolator}. + */ + @Test + public void testNoInterpolator() + { + config.setProperty("test", "${value}"); + config.setInterpolator(null); + assertEquals("Wrong result", "${value}", config.getString("test")); + } + @Test public void testGetHexadecimalValue() {