diff --git a/src/java/org/apache/commons/lang/Interpolation.java b/src/java/org/apache/commons/lang/Interpolation.java new file mode 100644 index 000000000..bef9b1498 --- /dev/null +++ b/src/java/org/apache/commons/lang/Interpolation.java @@ -0,0 +1,182 @@ +package org.apache.commons.lang; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Performs basic variable interpolation on a String for variables within + * a Map. Variables of the form, ${var}, are supported. + * + * @author Ken Fitzpatrick + * @author Henri Yandell + */ +public class Interpolation { + + // QUERY: Anyway to escape the ${..} variable so it is not interpolated? + + // TODO: Consider making these configurable? + private static final String SYMBOLIC_VALUE_MARKER_START = "${"; + private static final String SYMBOLIC_VALUE_MARKER_END = "}"; + + /** + *
+ * Returns a String that is the result of having performed
+ * variable interpolation on templateString
,
+ * using the value set found in values
.
+ *
+ * The solution is compatible with all JDK versions + * where Jakarta/Commons/Lang also is supported. + *
+ *
+ * The expected format of templateString
is:
+ *
+ * such that the key/value pairs found in
+ * The ${animal} jumped over the ${target}.
+ *
values
+ * are substituted into the string at the ${key-name}
markers.
+ * In the above example, valuesMap
could have been populated as:
+ *
+ * yielding:
+ *
+ * Map valuesMap = HashMap();
+ * valuesMap.put( "animal", "quick brown fox" );
+ * valuesMap.put( "target", "lazy dog" );
+ * String resolvedString = StringUtils.interpolate( templateString, valuesMap );
+ *
+ *
+ * The quick brown fox jumped over the lazy dog.
+ *
+ * The same templateString
from the above example could be reused as:
+ *
+ * yielding:
+ *
+ * Map valuesMap = HashMap();
+ * valuesMap.put( "animal", "cow" );
+ * valuesMap.put( "target", "moon" );
+ * String resolvedString = StringUtils.interpolate( templateString, valuesMap );
+ *
+ *
+ * The cow jumped over the moon.
+ *
+ * The value of templateString
is returned in an unaltered if templateString
+ * is null, empty, or contains no marked variables that can be resolved by the key/value pairs found in
+ * valuesMap
, or if valuesMap
is null, empty or has no key/value pairs that can be
+ * applied to the marked variables within templateString
.
+ *
templateString
+ * @return String
+ */
+ public static String interpolate( String templateString, Map valuesMap ) {
+ // pre-conditions
+ if ( valuesMap == null )
+ return templateString;
+ if ( templateString == null )
+ return templateString;
+ if ( templateString.length() < 1 )
+ return templateString;
+ if ( valuesMap.isEmpty() )
+ return templateString;
+
+ // default the returned String to the templateString
+ String returnString = templateString;
+ String nextKey = null;
+ Object substitutionBean = null;
+ String substitutionValue = null;
+ String nextValueToBeSubstituted = null;
+
+ // get a list of substitution valuesMap
+ Iterator keys = valuesMap.keySet().iterator();
+
+ while( keys.hasNext() ) {
+ nextKey = ( String ) keys.next();
+ substitutionValue = StringUtils.defaultString( ( String ) valuesMap.get( nextKey ) );
+ nextValueToBeSubstituted = SYMBOLIC_VALUE_MARKER_START + nextKey + SYMBOLIC_VALUE_MARKER_END;
+
+ returnString = StringUtils.replace( returnString, nextValueToBeSubstituted, substitutionValue );
+ }
+ return returnString;
+ }
+
+
+ /**
+ *
+ * Returns a String that is the result of having performed variable interpolation on
+ * templateString
, using the value set found in values
,
+ * repeatedly until there are no changes.
+ *
+ * The expected format of templateString
is:
+ *
+ * such that the key/value pairs found in
+ * The ${animal} jumped over the ${target}.
+ *
values
are substituted into the string at the
+ * ${key-name}
markers. In the above example, valuesMap
+ * could have been populated as:
+ *
+ * yielding:
+ *
+ * Map valuesMap = 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" );
+ * String resolvedString = StringUtils.interpolate( templateString, valuesMap, true );
+ *
+ *
+ * The quick brown fox jumped over the lazy dog.
+ *
+ * The cow jumped over the moon.
+ *
+ *
+ *
+ * The value of templateString
is returned in an unaltered form if
+ * templateString
is null, empty, or
+ * contains no marked variables that can be resolved by the key/value pairs found in
+ * valuesMap
, or if valuesMap
is null, empty or has no key/value
+ * pairs that can be applied to the marked variables within templateString
.
+ *
templateString
+ * @return String
+ */
+ public static String interpolateRepeatedly(
+ String templateString,
+ Map valuesMap)
+ {
+ // pre-conditions
+ if ( valuesMap == null )
+ return templateString;
+ if ( templateString == null )
+ return templateString;
+ if ( templateString.length() < 1 )
+ return templateString;
+ if ( valuesMap.isEmpty() )
+ return templateString;
+
+ String currentResult = templateString;
+ String previousResult = null;
+ while( ! StringUtils.equals( currentResult, previousResult ) )
+ {
+ previousResult = currentResult;
+ currentResult = Interpolation.interpolate( previousResult, valuesMap );
+ }
+
+ return currentResult;
+ }
+
+}
diff --git a/src/test/org/apache/commons/lang/InterpolationTest.java b/src/test/org/apache/commons/lang/InterpolationTest.java
new file mode 100644
index 000000000..991849abb
--- /dev/null
+++ b/src/test/org/apache/commons/lang/InterpolationTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2002-2004 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;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Unit tests {@link org.apache.commons.lang.Interpolation}.
+ *
+ * @author Henri Yandell
+ * @author Ken Fitzpatrick
+ * @version $Id: InterpolationTest.java,v 1.1 2004/07/04 04:51:25 bayard Exp $
+ */
+public class InterpolationTest extends TestCase {
+
+ private static final String INPUT_TEMPLATE = "The ${animal} jumped over the ${target}.";
+ private static final String EXPECTED_RESULTS_1 = "The quick brown fox jumped over the lazy dog.";
+ private static final String EXPECTED_RESULTS_2 = "The cow jumped over the moon.";
+
+ public InterpolationTest(String name) {
+ super(name);
+ }
+
+ public static void main(String[] args) {
+ TestRunner.run(suite());
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(InterpolationTest.class);
+ suite.setName("Interpolation Tests");
+ return suite;
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testSimpleVariableSubstitution() {
+
+ // test case: "The quick brown fox jumped over the lazy dog."
+ Map valuesMap = new HashMap();
+ valuesMap.put( "animal", "quick brown fox" );
+ valuesMap.put( "target", "lazy dog" );
+ assertEquals( "Test case 1: simple variable substitution", EXPECTED_RESULTS_1,
+ Interpolation.interpolate( INPUT_TEMPLATE, valuesMap) );
+
+ // test case: "The cow jumped over the moon."
+ valuesMap = new HashMap();
+ valuesMap.put( "animal", "cow" );
+ valuesMap.put( "target", "moon" );
+ assertEquals( "Test case 2: template reuse, different results" ,EXPECTED_RESULTS_2,
+ Interpolation.interpolate( INPUT_TEMPLATE, valuesMap) );
+ }
+
+ public void testNullMap() {
+ // negative test case: Map == null
+ Map valuesMap = null;
+ assertEquals( "Test case 3: Map == null", INPUT_TEMPLATE,
+ Interpolation.interpolate( INPUT_TEMPLATE, valuesMap) );
+ }
+
+ public void testEmptyMap() {
+ // negative test case: Map.isEmpty()
+ Map valuesMap = new HashMap();
+ assertEquals( "Test case 4: Map.isEmpty()", INPUT_TEMPLATE,
+ Interpolation.interpolate( INPUT_TEMPLATE, valuesMap) );
+ }
+
+ public void testNullTemplate() {
+ // negative test case: INPUT_TEMPLATE == null
+ Map valuesMap = new HashMap();
+ valuesMap.put( "animal", "cow" );
+ valuesMap.put( "target", "moon" );
+ assertNull( "Test case 5: template == null",
+ Interpolation.interpolate( null, valuesMap) );
+ }
+
+ public void testRecursive() {
+ // test case: process repeatedly
+ 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" );
+ assertEquals( "Test case 6: interpolateRepeatedly", EXPECTED_RESULTS_1,
+ Interpolation.interpolateRepeatedly( INPUT_TEMPLATE, valuesMap ) );
+
+ // test case: process repeatedly
+ valuesMap = new HashMap();
+ valuesMap.put( "animal", "cow" );
+ valuesMap.put( "target", "${celestialObject}" );
+ valuesMap.put( "celestialObject", "moon" );
+ assertEquals( "Test case 8: interpolateRepeatedly", EXPECTED_RESULTS_2,
+ Interpolation.interpolateRepeatedly( INPUT_TEMPLATE, valuesMap ) );
+ }
+
+}
diff --git a/src/test/org/apache/commons/lang/LangTestSuite.java b/src/test/org/apache/commons/lang/LangTestSuite.java
index cb733f649..dbc69aedb 100644
--- a/src/test/org/apache/commons/lang/LangTestSuite.java
+++ b/src/test/org/apache/commons/lang/LangTestSuite.java
@@ -26,7 +26,7 @@ import junit.textui.TestRunner;
* @author Stephen Colebourne
* @author Ringo De Smet
* @author Matthew Hawthorne
- * @version $Id: LangTestSuite.java,v 1.26 2004/02/18 23:06:19 ggregory Exp $
+ * @version $Id: LangTestSuite.java,v 1.27 2004/07/04 04:51:25 bayard Exp $
*/
public class LangTestSuite extends TestCase {
@@ -62,6 +62,7 @@ public class LangTestSuite extends TestCase {
suite.addTest(EntitiesTest.suite());
suite.addTest(IllegalClassExceptionTest.suite());
suite.addTest(IncompleteArgumentExceptionTest.suite());
+ suite.addTest(InterpolationTest.suite());
suite.addTest(NotImplementedExceptionTest.suite());
suite.addTest(NullArgumentExceptionTest.suite());
suite.addTest(NumberRangeTest.suite());