package tcg.common.util;
import java.util.ArrayList;
import org.codehaus.janino.ExpressionEvaluator;
public class ExpressionParser
{
private String expression_ = "";
private Class<?> returnType_ = int.class;
private ExpressionEvaluator evaluator_ = null;
//list of variables
private ArrayList<String> varNames_ = new ArrayList<String>();
private ArrayList<Class<?>> varTypes_ = new ArrayList<Class<?>>();
private ArrayList<Object> varValues_ = new ArrayList<Object>();
//Once the expression is parsed, if it contains invalid expression this flag
//will be set to false. This will prevent more attempt of parsing.
private boolean isValidExpression_ = true;
/**
* Default constructor.
* The preferred ctor is the one with parameters: expression string and return type
*/
public ExpressionParser()
{
//default constructor
}
/**
* Preferred constructor. Provide the expression string and the return type
* @param expression - the expression string
* @param type - the return type
*/
public ExpressionParser(String expression, Class<?> type)
{
if (expression != null)
{
this.expression_ = expression;
}
if (type != null)
{
this.returnType_ = type;
}
}
/**
* Set expression. If one uses the default ctor, he must call this right after.
* Note: The preferred way is to use ctor with the parameters.
*
* @param expression - the expression string
* @param type - the return type
*/
public void setExpression(String expression, Class<?> type)
{
if (expression == null)
{
return;
}
if (type == null)
{
return;
}
//reset the class just in case
this.reset();
//copy to local variable
this.expression_ = expression;
this.returnType_ = type;
}
/**
* Reset the expression parser.
*/
protected void reset()
{
expression_ = "";
evaluator_ = null;
varNames_.clear();
varTypes_.clear();
varValues_.clear();
//always default to valid (true), otherwise the parser will never get initialized
isValidExpression_ = true;
}
/**
* Get current expression
* @return - the current expression
*/
public String getExpression()
{
return expression_;
}
/**
* Check if the expression is valid.
* @return - true if the expression is valid, false otherwise.
* if the expression is not parsed (ie. not initialized), it will always return true.
* if the expression is never set, then it will return false
*/
public boolean isValid()
{
//if expression is never set, always return as not valid
if (expression_.isEmpty())
{
return false;
}
return isValidExpression_;
};
/**
* Check if the expression has been successfully parsed.
* @return - true if the expression has been successfully parsed, false otherwise
*/
public boolean isInitialized()
{
return (evaluator_ != null);
}
/**
* Evaluate the expression.
* <br>
* If the expression is not yet parsed, it will be parsed. If the operation (both the parsing,
* if it is not yet done, and the evaluation) fails, the return value is null.
* The caller is responsible for converting the result into the correct type.
*
* @return the result of the expression if successful, null otherwise.
* @throws Exception when the evaluation fails, exception is thrown.
*/
public Object evaluate() throws Exception
{
//if expression is never set, return null
if (evaluator_ == null && expression_.isEmpty())
{
//not set yet
return null;
}
//if it is already flag as invalid, return null
if (!isValidExpression_)
{
return null;
}
if (evaluator_ == null)
{
//create the parser
try
{
evaluator_ = new ExpressionEvaluator(
expression_, // expression
returnType_, // expressionType
varNames_.toArray(new String[0]), // parameterNames
varTypes_.toArray(new Class<?>[0]) // parameterTypes
);
isValidExpression_ = true;
}
catch(Exception ex)
{
//set the invalid flag to prevent re-parsing next time
isValidExpression_ = false;
throw new Exception("evaluate(): " + ex.toString());
}
}
//evaluate the expression
Object result = null;
try
{
result = evaluator_.evaluate(varValues_.toArray(new Object[0]));
}
catch (Exception ex)
{
throw new Exception("evaluate(): " + ex.toString());
}
return result;
}
/**
* Create internal variable.
* You must create all the necessary variable before you execute the expression.
*
* @param varName - variable name
* @param varType - variable class type
* @param initValue - initial value
* @return - the index in the variable list if successful, -1 otherwise
*/
public int createVariable(String varName, Class<?> varType, Object initValue)
{
if (varName == null || varName.length() == 0 || varType == null || initValue == null)
{
return -1;
}
varNames_.add(varName);
varTypes_.add(varType);
varValues_.add(initValue);
return varNames_.size()-1;
}
/**
* Set internal variable value. Note: caller need to translate the raw value to generic object.
* @param varName - variable name
* @param value - new value
* @return status of the operation
*/
public boolean set(String varName, Object value)
{
if (value == null)
{
return false;
}
//can not simply use "idx = varnames_.indexOf(varName);" because the String object is immutable.
//That is, even if the actual string value is the same, a different String object will return
// back inequality.
int idx = -1;
for (int i=0; i<varNames_.size(); i++)
{
if (varNames_.get(i).compareTo(varName) == 0)
{
idx = i;
break;
}
}
if (idx >= 0)
{
varValues_.set(idx, value);
return true;
}
return false;
}
/**
* Set internal variable value. Note: caller need to translate the raw value to generic object.
* @param idx - variable index/position
* @param value - new value
* @return status of the operation
*/
public boolean set(int idx, Object value)
{
if (idx < 0 || idx >= varNames_.size() || value == null)
{
return false;
}
varValues_.set(idx, value);
return true;
}
/**
* Get internal variable value.
* @param varName - variable name
* @return - the variable value
*/
public Object get(String varName)
{
int idx = -1;
for (int i=0; i<varNames_.size(); i++)
{
if (varNames_.get(i).compareTo(varName) == 0)
{
idx = i;
break;
}
}
if (idx >= 0)
{
return varValues_.get(idx);
}
return null;
}
/**
* Get internal variable value.
* @param idx - variable index/position
* @return - the variable value
*/
public Object get(int idx)
{
if (idx < 0 || idx >= varNames_.size())
{
return null;
}
return varValues_.get(idx);
}
}