org.mule.util.TemplateParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mule-core Show documentation
Show all versions of mule-core Show documentation
Mule server and core classes
/*
* Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com
* The software in this package is published under the terms of the CPAL v1.0
* license, a copy of which has been included with this distribution in the
* LICENSE.txt file.
*/
package org.mule.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* TemplateParser
is a simple string parser that will substitute
* tokens in a string with values supplied in a Map.
*/
public final class TemplateParser
{
public static final String ANT_TEMPLATE_STYLE = "ant";
public static final String SQUARE_TEMPLATE_STYLE = "square";
public static final String CURLY_TEMPLATE_STYLE = "curly";
public static final String WIGGLY_MULE_TEMPLATE_STYLE = "mule";
private static final String DOLLAR_ESCAPE = "@@@";
private static final String NULL_AS_STRING = "null";
private static final Map patterns = new HashMap();
static
{
patterns.put(ANT_TEMPLATE_STYLE, new PatternInfo(ANT_TEMPLATE_STYLE, "\\$\\{[^\\{\\}]+\\}", "${", "}"));
patterns.put(SQUARE_TEMPLATE_STYLE, new PatternInfo(SQUARE_TEMPLATE_STYLE, "\\[[^\\[\\]]+\\]", "[", "]"));
patterns.put(CURLY_TEMPLATE_STYLE, new PatternInfo(CURLY_TEMPLATE_STYLE, "\\{[^\\{\\}}]+\\}", "{", "}"));
// Such a complex regex is needed to support nested expressions, otherwise we
// have to do this manually or using an ANTLR grammar etc.
// Support for 6 levels (5 nested)
patterns.put(WIGGLY_MULE_TEMPLATE_STYLE, new PatternInfo(WIGGLY_MULE_TEMPLATE_STYLE,
"#\\[((?:#?\\[(?:#?\\[(?:#?\\[(?:#?\\[(?:#?\\[.*?\\]|[^\\[\\]])*?\\]|[^\\[\\]])*?\\]|[^\\[\\]])*?\\]|[^\\[\\]])*?\\]|[^\\[\\]])*?)\\]", "#[", "]"));
}
/**
* logger used by this class
*/
protected static final Log logger = LogFactory.getLog(TemplateParser.class);
public static final Pattern ANT_TEMPLATE_PATTERN = patterns.get(ANT_TEMPLATE_STYLE).getPattern();
public static final Pattern SQUARE_TEMPLATE_PATTERN = patterns.get(SQUARE_TEMPLATE_STYLE).getPattern();
public static final Pattern CURLY_TEMPLATE_PATTERN = patterns.get(CURLY_TEMPLATE_STYLE).getPattern();
public static final Pattern WIGGLY_MULE_TEMPLATE_PATTERN = patterns.get(WIGGLY_MULE_TEMPLATE_STYLE).getPattern();
private final Pattern pattern;
private final int pre;
private final int post;
private final PatternInfo style;
public static TemplateParser createAntStyleParser()
{
return new TemplateParser(ANT_TEMPLATE_STYLE);
}
public static TemplateParser createSquareBracesStyleParser()
{
return new TemplateParser(SQUARE_TEMPLATE_STYLE);
}
public static TemplateParser createCurlyBracesStyleParser()
{
return new TemplateParser(CURLY_TEMPLATE_STYLE);
}
public static TemplateParser createMuleStyleParser()
{
return new TemplateParser(WIGGLY_MULE_TEMPLATE_STYLE);
}
private TemplateParser(String styleName)
{
this.style = patterns.get(styleName);
if (this.style == null)
{
throw new IllegalArgumentException("Unknown template style: " + styleName);
}
pattern = style.getPattern();
pre = style.getPrefix().length();
post = style.getSuffix().length();
}
/**
* Matches one or more templates against a Map of key value pairs. If a value for
* a template is not found in the map the template is left as is in the return
* String
*
* @param props the key/value pairs to match against
* @param template the string containing the template place holders i.e. My name
* is ${name}
* @return the parsed String
*/
public String parse(Map props, String template)
{
return parse(props, template, null);
}
/**
* Matches one or more templates against a Map of key value pairs. If a value for
* a template is not found in the map the template is left as is in the return
* String
*
* @param callback a callback used to resolve the property name
* @param template the string containing the template place holders i.e. My name
* is ${name}
* @return the parsed String
*/
public String parse(TemplateCallback callback, String template)
{
return parse(null, template, callback);
}
protected String parse(Map props, String template, TemplateCallback callback)
{
String result = template;
Map newProps = props;
if (props != null && !(props instanceof CaseInsensitiveHashMap))
{
newProps = new CaseInsensitiveHashMap(props);
}
Matcher m = pattern.matcher(result);
while (m.find())
{
Object value = null;
String match = m.group();
String propname = match.substring(pre, match.length() - post);
if (callback != null)
{
value = callback.match(propname);
if (value == null)
{
value = NULL_AS_STRING;
}
}
else if (newProps != null)
{
value = newProps.get(propname);
}
if (value == null)
{
if (logger.isDebugEnabled())
{
logger.debug("Value " + propname + " not found in context");
}
}
else
{
String matchRegex = Pattern.quote(match);
String valueString = value.toString();
//need to escape $ as they resolve into group references, escaping them was not enough
//This smells a bit like a hack, but one way or another these characters need to be escaped
if (valueString.indexOf('$') != -1)
{
valueString = valueString.replaceAll("\\$", DOLLAR_ESCAPE);
}
if (valueString.indexOf('\\') != -1)
{
valueString = valueString.replaceAll("\\\\", "\\\\\\\\");
}
result = result.replaceAll(matchRegex, valueString);
}
}
if (result.indexOf(DOLLAR_ESCAPE) != -1)
{
result = result.replaceAll(DOLLAR_ESCAPE, "\\$");
}
return result;
}
/**
* Matches one or more templates against a Map of key value pairs. If a value for
* a template is not found in the map the template is left as is in the return
* String
*
* @param props the key/value pairs to match against
* @param templates A List of templates
* @return the parsed String
*/
public List parse(Map props, List templates)
{
if (templates == null)
{
return new ArrayList