You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by gg...@apache.org on 2005/07/05 18:27:13 UTC

svn commit: r209296 - in /jakarta/commons/proper/lang/trunk/src: java/org/apache/commons/lang/text/VariableFormat.java test/org/apache/commons/lang/text/VariableFormatTest.java

Author: ggregory
Date: Tue Jul  5 09:27:10 2005
New Revision: 209296

URL: http://svn.apache.org/viewcvs?rev=209296&view=rev
Log:
Towards version 2.2:
 - Set the component version to 2.2-dev.
 - Add .text classes VariableFormat and VariableFormatTest.
 - Enable build of .text package.

Added:
    jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/text/VariableFormat.java   (with props)
    jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/text/VariableFormatTest.java   (with props)

Added: jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/text/VariableFormat.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/text/VariableFormat.java?rev=209296&view=auto
==============================================================================
--- jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/text/VariableFormat.java (added)
+++ jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/text/VariableFormat.java Tue Jul  5 09:27:10 2005
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.lang.text;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * Candidate class to replace Interpolation and MappedMessageFormat?
+ * 
+ * <p>
+ * A class for variable interpolation (substitution).
+ * </p>
+ * <p>
+ * This class can be given a text which can contain an arbitrary number of variables. It will then try to replace all
+ * variables by their current values, which are obtained from a map. A variable per default is specified using the
+ * typical notation &quot; <code>${&lt;varname&gt;}</code> &quot;. However by calling the
+ * <code>setVariablePrefix()</code> and <code>setVariableSuffix()</code> methods it is possible to use a different
+ * prefix or suffix.
+ * </p>
+ * <p>
+ * Typical usage of this class follows the following pattern: First an instance is created and initialized with the map
+ * that contains the values for the available variables. If a prefix and/or suffix for variables should be used other
+ * than the default ones, the appropriate settings can be performed. After that the <code>replace()</code> method can
+ * be called passing in the source text for interpolation. In the returned text all variable references (as long as
+ * their values are known) will be resolved. The following example demonstrates this:
+ * </p>
+ * <p>
+ * <code><pre>
+ * Map valuesMap = HashMap();
+ * valuesMap.put(&quot;animal&quot;, &quot;quick brown fox&quot;);
+ * valuesMap.put(&quot;target&quot;, &quot;lazy dog&quot;);
+ * String templateString = &quot;The ${animal} jumped over the ${target}.&quot;;
+ * VariableFormat vf = new VariableVormat(valuesMap);
+ * String resolvedString = cf.replace(templateString);
+ * </pre></code> yielding: <code><pre>
+ *    The quick brown fox jumped over the lazy dog.
+ * </pre></code>
+ * </p>
+ * <p>
+ * In addition to this usage pattern there are some static convenience methods that cover the most common use cases.
+ * These methods can be used without the need of creating an instance. However if multiple replace operations are to be
+ * performed, creating and reusing an instance of this class will be more efficient.
+ * </p>
+ * <p>
+ * Variable replacement works in a recursive way, i.e. it is possible that a variable's value is a text which again
+ * contains variable references. These new variables will be replaced, too. Cyclic replacements are detected and will
+ * cause an exception to be thrown.
+ * </p>
+ * <p>
+ * Sometimes the interpolation's result must contain a variable prefix. As an example take the following source text:
+ * </p>
+ * <p>
+ * <code>The variable ${${name}} must be used.</code>
+ * </p>
+ * <p>
+ * Here only the variable's name refered to in the text should be replaced resulting in the text (assuming that the
+ * value of the <code>name</code> variable is <code>x</code>:
+ * </p>
+ * <p>
+ * <code>The variable ${x} must be used.</code>
+ * </p>
+ * <p>
+ * To achieve this effect there are two possibilities: Either set a different prefix and suffix for variables which do
+ * not conflict with the result text you want to produce. The other possibility is to use the escape character that can
+ * be set through the <code>setEscapeCharacter()</code> method. If this character is placed before a variable
+ * reference, this reference is ignored and won't be replaced. It can also be placed before a variable suffix, then this
+ * suffix will be ignored, too. Per default the escape character is set to the <code>$</code> character, so that in
+ * the example above the text could have run:
+ * </p>
+ * <p>
+ * <code>The variable $${${name$}} must be used.</code>
+ * </p>
+ * 
+ * 
+ * @author Oliver Heger
+ * @version $Id$
+ * @since 2.2
+ */
+public class VariableFormat {
+    /** Constant for the default variable prefix. */
+    static final String DEFAULT_PREFIX = "${";
+
+    /** Constant for the default variable suffix. */
+    static final String DEFAULT_SUFFIX = "}";
+
+    /** Constant for the default escape character. */
+    static final char DEFAULT_ESCAPE = '$';
+
+    /** Stores the map with the variables' values. */
+    private Map valueMap;
+
+    /** Stores the variable prefix. */
+    private String variablePrefix;
+
+    /** Stores the variable suffix. */
+    private String variableSuffix;
+
+    /** Stores the escape character. */
+    private char escapeCharacter;
+
+    /**
+     * Creates a new instance of <code>VariableFormat</code> and initializes it.
+     * 
+     * @param valueMap
+     *            the map with the variables' values
+     * @param prefix
+     *            the prefix for variables
+     * @param suffix
+     *            the suffix for variables
+     * @param escape
+     *            the escape character
+     * @throws IllegalArgumentException
+     *             if the map is undefined
+     */
+    public VariableFormat(Map valueMap, String prefix, String suffix, char escape) {
+        setValueMap(valueMap);
+        setVariablePrefix(prefix);
+        setVariableSuffix(suffix);
+        setEscapeCharacter(escape);
+    }
+
+    /**
+     * Creates a new instance of <code>VariableFormat</code> and initializes it. Uses a default escaping character.
+     * 
+     * @param valueMap
+     *            the map with the variables' values
+     * @param prefix
+     *            the prefix for variables
+     * @param suffix
+     *            the suffix for variables
+     * @throws IllegalArgumentException
+     *             if the map is undefined
+     */
+    public VariableFormat(Map valueMap, String prefix, String suffix) {
+        this(valueMap, prefix, suffix, DEFAULT_ESCAPE);
+    }
+
+    /**
+     * Creates a new instance of <code>VariableFormat</code> and initializes it. Uses defaults for variable prefix and
+     * suffix and the escaping character.
+     * 
+     * @param valueMap
+     *            the map with the variables' values
+     * @throws IllegalArgumentException
+     *             if the map is undefined
+     */
+    public VariableFormat(Map valueMap) {
+        this(valueMap, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE);
+    }
+
+    /**
+     * Returns the escape character.
+     * 
+     * @return the character used for escaping variable references
+     */
+    public char getEscapeCharacter() {
+        return this.escapeCharacter;
+    }
+
+    /**
+     * Sets the escape character. If this character is placed before a variable reference in the source text, this
+     * variable will be ignored.
+     * 
+     * @param escapeCharacter
+     *            the escape character (0 for disabling escaping)
+     */
+    public void setEscapeCharacter(char escapeCharacter) {
+        this.escapeCharacter = escapeCharacter;
+    }
+
+    /**
+     * Returns the map with the variables' values.
+     * 
+     * @return the values of the variables
+     */
+    public Map getValueMap() {
+        return this.valueMap;
+    }
+
+    /**
+     * Sets the map with the variables' values.
+     * 
+     * @param valueMap
+     *            the values of the variables
+     * @throws IllegalArgumentException
+     *             if <code>valueMap</code> is <b>null</b>
+     */
+    public void setValueMap(Map valueMap) throws IllegalArgumentException {
+        if (valueMap == null) {
+            throw new IllegalArgumentException("Value map must not be null");
+        }
+        this.valueMap = valueMap;
+    }
+
+    /**
+     * Returns the prefix for variables.
+     * 
+     * @return the prefix for variables
+     */
+    public String getVariablePrefix() {
+        return this.variablePrefix;
+    }
+
+    /**
+     * Sets the prefix for variables.
+     * 
+     * @param variablePrefix
+     *            the prefix for variables
+     * @throws IllegalArgumentException
+     *             if the prefix is <b>null</b>
+     */
+    public void setVariablePrefix(String variablePrefix) throws IllegalArgumentException {
+        if (variablePrefix == null) {
+            throw new IllegalArgumentException("Variable prefix must not be null!");
+        }
+        this.variablePrefix = variablePrefix;
+    }
+
+    /**
+     * Returns the suffix for variables.
+     * 
+     * @return the suffix for variables
+     */
+    public String getVariableSuffix() {
+        return this.variableSuffix;
+    }
+
+    /**
+     * Sets the suffix for variables
+     * 
+     * @param variableSuffix
+     *            the suffix for variables
+     * @throws IllegalArgumentException
+     *             if the prefix is <b>null</b>
+     */
+    public void setVariableSuffix(String variableSuffix) throws IllegalArgumentException {
+        if (variableSuffix == null) {
+            throw new IllegalArgumentException("Variable suffix must not be null!");
+        }
+        this.variableSuffix = variableSuffix;
+    }
+
+    /**
+     * Replaces the occurrences of all variables in the given source data by their current values. If the source
+     * consists only of a single variable reference, this method directly returns the value of this variable (which can
+     * be an arbitrary object). If the source contains multiple variable references or static text, the return value
+     * will always be a String with the concatenation of all these elements.
+     * 
+     * @param source
+     *            the text to be interpolated; this can be an arbitrary object whose <code>toString()</code> method
+     *            will be called
+     * @return the result of the replace operation
+     */
+    public Object replaceObject(Object source) {
+        return doReplace(source, null);
+    }
+
+    /**
+     * Replaces the occurrences of all variables in the given source data by their current values.
+     * 
+     * @param source
+     *            the text to be interpolated; this can be an arbitrary object whose <code>toString()</code> method
+     *            will be called
+     * @return the result of the replace operation
+     */
+    public String replace(Object source) {
+        Object result = replaceObject(source);
+        return (result == null) ? null : result.toString();
+    }
+
+    /**
+     * Replaces the occurrences of all variables in the given source data by their current values obtained from the
+     * passed in map.
+     * 
+     * @param valueMap
+     *            the map with the values
+     * @param source
+     *            the source text
+     * @return the result of the replace operation
+     */
+    public static String replace(Map valueMap, Object source) {
+        return new VariableFormat(valueMap).replace(source);
+    }
+
+    /**
+     * Replaces the occurrences of all variables in the given source data by their current values obtained from the
+     * passed in map. This method allows to specifiy a custom variable prefix and suffix
+     * 
+     * @param valueMap
+     *            the map with the values
+     * @param prefix
+     *            the prefix of variables
+     * @param suffix
+     *            the suffix of variables
+     * @param source
+     *            the source text
+     * @return the result of the replace operation
+     */
+    public static String replace(Map valueMap, String prefix, String suffix, Object source) {
+        return new VariableFormat(valueMap, prefix, suffix).replace(source);
+    }
+
+    /**
+     * Replaces all variables in the given source data with values obtained from system properties.
+     * 
+     * @param source
+     *            the source text
+     * @return the result of the replace operation
+     */
+    public static String replaceSystemProperties(Object source) {
+        return new VariableFormat(System.getProperties()).replace(source);
+    }
+
+    /**
+     * Checks if the variable reference found at the specified position is escaped and if this is the case, where the
+     * escaped text starts.
+     * 
+     * @param text
+     *            the text to be processed
+     * @param beginIndex
+     *            the start index of the variable reference to check
+     * @return the starting index of the escaped text or -1 if this reference is not escaped
+     */
+    protected int escaped(String text, int beginIndex) {
+        if (beginIndex < 1 || text.charAt(beginIndex - 1) != getEscapeCharacter()) {
+            return -1;
+        }
+        int idx = beginIndex - 2;
+        while (idx >= 0 && text.charAt(idx) == getEscapeCharacter()) {
+            idx--;
+        }
+        return idx + 1;
+    }
+
+    /**
+     * Unescapes an escaped variable reference. This method is called if <code>escaped()</code> has determined an
+     * escaped variable reference. Its purpose is to remove any escaping characters and to add the resulting text into
+     * the target buffer. This implementation will remove the first escape character. So if the default values are used,
+     * a text portion of <code>$${myvar}</code> will become <code>${myvar}</code>,
+     * <code>$$$${var with dollars}</code> will result in <code>$$${var with dollars}</code>. Text between the
+     * first variable start token and the last unescaped variable end token can contain variable references and will be
+     * recursively replaced. So constructs of the following form can be built:
+     * <code>Variable $${${varName$}} is incorrect!</code> (note how the first &quot;}&quot; character is escaped, so
+     * that the second &quot;}&quot; marks the end of this construct.
+     * 
+     * @param buf
+     *            the target buffer
+     * @param text
+     *            the text to be processed
+     * @param beginIndex
+     *            the begin index of the escaped variable reference
+     * @param endIndex
+     *            the end index of the escaped variable reference
+     * @param priorVariables
+     *            keeps track of the replaced variables
+     */
+    protected void unescape(StringBuffer buf, String text, int beginIndex, int endIndex, List priorVariables) {
+        int startToken = text.indexOf(getVariablePrefix(), beginIndex);
+        buf.append(text.substring(beginIndex + 1, startToken));
+        buf.append(getVariablePrefix());
+        String escapedContent = text.substring(startToken + getVariablePrefix().length(), endIndex);
+        buf.append(doReplace(StringUtils.replace(escapedContent, String.valueOf(getEscapeCharacter())
+            + getVariableSuffix(), getVariableSuffix()), priorVariables));
+    }
+
+    /**
+     * Searches for a variable end token in the given string from the specified start position.
+     * 
+     * @param text
+     *            the text to search
+     * @param beginIndex
+     *            the start index
+     * @return the index of the end token or -1 if none was found
+     */
+    protected int findEndToken(String text, int beginIndex) {
+        int pos = beginIndex - getVariableSuffix().length();
+
+        do {
+            pos = text.indexOf(getVariableSuffix(), pos + getVariableSuffix().length());
+        } while (pos > 0 && getEscapeCharacter() == text.charAt(pos - 1));
+
+        return pos;
+    }
+
+    /**
+     * Resolves the specified variable. This method is called whenever a variable reference is detected in the source
+     * text. It is passed the variable's name and must return the corresponding value. This implementation accesses the
+     * value map using the variable's name as key. Derived classes may overload this method to implement a different
+     * strategy for resolving variables.
+     * 
+     * @param name
+     *            the name of the variable
+     * @return the variable's value or <b>null</b> if the variable is unknown
+     */
+    protected Object resolveVariable(String name) {
+        return getValueMap().get(name);
+    }
+
+    /**
+     * Recursive handler for multple levels of interpolation. This is the main interpolation method, which resolves the
+     * values of all variable references contained in the passed in text.
+     * 
+     * @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. When called for the first time, this argument should be
+     *            <b>null </b>.
+     * @param obj
+     *            the text to be interpolated (as object)
+     * @param priorVariables
+     *            keeps track of the replaced variables
+     * @return the result of the interpolation process
+     */
+    private Object doReplace(Object obj, List priorVariables) {
+        if (obj == null) {
+            return null;
+        }
+
+        String base = obj.toString();
+        if (base.indexOf(getVariablePrefix()) < 0) {
+            return obj;
+        }
+
+        // on the first call initialize priorVariables
+        // and add base as the first element
+        if (priorVariables == null) {
+            priorVariables = new ArrayList();
+            priorVariables.add(base);
+        }
+
+        int begin = -1;
+        int end = -1;
+        int prec = 0 - getVariableSuffix().length();
+        String variable = null;
+        StringBuffer result = new StringBuffer();
+        Object objResult = null;
+        int objLen = 0;
+
+        while (((begin = base.indexOf(getVariablePrefix(), prec + getVariableSuffix().length())) > -1)
+            && ((end = findEndToken(base, begin)) > -1)) {
+            int escBegin = escaped(base, begin);
+            if (escBegin >= 0) {
+                result.append(base.substring(prec + getVariableSuffix().length(), escBegin));
+                unescape(result, base, escBegin, end + getVariableSuffix().length(), priorVariables);
+            }
+
+            else {
+                result.append(base.substring(prec + getVariableSuffix().length(), begin));
+                variable = base.substring(begin + getVariablePrefix().length(), end);
+
+                // if we've got a loop, create a useful exception message and
+                // throw
+                if (priorVariables.contains(variable)) {
+                    String initialBase = priorVariables.remove(0).toString();
+                    priorVariables.add(variable);
+                    StringBuffer priorVariableSb = new StringBuffer();
+
+                    // create a nice trace of interpolated variables like so:
+                    // var1->var2->var3
+                    for (Iterator it = priorVariables.iterator(); it.hasNext();) {
+                        priorVariableSb.append(it.next());
+                        if (it.hasNext()) {
+                            priorVariableSb.append("->");
+                        }
+                    }
+                    throw new IllegalStateException("Infinite loop in property interpolation of "
+                        + initialBase
+                        + ": "
+                        + priorVariableSb.toString());
+                }
+                // otherwise, add this variable to the interpolation list.
+                priorVariables.add(variable);
+
+                objResult = resolveVariable(variable);
+                if (objResult != null) {
+                    objResult = doReplace(objResult, priorVariables);
+                    result.append(objResult);
+                    objLen = objResult.toString().length();
+
+                    // pop the interpolated variable off the stack
+                    // this maintains priorVariables correctness for
+                    // properties with multiple interpolations, e.g.
+                    // prop.name=${some.other.prop1}/blahblah/${some.other.prop2}
+                    priorVariables.remove(priorVariables.size() - 1);
+                } else {
+                    // variable not defined - so put it back in the value
+                    result.append(getVariablePrefix()).append(variable).append(getVariableSuffix());
+                }
+            }
+
+            prec = end;
+        }
+
+        result.append(base.substring(prec + getVariableSuffix().length(), base.length()));
+        return (objResult != null && objLen > 0 && objLen == result.length()) ? objResult : result.toString();
+    }
+}

Propchange: jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/text/VariableFormat.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/text/VariableFormat.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Jul  5 09:27:10 2005
@@ -0,0 +1 @@
+LastChangedDate Date LastChangedRevision Revision Rev LastChangedBy Author HeadURL URL Id

Propchange: jakarta/commons/proper/lang/trunk/src/java/org/apache/commons/lang/text/VariableFormat.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/text/VariableFormatTest.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/text/VariableFormatTest.java?rev=209296&view=auto
==============================================================================
--- jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/text/VariableFormatTest.java (added)
+++ jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/text/VariableFormatTest.java Tue Jul  5 09:27:10 2005
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ * 
+ * Licensed 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.lang.text;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+/**
+ * Test class for VariableResolver.
+ * 
+ * @author Oliver Heger
+ * @version $Id$
+ */
+public class VariableFormatTest extends TestCase {
+    static final String REPLACE_TEMPLATE = "The ${animal} jumps over the ${target}.";
+
+    private VariableFormat format;
+
+    private Map values;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        Map map = new HashMap();
+        map.put("animal", "quick brown fox");
+        map.put("target", "lazy dog");
+        setValues(map);
+        setFormat(new VariableFormat(map));
+    }
+
+    /**
+     * Tests creating new <code>VariableFormat</code> objects.
+     */
+    public void testInitialize() {
+        assertNotNull(format.getValueMap());
+        assertEquals(VariableFormat.DEFAULT_PREFIX, format.getVariablePrefix());
+        assertEquals(VariableFormat.DEFAULT_SUFFIX, format.getVariableSuffix());
+        assertEquals(VariableFormat.DEFAULT_ESCAPE, format.getEscapeCharacter());
+
+        format = new VariableFormat(values, "<<", ">>", '\\');
+        assertEquals("<<", format.getVariablePrefix());
+        assertEquals(">>", format.getVariableSuffix());
+        assertEquals('\\', format.getEscapeCharacter());
+
+        try {
+            format = new VariableFormat(null);
+            fail("Could create format object with null map!");
+        } catch (IllegalArgumentException iex) {
+            // ok
+        }
+
+        try {
+            format = new VariableFormat(values, "${", null);
+            fail("Could create format object with undefined suffix!");
+        } catch (IllegalArgumentException iex) {
+            // ok
+        }
+
+        try {
+            format = new VariableFormat(values, null, "]");
+            fail("Could create format object with undefined prefix!");
+        } catch (IllegalArgumentException iex) {
+            // ok
+        }
+    }
+
+    /**
+     * Tests typical replace operations.
+     */
+    public void testReplace() {
+        assertEquals("The quick brown fox jumps over the lazy dog.", format.replaceObject(REPLACE_TEMPLATE));
+
+        format.getValueMap().put("animal", "cow");
+        format.getValueMap().put("target", "moon");
+        assertEquals("The cow jumps over the moon.", format.replace(REPLACE_TEMPLATE));
+
+        assertEquals("Variable ${var} is unknown!", format.replace("Variable ${var} is unknown!"));
+    }
+
+    /**
+     * Tests source texts with nothing to replace.
+     */
+    public void testReplaceNothing() {
+        assertNull(format.replace(null));
+        assertEquals("Nothing to replace.", format.replace("Nothing to replace."));
+        assertEquals("42", format.replace(new Integer(42)));
+    }
+
+    /**
+     * Tests escaping variable references.
+     */
+    public void testEscape() {
+        assertEquals("${animal}", format.replace("$${animal}"));
+        format.getValueMap().put("var_name", "x");
+        assertEquals("Many $$$$${target} $s", format.replace("Many $$$$$${target} $s"));
+        assertEquals("Variable ${x} must be used!", format.replace("Variable $${${var_name$}} must be used!"));
+    }
+
+    /**
+     * Tests recursive replacements.
+     */
+    public void testRecursiveReplacement() {
+        Map valuesMap = new HashMap();
+        valuesMap.put("animal", "${critter}");
+        valuesMap.put("target", "${pet}");
+        valuesMap.put("pet", "${petCharacteristic} dog");
+        valuesMap.put("petCharacteristic", "lazy");
+        valuesMap.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
+        valuesMap.put("critterSpeed", "quick");
+        valuesMap.put("critterColor", "brown");
+        valuesMap.put("critterType", "fox");
+        format.setValueMap(valuesMap);
+        assertEquals("The quick brown fox jumps over the lazy dog.", format.replace(REPLACE_TEMPLATE));
+    }
+
+    /**
+     * Tests a cyclic replace operation. The cycle should be detected and cause an exception to be thrown.
+     */
+    public void testCyclicReplacement() {
+        Map valuesMap = new HashMap();
+        valuesMap.put("animal", "${critter}");
+        valuesMap.put("target", "${pet}");
+        valuesMap.put("pet", "${petCharacteristic} dog");
+        valuesMap.put("petCharacteristic", "lazy");
+        valuesMap.put("critter", "${critterSpeed} ${critterColor} ${critterType}");
+        valuesMap.put("critterSpeed", "quick");
+        valuesMap.put("critterColor", "brown");
+        valuesMap.put("critterType", "${animal}");
+        format.setValueMap(valuesMap);
+        try {
+            format.replace(REPLACE_TEMPLATE);
+            fail("Cyclic replacement was not detected!");
+        } catch (IllegalStateException isx) {
+            // ok
+        }
+    }
+
+    /**
+     * Tests operating on objects.
+     */
+    public void testReplaceObject() {
+        format.getValueMap().put("value", new Integer(42));
+        assertEquals(new Integer(42), format.replaceObject("${value}"));
+        assertEquals("The answer is 42.", format.replaceObject("The answer is ${value}."));
+    }
+
+    /**
+     * Tests chaning variable prefix and suffix and the escaping character.
+     */
+    public void testNonDefaultTokens() {
+        format = new VariableFormat(values, "<<", ">>", '\\');
+        assertEquals("The quick brown fox jumps over the lazy dog.", format
+                .replace("The <<animal>> jumps over the <<target>>."));
+        assertEquals("The quick brown fox jumps over the <<target>>.", format
+                .replace("The <<animal>> jumps over the \\<<target>>."));
+    }
+
+    /**
+     * Tests invoking the static convenience methods.
+     */
+    public void testNonInstanceMethods() {
+        assertEquals("The quick brown fox jumps over the lazy dog.", VariableFormat.replace(values, REPLACE_TEMPLATE));
+        values.put("animal", "cow");
+        values.put("target", "moon");
+        assertEquals("The cow jumps over the moon.", VariableFormat.replace(values, "&", ";",
+                "The &animal; jumps over the &target;."));
+    }
+
+    /**
+     * Tests interpolation with system properties.
+     */
+    public void testReplaceSystemProperties() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("Hi ").append(System.getProperty("user.name"));
+        buf.append(", you are working with ");
+        buf.append(System.getProperty("os.name"));
+        buf.append(", your home directory is ");
+        buf.append(System.getProperty("user.home")).append('.');
+        assertEquals(buf.toString(), VariableFormat.replaceSystemProperties("Hi ${user.name}, you are "
+            + "working with ${os.name}, your home "
+            + "directory is ${user.home}."));
+    }
+
+    Map getValues() {
+        return this.values;
+    }
+
+    void setValues(Map values) {
+        this.values = values;
+    }
+
+    VariableFormat getFormat() {
+        return this.format;
+    }
+
+    void setFormat(VariableFormat format) {
+        this.format = format;
+    }
+}

Propchange: jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/text/VariableFormatTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/text/VariableFormatTest.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Tue Jul  5 09:27:10 2005
@@ -0,0 +1 @@
+LastChangedDate Date LastChangedRevision Revision Rev LastChangedBy Author HeadURL URL Id

Propchange: jakarta/commons/proper/lang/trunk/src/test/org/apache/commons/lang/text/VariableFormatTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org