/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package com.espertech.esper.epl.expression;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.util.JavaClassHelper;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Represents the INSTANCEOF(a,b,...) function is an expression tree.
*/
public class ExprInstanceofNode extends ExprNodeBase implements ExprEvaluator
{
private final String[] classIdentifiers;
private Class[] classes;
private CopyOnWriteArrayList<Pair<Class, Boolean>> resultCache = new CopyOnWriteArrayList<Pair<Class, Boolean>>();
private transient ExprEvaluator evaluator;
private static final long serialVersionUID = 3358616797009364727L;
/**
* Ctor.
* @param classIdentifiers is a list of type names to check type for
*/
public ExprInstanceofNode(String[] classIdentifiers)
{
this.classIdentifiers = classIdentifiers;
}
public ExprEvaluator getExprEvaluator()
{
return this;
}
public Map<String, Object> getEventType() {
return null;
}
public void validate(ExprValidationContext validationContext) throws ExprValidationException
{
if (this.getChildNodes().size() != 1)
{
throw new ExprValidationException("Instanceof node must have 1 child expression node supplying the expression to test");
}
if ((classIdentifiers == null) || (classIdentifiers.length == 0))
{
throw new ExprValidationException("Instanceof node must have 1 or more class identifiers to verify type against");
}
evaluator = this.getChildNodes().get(0).getExprEvaluator();
Set<Class> classList = getClassSet(classIdentifiers);
synchronized(this) {
classes = classList.toArray(new Class[classList.size()]);
}
}
public boolean isConstantResult()
{
return false;
}
public Class getType()
{
return Boolean.class;
}
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext)
{
Object result = evaluator.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (result == null)
{
return false;
}
// return cached value
for (Pair<Class, Boolean> pair : resultCache)
{
if (pair.getFirst() == result.getClass())
{
return pair.getSecond();
}
}
return checkAddType(result.getClass());
}
// Checks type and adds to cache
private synchronized Boolean checkAddType(Class type)
{
// check again in synchronized block
for (Pair<Class, Boolean> pair : resultCache)
{
if (pair.getFirst() == type)
{
return pair.getSecond();
}
}
// get the types superclasses and interfaces, and their superclasses and interfaces
Set<Class> classesToCheck = new HashSet<Class>();
JavaClassHelper.getSuper(type, classesToCheck);
classesToCheck.add(type);
// check type against each class
boolean fits = false;
for (Class clazz : classes)
{
if (classesToCheck.contains(clazz))
{
fits = true;
break;
}
}
resultCache.add(new Pair<Class, Boolean>(type, fits));
return fits;
}
public String toExpressionString()
{
StringBuilder buffer = new StringBuilder();
buffer.append("instanceof(");
buffer.append(this.getChildNodes().get(0).toExpressionString());
buffer.append(", ");
String delimiter = "";
for (int i = 0; i < classIdentifiers.length; i++)
{
buffer.append(delimiter);
buffer.append(classIdentifiers[i]);
delimiter = ", ";
}
buffer.append(')');
return buffer.toString();
}
public boolean equalsNode(ExprNode node)
{
if (!(node instanceof ExprInstanceofNode))
{
return false;
}
ExprInstanceofNode other = (ExprInstanceofNode) node;
if (Arrays.equals(other.classIdentifiers, classIdentifiers))
{
return true;
}
return false;
}
/**
* Returns the list of class names or types to check instance of.
* @return class names
*/
public String[] getClassIdentifiers()
{
return classIdentifiers;
}
private Set<Class> getClassSet(String[] classIdentifiers)
throws ExprValidationException
{
Set<Class> classList = new HashSet<Class>();
for (String className : classIdentifiers)
{
Class clazz;
// try the primitive names including "string"
clazz = JavaClassHelper.getPrimitiveClassForName(className.trim());
if (clazz != null)
{
classList.add(clazz);
classList.add(JavaClassHelper.getBoxedType(clazz));
continue;
}
// try to look up the class, not a primitive type name
try
{
clazz = JavaClassHelper.getClassForName(className.trim());
}
catch (ClassNotFoundException e)
{
throw new ExprValidationException("Class as listed in instanceof function by name '" + className + "' cannot be loaded", e);
}
// Add primitive and boxed types, or type itself if not built-in
classList.add(JavaClassHelper.getPrimitiveType(clazz));
classList.add(JavaClassHelper.getBoxedType(clazz));
}
return classList;
}
}