/**************************************************************************************
* 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.asper.sources.net.sf.cglib.reflect.FastClass;
import com.asper.sources.net.sf.cglib.reflect.FastMethod;
import com.espertech.esper.client.EventPropertyGetter;
import com.espertech.esper.client.EventType;
import com.espertech.esper.epl.datetime.eval.DatetimeMethodEnum;
import com.espertech.esper.epl.datetime.eval.ExprDotEvalDTFactory;
import com.espertech.esper.epl.datetime.eval.ExprDotEvalDTMethodDesc;
import com.espertech.esper.epl.datetime.eval.ExprDotNodeFilterAnalyzerDesc;
import com.espertech.esper.epl.enummethod.dot.*;
import com.espertech.esper.event.EventTypeMetadata;
import com.espertech.esper.event.arr.ObjectArrayEventType;
import com.espertech.esper.event.map.MapEventType;
import com.espertech.esper.util.JavaClassHelper;
import java.lang.reflect.Method;
import java.util.*;
public class ExprDotNodeUtility
{
public static boolean isDatetimeOrEnumMethod(String name) {
return EnumMethodEnum.isEnumerationMethod(name) || DatetimeMethodEnum.isDateTimeMethod(name);
}
public static ExprDotNodeRealizedChain getChainEvaluators(ExprDotEvalTypeInfo inputType,
List<ExprChainedSpec> chainSpec,
ExprValidationContext validationContext,
boolean isDuckTyping,
ExprDotNodeFilterAnalyzerInput inputDesc)
throws ExprValidationException
{
List<ExprDotEval> methodEvals = new ArrayList<ExprDotEval>();
ExprDotEvalTypeInfo currentInputType = inputType;
EnumMethodEnum lastLambdaFunc = null;
ExprChainedSpec lastElement = chainSpec.isEmpty() ? null : chainSpec.get(chainSpec.size() - 1);
ExprDotNodeFilterAnalyzerDesc filterAnalyzerDesc = null;
Deque<ExprChainedSpec> chainSpecStack = new ArrayDeque<ExprChainedSpec>(chainSpec);
while (!chainSpecStack.isEmpty()) {
ExprChainedSpec chainElement = chainSpecStack.removeFirst();
lastLambdaFunc = null; // reset
// compile parameters for chain element
ExprEvaluator[] paramEvals = new ExprEvaluator[chainElement.getParameters().size()];
Class[] paramTypes = new Class[chainElement.getParameters().size()];
for (int i = 0; i < chainElement.getParameters().size(); i++) {
paramEvals[i] = chainElement.getParameters().get(i).getExprEvaluator();
paramTypes[i] = paramEvals[i].getType();
}
// check if special 'size' method
if ( (currentInputType.isScalar() && currentInputType.getScalar().isArray()) ||
(currentInputType.getComponent() != null)) {
if (chainElement.getName().toLowerCase().equals("size") && paramTypes.length == 0 && lastElement == chainElement) {
ExprDotEvalArraySize sizeExpr = new ExprDotEvalArraySize();
methodEvals.add(sizeExpr);
currentInputType = sizeExpr.getTypeInfo();
continue;
}
if (chainElement.getName().toLowerCase().equals("get") && paramTypes.length == 1 && JavaClassHelper.getBoxedType(paramTypes[0]) == Integer.class) {
Class componentType = currentInputType.getComponent() != null ? currentInputType.getComponent() : currentInputType.getScalar().getComponentType();
ExprDotEvalArrayGet get = new ExprDotEvalArrayGet(paramEvals[0], componentType);
methodEvals.add(get);
currentInputType = get.getTypeInfo();
continue;
}
}
// resolve lambda
if (EnumMethodEnum.isEnumerationMethod(chainElement.getName())) {
EnumMethodEnum enumerationMethod = EnumMethodEnum.fromName(chainElement.getName());
ExprDotEvalEnumMethod eval = (ExprDotEvalEnumMethod) JavaClassHelper.instantiate(ExprDotEvalEnumMethod.class, enumerationMethod.getImplementation().getName());
eval.init(enumerationMethod, chainElement.getName(), currentInputType, chainElement.getParameters(), validationContext);
currentInputType = eval.getTypeInfo();
if (currentInputType == null) {
throw new IllegalStateException("Enumeration method '" + chainElement.getName() + "' has not returned type information");
}
methodEvals.add(eval);
lastLambdaFunc = enumerationMethod;
continue;
}
// resolve datetime
if (DatetimeMethodEnum.isDateTimeMethod(chainElement.getName())) {
DatetimeMethodEnum datetimeMethod = DatetimeMethodEnum.fromName(chainElement.getName());
ExprDotEvalDTMethodDesc datetimeImpl = ExprDotEvalDTFactory.validateMake(validationContext.getStreamTypeService(), chainSpecStack, datetimeMethod, chainElement.getName(), currentInputType, chainElement.getParameters(), inputDesc);
currentInputType = datetimeImpl.getReturnType();
if (currentInputType == null) {
throw new IllegalStateException("Date-time method '" + chainElement.getName() + "' has not returned type information");
}
methodEvals.add(datetimeImpl.getEval());
filterAnalyzerDesc = datetimeImpl.getIntervalFilterDesc();
continue;
}
// try to resolve as property if the last method returned a type
if (currentInputType.getEventType() != null) {
Class type = currentInputType.getEventType().getPropertyType(chainElement.getName());
EventPropertyGetter getter = currentInputType.getEventType().getGetter(chainElement.getName());
if (type != null && getter != null) {
ExprDotEvalProperty noduck = new ExprDotEvalProperty(getter, ExprDotEvalTypeInfo.scalarOrUnderlying(JavaClassHelper.getBoxedType(type)));
methodEvals.add(noduck);
currentInputType = ExprDotEvalTypeInfo.scalarOrUnderlying(noduck.getTypeInfo().getScalar());
continue;
}
// preresolve as method
try {
if (currentInputType.isScalar()) {
validationContext.getMethodResolutionService().resolveMethod(currentInputType.getScalar(), chainElement.getName(), paramTypes);
}
}
catch (Exception ex) {
throw new ExprValidationException("Could not resolve '" + chainElement.getName() + "' to a property of event type '" + currentInputType.getEventType().getName() + "' or method on type '" + currentInputType + "'");
}
}
// Try to resolve the method
if (currentInputType.isScalar() || currentInputType.getEventType() != null) {
try
{
Class target;
if (currentInputType.isScalar()) {
target = currentInputType.getScalar();
}
else {
target = currentInputType.getEventType().getUnderlyingType();
}
Method method = validationContext.getMethodResolutionService().resolveMethod(target, chainElement.getName(), paramTypes);
FastClass declaringClass = FastClass.create(Thread.currentThread().getContextClassLoader(), method.getDeclaringClass());
FastMethod fastMethod = declaringClass.getMethod(method);
ExprDotEval eval;
if (currentInputType.isScalar()) {
// if followed by an enumeration method, convert array to collection
if (fastMethod.getReturnType().isArray() && !chainSpecStack.isEmpty() && EnumMethodEnum.isEnumerationMethod(chainSpecStack.getFirst().getName())) {
eval = new ExprDotMethodEvalNoDuckWrapArray(validationContext.getStatementName(), fastMethod, paramEvals);
}
else {
eval = new ExprDotMethodEvalNoDuck(validationContext.getStatementName(), fastMethod, paramEvals);
}
}
else {
eval = new ExprDotMethodEvalNoDuckUnderlying(validationContext.getStatementName(), fastMethod, paramEvals);
}
methodEvals.add(eval);
currentInputType = eval.getTypeInfo();
}
catch(Exception e)
{
if (!isDuckTyping) {
throw new ExprValidationException(e.getMessage(), e);
}
else {
ExprDotMethodEvalDuck duck = new ExprDotMethodEvalDuck(validationContext.getStatementName(), validationContext.getMethodResolutionService(), chainElement.getName(), paramTypes, paramEvals);
methodEvals.add(duck);
currentInputType = duck.getTypeInfo();
}
}
continue;
}
String message = "Could not find event property, enumeration method or instance method named '" +
chainElement.getName() + "' in " + currentInputType.toTypeName();
throw new ExprValidationException(message);
}
ExprDotEval[] intermediateEvals = methodEvals.toArray(new ExprDotEval[methodEvals.size()]);
if (lastLambdaFunc != null) {
if (currentInputType.getEventTypeColl() != null) {
methodEvals.add(new ExprDotEvalUnpackCollEventBean(currentInputType.getEventTypeColl()));
}
else if (currentInputType.getEventType() != null) {
methodEvals.add(new ExprDotEvalUnpackBean(currentInputType.getEventType()));
}
}
ExprDotEval[] unpackingEvals = methodEvals.toArray(new ExprDotEval[methodEvals.size()]);
return new ExprDotNodeRealizedChain(intermediateEvals, unpackingEvals, filterAnalyzerDesc);
}
public static ObjectArrayEventType makeTransientOAType(String enumMethod, String propertyName, Class type) {
Map<String, Object> propsResult = new HashMap<String, Object>();
propsResult.put(propertyName, type);
String typeName = enumMethod + "__" + propertyName;
return new ObjectArrayEventType(EventTypeMetadata.createAnonymous(typeName), typeName, 0, null, propsResult, null, null, null);
}
public static EventType[] getSingleLambdaParamEventType(String enumMethodUsedName, List<String> goesToNames, EventType inputEventType, Class collectionComponentType) {
if (inputEventType != null) {
return new EventType[] {inputEventType};
}
else {
return new EventType[] {ExprDotNodeUtility.makeTransientOAType(enumMethodUsedName, goesToNames.get(0), collectionComponentType)};
}
}
}