
uk.org.retep.util.string.PatternReplacer Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2010, Peter T Mount
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the retep.org.uk nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package uk.org.retep.util.string;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.concurrent.NotThreadSafe;
/**
* PatternReplacer replaces all keywords found within a String or StringBuffer
* with values stored within a Map.
*
* The pattern must contain two groups. The second group is taken to be the key
* to lookup within the string, whilst the first the delimiters for finding the key.
*
* For example: In the default pattern "(@@(.*?)@@)" group 2 is (.*?) and would
* be in the source template as "@@mykey@@". We use a nested group so that the "@@"
* symbols are removed from the output as well.
*
* @author Peter T Mount
* @version 8.7
*/
@NotThreadSafe
public class PatternReplacer
{
/**
* The default pattern that matches @@key@@
*
* "(@@(.*?)@@)" where group 2 is the key "(.*?)" and group 1 contains
* they key identifiers "@@"
*/
public static final String DEFAULT_PATTERN = "(@@(.*?)@@)";
/** Holds value of property pattern. */
private Pattern pattern;
/** Holds value of property values. */
private Map values;
/** Holds value of property customReplacer. */
private PatternReplacer.Replacer customReplacer;
/**
* Create an instance using DEFAULT_PATTERN
*/
public PatternReplacer()
{
this( DEFAULT_PATTERN, null );
}
/**
* Create an instance using an alternative pattern.
* This pattern must contain two groups.
*
* @see #DEFAULT_PATTERN
* @param pattern to use
*/
public PatternReplacer( final String pattern )
{
this( pattern, null );
}
/**
* Create an instance using an alternative pattern.
* This pattern must contain two groups.
*
* @see #DEFAULT_PATTERN
* @param replacer to use
*/
public PatternReplacer( final PatternReplacer.Replacer replacer )
{
this( DEFAULT_PATTERN, replacer );
}
/**
* Create an instance using an alternative pattern.
* This pattern must contain two groups.
*
* @see #DEFAULT_PATTERN
* @param pattern to use
* @param replacer PatternReplacer.Replacer instance to use, null for default lookup
*/
public PatternReplacer( final String pattern,
final PatternReplacer.Replacer replacer )
{
this.customReplacer = replacer;
this.pattern = Pattern.compile( pattern );
}
/**
* Given a String return a String with all instances of the pattern replaced.
*
* @param string String containing the template
* @param values Map containing the values to use
* @return new String containing the replaced text
*/
public String replaceString( final String string,
final Map values )
{
setValues( values );
return replaceString( string );
}
/**
* Given a String return a String with all instances of the pattern replaced.
*
* @param string String containing the template
* @return new String containing the replaced text
*/
public String replaceString( final String string )
{
return replace( string ).toString();
}
/**
* Given a StringBuffer return a new StringBuffer with all instances of the
* pattern replaced.
*
* @param sb StringBuffer containing the template
* @param values Map containing the values to use
* @return new StringBuffer containing the replaced text
*/
public StringBuffer replaceStringBuffer( final StringBuffer sb,
final Map values )
{
setValues( values );
return replaceStringBuffer( sb );
}
/**
* Given a StringBuffer return a new StringBuffer with all instances of the
* pattern replaced.
*
* @param sb StringBuffer containing the template
* @return new StringBuffer containing the replaced text
*/
public StringBuffer replaceStringBuffer( final StringBuffer sb )
{
return (StringBuffer) replace( sb );
}
/**
* Given a StringBuffer return a new StringBuffer with all instances of the
* pattern replaced.
*
* @param sb StringBuilder containing the template
* @param values Map containing the values to use
* @return new StringBuffer containing the replaced text
*/
public StringBuilder replaceStringBuilder( final StringBuilder sb,
final Map values )
{
setValues( values );
return replaceStringBuilder( sb );
}
/**
* Given a StringBuffer return a new StringBuffer with all instances of the
* pattern replaced.
*
* @param sb StringBuilder containing the template
* @return new StringBuffer containing the replaced text
*/
public StringBuilder replaceStringBuilder( final StringBuilder sb )
{
return new StringBuilder( replace( sb ) );
}
/**
* Given a Map return a new Map with all values of the
* pattern replaced.
*
* @param map Map who's values contain the templates
* @param values Map containing the values to use
* @return new StringBuffer containing the replaced text
*/
public Map replaceMap( final Map map,
final Map values )
{
setValues( values );
return replaceMap( map );
}
/**
* Given a Map return a new Map with all values of the
* pattern replaced.
*
* @param map Map who's values contain the templates
* @return new StringBuffer containing the replaced text
*/
public Map replaceMap( final Map map )
{
final Map newMap = new HashMap();
for( Map.Entry entry : map.entrySet() )
{
newMap.put( entry.getKey(), replace( entry.getValue() ).toString() );
}
return newMap;
}
/**
* Given a CharSequence return a new CharSequence with all instances of the
* pattern replaced.
*
* @param seq CharSequence containing the template
* @param values Map containing the values to use
* @return new CharSequence containing the replaced text
*/
public CharSequence replace( final CharSequence seq,
final Map values )
{
setValues( values );
return replace( seq );
}
/**
* Given a StringBuffer return a new StringBuffer with all instances of the
* pattern replaced.
*
* @param seq CharSequnce containing the template
* @return new CharSequence containing the replaced text
*/
public CharSequence replace( final CharSequence seq )
{
final StringBuffer sb = new StringBuffer();
final Matcher mat = pattern.matcher( seq );
while( mat.find() )
{
mat.appendReplacement( sb, replace( mat, values ) );
}
mat.appendTail( sb );
return (CharSequence) sb;
}
/**
* This method converts the replacement text.
* If there is no customReplacer, then it just uses the key for the value
* in the Map. But if a customReplacer exists, then it uses it to create
* the replacement text instead.
*/
private String replace( final Matcher matcher,
final Map values )
{
String ret = "";
if( getCustomReplacer() == null )
{
// default - just lookup the value
String key = matcher.group( 2 );
ret = values.containsKey( key ) ? values.get( key ).toString() : "";
}
else
{
// Use the custom replacer to resolve
ret = getCustomReplacer().lookupReplacement( matcher, values );
}
return ret;
}
/**
* Return the Pattern object being used
* @return Pattern
*
*/
public Pattern getPattern()
{
return this.pattern;
}
/**
* Return the current/last used values Map
* @return Map of values
*
*/
public Map getValues()
{
return this.values;
}
/**
* Set the values to be used in the next call to replace.
* @param values New Map containing values
*
*/
public void setValues( final Map values )
{
this.values = values;
}
/**
* Returns the custom replacer instance for this object
* @return The custom replacer instance for this object
*
*/
public PatternReplacer.Replacer getCustomReplacer()
{
return customReplacer;
}
/**
* Interface that a custom replace handler must implement.
*
* By default, no instance is used, and the key name is used as the key
* within the Map. However, by implementing this interface, any complex
* pattern may be used.
*
* For example, implementations of this interface can perform specialist
* transformations, like using Apache BeanUtils to perform bean lookups
* from a bean contained in the map.
*
* ie: the pattern (@@(.*?)\\.(.*?)@@) could be used for the syntax:
* @@bean.property@@ where bean is retrieved using matcher.group(2)
* and property by using matcher.group(3). Then the entire pattern is
* replaced with the value of that property.
*/
public static interface Replacer
{
/**
* Given a Matcher, use its group(int) method to lookup
* a replacement value from the map, and return as a String.
*
* @param matcher Matcher with current value
* @param values Map containing the supplied values
* @return replacement value
*/
public String lookupReplacement( Matcher matcher,
Map values );
}
}