/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* 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.ops;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.epl.expression.core.*;
import com.espertech.esper.event.EventAdapterService;
import com.espertech.esper.metrics.instrumentation.InstrumentationHelper;
import com.espertech.esper.util.CoercionException;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.SimpleNumberCoercer;
import com.espertech.esper.util.SimpleNumberCoercerFactory;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.util.*;
/**
* Represents an array in a filter expressiun tree.
*/
public class ExprArrayNode extends ExprNodeBase implements ExprEvaluator, ExprEvaluatorEnumeration {
private Class arrayReturnType;
private boolean mustCoerce;
private int length;
private transient SimpleNumberCoercer coercer;
private transient Object constantResult;
private transient ExprEvaluator[] evaluators;
private volatile transient Collection constantResultList;
private static final long serialVersionUID = 5533223915923867651L;
/**
* Ctor.
*/
public ExprArrayNode() {
}
public ExprEvaluator getExprEvaluator() {
return this;
}
public ExprNode validate(ExprValidationContext validationContext) throws ExprValidationException {
length = this.getChildNodes().length;
evaluators = ExprNodeUtility.getEvaluators(this.getChildNodes());
// Can be an empty array with no content
if (this.getChildNodes().length == 0) {
arrayReturnType = Object.class;
constantResult = new Object[0];
return null;
}
List<Class> comparedTypes = new LinkedList<Class>();
for (int i = 0; i < length; i++) {
comparedTypes.add(evaluators[i].getType());
}
// Determine common denominator type
try {
arrayReturnType = JavaClassHelper.getCommonCoercionType(comparedTypes.toArray(new Class[comparedTypes.size()]));
// Determine if we need to coerce numbers when one type doesn't match any other type
if (JavaClassHelper.isNumeric(arrayReturnType)) {
mustCoerce = false;
for (Class comparedType : comparedTypes) {
if (comparedType != arrayReturnType) {
mustCoerce = true;
}
}
if (mustCoerce) {
coercer = SimpleNumberCoercerFactory.getCoercer(null, arrayReturnType);
}
}
} catch (CoercionException ex) {
// expected, such as mixing String and int values, or Java classes (not boxed) and primitives
// use Object[] in such cases
}
if (arrayReturnType == null) {
arrayReturnType = Object.class;
}
// Determine if we are dealing with constants only
Object[] results = new Object[length];
int index = 0;
for (ExprNode child : this.getChildNodes()) {
if (!child.isConstantResult()) {
results = null; // not using a constant result
break;
}
results[index] = evaluators[index].evaluate(null, false, validationContext.getExprEvaluatorContext());
index++;
}
// Copy constants into array and coerce, if required
if (results != null) {
constantResult = Array.newInstance(arrayReturnType, length);
for (int i = 0; i < length; i++) {
if (mustCoerce) {
Number boxed = (Number) results[i];
if (boxed != null) {
Object coercedResult = coercer.coerceBoxed(boxed);
Array.set(constantResult, i, coercedResult);
}
} else {
Array.set(constantResult, i, results[i]);
}
}
}
return null;
}
public boolean isConstantResult() {
return constantResult != null;
}
public Class getType() {
return Array.newInstance(arrayReturnType, 0).getClass();
}
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().qExprArray(this);
}
if (constantResult != null) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aExprArray(constantResult);
}
return constantResult;
}
Object array = Array.newInstance(arrayReturnType, length);
if (length == 0) {
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aExprArray(array);
}
return array;
}
int index = 0;
for (ExprEvaluator child : evaluators) {
Object result = child.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (result != null) {
if (mustCoerce) {
Number boxed = (Number) result;
Object coercedResult = coercer.coerceBoxed(boxed);
Array.set(array, index, coercedResult);
} else {
Array.set(array, index, result);
}
}
index++;
}
if (InstrumentationHelper.ENABLED) {
InstrumentationHelper.get().aExprArray(array);
}
return array;
}
public void toPrecedenceFreeEPL(StringWriter writer) {
String delimiter = "";
writer.append("{");
for (ExprNode expr : this.getChildNodes()) {
writer.append(delimiter);
expr.toEPL(writer, ExprPrecedenceEnum.MINIMUM);
delimiter = ",";
}
writer.append('}');
}
public ExprPrecedenceEnum getPrecedence() {
return ExprPrecedenceEnum.UNARY;
}
public Class getComponentTypeCollection() throws ExprValidationException {
return arrayReturnType;
}
public Collection evaluateGetROCollectionScalar(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
if (constantResult != null) {
if (constantResultList != null) {
return constantResultList;
}
ArrayList list = new ArrayList();
for (int i = 0; i < length; i++) {
list.add(Array.get(constantResult, i));
}
constantResultList = list;
return list;
}
if (length == 0) {
return Collections.emptyList();
}
List resultList = new ArrayList();
int index = 0;
for (ExprEvaluator child : evaluators) {
Object result = child.evaluate(eventsPerStream, isNewData, context);
if (result != null) {
if (mustCoerce) {
Number boxed = (Number) result;
Object coercedResult = coercer.coerceBoxed(boxed);
resultList.add(coercedResult);
} else {
resultList.add(result);
}
}
index++;
}
return resultList;
}
public EventType getEventTypeCollection(EventAdapterService eventAdapterService, int statementId) throws ExprValidationException {
return null;
}
public Collection<EventBean> evaluateGetROCollectionEvents(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
return null;
}
public EventType getEventTypeSingle(EventAdapterService eventAdapterService, int statementId) throws ExprValidationException {
return null;
}
public EventBean evaluateGetEventBean(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
return null;
}
public boolean equalsNode(ExprNode node, boolean ignoreStreamPrefix) {
if (!(node instanceof ExprArrayNode)) {
return false;
}
return true;
}
}