commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bugzi...@apache.org
Subject DO NOT REPLY [Bug 18962] New: - New Method: getSubstitutedValues( String, Map )
Date Fri, 11 Apr 2003 16:29:21 GMT
DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG 
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://nagoya.apache.org/bugzilla/show_bug.cgi?id=18962>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND 
INSERTED IN THE BUG DATABASE.

http://nagoya.apache.org/bugzilla/show_bug.cgi?id=18962

New Method: getSubstitutedValues( String, Map )

           Summary: New Method: getSubstitutedValues( String, Map )
           Product: Commons
           Version: 1.0.1 Final
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: Enhancement
          Priority: Other
         Component: Lang
        AssignedTo: commons-dev@jakarta.apache.org
        ReportedBy: kenfitzpatrick@yahoo.com


Provide a method that performs value substitution over a String, as follows:
    Map valuesMap = new HashMap();
    valuesMap.put( "animal", "quick brown fox" );
    valuesMap.put( "target", "lazy dog" );
    StringUtils.getSubstitutedValues( "The ${animal} jumped over the
${target}.", valuesMap );

yielding: 
    The quick brown fox jumped over the lazy dog.

This would be analgous to the use of symbolic values in Jelly, Jexl, etc., but
made available in a simpler processing context (than only within components such
as those.)

If needed, I have developed and provided an implementation following this text.

Thank you!
Ken Fitzpatrick

-- Implementation ------------------------------
/**
 * StringUtilsProposals
 *
 * @author Ken Fitzpatrick
 * Created on Apr 11, 2003
 *
 */

package org.apache.commons.lang.proposed;

import org.apache.commons.lang.*;
import org.apache.commons.logging.*;

import java.util.*;

/**
    This class is a set of proposed methods to
    contribute to Jakarta/Commons/Lang/StringUtilsProposals.
 *
 * @author Ken Fitzpatrick
 */
public class StringUtilsProposals
{

    private static final Log log = LogFactory.getLog(StringUtilsProposals.class);
    private static final String SYMBOLIC_VALUE_MARKER_START = "${";
    private static final String SYMBOLIC_VALUE_MARKER_END = "}";

    /**
        <p>
        Returns a String that is the result of having performed
        symbolic variable substitution on
        <code>templateString</code>,
        using the value set found in
        <code>values</code>.
        </p>
        <p>
        The solution is compatible with all	JDK versions
        where Jakarta/Commons/Lang also is supported.
        </p>
        <p>
        The expected format of
        <code>templateString</code>
        is:
<pre>
    <code>The ${animal} jumped over the ${target}.</code>
</pre>
        such that the key/value pairs found in
        <code>values</code>
        are substituted into the string at the
        <code>${key-name}</code>
        markers.
        In the above example,
        <code>valuesMap</code>
        could have been populated as:
<pre>
    <code>Map valuesMap = HashMap();
    valuesMap.put( "animal", "quick brown fox" );
    valuesMap.put( "target", "lazy dog" );
    String resolvedString = StringUtils.getSubstitutedValues( templateString,
valuesMap );</code>
</pre>
        yielding:
<pre>
    <code>The quick brown fox jumped over the lazy dog.</code>
</pre>
        </p>
        <p>
        The same
        <code>templateString</code>
        from the above example could be reused as:
<pre>
    <code>Map valuesMap = HashMap();
    valuesMap.put( "animal", "cow" );
    valuesMap.put( "target", "moon" );
    String resolvedString = StringUtils.getSubstitutedValues( templateString,
valuesMap );</code>
</pre>
        yielding:
<pre>
    <code>The cow jumped over the moon.</code>
</pre>
        </p>
        <p>
        The value of
        <code>templateString</code>
        is returned in an unaltered if
        <code>templateString</code>
        is null, empty, or
        contains no marked variables that can be resolved by the
        key/value pairs found in
        <code>valuesMap</code>,
        or if
        <code>valuesMap</code>
        is null, empty or has no key/value pairs that can be
        applied to the marked variables within
        <code>templateString</code>.
        </p>
        <p>
        The value returned is not guaranteed to be the
        same String instance as
        <code>templateString</code>,
        so the following convention always should be used:
<pre>
    <code>String result = StringUtilsProposals.getSubstitutedValues(
previousResult, valuesMap );</code>
</pre>
        </p>
     * @param templateString
            String containing any mixture of variable and non-variable
            content, to be used as a template for the value substitution process
     * @param valuesMap
            Map containing the key/value pairs to be used to resolve
            the values of the marked variables found within
            <code>templateString</code>
     * @return String
     */
    public static String getSubstitutedValues(
            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() )
        {
            try
            {
                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 );
            }
            catch ( Exception ex )
            {
                log.error( "StringUtils.getSubstitutedValues(): " +
                    "Error encountered while attempting to " +
                    "substitute a value" +
                    "key=" + nextKey +
                    ", substitutionValue=" + substitutionValue +
                    ", nextValueToBeSubstituted=" + nextValueToBeSubstituted +
                    ", returnString=" + returnString +
                    ", Error=" + ex );
            }
        }
        return returnString;
    }


    /**
        <p>
        Returns a String that is the result of having performed
        symbolic variable substitution on
        <code>templateString</code>,
        using the value set found in
        <code>values</code>,
        in an optional recursive manner, indicated by
        <code>processRecursively</code>,
        </p>
        <p>
        If
        <code>processRecursively</code>
        is
        <code>true</code>,
        the value substitution process is processed
        successively until the resulting String no longer
        from the input String.
        </p>
        <p>
        The solution is compatible with all	JDK versions
        where Jakarta/Commons/Lang also is supported.
        </p>
        <p>
        The expected format of
        <code>templateString</code>
        is:
<pre>
    <code>The ${animal} jumped over the ${target}.</code>
</pre>
        such that the key/value pairs found in
        <code>values</code>
        are substituted into the string at the
        <code>${key-name}</code>
        markers.
        In the above example,
        <code>valuesMap</code>
        could have been populated as:
<pre>
    <code>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.getSubstitutedValues( templateString,
valuesMap, true );</code>
</pre>
        yielding:
<pre>
    <code>The quick brown fox jumped over the lazy dog.</code>
</pre>
        </p>
        <p>
        The same
        <code>templateString</code>
        from the above example could be reused as:
<pre>
    <code>Map valuesMap = HashMap();
    valuesMap.put( "animal", "cow" );
    valuesMap.put( "target", "moon" );
    String resolvedString = StringUtils.getSubstitutedValues( templateString,
valuesMap, true );</code>
</pre>
        yielding:
<pre>
    <code>The cow jumped over the moon.</code>
</pre>
        </p>
        <p>
        The value of
        <code>templateString</code>
        is returned in an unaltered if
        <code>templateString</code>
        is null, empty, or
        contains no marked variables that can be resolved by the
        key/value pairs found in
        <code>valuesMap</code>,
        or if
        <code>valuesMap</code>
        is null, empty or has no key/value pairs that can be
        applied to the marked variables within
        <code>templateString</code>.
        </p>
        <p>
        The value returned is not guaranteed to be the
        same String instance as
        <code>templateString</code>,
        so the following convention always should be used:
<pre>
    <code>String result = StringUtils.getSubstitutedValues( previousResult,
valuesMap, true );</code>
</pre>
        </p>
     * @param templateString
            String containing any mixture of variable and non-variable
            content, to be used as a template for the value substitution process
     * @param valuesMap
            Map containing the key/value pairs to be used to resolve
            the values of the marked variables found within
            <code>templateString</code>
     * @param processRecursively
            boolean value indicating whether the value substitution process
            should be performed recursively
     * @return String
     */
    public static String getSubstitutedValues(
            String templateString,
            Map valuesMap,
            boolean processRecursively )
    {
        if ( ! processRecursively )
            return StringUtilsProposals.getSubstitutedValues( templateString,
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 = StringUtilsProposals.getSubstitutedValues(
previousResult, valuesMap );
        }

        return currentResult;
    }

    public static void main( String[] args )
    {
        final String TEMPLATE = "The ${animal} jumped over the ${target}.";
        final String EXPECTED_RESULTS_1 = "The quick brown fox jumped over the
lazy dog.";
        final String EXPECTED_RESULTS_2 = "The cow jumped over the moon.";
        final String EXPECTED_RESULTS_3 = "The quick ${critterColor} fox jumped
over the ${pet}.";
        final String EXPECTED_RESULTS_4 = "The quick ${critterColor} fox jumped
over the ${pet}.";

        System.out.println( "Before substitution, template=" + TEMPLATE );
        Map valuesMap = null;
        String templateString = TEMPLATE;

        // test case: "The quick brown fox jumped over the lazy dog."
        valuesMap = new HashMap();
        valuesMap.put( "animal", "quick brown fox" );
        valuesMap.put( "target", "lazy dog" );
        System.out.println( "Test case 1: simple variable substitution" );
        System.out.println( "Expected result=" + EXPECTED_RESULTS_1 );
        System.out.println( "Actual   result=" +
            StringUtilsProposals.getSubstitutedValues( templateString, valuesMap
) );

        // test case: "The cow jumped over the moon."
        valuesMap = new HashMap();
        valuesMap.put( "animal", "cow" );
        valuesMap.put( "target", "moon" );
        System.out.println( "Test case 2: template reuse, different results" );
        System.out.println( "Expected result=" + EXPECTED_RESULTS_2 );
        System.out.println( "Actual   result=" +
            StringUtilsProposals.getSubstitutedValues( templateString, valuesMap
) );

        // negative test case: Map == null
        valuesMap = null;
        System.out.println( "Test case 3: Map == null" );
        System.out.println( "Expected result=" + TEMPLATE );
        System.out.println( "Actual   result=" +
            StringUtilsProposals.getSubstitutedValues( templateString, valuesMap
) );

        // negative test case: Map.isEmpty()
        valuesMap = new HashMap();
        System.out.println( "Test case 4: Map.isEmpty()" );
        System.out.println( "Expected result=" + TEMPLATE );
        System.out.println( "Actual   result=" +
            StringUtilsProposals.getSubstitutedValues( templateString, valuesMap
) );

        // negative test case: templateString == null
        valuesMap = new HashMap();
        valuesMap.put( "animal", "cow" );
        valuesMap.put( "target", "moon" );
        templateString = null;
        System.out.println( "Test case 5: template == null" );
        System.out.println( "Expected result=" + null );
        System.out.println( "Actual   result=" +
            StringUtilsProposals.getSubstitutedValues( templateString, valuesMap
) );

        // test case: process recursively
        templateString = TEMPLATE;
        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" );
        System.out.println( "Test case 6: processRecursively" );
        System.out.println( "Expected result=" + EXPECTED_RESULTS_1 );
        System.out.println( "Actual   result=" +
            StringUtilsProposals.getSubstitutedValues( templateString,
valuesMap, true ) );

        // test case: process recursively (false)
        System.out.println( "Test case 7: processRecursively == false" );
        System.out.println( "Expected result=" + EXPECTED_RESULTS_4 );
        System.out.println( "Actual   result=" +
            StringUtilsProposals.getSubstitutedValues( templateString,
valuesMap, false ) );

        // test case: process recursively
        templateString = TEMPLATE;
        valuesMap = new HashMap();
        valuesMap.put( "animal", "cow" );
        valuesMap.put( "target", "${celestialObject}" );
        valuesMap.put( "celestialObject", "moon" );
        System.out.println( "Test case 8: processRecursively == false" );
        System.out.println( "Expected result=" + EXPECTED_RESULTS_2 );
        System.out.println( "Actual   result=" +
            StringUtilsProposals.getSubstitutedValues( templateString,
valuesMap, true ) );
    }
}

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


Mime
View raw message