package jeql.syntax.operation;
import java.util.Date;
import jeql.api.error.ExecutionException;
import jeql.api.table.Table;
import jeql.engine.Scope;
import jeql.syntax.ParseTreeNode;
import jeql.util.ClassUtil;
import com.vividsolutions.jts.geom.Geometry;
public abstract class Operation
{
public static final int MUL = 1;
public static final int DIV = 2;
public static final int ADD = 3;
public static final int SUB = 4;
public static final int MOD = 5;
public static final int GT = 10;
public static final int GE = 11;
public static final int LT = 12;
public static final int LE = 13;
public static final int EQ = 14;
public static final int NE = 15;
public static final int RE_FIND = 16;
public static final int RE_MATCH = 17;
public static final int AND = 20;
public static final int OR = 21;
public static final int XOR = 22;
public static final int NOT = 23;
public static final int UNION = 23;
public static boolean isRelOp(int op)
{
return op == EQ
|| op == NE
|| op == GT
|| op == GE
|| op == LT
|| op == LE
|| op == RE_FIND
|| op == RE_MATCH;
}
public static boolean isBooleanTypeOp(int op)
{
return op == AND
|| op == OR
|| op == NOT
|| isRelOp(op);
}
public static int toOpcode(String opStr)
{
if (opStr.equals("*")) return MUL;
if (opStr.equals("/")) return DIV;
if (opStr.equals("+")) return ADD;
if (opStr.equals("-")) return SUB;
if (opStr.equals("%")) return MOD;
if (opStr.equals(">")) return GT;
if (opStr.equals(">=")) return GE;
if (opStr.equals("<")) return LT;
if (opStr.equals("<=")) return LE;
if (opStr.equals("==")) return EQ;
if (opStr.equals("!=")) return NE;
if (opStr.equals("~")) return RE_FIND;
if (opStr.equals("~=")) return RE_MATCH;
if (opStr.equalsIgnoreCase("and")) return AND;
if (opStr.equalsIgnoreCase("or")) return OR;
if (opStr.equalsIgnoreCase("xor")) return XOR;
if (opStr.equalsIgnoreCase("not")) return NOT;
//if (opStr.equalsIgnoreCase("union")) return UNION;
return -1;
}
protected ParseTreeNode e1;
protected ParseTreeNode e2;
protected int opCode;
protected String opStr;
public Operation(ParseTreeNode e1, ParseTreeNode e2, String opStr, int opCode) {
this.e1 = e1;
this.e2 = e2;
this.opCode = opCode;
this.opStr = opStr;
}
public Class getType(Scope scope) {
Class t1 = e1.getType(scope);
Class t2 = e2.getType(scope);
return getResultType(t1, t2);
}
/**
* Determines the result type of a mixed-type operator
*
* @param t1
* @param t2
* @return
*/
public static Class getResultType(Class t1, Class t2) {
Class exprType = Integer.class;
// order is important in the following
if (t1 == String.class || t2 == String.class) {
exprType = String.class;
} else if (t1 == Double.class || t2 == Double.class) {
exprType = Double.class;
} else if (t1 == Date.class || t2 == Date.class) {
exprType = Date.class;
} else if (t1 == Boolean.class || t2 == Boolean.class) {
exprType = Boolean.class;
} else if (t1 == Table.class || t2 == Table.class) {
exprType = Table.class;
} else if (Geometry.class.isAssignableFrom(t1)
|| Geometry.class.isAssignableFrom(t2))
exprType = Geometry.class;
return exprType;
}
/**
*
* @param o1
* @param o2
* @return null if both inputs are null
*/
protected static Class getMostGeneralType(Object o1, Object o2) {
if (o1 == null && o2 == null) return null;
if (o1 == null)
return o2.getClass();
if (o2 == null)
return o1.getClass();
return Operation.getResultType(o1.getClass(), o2.getClass());
}
/**
* A few type coercions are allowed.
*
* @param o
* @param reqType
* @return
*/
protected Object coerce(Object o, Class reqType) {
if (o == null)
return null;
if (reqType == null) return o;
if (o.getClass() == reqType)
return o;
if (reqType == String.class) {
return o.toString();
}
if (reqType == Double.class) {
if (o instanceof Integer)
return new Double(((Integer) o).intValue());
}
if (reqType == Geometry.class) {
if (o instanceof Geometry)
return o;
//TODO: handle WKT strings
}
throw new ExecutionException("Can't convert type "
+ ClassUtil.classname(o.getClass()) + " to "
+ ClassUtil.classname(reqType)
+ " in operation " + opStr);
}
protected void require(Object o, Class reqType) {
if (o == null)
return;
if (o.getClass() == reqType)
return;
throw new ExecutionException("Type "
+ ClassUtil.classname(o.getClass())
+ " is not of the required type "
+ ClassUtil.classname(reqType)
+ " in operation " + opStr);
}
/**
* Implementors must handle null operands.
*
* @param o1
* @param o2
* @return
*/
public abstract Object compute(Object o1, Object o2);
}