package prefuse.data.expression;
import java.util.Comparator;
import prefuse.data.Schema;
import prefuse.data.Tuple;
import prefuse.util.TypeLib;
import prefuse.util.collections.DefaultLiteralComparator;
import prefuse.util.collections.LiteralComparator;
/**
* Predicate instance that evaluates if a value is contained within
* a bounded range.
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class RangePredicate extends BinaryExpression implements Predicate {
/** Indicates the both the left and right bounds are inclusive */
public static final int IN_IN = 0;
/** Indicates an inclusive left bound and exclusive right bound */
public static final int IN_EX = 1;
/** Indicates an exclusive left bound and inclusive right bound */
public static final int EX_IN = 2;
/** Indicates the both the left and right bounds are exclusive */
public static final int EX_EX = 3;
private Expression m_middle;
private Comparator m_cmp;
// ------------------------------------------------------------------------
// Constructors
/**
* Create a new RangePredicate. Both bounds are assumed to be inclusive.
* @param middle the value to test for membership in the range
* @param left the lower range bound
* @param right the upper range bound
*/
public RangePredicate(Expression middle,
Expression left, Expression right)
{
this(IN_IN, middle, left, right,
DefaultLiteralComparator.getInstance());
}
/**
* Create a new RangePredicate. Both bounds are assumed to be inclusive.
* @param middle the value to test for membership in the range
* @param left the lower range bound
* @param right the upper range bound
* @param cmp the comparator to use for comparing data values
*/
public RangePredicate(Expression middle,
Expression left, Expression right, Comparator cmp)
{
this(IN_IN, middle, left, right, cmp);
}
/**
* Create a new RangePredicate.
* @param operation operation code indicating the inclusiveness /
* exclusiveness of the bounds
* @param middle the value to test for membership in the range
* @param left the lower range bound
* @param right the upper range bound
*/
public RangePredicate(int operation, Expression middle,
Expression left, Expression right)
{
this(operation, middle, left, right,
DefaultLiteralComparator.getInstance());
}
/**
* Create a new RangePredicate.
* @param operation operation code indicating the inclusiveness /
* exclusiveness of the bounds
* @param middle the value to test for membership in the range
* @param left the lower range bound
* @param right the upper range bound
* @param cmp the comparator to use for comparing data values
*/
public RangePredicate(int operation, Expression middle,
Expression left, Expression right, Comparator cmp)
{
super(operation, IN_IN, EX_EX, left, right);
this.m_middle = middle;
this.m_cmp = cmp;
}
// ------------------------------------------------------------------------
// Accessors
/**
* Get the middle expression being tested for inclusion in the range
* @return the middle expression
*/
public Expression getMiddleExpression() {
return m_middle;
}
/**
* Get the comparator used to compare data values.
* @return the comparator used to compare data values
*/
public Comparator getComparator() {
return m_cmp;
}
// ------------------------------------------------------------------------
// Expression Interface
/**
* @see prefuse.data.expression.Expression#getBoolean(prefuse.data.Tuple)
*/
public boolean getBoolean(Tuple t) {
Class lType = m_left.getType(t.getSchema());
Class rType = m_right.getType(t.getSchema());
Class mType = m_middle.getType(t.getSchema());
Class sType = null;
// see if we can match the end-points' type
if ( lType.isAssignableFrom(rType) ) {
sType = lType;
} else if ( rType.isAssignableFrom(lType) ) {
sType = rType;
}
int c1, c2 = 0;
if ( sType != null && TypeLib.isNumericType(sType) &&
TypeLib.isNumericType(mType) )
{
// the range is of numeric types
Class type = TypeLib.getNumericType(sType, mType);
if ( type == int.class ) {
int lo = m_left.getInt(t);
int hi = m_right.getInt(t);
int x = m_middle.getInt(t);
c1 = ((LiteralComparator)m_cmp).compare(x,lo);
c2 = ((LiteralComparator)m_cmp).compare(x,hi);
} else if ( type == long.class ) {
long lo = m_left.getLong(t);
long hi = m_right.getLong(t);
long x = m_middle.getLong(t);
c1 = ((LiteralComparator)m_cmp).compare(x,lo);
c2 = ((LiteralComparator)m_cmp).compare(x,hi);
} else if ( type == float.class ) {
float lo = m_left.getFloat(t);
float hi = m_right.getFloat(t);
float x = m_middle.getFloat(t);
c1 = ((LiteralComparator)m_cmp).compare(x,lo);
c2 = ((LiteralComparator)m_cmp).compare(x,hi);
} else if ( type == double.class ) {
double lo = m_left.getDouble(t);
double hi = m_right.getDouble(t);
double x = m_middle.getDouble(t);
c1 = ((LiteralComparator)m_cmp).compare(x,lo);
c2 = ((LiteralComparator)m_cmp).compare(x,hi);
} else {
throw new IllegalStateException();
}
} else {
Object lo = m_left.get(t);
Object hi = m_right.get(t);
Object x = m_middle.get(t);
c1 = m_cmp.compare(x, lo);
c2 = m_cmp.compare(x, hi);
}
// check the comparison values to see if it is in-range
switch ( m_op ) {
case IN_IN:
return ( c1 >= 0 && c2 <= 0 );
case IN_EX:
return ( c1 >= 0 && c2 < 0 );
case EX_IN:
return ( c1 > 0 && c2 <= 0 );
case EX_EX:
return ( c1 > 0 && c2 < 0 );
default:
throw new IllegalStateException("Unknown operation.");
}
}
/**
* @see prefuse.data.expression.Expression#getType(prefuse.data.Schema)
*/
public Class getType(Schema s) {
return boolean.class;
}
/**
* @see prefuse.data.expression.Expression#get(prefuse.data.Tuple)
*/
public Object get(Tuple t) {
return ( getBoolean(t) ? Boolean.TRUE : Boolean.FALSE );
}
/**
* @see prefuse.data.expression.Expression#visit(prefuse.data.expression.ExpressionVisitor)
*/
public void visit(ExpressionVisitor v) {
v.visitExpression(this);
v.down(); m_left.visit(v); v.up();
v.down(); m_middle.visit(v); v.up();
v.down(); m_right.visit(v); v.up();
}
/**
* @see prefuse.data.expression.AbstractExpression#addChildListeners()
*/
protected void addChildListeners() {
super.addChildListeners();
m_middle.addExpressionListener(this);
}
/**
* @see prefuse.data.expression.AbstractExpression#removeChildListeners()
*/
protected void removeChildListeners() {
super.removeChildListeners();
m_middle.removeExpressionListener(this);
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
String lop = "?", rop = "?";
switch ( m_op ) {
case IN_IN:
lop = rop = "<=";
break;
case IN_EX:
lop = "<="; rop = "<";
break;
case EX_IN:
lop = "<"; rop = "<=";
break;
case EX_EX:
lop = rop = "<";
break;
}
return '('+m_left.toString()+' '+lop+' '+m_middle.toString()+" AND "+
m_middle.toString()+' '+rop+' '+m_right.toString()+')';
}
} // end of class RangePredicate