/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package com.espertech.esper.util;
import java.util.List;
import java.util.LinkedList;
import java.util.ArrayList;
/**
* Parser for strings with substitution parameters of the form ${parameter}.
*/
public class PlaceholderParser
{
/**
* Parses a string to find placeholders of format ${placeholder}.
* <p>
* Example: "My ${thing} is ${color}"
* <p>
* The example above parses into 4 fragements: a text fragment of value "My ",
* a parameter fragment "thing", a text fragement " is " and a parameter
* fragment "color".
* @param parseString is the string to parse
* @return list of fragements that can be either text fragments or placeholder fragments
* @throws PlaceholderParseException if the string cannot be parsed to indicate syntax errors
*/
public static List<Fragment> parsePlaceholder(String parseString) throws PlaceholderParseException
{
List<Fragment> result = new ArrayList<Fragment>();
int currOutputIndex = 0;
int currSearchIndex = 0;
while(true)
{
if (currSearchIndex == parseString.length())
{
break;
}
int startIndex = parseString.indexOf("${", currSearchIndex);
if (startIndex == -1)
{
// no more parameters, add any remainder of string
if (currOutputIndex < parseString.length())
{
String endString = parseString.substring(currOutputIndex, parseString.length());
TextFragment textFragment = new TextFragment(endString);
result.add(textFragment);
}
break;
}
// add text so far
if (startIndex > 0)
{
String textSoFar = parseString.substring(currOutputIndex, startIndex);
if (textSoFar.length() != 0)
{
result.add(new TextFragment(textSoFar));
}
}
// check if the parameter is escaped
if ((startIndex > 0) && (parseString.charAt(startIndex - 1) == '$'))
{
currOutputIndex = startIndex + 1;
currSearchIndex = startIndex + 1;
continue;
}
int endIndex = parseString.indexOf('}', startIndex);
if (endIndex == -1)
{
throw new PlaceholderParseException("Syntax error in property or variable: '" + parseString.substring(startIndex, parseString.length()) + "'");
}
// add placeholder
String between = parseString.substring(startIndex+2, endIndex);
ParameterFragment parameterFragment = new ParameterFragment(between);
result.add(parameterFragment);
currOutputIndex = endIndex + 1;
currSearchIndex = endIndex;
}
// Combine adjacent text fragements
LinkedList<Fragment> fragments = new LinkedList<Fragment>();
fragments.add(result.get(0));
for (int i = 1; i < result.size(); i++)
{
Fragment fragment = result.get(i);
if (!(result.get(i) instanceof TextFragment))
{
fragments.add(fragment);
continue;
}
if (!(fragments.getLast() instanceof TextFragment))
{
fragments.add(fragment);
continue;
}
TextFragment textFragment = (TextFragment) fragments.getLast();
fragments.removeLast();
fragments.add(new TextFragment(textFragment.getValue() + fragment.getValue()));
}
return fragments;
}
/**
* Fragment is a parse result, a parse results in an ordered list of fragments.
*/
public static abstract class Fragment
{
private String value;
/**
* Ctor.
* @param value is the fragment text
*/
protected Fragment(String value)
{
this.value = value;
}
/**
* Returns the string text of the fragment.
* @return fragment string
*/
public String getValue()
{
return value;
}
/**
* Returns true to indicate this is a parameter and not a text fragment.
* @return true if parameter fragement, false if text fragment.
*/
public abstract boolean isParameter();
public int hashCode()
{
return (value != null ? value.hashCode() : 0);
}
}
/**
* Represents a piece of text in a parse string with placeholder values.
*/
public static class TextFragment extends Fragment
{
/**
* Ctor.
* @param value is the text
*/
public TextFragment(String value)
{
super(value);
}
public boolean equals(Object obj)
{
if (!(obj instanceof TextFragment))
{
return false;
}
TextFragment other = (TextFragment) obj;
return other.getValue().equals(this.getValue());
}
public String toString()
{
return "text=" + getValue();
}
public boolean isParameter()
{
return false;
}
}
/**
* Represents a parameter in a parsed string of texts and parameters.
*/
public static class ParameterFragment extends Fragment
{
/**
* Ctor.
* @param value is the parameter name
*/
public ParameterFragment(String value)
{
super(value);
}
public boolean equals(Object obj)
{
if (!(obj instanceof ParameterFragment))
{
return false;
}
ParameterFragment other = (ParameterFragment) obj;
return other.getValue().equals(this.getValue());
}
public boolean isParameter()
{
return true;
}
public String toString()
{
return "param=" + getValue();
}
}
}