package jeql.engine.function; import java.util.List; import jeql.engine.CompilationException; import jeql.engine.Scope; import jeql.engine.query.QueryScope; import jeql.syntax.ParseTreeNode; import jeql.util.TypeUtil; /** * Implements the COUNTER pseudo-function semantics. * <p> * Syntax: * <pre> * COUNTER(incCondExpr [, resetCondExpr] ) * </pre> * COUNTER returns a integer value * which is incremented whenever incCondExpr is true. * The counter value is reset whenever resetCondExpr is true. * The initial value of COUNTER(...) is 1. * * This allows assigning sequence numbers to streams * which contian tag values that are fixed and known in advance. * An example is a stream of table cells, where the row and cell delimiters are known. * * @author Martin Davis * */ public class CounterFunctionEvaluator implements FunctionEvaluator { private int argCount = 0; private ParseTreeNode incCondExpr; private ParseTreeNode resetCondExpr = null; private int initialValue = 0; public static final String FN_NAME = "counter"; public CounterFunctionEvaluator() { } public void bind(Scope scope, List args) { argCount = args.size(); incCondExpr = (ParseTreeNode) args.get(0); if (args.size() >= 2) resetCondExpr = (ParseTreeNode) args.get(1); if (argCount < 1 || argCount > 2) throw new CompilationException("COUNTER() function must have 1 or 2 arguments"); } public Object eval(Scope scope) { // if this fails, function is being called outside of a SELECT => error QueryScope qScope = (QueryScope) scope; int counterValue = initialValue; if (qScope.getRowNum() <= 1) { counterValue = initialValue; // TODO: add initial value optional parameter //if (initExpr != null) // prevVal = initExpr.eval(scope); } else { counterValue = (Integer) qScope.getValue(this); } Object resetCondObj = null; if (resetCondExpr != null) resetCondObj = resetCondExpr.eval(scope); boolean resetVal = TypeUtil.toBoolean(resetCondObj); Object incCondObj = incCondExpr.eval(scope); boolean incVal = TypeUtil.toBoolean(incCondObj); if (resetVal) { // TODO: set this from optional value counterValue = initialValue; } if (incVal) { counterValue++; } qScope.setValue(this, counterValue); return counterValue; } public Class getType(Scope scope) { return Integer.class; } }