/*
* JBoss, Home of Professional Open Source
* Copyright 2005-2008, Red Hat Middleware LLC, and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.messaging.core.filter.impl;
import java.util.HashSet;
/**
* Implementations of the operators used in JBM filter expressions
*
* @author Norbert Lataille (Norbert.Lataille@m4x.org)
* @author droy@boostmyscore.com
* @author Scott.Stark@jboss.org
* @author adrian@jboss.com
* @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a>
* @version $Revision: 2681 $
*/
public class Operator
{
int operation;
Object oper1;
Object oper2;
Object oper3;
Object arg1;
Object arg2;
Object arg3;
int class1;
int class2;
int class3;
// info about the regular expression
// if this is a LIKE operator
// (perhaps this should be a subclass)
RegExp re = null;
public final static int EQUAL = 0;
public final static int NOT = 1;
public final static int AND = 2;
public final static int OR = 3;
public final static int GT = 4;
public final static int GE = 5;
public final static int LT = 6;
public final static int LE = 7;
public final static int DIFFERENT = 8;
public final static int ADD = 9;
public final static int SUB = 10;
public final static int NEG = 11;
public final static int MUL = 12;
public final static int DIV = 13;
public final static int BETWEEN = 14;
public final static int NOT_BETWEEN = 15;
public final static int LIKE = 16;
public final static int NOT_LIKE = 17;
public final static int LIKE_ESCAPE = 18;
public final static int NOT_LIKE_ESCAPE = 19;
public final static int IS_NULL = 20;
public final static int IS_NOT_NULL = 21;
public final static int IN = 22;
public final static int NOT_IN = 23;
public final static int STRING = 0;
public final static int DOUBLE = 1;
//DOUBLE FLOAT
public final static int LONG = 2;
//LONG BYTE SHORT INTEGER
public final static int BOOLEAN = 3;
public Operator(int operation, Object oper1, Object oper2, Object oper3)
{
this.operation = operation;
this.oper1 = oper1;
this.oper2 = oper2;
this.oper3 = oper3;
}
public Operator(int operation, Object oper1, Object oper2)
{
this.operation = operation;
this.oper1 = oper1;
this.oper2 = oper2;
this.oper3 = null;
}
public Operator(int operation, Object oper1)
{
this.operation = operation;
this.oper1 = oper1;
this.oper2 = null;
this.oper3 = null;
}
//--- Print functions ---
public String toString()
{
return print("");
}
public String print(String level)
{
String st = level + operation + ":" + operationString(operation) + "(\n";
String nextLevel = level + " ";
if (oper1 == null)
st += nextLevel + "null\n";
else if (oper1 instanceof Operator)
st += ((Operator) oper1).print( nextLevel );
else
st += nextLevel + oper1.toString() + "\n";
if (oper2 != null)
{
if (oper2 instanceof Operator)
st += ((Operator) oper2).print(nextLevel);
else
st += nextLevel + oper2.toString() + "\n";
}
if (oper3 != null)
{
if (oper3 instanceof Operator)
st += ((Operator) oper3).print(nextLevel);
else
st += nextLevel + oper3.toString() + "\n";
}
st += level + ")\n";
return st;
}
//Operator 20
Object is_null() throws Exception
{
computeArgument1();
if (arg1 == null)
return Boolean.TRUE;
else
return Boolean.FALSE;
}
//Operator 21
Object is_not_null() throws Exception
{
computeArgument1();
if (arg1 != null)
return Boolean.TRUE;
else
return Boolean.FALSE;
}
//Operation 0
Object equal() throws Exception
{
computeArgument1();
if (arg1 == null)
return Boolean.FALSE;
switch (class1)
{
case LONG:
computeArgument1();
if (arg1 == null)
return null;
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).longValue() == ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).longValue() == ((Number) arg2).doubleValue());
return Boolean.FALSE;
case DOUBLE:
computeArgument1();
if (arg1 == null)
return null;
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).doubleValue() == ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).doubleValue() == ((Number) arg2).doubleValue());
return Boolean.FALSE;
case STRING:
case BOOLEAN:
computeArgument2();
if (arg2 == null)
return Boolean.FALSE;
if (class2 != class1)
throwBadObjectException(class1, class2);
return Boolean.valueOf(arg1.equals(arg2));
default:
throwBadObjectException(class1);
return null;
}
}
//Operation 1
Object not() throws Exception
{
computeArgument1();
if (arg1 == null)
return null;
if (class1 != BOOLEAN)
throwBadObjectException(class1);
if (((Boolean)arg1).booleanValue())
return Boolean.FALSE;
else
return Boolean.TRUE;
}
//Operation 2
Object and() throws Exception
{
computeArgument1();
if (arg1 == null)
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 != BOOLEAN)
throwBadObjectException(class2);
if (((Boolean) arg2).booleanValue() == false )
return Boolean.FALSE;
return null;
}
if (class1 == BOOLEAN)
{
if (((Boolean) arg1).booleanValue() == false)
return Boolean.FALSE;
computeArgument2();
if (arg2 == null)
return null;
if (class2 != BOOLEAN)
throwBadObjectException(class2);
return arg2;
}
throwBadObjectException(class1);
return null;
}
/**
* Operation 3
*
* | OR | T | F | U
* +------+-------+-------+--------
* | T | T | T | T
* | F | T | F | U
* | U | T | U | U
* +------+-------+-------+-------
*/
Object or() throws Exception
{
short falseCounter=0;
computeArgument1();
if (arg1 != null)
{
if (class1 != BOOLEAN)
throwBadObjectException(class1);
if (((Boolean) arg1).booleanValue())
return Boolean.TRUE;
else
falseCounter++;
}
computeArgument2();
if (arg2 != null)
{
if (class2 != BOOLEAN)
throwBadObjectException(class2);
if (((Boolean)arg2).booleanValue())
return Boolean.TRUE;
else
falseCounter++;
}
if (falseCounter == 2)
return Boolean.FALSE;
return null;
}
//Operation 4
Object gt() throws Exception
{
computeArgument1();
if (arg1 == null)
return null;
if (class1 == LONG)
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).longValue() > ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).longValue() > ((Number) arg2).doubleValue());
}
else if ( class1 == DOUBLE )
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).doubleValue() > ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).doubleValue() > ((Number) arg2).doubleValue());
return Boolean.FALSE;
}
return Boolean.FALSE;
}
//Operation 5
Object ge() throws Exception
{
computeArgument1();
if (arg1 == null)
return null;
if (class1 == LONG)
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).longValue() >= ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).longValue() >= ((Number) arg2).doubleValue());
}
else if ( class1 == DOUBLE )
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).longValue() >= ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).doubleValue() >= ((Number) arg2).doubleValue());
return Boolean.FALSE;
}
return Boolean.FALSE;
}
//Operation 6
Object lt() throws Exception
{
computeArgument1();
if (arg1 == null)
return null;
if (class1 == LONG)
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).longValue() < ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).longValue() < ((Number) arg2).doubleValue());
}
else if (class1 == DOUBLE)
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).doubleValue() < ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).doubleValue() < ((Number) arg2).doubleValue());
}
return Boolean.FALSE;
}
//Operation 7
Object le() throws Exception
{
computeArgument1();
if (arg1 == null)
return null;
if (class1 == LONG)
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).longValue() <= ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).longValue() <= ((Number) arg2).doubleValue());
}
else if (class1 == DOUBLE)
{
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).doubleValue() <= ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).doubleValue() <= ((Number) arg2).doubleValue());
}
return Boolean.FALSE;
}
//Operation 8
Object different() throws Exception
{
computeArgument1();
if ( arg1 == null )
return Boolean.FALSE;
switch (class1)
{
case LONG:
computeArgument1();
if (arg1 == null)
return null;
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).longValue() != ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).longValue() != ((Number) arg2).doubleValue());
return Boolean.FALSE;
case DOUBLE:
computeArgument1();
if (arg1 == null)
return null;
computeArgument2();
if (arg2 == null)
return null;
if (class2 == LONG)
return Boolean.valueOf(((Number) arg1).doubleValue() != ((Number) arg2).longValue());
if (class2 == DOUBLE)
return Boolean.valueOf(((Number) arg1).doubleValue() != ((Number) arg2).doubleValue());
return Boolean.FALSE;
case STRING:
case BOOLEAN:
computeArgument2();
if (arg2 == null)
return Boolean.FALSE;
if (class2 != class1)
throwBadObjectException(class1, class2);
return Boolean.valueOf(arg1.equals(arg2) == false);
default:
throwBadObjectException(class1);
}
return null;
}
//Operator 9
Object add() throws Exception
{
computeArgument1();
computeArgument2();
if (arg1 == null || arg2 == null)
return null;
switch (class1)
{
case DOUBLE:
switch (class2)
{
case DOUBLE:
return new Double(((Number) arg1).doubleValue() + ((Number) arg2).doubleValue());
case LONG:
return new Double(((Number) arg1).doubleValue() + ((Number) arg2).doubleValue());
default:
throwBadObjectException(class2);
}
case LONG:
switch (class2)
{
case DOUBLE:
return new Double(((Number) arg1).doubleValue() + ((Number) arg2).doubleValue());
case LONG:
return new Long(((Number) arg1).longValue() + ((Number) arg2).longValue());
default:
throwBadObjectException(class2);
}
default:
throwBadObjectException(class1);
}
return null;
}
//Operator 10
Object sub() throws Exception
{
computeArgument1();
computeArgument2();
if (arg1 == null || arg2 == null)
return null;
switch (class1)
{
case DOUBLE:
switch (class2)
{
case DOUBLE:
return new Double(((Number) arg1).doubleValue() - ((Number) arg2).doubleValue());
case LONG:
return new Double(((Number) arg1).doubleValue() - ((Number) arg2).doubleValue());
default:
throwBadObjectException(class2);
}
case LONG:
switch (class2)
{
case DOUBLE:
return new Double(((Number) arg1).doubleValue() - ((Number) arg2).doubleValue());
case LONG:
return new Long(((Number) arg1).longValue() - ((Number) arg2).longValue());
default:
throwBadObjectException(class2);
}
default:
throwBadObjectException(class1);
}
return null;
}
//Operator 11
Object neg() throws Exception
{
computeArgument1();
if (arg1 == null)
return null;
switch (class1)
{
case DOUBLE:
return new Double(-((Number) arg1).doubleValue());
case LONG:
return new Long(-((Number)arg1).longValue());
default:
throwBadObjectException(class1);
}
return null;
}
//Operator 12
Object mul() throws Exception
{
computeArgument1();
computeArgument2();
if (arg1 == null || arg2 == null)
return null;
switch (class1)
{
case DOUBLE:
switch (class2)
{
case DOUBLE:
return new Double(((Number) arg1).doubleValue() * ((Number) arg2).doubleValue());
case LONG:
return new Double(((Number) arg1).doubleValue() * ((Number) arg2).doubleValue());
default:
throwBadObjectException(class2);
}
case LONG:
switch (class2)
{
case DOUBLE:
return new Double(((Number) arg1).doubleValue() * ((Number) arg2).doubleValue());
case LONG:
return new Long(((Number) arg1).longValue() * ((Number) arg2).longValue());
default:
throwBadObjectException(class2);
}
default:
throwBadObjectException(class1);
}
return null;
}
//Operator 13
Object div() throws Exception
{
//Can throw Divide by zero exception...
computeArgument1();
computeArgument2();
if (arg1 == null || arg2 == null)
return null;
switch (class1)
{
case DOUBLE:
switch (class2)
{
case DOUBLE:
return new Double(((Number) arg1).doubleValue() / ((Number) arg2).doubleValue());
case LONG:
return new Double(((Number) arg1).doubleValue() / ((Number) arg2).doubleValue());
default:
throwBadObjectException(class2);
}
case LONG:
switch (class2)
{
case DOUBLE:
return new Double(((Number) arg1).doubleValue() / ((Number) arg2).doubleValue());
case LONG:
return new Long(((Number) arg1).longValue() / ((Number) arg2).longValue());
default:
throwBadObjectException(class2);
}
default:
throwBadObjectException(class1);
}
return null;
}
//Operator 14
Object between() throws Exception
{
Object res = ge();
if (res == null)
return null;
if (((Boolean) res).booleanValue() == false)
return res;
Object oper4 = oper2;
oper2 = oper3;
res = le();
oper2 = oper4;
return res;
}
//Operator 15
Object not_between() throws Exception
{
Object res = lt();
if (res == null)
return null;
if (((Boolean) res).booleanValue())
return res;
Object oper4 = oper2;
oper2 = oper3;
res = gt();
oper2 = oper4;
return res;
}
//Operation 16,17,18,19
/**
* Handle LIKE, NOT LIKE, LIKE ESCAPE, and NOT LIKE ESCAPE operators.
*
* @param not true if this is a NOT LIKE construct, false if this
* is a LIKE construct.
* @param use_escape true if this is a LIKE ESCAPE construct, false if
* there is no ESCAPE clause
* @return Description of the Returned Value
* @exception Exception Description of Exception
*/
Object like(boolean not, boolean use_escape) throws Exception
{
Character escapeChar = null;
computeArgument1();
if (arg1 == null)
return null;
if (class1 != STRING)
throwBadObjectException(class1);
computeArgument2();
if (arg2 == null)
return null;
if (class2 != STRING)
throwBadObjectException(class2);
if (use_escape)
{
computeArgument3();
if (arg3 == null)
return null;
if (class3 != STRING)
throwBadObjectException(class3);
StringBuffer escapeBuf = new StringBuffer((String) arg3);
if (escapeBuf.length() != 1)
throw new Exception("LIKE ESCAPE: Bad escape character " + escapeBuf.toString());
escapeChar = new Character(escapeBuf.charAt(0));
}
if (re == null)
// the first time through we prepare the regular expression
re = new RegExp ((String) arg2, escapeChar);
boolean result = re.isMatch (arg1);
if (not)
result = !result;
if (result == true)
return Boolean.TRUE;
else
return Boolean.FALSE;
}
//Operator 22
Object in() throws Exception
{
computeArgument1();
if (arg1 == null)
return null;
if (((HashSet) oper2).contains(arg1))
return Boolean.TRUE;
else
return Boolean.FALSE;
}
//Operator 23
Object not_in() throws Exception
{
computeArgument1();
if (arg1 == null)
return null;
if (class1 != STRING)
throwBadObjectException(class1);
if (((HashSet) oper2).contains(arg1))
return Boolean.FALSE;
else
return Boolean.TRUE;
}
void computeArgument1() throws Exception
{
Class className = oper1.getClass();
if (className == Identifier.class)
arg1 = ((Identifier) oper1).getValue();
else if (className == Operator.class)
arg1 = ((Operator) oper1).apply();
else
arg1 = oper1;
if (arg1 == null)
{
class1 = 0;
return;
}
className = arg1.getClass();
if (className == String.class)
class1 = STRING;
else if (className == Double.class)
class1 = DOUBLE;
else if (className == Long.class)
class1 = LONG;
else if (className == Integer.class)
{
class1 = LONG;
arg1 = new Long(((Integer) arg1).longValue());
}
else if (className == Short.class)
{
class1 = LONG;
arg1 = new Long(((Short) arg1).longValue());
}
else if (className == Byte.class)
{
class1 = LONG;
arg1 = new Long(((Byte) arg1).longValue());
}
else if (className == Float.class)
{
class1 = DOUBLE;
arg1 = new Double(((Float) arg1).doubleValue());
}
else if (className == Boolean.class)
class1 = BOOLEAN;
else
throwBadObjectException(className);
}
void computeArgument2() throws Exception
{
Class className = oper2.getClass();
if (className == Identifier.class)
arg2 = ((Identifier) oper2).getValue();
else if (className == Operator.class)
arg2 = ((Operator) oper2).apply();
else
arg2 = oper2;
if (arg2 == null)
{
class2 = 0;
return;
}
className = arg2.getClass();
if (className == String.class)
class2 = STRING;
else if (className == Double.class)
class2 = DOUBLE;
else if (className == Long.class)
class2 = LONG;
else if (className == Integer.class)
{
class2 = LONG;
arg2 = new Long(((Integer) arg2).longValue());
}
else if (className == Short.class)
{
class2 = LONG;
arg2 = new Long(((Short) arg2).longValue());
}
else if (className == Byte.class)
{
class2 = LONG;
arg2 = new Long(((Byte) arg2).longValue());
}
else if (className == Float.class)
{
class2 = DOUBLE;
arg2 = new Double(((Float) arg2).doubleValue());
}
else if (className == Boolean.class)
class2 = BOOLEAN;
else
throwBadObjectException(className);
}
void computeArgument3() throws Exception
{
Class className = oper3.getClass();
if (className == Identifier.class)
arg3 = ((Identifier) oper3).getValue();
else if (className == Operator.class)
arg3 = ((Operator ) oper3).apply();
else
arg3 = oper3;
if (arg3 == null)
{
class3 = 0;
return;
}
className = arg3.getClass();
if (className == String.class)
class3 = STRING;
else if (className == Double.class)
class3 = DOUBLE;
else if (className == Long.class)
class3 = LONG;
else if (className == Integer.class)
{
class3 = LONG;
arg3 = new Long(((Integer) arg3).longValue());
}
else if (className == Short.class)
{
class3 = LONG;
arg3 = new Long(((Short) arg3).longValue());
}
else if (className == Byte.class)
{
class3 = LONG;
arg3 = new Long(((Byte) arg3).longValue());
}
else if (className == Float.class)
{
class3 = DOUBLE;
arg3 = new Double(((Float) arg3).doubleValue());
}
else if (className == Boolean.class)
class3 = BOOLEAN;
else
throwBadObjectException(className);
}
public Object apply() throws Exception
{
switch (operation)
{
case EQUAL:
return equal();
case NOT:
return not();
case AND:
return and();
case OR:
return or();
case GT:
return gt();
case GE:
return ge();
case LT:
return lt();
case LE:
return le();
case DIFFERENT:
return different();
case ADD:
return add();
case SUB:
return sub();
case NEG:
return neg();
case MUL:
return mul();
case DIV:
return div();
case BETWEEN:
return between();
case NOT_BETWEEN:
return not_between();
case LIKE:
return like(false, false);
case NOT_LIKE:
return like(true, false);
case LIKE_ESCAPE:
return like(false, true);
case NOT_LIKE_ESCAPE:
return like(true, true);
case IS_NULL:
return is_null();
case IS_NOT_NULL:
return is_not_null();
case IN:
return in();
case NOT_IN:
return not_in();
}
throw new Exception("Unknown operation: " + toString());
}
public void throwBadObjectException(Class class1) throws Exception
{
throw new Exception("Bad Object: '" + class1.getName() + "' for operation: " + toString());
}
public void throwBadObjectException(int class1) throws Exception
{
throw new Exception("Bad Object: '" + getClassName(class1) + "' for operation: " + toString());
}
public void throwBadObjectException(int class1, int class2) throws Exception
{
throw new Exception("Bad Object: expected '" + getClassName(class1) + "' got '" + getClassName(class2) + "' for operation: " + toString());
}
static String getClassName(int class1)
{
String str = "Unknown";
switch (class1)
{
case STRING:
str = "String";
break;
case LONG:
str = "Long";
break;
case DOUBLE:
str = "Double";
break;
case BOOLEAN:
str = "Boolean";
break;
}
return str;
}
static String operationString(int operation)
{
String str = "Unknown";
switch( operation )
{
case EQUAL:
str = "EQUAL";
break;
case NOT:
str = "NOT";
break;
case AND:
str = "AND";
break;
case OR:
str = "OR";
break;
case GT:
str = "GT";
break;
case GE:
str = "GE";
break;
case LT:
str = "LT";
break;
case LE:
str = "LE";
break;
case DIFFERENT:
str = "DIFFERENT";
break;
case ADD:
str = "ADD";
break;
case SUB:
str = "SUB";
break;
case NEG:
str = "NEG";
break;
case MUL:
str = "MUL";
break;
case DIV:
str = "DIV";
break;
case BETWEEN:
str = "BETWEEN";
break;
case NOT_BETWEEN:
str = "NOT_BETWEEN";
break;
case LIKE:
str = "LIKE";
break;
case NOT_LIKE:
str = "NOT_LIKE";
break;
case LIKE_ESCAPE:
str = "LIKE_ESCAPE";
break;
case NOT_LIKE_ESCAPE:
str = "NOT_LIKE_ESCAPE";
break;
case IS_NULL:
str = "IS_NULL";
break;
case IS_NOT_NULL:
str = "IS_NOT_NULL";
break;
case IN:
str = "IN";
break;
case NOT_IN:
str = "NOT_IN";
break;
}
return str;
}
}