// the root expression may also provide a lambda-function input (Iterator<EventBean>)
// Determine collection-type and evaluator if any for root node
Pair<ExprEvaluatorEnumeration, ExpressionReturnType> enumSrc = getEnumerationSource(rootNode, validationContext.getStreamTypeService(), validationContext.getEventAdapterService(), validationContext.getStatementId(), hasEnumerationMethod);
ExpressionReturnType typeInfo;
if (enumSrc.getSecond() == null) {
typeInfo = ExpressionReturnType.singleValue(rootNodeEvaluator.getType()); // not a collection type, treat as scalar
}
else {
typeInfo = enumSrc.getSecond();
}
ExprDotNodeRealizedChain evals = ExprDotNodeUtility.getChainEvaluators(typeInfo, chainSpec, validationContext, isDuckTyping, new ExprDotNodeFilterAnalyzerInputExpr());
exprEvaluator = new ExprDotEvalRootChild(rootNodeEvaluator, enumSrc.getFirst(), typeInfo, evals.getChain(), evals.getChainWithUnpack());
return;
}
// No root node, and this is a 1-element chain i.e. "something(param,...)".
// Plug-in single-row methods are not handled here.
// Plug-in aggregation methods are not handled here.
if (chainSpec.size() == 1) {
ExprChainedSpec spec = chainSpec.get(0);
if (spec.getParameters().isEmpty()) {
throw handleNotFound(spec.getName());
}
// single-parameter can resolve to a property
Pair<PropertyResolutionDescriptor, String> propertyInfoPair = null;
try {
propertyInfoPair = ExprIdentNodeUtil.getTypeFromStream(streamTypeService, spec.getName(), streamTypeService.hasPropertyAgnosticType(), false);
}
catch (ExprValidationPropertyException ex) {
// fine
}
// if not a property then try built-in single-row non-grammar functions
if (propertyInfoPair == null && spec.getName().toLowerCase().equals(EngineImportService.EXT_SINGLEROW_FUNCTION_TRANSPOSE)) {
if (spec.getParameters().size() != 1) {
throw new ExprValidationException("The " + EngineImportService.EXT_SINGLEROW_FUNCTION_TRANSPOSE + " function requires a single parameter expression");
}
exprEvaluator = new ExprDotEvalTransposeAsStream(chainSpec.get(0).getParameters().get(0).getExprEvaluator());
}
else if (spec.getParameters().size() != 1) {
throw handleNotFound(spec.getName());
}
else {
if (propertyInfoPair == null) {
throw new ExprValidationException("Unknown single-row function, aggregation function or mapped or indexed property named '" + spec.getName() + "' could not be resolved");
}
exprEvaluator = getPropertyPairEvaluator(spec.getParameters().get(0).getExprEvaluator(), propertyInfoPair, validationContext);
}
return;
}
// handle the case where the first chain spec element is a stream name.
if (prefixedStreamNumber != -1) {
ExprChainedSpec specAfterStreamName = chainSpec.get(1);
// Attempt to resolve as property
Pair<PropertyResolutionDescriptor, String> propertyInfoPair = null;
try {
String propName = chainSpec.get(0).getName() + "." + specAfterStreamName.getName();
propertyInfoPair = ExprIdentNodeUtil.getTypeFromStream(streamTypeService, propName, streamTypeService.hasPropertyAgnosticType(), false);
}
catch (ExprValidationPropertyException ex) {
// fine
}
if (propertyInfoPair != null) {
if (specAfterStreamName.getParameters().size() != 1) {
throw handleNotFound(specAfterStreamName.getName());
}
exprEvaluator = getPropertyPairEvaluator(specAfterStreamName.getParameters().get(0).getExprEvaluator(), propertyInfoPair, validationContext);
}
else {
// Attempt to resolve as event-underlying object instance method
EventType eventType = validationContext.getStreamTypeService().getEventTypes()[prefixedStreamNumber];
Class type = eventType.getUnderlyingType();
List<ExprChainedSpec> remainderChain = new ArrayList<ExprChainedSpec>(chainSpec);
remainderChain.remove(0);
ExprValidationException methodEx = null;
ExprDotEval[] underlyingMethodChain = null;
try {
ExpressionReturnType typeInfo = ExpressionReturnType.singleValue(type);
underlyingMethodChain = ExprDotNodeUtility.getChainEvaluators(typeInfo, remainderChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStream(prefixedStreamNumber)).getChainWithUnpack();
}
catch (ExprValidationException ex) {
methodEx = ex;
// expected - may not be able to find the methods on the underlying
}
ExprDotEval[] eventTypeMethodChain = null;
ExprValidationException enumDatetimeEx = null;
try {
ExpressionReturnType typeInfo = ExpressionReturnType.singleEvent(eventType);
ExprDotNodeRealizedChain chain = ExprDotNodeUtility.getChainEvaluators(typeInfo, remainderChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStream(prefixedStreamNumber));
eventTypeMethodChain = chain.getChainWithUnpack();
exprDotNodeFilterAnalyzerDesc = chain.getFilterAnalyzerDesc();
}
catch (ExprValidationException ex) {
enumDatetimeEx = ex;
// expected - may not be able to find the methods on the underlying
}
if (underlyingMethodChain != null) {
exprEvaluator = new ExprDotEvalStreamMethod(prefixedStreamNumber, underlyingMethodChain);
}
else if (eventTypeMethodChain != null) {
exprEvaluator = new ExprDotEvalStreamEventBean(prefixedStreamNumber, eventTypeMethodChain);
}
else {
if (ExprDotNodeUtility.isDatetimeOrEnumMethod(remainderChain.get(0).getName())) {
throw enumDatetimeEx;
}
throw new ExprValidationException("Failed to solve '" + remainderChain.get(0).getName() + "' to either an date-time or enumeration method, an event property or a method on the event underlying object: " + methodEx.getMessage(), methodEx);
}
}
return;
}
// There no root node, in this case the classname or property name is provided as part of the chain.
// Such as "MyClass.myStaticLib(...)" or "mycollectionproperty.doIt(...)"
//
List<ExprChainedSpec> modifiedChain = new ArrayList<ExprChainedSpec>(chainSpec);
ExprChainedSpec firstItem = modifiedChain.remove(0);
Pair<PropertyResolutionDescriptor, String> propertyInfoPair = null;
try {
propertyInfoPair = ExprIdentNodeUtil.getTypeFromStream(streamTypeService, firstItem.getName(), streamTypeService.hasPropertyAgnosticType(), true);
}
catch (ExprValidationPropertyException ex) {
// not a property
}
// If property then treat it as such
if (propertyInfoPair != null) {
String propertyName = propertyInfoPair.getFirst().getPropertyName();
int streamId = propertyInfoPair.getFirst().getStreamNum();
EventType streamType = streamTypeService.getEventTypes()[streamId];
ExpressionReturnType typeInfo;
ExprEvaluatorEnumeration enumerationEval = null;
ExpressionReturnType inputType;
ExprEvaluator rootNodeEvaluator = null;
EventPropertyGetter getter;
if (firstItem.getParameters().isEmpty()) {
getter = streamType.getGetter(propertyInfoPair.getFirst().getPropertyName());
Pair<ExprEvaluatorEnumeration, ExpressionReturnType> propertyEval = getPropertyEnumerationSource(propertyInfoPair.getFirst().getPropertyName(), streamId, streamType, hasEnumerationMethod);
typeInfo = propertyEval.getSecond();
enumerationEval = propertyEval.getFirst();
inputType = propertyEval.getSecond();
rootNodeEvaluator = new PropertyExprEvaluatorNonLambda(streamId, getter, propertyInfoPair.getFirst().getPropertyType());
}
else {
// property with parameter - mapped or indexed property
EventPropertyDescriptor desc = EventTypeUtility.getNestablePropertyDescriptor(streamTypeService.getEventTypes()[propertyInfoPair.getFirst().getStreamNum()], firstItem.getName());
if (firstItem.getParameters().size() > 1) {
throw new ExprValidationException("Property '" + firstItem.getName() + "' may not be accessed passing 2 or more parameters");
}
ExprEvaluator paramEval = firstItem.getParameters().get(0).getExprEvaluator();
typeInfo = ExpressionReturnType.singleValue(desc.getPropertyComponentType());
inputType = typeInfo;
getter = null;
if (desc.isMapped()) {
if (paramEval.getType() != String.class) {
throw new ExprValidationException("Parameter expression to mapped property '" + propertyName + "' is expected to return a string-type value but returns " + JavaClassHelper.getClassNameFullyQualPretty(paramEval.getType()));
}
EventPropertyGetterMapped mappedGetter = propertyInfoPair.getFirst().getStreamEventType().getGetterMapped(propertyInfoPair.getFirst().getPropertyName());
if (mappedGetter == null) {
throw new ExprValidationException("Mapped property named '" + propertyName + "' failed to obtain getter-object");
}
rootNodeEvaluator = new PropertyExprEvaluatorNonLambdaMapped(streamId, mappedGetter, paramEval, desc.getPropertyComponentType());
}
if (desc.isIndexed()) {
if (JavaClassHelper.getBoxedType(paramEval.getType()) != Integer.class) {
throw new ExprValidationException("Parameter expression to mapped property '" + propertyName + "' is expected to return a Integer-type value but returns " + JavaClassHelper.getClassNameFullyQualPretty(paramEval.getType()));
}
EventPropertyGetterIndexed indexedGetter = propertyInfoPair.getFirst().getStreamEventType().getGetterIndexed(propertyInfoPair.getFirst().getPropertyName());
if (indexedGetter == null) {
throw new ExprValidationException("Mapped property named '" + propertyName + "' failed to obtain getter-object");
}
rootNodeEvaluator = new PropertyExprEvaluatorNonLambdaIndexed(streamId, indexedGetter, paramEval, desc.getPropertyComponentType());
}
}
if (typeInfo == null) {
throw new ExprValidationException("Property '" + propertyName + "' is not a mapped or indexed property");
}
// try to build chain based on the input (non-fragment)
ExprDotNodeRealizedChain evals;
ExprDotNodeFilterAnalyzerInputProp filterAnalyzerInputProp = new ExprDotNodeFilterAnalyzerInputProp(propertyInfoPair.getFirst().getStreamNum(), propertyInfoPair.getFirst().getPropertyName());
try {
evals = ExprDotNodeUtility.getChainEvaluators(inputType, modifiedChain, validationContext, isDuckTyping, filterAnalyzerInputProp);
}
catch (ExprValidationException ex) {
// try building the chain based on the fragment event type (i.e. A.after(B) based on A-configured start time where A is a fragment)
FragmentEventType fragment = propertyInfoPair.getFirst().getFragmentEventType();
if (fragment == null) {
throw ex;
}
ExpressionReturnType fragmentTypeInfo;
if (fragment.isIndexed()) {
fragmentTypeInfo = ExpressionReturnType.collectionOfEvents(fragment.getFragmentType());
}
else {
fragmentTypeInfo = ExpressionReturnType.singleEvent(fragment.getFragmentType());
}
evals = ExprDotNodeUtility.getChainEvaluators(fragmentTypeInfo, modifiedChain, validationContext, isDuckTyping, filterAnalyzerInputProp);
rootNodeEvaluator = new PropertyExprEvaluatorNonLambdaFragment(streamId, getter, fragment.getFragmentType().getUnderlyingType());
}
exprEvaluator = new ExprDotEvalRootChild(rootNodeEvaluator, enumerationEval, inputType, evals.getChain(), evals.getChainWithUnpack());
exprDotNodeFilterAnalyzerDesc = evals.getFilterAnalyzerDesc();
return;
}
// If variable then resolve as such
VariableReader variableReader = validationContext.getVariableService().getReader(firstItem.getName());
if (variableReader != null) {
ExpressionReturnType typeInfo;
ExprDotStaticMethodWrap wrap;
if (variableReader.getType().isArray()) {
typeInfo = ExpressionReturnType.collectionOfSingleValue(variableReader.getType().getComponentType());
wrap = new ExprDotStaticMethodWrapArrayScalar(variableReader.getVariableName(), variableReader.getType().getComponentType());
}
else if (variableReader.getEventType() != null) {
typeInfo = ExpressionReturnType.singleEvent(variableReader.getEventType());
wrap = null;
}
else {
typeInfo = ExpressionReturnType.singleValue(variableReader.getType());
wrap = null;
}
ExprDotNodeRealizedChain evals = ExprDotNodeUtility.getChainEvaluators(typeInfo, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic());
exprEvaluator = new ExprDotEvalVariable(variableReader, wrap, evals.getChainWithUnpack());
return;
}
// try resolve as enumeration class with value
Object enumconstant = JavaClassHelper.resolveIdentAsEnumConst(firstItem.getName(), validationContext.getMethodResolutionService(), null);
if (enumconstant != null) {
// try resolve method
final ExprChainedSpec methodSpec = modifiedChain.get(0);
final String enumvalue = firstItem.getName();
ExprNodeUtilResolveExceptionHandler handler = new ExprNodeUtilResolveExceptionHandler() {
public ExprValidationException handle(Exception ex) {
return new ExprValidationException("Failed to resolve method '" + methodSpec.getName() + "' on enumeration value '" + enumvalue + "': " + ex.getMessage());
}
};
EventType wildcardType = validationContext.getStreamTypeService().getEventTypes().length != 1 ? null : validationContext.getStreamTypeService().getEventTypes()[0];
ExprNodeUtilMethodDesc methodDesc = ExprNodeUtility.resolveMethodAllowWildcardAndStream(enumconstant.getClass().getName(), enumconstant.getClass(), methodSpec.getName(), methodSpec.getParameters(), validationContext.getMethodResolutionService(), validationContext.getEventAdapterService(), validationContext.getStatementId(), wildcardType != null, wildcardType, handler, methodSpec.getName());
// method resolved, hook up
modifiedChain.remove(0); // we identified this piece
ExprDotStaticMethodWrap optionalLambdaWrap = ExprDotStaticMethodWrapFactory.make(methodDesc.getReflectionMethod(), validationContext.getEventAdapterService(), modifiedChain);
ExpressionReturnType typeInfo = optionalLambdaWrap != null ? optionalLambdaWrap.getTypeInfo() : ExpressionReturnType.singleValue(methodDesc.getFastMethod().getReturnType());
ExprDotNodeRealizedChain evals = ExprDotNodeUtility.getChainEvaluators(typeInfo, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic());
exprEvaluator = new ExprDotEvalStaticMethod(validationContext.getStatementName(), firstItem.getName(), methodDesc.getFastMethod(),
methodDesc.getChildEvals(), false, optionalLambdaWrap, evals.getChainWithUnpack(), false, enumconstant);
return;
}
// If class then resolve as class
ExprChainedSpec secondItem = modifiedChain.remove(0);
boolean allowWildcard = validationContext.getStreamTypeService().getEventTypes().length == 1;
EventType streamZeroType = null;
if (validationContext.getStreamTypeService().getEventTypes().length > 0) {
streamZeroType = validationContext.getStreamTypeService().getEventTypes()[0];
}
ExprNodeUtilMethodDesc method = ExprNodeUtility.resolveMethodAllowWildcardAndStream(firstItem.getName(), null, secondItem.getName(), secondItem.getParameters(), validationContext.getMethodResolutionService(), validationContext.getEventAdapterService(), validationContext.getStatementId(), allowWildcard, streamZeroType, new ExprNodeUtilResolveExceptionHandlerDefault(firstItem.getName() + "." + secondItem.getName(), false), secondItem.getName());
boolean isConstantParameters = method.isAllConstants() && isUDFCache;
isReturnsConstantResult = isConstantParameters && modifiedChain.isEmpty();
// this may return a pair of null if there is no lambda or the result cannot be wrapped for lambda-function use
ExprDotStaticMethodWrap optionalLambdaWrap = ExprDotStaticMethodWrapFactory.make(method.getReflectionMethod(), validationContext.getEventAdapterService(), modifiedChain);
ExpressionReturnType typeInfo = optionalLambdaWrap != null ? optionalLambdaWrap.getTypeInfo() : ExpressionReturnType.singleValue(method.getReflectionMethod().getReturnType());
ExprDotNodeRealizedChain evals = ExprDotNodeUtility.getChainEvaluators(typeInfo, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic());
exprEvaluator = new ExprDotEvalStaticMethod(validationContext.getStatementName(), firstItem.getName(), method.getFastMethod(), method.getChildEvals(), isConstantParameters, optionalLambdaWrap, evals.getChainWithUnpack(), false, null);
}