/* *************************************************************************************** * 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.dot; import com.espertech.esper.client.*; import com.espertech.esper.epl.core.StreamTypeService; 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.join.plan.FilterExprAnalyzerAffector; import com.espertech.esper.epl.enummethod.dot.*; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.rettype.*; import com.espertech.esper.epl.table.mgmt.TableMetadata; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.event.EventTypeMetadata; import com.espertech.esper.event.EventTypeUtility; import com.espertech.esper.event.arr.ObjectArrayEventType; import com.espertech.esper.metrics.instrumentation.InstrumentationHelper; import com.espertech.esper.util.JavaClassHelper; import net.sf.cglib.reflect.FastMethod; import java.util.*; public class ExprDotNodeUtility { public static boolean isDatetimeOrEnumMethod(String name) { return EnumMethodEnum.isEnumerationMethod(name) || DatetimeMethodEnum.isDateTimeMethod(name); } public static ExprDotNodeRealizedChain getChainEvaluators( Integer streamOfProviderIfApplicable, EPType inputType, List<ExprChainedSpec> chainSpec, ExprValidationContext validationContext, boolean isDuckTyping, ExprDotNodeFilterAnalyzerInput inputDesc) throws ExprValidationException { List<ExprDotEval> methodEvals = new ArrayList<ExprDotEval>(); EPType currentInputType = inputType; EnumMethodEnum lastLambdaFunc = null; ExprChainedSpec lastElement = chainSpec.isEmpty() ? null : chainSpec.get(chainSpec.size() - 1); FilterExprAnalyzerAffector 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 instanceof ClassMultiValuedEPType) { ClassMultiValuedEPType type = (ClassMultiValuedEPType) currentInputType; if (chainElement.getName().toLowerCase(Locale.ENGLISH).equals("size") && paramTypes.length == 0 && lastElement == chainElement) { ExprDotEvalArraySize sizeExpr = new ExprDotEvalArraySize(); methodEvals.add(sizeExpr); currentInputType = sizeExpr.getTypeInfo(); continue; } if (chainElement.getName().toLowerCase(Locale.ENGLISH).equals("get") && paramTypes.length == 1 && JavaClassHelper.getBoxedType(paramTypes[0]) == Integer.class) { Class componentType = type.getComponent(); ExprDotEvalArrayGet get = new ExprDotEvalArrayGet(paramEvals[0], componentType); methodEvals.add(get); currentInputType = get.getTypeInfo(); continue; } } // determine if there is a matching method boolean matchingMethod = false; Class methodTarget = getMethodTarget(currentInputType); if (methodTarget != null) { try { getValidateMethodDescriptor(methodTarget, chainElement.getName(), chainElement.getParameters(), validationContext); matchingMethod = true; } catch (ExprValidationException ex) { // expected } } // resolve lambda if (EnumMethodEnum.isEnumerationMethod(chainElement.getName()) && (!matchingMethod || methodTarget.isArray() || JavaClassHelper.isImplementsInterface(methodTarget, Collection.class))) { EnumMethodEnum enumerationMethod = EnumMethodEnum.fromName(chainElement.getName()); ExprDotEvalEnumMethod eval = (ExprDotEvalEnumMethod) JavaClassHelper.instantiate(ExprDotEvalEnumMethod.class, enumerationMethod.getImplementation()); eval.init(streamOfProviderIfApplicable, 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()) && (!matchingMethod || methodTarget == Calendar.class || methodTarget == Date.class)) { DatetimeMethodEnum datetimeMethod = DatetimeMethodEnum.fromName(chainElement.getName()); ExprDotEvalDTMethodDesc datetimeImpl = ExprDotEvalDTFactory.validateMake(validationContext.getStreamTypeService(), chainSpecStack, datetimeMethod, chainElement.getName(), currentInputType, chainElement.getParameters(), inputDesc, validationContext.getEngineImportService().getTimeZone(), validationContext.getEngineImportService().getTimeAbacus(), validationContext.getExprEvaluatorContext()); 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 instanceof EventEPType) { EventType inputEventType = ((EventEPType) currentInputType).getType(); Class type = inputEventType.getPropertyType(chainElement.getName()); EventPropertyGetter getter = inputEventType.getGetter(chainElement.getName()); if (type != null && getter != null) { ExprDotEvalProperty noduck = new ExprDotEvalProperty(getter, EPTypeHelper.singleValue(JavaClassHelper.getBoxedType(type))); methodEvals.add(noduck); currentInputType = EPTypeHelper.singleValue(EPTypeHelper.getClassSingleValued(noduck.getTypeInfo())); continue; } } // Finally try to resolve the method if (methodTarget != null) { try { // find descriptor again, allow for duck typing ExprNodeUtilMethodDesc desc = getValidateMethodDescriptor(methodTarget, chainElement.getName(), chainElement.getParameters(), validationContext); FastMethod fastMethod = desc.getFastMethod(); paramEvals = desc.getChildEvals(); ExprDotEval eval; if (currentInputType instanceof ClassEPType) { // 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.getEngineImportService(), 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 " + EPTypeHelper.toTypeDescriptive(currentInputType); throw new ExprValidationException(message); } ExprDotEval[] intermediateEvals = methodEvals.toArray(new ExprDotEval[methodEvals.size()]); if (lastLambdaFunc != null) { ExprDotEval finalEval = null; if (currentInputType instanceof EventMultiValuedEPType) { EventMultiValuedEPType mvType = (EventMultiValuedEPType) currentInputType; TableMetadata tableMetadata = validationContext.getTableService().getTableMetadataFromEventType(mvType.getComponent()); if (tableMetadata != null) { finalEval = new ExprDotEvalUnpackCollEventBeanTable(mvType.getComponent(), tableMetadata); } else { finalEval = new ExprDotEvalUnpackCollEventBean(mvType.getComponent()); } } else if (currentInputType instanceof EventEPType) { EventEPType epType = (EventEPType) currentInputType; TableMetadata tableMetadata = validationContext.getTableService().getTableMetadataFromEventType(epType.getType()); if (tableMetadata != null) { finalEval = new ExprDotEvalUnpackBeanTable(epType.getType(), tableMetadata); } else { finalEval = new ExprDotEvalUnpackBean(epType.getType()); } } if (finalEval != null) { methodEvals.add(finalEval); } } ExprDotEval[] unpackingEvals = methodEvals.toArray(new ExprDotEval[methodEvals.size()]); return new ExprDotNodeRealizedChain(intermediateEvals, unpackingEvals, filterAnalyzerDesc); } private static Class getMethodTarget(EPType currentInputType) { if (currentInputType instanceof ClassEPType) { return ((ClassEPType) currentInputType).getType(); } else if (currentInputType instanceof EventEPType) { return ((EventEPType) currentInputType).getType().getUnderlyingType(); } return null; } public static ObjectArrayEventType makeTransientOAType(String enumMethod, String propertyName, Class type, EventAdapterService eventAdapterService) { Map<String, Object> propsResult = new HashMap<String, Object>(); propsResult.put(propertyName, type); String typeName = enumMethod + "__" + propertyName; return new ObjectArrayEventType(EventTypeMetadata.createAnonymous(typeName, EventTypeMetadata.ApplicationType.OBJECTARR), typeName, 0, eventAdapterService, propsResult, null, null, null); } public static EventType[] getSingleLambdaParamEventType(String enumMethodUsedName, List<String> goesToNames, EventType inputEventType, Class collectionComponentType, EventAdapterService eventAdapterService) { if (inputEventType != null) { return new EventType[]{inputEventType}; } else { return new EventType[]{ExprDotNodeUtility.makeTransientOAType(enumMethodUsedName, goesToNames.get(0), collectionComponentType, eventAdapterService)}; } } public static Object evaluateChain(ExprDotEval[] evaluators, Object inner, EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { if (InstrumentationHelper.ENABLED) { int i = -1; for (ExprDotEval methodEval : evaluators) { i++; InstrumentationHelper.get().qExprDotChainElement(i, methodEval); inner = methodEval.evaluate(inner, eventsPerStream, isNewData, context); InstrumentationHelper.get().aExprDotChainElement(methodEval.getTypeInfo(), inner); if (inner == null) { break; } } return inner; } else { for (ExprDotEval methodEval : evaluators) { inner = methodEval.evaluate(inner, eventsPerStream, isNewData, context); if (inner == null) { break; } } return inner; } } public static Object evaluateChainWithWrap(ExprDotStaticMethodWrap resultWrapLambda, Object result, EventType optionalResultSingleEventType, Class resultType, ExprDotEval[] chainEval, EventBean[] eventsPerStream, boolean newData, ExprEvaluatorContext exprEvaluatorContext) { if (result == null) { return null; } if (resultWrapLambda != null) { result = resultWrapLambda.convert(result); } if (InstrumentationHelper.ENABLED) { EPType typeInfo; if (resultWrapLambda != null) { typeInfo = resultWrapLambda.getTypeInfo(); } else { if (optionalResultSingleEventType != null) { typeInfo = EPTypeHelper.singleEvent(optionalResultSingleEventType); } else { typeInfo = EPTypeHelper.singleValue(resultType); } } InstrumentationHelper.get().qExprDotChain(typeInfo, result, chainEval); int i = -1; for (ExprDotEval aChainEval : chainEval) { i++; InstrumentationHelper.get().qExprDotChainElement(i, aChainEval); result = aChainEval.evaluate(result, eventsPerStream, newData, exprEvaluatorContext); InstrumentationHelper.get().aExprDotChainElement(aChainEval.getTypeInfo(), result); if (result == null) { break; } } InstrumentationHelper.get().aExprDotChain(); return result; } for (ExprDotEval aChainEval : chainEval) { result = aChainEval.evaluate(result, eventsPerStream, newData, exprEvaluatorContext); if (result == null) { return result; } } return result; } public static ExprDotEnumerationSource getEnumerationSource(ExprNode inputExpression, StreamTypeService streamTypeService, EventAdapterService eventAdapterService, int statementId, boolean hasEnumerationMethod, boolean disablePropertyExpressionEventCollCache) throws ExprValidationException { ExprEvaluator rootNodeEvaluator = inputExpression.getExprEvaluator(); ExprEvaluatorEnumeration rootLambdaEvaluator = null; EPType info = null; if (rootNodeEvaluator instanceof ExprEvaluatorEnumeration) { rootLambdaEvaluator = (ExprEvaluatorEnumeration) rootNodeEvaluator; if (rootLambdaEvaluator.getEventTypeCollection(eventAdapterService, statementId) != null) { info = EPTypeHelper.collectionOfEvents(rootLambdaEvaluator.getEventTypeCollection(eventAdapterService, statementId)); } else if (rootLambdaEvaluator.getEventTypeSingle(eventAdapterService, statementId) != null) { info = EPTypeHelper.singleEvent(rootLambdaEvaluator.getEventTypeSingle(eventAdapterService, statementId)); } else if (rootLambdaEvaluator.getComponentTypeCollection() != null) { info = EPTypeHelper.collectionOfSingleValue(rootLambdaEvaluator.getComponentTypeCollection()); } else { rootLambdaEvaluator = null; // not a lambda evaluator } } else if (inputExpression instanceof ExprIdentNode) { ExprIdentNode identNode = (ExprIdentNode) inputExpression; int streamId = identNode.getStreamId(); EventType streamType = streamTypeService.getEventTypes()[streamId]; return getPropertyEnumerationSource(identNode.getResolvedPropertyName(), streamId, streamType, hasEnumerationMethod, disablePropertyExpressionEventCollCache); } return new ExprDotEnumerationSource(info, null, rootLambdaEvaluator); } public static ExprDotEnumerationSourceForProps getPropertyEnumerationSource(String propertyName, int streamId, EventType streamType, boolean allowEnumType, boolean disablePropertyExpressionEventCollCache) { Class propertyType = streamType.getPropertyType(propertyName); EPType typeInfo = EPTypeHelper.singleValue(propertyType); // assume scalar for now // no enumeration methods, no need to expose as an enumeration if (!allowEnumType) { return new ExprDotEnumerationSourceForProps(null, typeInfo, streamId, null); } FragmentEventType fragmentEventType = streamType.getFragmentType(propertyName); EventPropertyGetter getter = streamType.getGetter(propertyName); ExprEvaluatorEnumeration enumEvaluator = null; if (getter != null && fragmentEventType != null) { if (fragmentEventType.isIndexed()) { enumEvaluator = new PropertyExprEvaluatorEventCollection(propertyName, streamId, fragmentEventType.getFragmentType(), getter, disablePropertyExpressionEventCollCache); typeInfo = EPTypeHelper.collectionOfEvents(fragmentEventType.getFragmentType()); } else { // we don't want native to require an eventbean instance enumEvaluator = new PropertyExprEvaluatorEventSingle(streamId, fragmentEventType.getFragmentType(), getter); typeInfo = EPTypeHelper.singleEvent(fragmentEventType.getFragmentType()); } } else { EventPropertyDescriptor desc = EventTypeUtility.getNestablePropertyDescriptor(streamType, propertyName); if (desc != null && desc.isIndexed() && !desc.isRequiresIndex() && desc.getPropertyComponentType() != null) { if (JavaClassHelper.isImplementsInterface(propertyType, Collection.class)) { enumEvaluator = new PropertyExprEvaluatorScalarCollection(propertyName, streamId, getter, desc.getPropertyComponentType()); } else if (JavaClassHelper.isImplementsInterface(propertyType, Iterable.class)) { enumEvaluator = new PropertyExprEvaluatorScalarIterable(propertyName, streamId, getter, desc.getPropertyComponentType()); } else if (propertyType.isArray()) { enumEvaluator = new PropertyExprEvaluatorScalarArray(propertyName, streamId, getter, desc.getPropertyComponentType()); } else { throw new IllegalStateException("Property indicated indexed-type but failed to find proper collection adapter for use with enumeration methods"); } typeInfo = EPTypeHelper.collectionOfSingleValue(desc.getPropertyComponentType()); } } ExprEvaluatorEnumerationGivenEvent enumEvaluatorGivenEvent = (ExprEvaluatorEnumerationGivenEvent) enumEvaluator; return new ExprDotEnumerationSourceForProps(enumEvaluator, typeInfo, streamId, enumEvaluatorGivenEvent); } private static ExprNodeUtilMethodDesc getValidateMethodDescriptor(Class methodTarget, final String methodName, List<ExprNode> parameters, ExprValidationContext validationContext) throws ExprValidationException { ExprNodeUtilResolveExceptionHandler exceptionHandler = new ExprNodeUtilResolveExceptionHandler() { public ExprValidationException handle(Exception e) { return new ExprValidationException("Failed to resolve method '" + methodName + "': " + e.getMessage(), e); } }; EventType wildcardType = validationContext.getStreamTypeService().getEventTypes().length != 1 ? null : validationContext.getStreamTypeService().getEventTypes()[0]; return ExprNodeUtility.resolveMethodAllowWildcardAndStream(methodTarget.getName(), methodTarget, methodName, parameters, validationContext.getEngineImportService(), validationContext.getEventAdapterService(), validationContext.getStatementId(), wildcardType != null, wildcardType, exceptionHandler, methodName, validationContext.getTableService(), validationContext.getStreamTypeService().getEngineURIQualifier()); } }