/*
* Copyright (C) 2000 - 2010 TagServlet Ltd
*
* This file is part of Open BlueDragon (OpenBD) CFML Server Engine.
*
* OpenBD is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* Free Software Foundation,version 3.
*
* OpenBD is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenBD. If not, see http://www.gnu.org/licenses/
*
* Additional permission under GNU GPL version 3 section 7
*
* If you modify this Program, or any covered work, by linking or combining
* it with any of the JARS listed in the README.txt (or a modified version of
* (that library), containing parts covered by the terms of that JAR, the
* licensors of this Program grant you additional permission to convey the
* resulting work.
* README.txt @ http://www.openbluedragon.org/license/README.txt
*
* http://www.openbluedragon.org/
*/
package com.naryx.tagfusion.cfm.parser;
import org.antlr.runtime.Token;
import com.naryx.tagfusion.cfm.engine.cfBooleanData;
import com.naryx.tagfusion.cfm.engine.cfData;
import com.naryx.tagfusion.cfm.engine.cfDateData;
import com.naryx.tagfusion.cfm.engine.cfNullData;
import com.naryx.tagfusion.cfm.engine.cfNumberData;
import com.naryx.tagfusion.cfm.engine.cfStringData;
import com.naryx.tagfusion.cfm.engine.cfmRunTimeException;
import com.naryx.tagfusion.cfm.engine.dataNotSupportedException;
public class CFBinaryExpression extends CFExpression implements java.io.Serializable {
private static final long serialVersionUID = 1L;
static private final int _ERR = 0;
static private final int _NUM = 1;
static private final int _STR = 2;
static private final int _BOOL = 3;
static private final int _REF = 4; // cfStructs, cfArrays
static private final int _DATE = 5; // date ops
// instance vars
private int _kind;
private CFExpression _left;
private CFExpression _right;
private String operatorImage;
public CFBinaryExpression( Token t, CFExpression left, CFExpression right ) {
super(t);
_kind = t.getType();
operatorImage = t.getText();
if ( _kind == CFMLLexer.ANDOPERATOR ) {
_kind = CFMLLexer.AND;
} else if ( _kind == CFMLLexer.OROPERATOR ) {
_kind = CFMLLexer.OR;
} else if ( _kind == CFMLLexer.MODOPERATOR ) {
_kind = CFMLLexer.MOD;
}
_left = left;
_right = right;
}
public byte getType() {
return CFExpression.BINARY;
}
public cfData Eval( CFContext context ) throws cfmRunTimeException {
// note that boolean's are treated as numbers
cfData leftVal = null;
cfData rightVal = null;
cfData val = null;
double leftNum = 0.0;
double rightNum = 0.0;
double valNum = 0.0;
long leftDate = 0;
long rightDate = 0;
long valDate = 0;
String leftStr = "";
String rightStr = "";
String valStr = "";
boolean leftBool = false;
boolean rightBool = false;
boolean valBool = false;
int opTypes = _ERR;
// req'd for result type. Req'd cos for example "str EQ str" returns a
// boolean
int valType = _ERR;
setLineCol(context);
leftVal = _left.Eval(context);
setLineCol(context);
if ( leftVal.getDataType() == cfData.CFLDATA ) {
leftVal = ((cfLData) leftVal).Get(context);
}
if ( _kind != CFMLLexer.AND && _kind != CFMLLexer.OR ) {
rightVal = _right.Eval(context);
if ( rightVal.getDataType() == cfData.CFLDATA ) {
rightVal = ((cfLData) rightVal).Get(context);
}
}
// Convert the operands to appropriate types
switch (_kind) {
// numeric operations
case CFMLLexer.STAR:
case CFMLLexer.SLASH:
case CFMLLexer.BSLASH:
case CFMLLexer.MOD:
case CFMLLexer.POWER:
case CFMLLexer.PLUS:
case CFMLLexer.MINUS:
if ( leftVal.isNumberConvertible() && rightVal.isNumberConvertible() ) {
// Both operands must be numbers
leftNum = leftVal.getDouble();
rightNum = rightVal.getDouble();
opTypes = _NUM;
valType = _NUM;
} else if ( leftVal.isDateConvertible() && rightVal.isDateConvertible() ) {
leftDate = leftVal.getDateLong();
rightDate = rightVal.getDateLong();
opTypes = _DATE;
valType = _DATE;
} else {
throw new CFException(
"Can't perform this operation on these data types.", context);
}
break;
// comparator operators
case CFMLLexer.GT:
case CFMLLexer.LT:
case CFMLLexer.GTE:
case CFMLLexer.GE:
case CFMLLexer.LTE:
case CFMLLexer.LE:
case CFMLLexer.EQ:
case CFMLLexer.IS:
case CFMLLexer.NEQ:
if ( leftVal.isNumberConvertible() && rightVal.isNumberConvertible() ) {
leftNum = leftVal.getDouble();
rightNum = rightVal.getDouble();
opTypes = _NUM;
} else if ( leftVal.isDateConvertible() && rightVal.isDateConvertible() ) {
leftDate = leftVal.getDateLong();
rightDate = rightVal.getDateLong();
opTypes = _DATE;
} else if ( isStringConvertible(leftVal) && isStringConvertible(rightVal) ) {
// All simple data types are string convertible
leftStr = leftVal.getString();
rightStr = rightVal.getString();
opTypes = _STR;
} else {
throw new CFException(
"Can't perform this operation on these data types.", context);
}
valType = _BOOL;
break;
case CFMLLexer.CONTAINS:
case CFMLLexer.DOESNOTCONTAIN:
if ( isStringConvertible(leftVal) && isStringConvertible(rightVal) ) {
leftStr = leftVal.getString();
rightStr = rightVal.getString();
valType = _BOOL;
opTypes = _STR;
} else {
throw new CFException(
"Can't perform this operation on these data types.", context);
}
break;
case CFMLLexer.CONCAT:
if ( isStringConvertible(leftVal) && isStringConvertible(rightVal) ) {
leftStr = leftVal.getString();
rightStr = rightVal.getString();
valType = _STR;
opTypes = _STR;
} else {
throw new CFException(
"Can't perform this operation on these data types.", context);
}
break;
case CFMLLexer.AND:
case CFMLLexer.OR:
// don't want to touch the RHS until LHS is evaluated to true or not
if ( leftVal.isBooleanConvertible() ) {
// Both operands must be booleans
leftBool = leftVal.getBoolean();
opTypes = _BOOL;
valType = _BOOL;
} else {
throw new CFException(
"Can't perform this operation on these data types.", context);
}
break;
case CFMLLexer.XOR:
case CFMLLexer.IMP:
case CFMLLexer.EQV:
if ( leftVal.isBooleanConvertible() && rightVal.isBooleanConvertible() ) {
// Both operands must be booleans
leftBool = leftVal.getBoolean();
rightBool = rightVal.getBoolean();
opTypes = _BOOL;
valType = _BOOL;
} else {
throw new CFException(
"Can't perform this operation on these data types.", context);
}
break;
default:
break;
}
// finally perform the operation
switch (_kind) {
case CFMLLexer.AND:
if ( leftBool ) {
rightVal = _right.Eval(context);
if ( rightVal.getDataType() == cfData.CFLDATA ) {
rightVal = ((cfLData) rightVal).Get(context);
}
if ( rightVal.isBooleanConvertible() ) {
rightBool = rightVal.getBoolean();
valBool = rightBool; // && leftBool -- we know leftBool is true
} else {
throw new CFException(
"Can't perform this operation on these data types.", context);
}
} else {
valBool = false;
}
break;
case CFMLLexer.XOR:
valBool = (leftBool | rightBool) && !(leftBool && rightBool);
break;
case CFMLLexer.OR:
// only if the LHS evaluates to false should the RHS be evaluated
if ( !leftBool ) {
rightVal = _right.Eval(context);
if ( rightVal.getDataType() == cfData.CFLDATA ) {
rightVal = ((cfLData) rightVal).Get(context);
}
if ( rightVal.isBooleanConvertible() ) {
rightBool = rightVal.getBoolean();
valBool = rightBool; // we know leftBool is false
} else {
throw new CFException(
"Can't perform this operation on these data types.", context);
}
} else {
valBool = true;
}
break;
case CFMLLexer.IMP:
valBool = !(leftBool && !(rightBool));
break;
case CFMLLexer.EQV:
valBool = leftBool == rightBool;
break;
case CFMLLexer.CONCAT:
valStr = leftStr + rightStr;
break;
case CFMLLexer.MINUS:
if ( opTypes == _NUM ) {
valNum = leftNum - rightNum;
} else { // optype == _DATE
valDate = leftDate - rightDate;
}
break;
case CFMLLexer.STAR:
valNum = leftNum * rightNum;
break;
case CFMLLexer.SLASH:
valNum = leftNum / rightNum;
break;
case CFMLLexer.BSLASH:
valNum = (int) (leftNum / (int) rightNum);
break;
case CFMLLexer.MOD:
valNum = (int) leftNum % (int) rightNum;
break;
case CFMLLexer.POWER:
valNum = Math.pow(leftNum, rightNum);
break;
case CFMLLexer.PLUS:
if ( opTypes == _NUM ) {
valNum = leftNum + rightNum;
} else { // optype == _DATE
valDate = leftDate + rightDate;
}
break;
case CFMLLexer.GT:
switch (opTypes) {
case _NUM:
valBool = leftNum > rightNum;
break;
case _STR:
valBool = leftStr.compareTo(rightStr) > 0;
break;
case _DATE:
valBool = leftDate > rightDate;
break;
default:
throw new CFException(
"Internal error - invalid op types in >=. This should not happen.",
context);
}
break;
case CFMLLexer.LT:
switch (opTypes) {
case _NUM:
valBool = (leftNum < rightNum);
break;
case _STR:
valBool = leftStr.compareTo(rightStr) < 0;
break;
case _DATE:
valBool = leftDate < rightDate;
break;
default:
throw new CFException(
"Internal error - invalid op types in >=. This should not happen.",
context);
}
break;
case CFMLLexer.LE:
case CFMLLexer.LTE:
switch (opTypes) {
case _NUM:
valBool = (leftNum <= rightNum);
break;
case _STR:
valBool = leftStr.compareTo(rightStr) <= 0;
break;
case _DATE:
valBool = leftDate <= rightDate;
break;
default:
throw new CFException(
"Internal error - invalid op types in >=. This should not happen.",
context);
}
break;
case CFMLLexer.GE:
case CFMLLexer.GTE:
switch (opTypes) {
case _NUM:
valBool = (leftNum >= rightNum);
break;
case _STR:
valBool = leftStr.compareTo(rightStr) >= 0;
break;
case _DATE:
valBool = leftDate >= rightDate;
break;
default:
throw new CFException(
"Internal error - invalid op types in >=. This should not happen.",
context);
}
break;
case CFMLLexer.EQ:
switch (opTypes) {
case _NUM:
valBool = (leftNum == rightNum);
break;
case _STR:
valBool = leftStr.equalsIgnoreCase( rightStr );
break;
case _DATE:
valBool = leftDate == rightDate;
break;
default:
throw new CFException(
"Internal error - invalid op types in ==. This should not happen.",
context);
}
break;
case CFMLLexer.NEQ:
switch (opTypes) {
case _NUM:
valBool = (leftNum != rightNum);
break;
case _STR:
valBool = !leftStr.equalsIgnoreCase( rightStr );
break;
case _DATE:
valBool = leftDate != rightDate;
break;
default:
throw new CFException(
"Internal error - invalid op types in !=. This should not happen.",
context);
}
break;
case CFMLLexer.CONTAINS:
valBool = leftStr.toLowerCase().indexOf(rightStr.toLowerCase()) != -1;
break;
case CFMLLexer.DOESNOTCONTAIN:
valBool = leftStr.toLowerCase().indexOf(rightStr.toLowerCase()) == -1;
break;
default:
throw new CFException("Unknown binary operator (" + String.valueOf(_kind)
+ ").", context);
}
// Construct the expression value
switch (valType) {
case _NUM:
val = new cfNumberData(valNum);
break;
case _STR:
val = new cfStringData(valStr);
break;
case _BOOL:
val = cfBooleanData.getcfBooleanData(valBool);
break;
case _DATE:
val = new cfDateData(valDate);
break;
case _REF:
break;
default:
throw new CFException(
valType
+ " Internal error - invalid result type in binary expression. This should not happen."
+ "when Evaluating [" + _left.Decompile(0) + "] [" + _kind
+ "] [" + _right.Decompile(0) + "]", context);
}
return context._lastExpr = val;
}
public static cfData evaluate( CFContext _context, int _op, cfData _left,
cfData _right ) throws CFException, dataNotSupportedException {
double leftNum = 0.0;
double rightNum = 0.0;
double valNum = 0.0;
long leftDate = 0;
long rightDate = 0;
long valDate = 0;
String leftStr = "";
String rightStr = "";
String valStr = "";
boolean valBool = false;
int opTypes = _ERR;
// req'd for result type. Req'd cos for example "str EQ str" returns a
// boolean
int valType = _ERR;
switch (_op) {
// numeric operations
case CFMLLexer.STAR:
case CFMLLexer.SLASH:
case CFMLLexer.MOD:
case CFMLLexer.POWER:
case CFMLLexer.PLUS:
case CFMLLexer.MINUS:
if ( _left.isNumberConvertible() && _right.isNumberConvertible() ) {
// Both operands must be numbers
leftNum = _left.getDouble();
rightNum = _right.getDouble();
opTypes = _NUM;
valType = _NUM;
} else if ( _left.isDateConvertible() && _right.isDateConvertible() ) {
leftDate = _left.getDateLong();
rightDate = _right.getDateLong();
opTypes = _DATE;
valType = _DATE;
} else {
throw new CFException(
"Can't perform this operation on these data types.", _context);
}
break;
case CFMLLexer.CONCAT:
if ( isStringConvertible(_left) && isStringConvertible(_right) ) {
leftStr = _left.getString();
rightStr = _right.getString();
valType = _STR;
opTypes = _STR;
} else {
throw new CFException(
"Can't perform this operation on these data types.", _context);
}
break;
default:
break;
}
// finally perform the operation
switch (_op) {
case CFMLLexer.CONCAT:
valStr = leftStr + rightStr;
break;
case CFMLLexer.MINUS:
if ( opTypes == _NUM ) {
valNum = leftNum - rightNum;
} else { // optype == _DATE
valDate = leftDate - rightDate;
}
break;
case CFMLLexer.STAR:
valNum = leftNum * rightNum;
break;
case CFMLLexer.SLASH:
valNum = leftNum / rightNum;
break;
case CFMLLexer.BSLASH:
valNum = (int) (leftNum / (int) rightNum);
break;
case CFMLLexer.MOD:
valNum = (int) leftNum % (int) rightNum;
break;
case CFMLLexer.POWER:
valNum = Math.pow(leftNum, rightNum);
break;
case CFMLLexer.PLUS:
if ( opTypes == _NUM ) {
valNum = leftNum + rightNum;
} else { // optype == _DATE
valDate = leftDate + rightDate;
}
break;
default:
throw new CFException("Unknown binary operator (" + String.valueOf(_op)
+ ").", _context);
}
cfData val = cfNullData.NULL;
// Construct the expression value
switch (valType) {
case _NUM:
val = new cfNumberData(valNum);
break;
case _STR:
val = new cfStringData(valStr);
break;
case _BOOL:
val = cfBooleanData.getcfBooleanData(valBool);
break;
case _DATE:
val = new cfDateData(valDate);
break;
case _REF:
break;
}
return val;
}
public String Decompile( int indent ) {
String endChar = "";
if ( _kind == CFMLLexer.LEFTBRACKET ) {
endChar = "]";
}
return "" + _left.Decompile(indent) + operatorImage
+ _right.Decompile(indent) + endChar;
}
private static boolean isStringConvertible( cfData _val ) {
return cfData.isSimpleValue(_val)
|| _val.getDataType() == cfData.CFJAVAOBJECTDATA;
}
}