package flare.query { import flare.util.IEvaluable; import flare.util.IPredicate; /** * Base class for query expression operators. Expressions are organized * into a tree of operators that perform data processing or predicate * testing on input Object instances. */ public class Expression implements IEvaluable, IPredicate { /** * Evaluates this expression with the given input object. * @param o the input object to this expression * @return the result of evaluating the expression */ public function eval(o:Object=null):* { return o; } /** * Boolean predicate that tests the output of evaluating this * expression. Returns true if the expression evaluates to true, or * a non-null or non-zero value. Returns false if the expression * evaluates to false, or a null or zero value. * @param o the input object to this expression * @return the Boolean result of evaluating the expression */ public function predicate(o:Object):Boolean { return Boolean(eval(o)); } /** * The number of sub-expressions that are children of this expression. * @return the number of child expressions. */ public function get numChildren():int { return 0; } /** * Returns the sub-expression at the given index. * @param idx the index of the child sub-expression * @return the requested sub-expression. */ public function getChildAt(idx:int):Expression { return null; } /** * Set the sub-expression at the given index. * @param idx the index of the child sub-expression * @param expr the sub-expression to set * @return true if the the sub-expression was successfully set, * false otherwise */ public function setChildAt(idx:int, expr:Expression):Boolean { return false; } /** * Returns a string representation of the expression. * @return this expression as a string value */ public function toString():String { return null; } /** * Creates a cloned copy of the expression. Recursively clones any * sub-expressions. * @return the cloned expression. */ public function clone():Expression { throw new Error("This is an abstract method"); } /** * Sequentially invokes the input function on this expression and all * sub-expressions. Complete either when all expressions have been * visited or the input function returns true, thereby signalling an * early exit. * @param f the visiting function to invoke on this expression * @return true if the input function signalled an early exit */ public function visit(f:Function):Boolean { var iter:ExpressionIterator = new ExpressionIterator(this); return visitHelper(iter, f); } private function visitHelper(iter:ExpressionIterator, f:Function):Boolean { if (f(iter.current) as Boolean) return true; if (iter.down() != null) { do { if (visitHelper(iter, f)) return true; } while (iter.next() != null); iter.up(); } return false; } // -------------------------------------------------------------------- private static const _LBRACE:Number = "{".charCodeAt(0); private static const _RBRACE:Number = "}".charCodeAt(0); private static const _SQUOTE:Number = "'".charCodeAt(0); private static const _DQUOTE:Number = "\"".charCodeAt(0); /** * Utility method that maps an input value into an Expression. If the * input value is already an Expression, it is simply returned. If the * input value is a String, it is interpreted as either a variable or * string literal. If the first and last characters of the string are * single quotes (') or double quotes ("), the characters between the * quotes will be used as a string Literal. If there are no quotes, or * if the string is enclosed by curly braces ({}), the string value * (sans braces) will be used as the property name of a new Variable. * In all other cases (Numbers, Dates, etc), a new Literal expression * for the input value is returned. * @param o the input value * @return an Expression corresponding to the input value */ public static function expr(o:*):Expression { if (o is Expression) { return o as Expression; } else if (o is Class) { return new IsA(Class(o)); } else if (o is String) { var s:String = o as String; var c1:Number = s.charCodeAt(0); var c2:Number = s.charCodeAt(s.length-1); if (c1 == _LBRACE && c2 == _RBRACE) { // braces -> variable return new Variable(s.substr(1, s.length-2)); } else if (c1 == _SQUOTE && c2 == _SQUOTE) { // quote -> string return new Literal(s.substr(1, s.length-2)); } else if (c1 == _DQUOTE && c2 == _DQUOTE) { // quote -> string return new Literal(s.substr(1, s.length-2)); } else { // default -> variable return new Variable(s); } } else { return new Literal(o); } } } // end of class Expression }