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 " <code>${<varname>}</code> ". 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("animal", "quick brown fox"); + * valuesMap.put("target", "lazy dog"); + * String templateString = "The ${animal} jumped over the ${target}."; + * 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 "}" character is escaped, so + * that the second "}" 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 ⌖.")); + } + + /** + * 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: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]