/*
***************************************************************************************
* 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.collection.Pair;
import com.espertech.esper.core.start.EPStatementStartMethod;
import com.espertech.esper.epl.core.EngineImportApplicationDotMethod;
import com.espertech.esper.epl.core.EngineImportService;
import com.espertech.esper.epl.core.PropertyResolutionDescriptor;
import com.espertech.esper.epl.core.StreamTypeService;
import com.espertech.esper.epl.enummethod.dot.*;
import com.espertech.esper.epl.expression.core.*;
import com.espertech.esper.epl.expression.visitor.ExprNodeVisitor;
import com.espertech.esper.epl.expression.visitor.ExprNodeVisitorWithParent;
import com.espertech.esper.epl.index.quadtree.EngineImportApplicationDotMethodPointInsideRectange;
import com.espertech.esper.epl.index.quadtree.EngineImportApplicationDotMethodRectangeIntersectsRectangle;
import com.espertech.esper.epl.join.plan.FilterExprAnalyzerAffector;
import com.espertech.esper.epl.rettype.EPType;
import com.espertech.esper.epl.rettype.EPTypeHelper;
import com.espertech.esper.epl.variable.VariableMetaData;
import com.espertech.esper.epl.variable.VariableReader;
import com.espertech.esper.epl.variable.VariableService;
import com.espertech.esper.event.EventTypeUtility;
import com.espertech.esper.util.JavaClassHelper;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Represents an Dot-operator expression, for use when "(expression).method(...).method(...)"
*/
public class ExprDotNodeImpl extends ExprNodeBase implements ExprDotNode, ExprNodeInnerNodeProvider, ExprStreamRefNode {
private static final long serialVersionUID = 8105121208330622813L;
private final List<ExprChainedSpec> chainSpec;
private final boolean isDuckTyping;
private final boolean isUDFCache;
private transient ExprEvaluator exprEvaluator;
private boolean isReturnsConstantResult;
private transient FilterExprAnalyzerAffector filterExprAnalyzerAffector;
private Integer streamNumReferenced;
private String rootPropertyName;
public ExprDotNodeImpl(List<ExprChainedSpec> chainSpec, boolean isDuckTyping, boolean isUDFCache) {
this.chainSpec = new ArrayList<ExprChainedSpec>(chainSpec); // for safety, copy the list
this.isDuckTyping = isDuckTyping;
this.isUDFCache = isUDFCache;
}
public Integer getStreamReferencedIfAny() {
if (exprEvaluator == null) {
throw new IllegalStateException("Identifier expression has not been validated");
}
return streamNumReferenced;
}
public String getRootPropertyNameIfAny() {
if (exprEvaluator == null) {
throw new IllegalStateException("Identifier expression has not been validated");
}
return rootPropertyName;
}
public ExprNode validate(ExprValidationContext validationContext) throws ExprValidationException {
// check for plannable methods: these are validated according to different rules
ExprAppDotMethodImpl appDotMethod = getAppDotMethod(validationContext.isFilterExpression());
if (appDotMethod != null) {
return appDotMethod;
}
// validate all parameters
ExprNodeUtility.validate(ExprNodeOrigin.DOTNODEPARAMETER, chainSpec, validationContext);
// determine if there are enumeration method expressions in the chain
boolean hasEnumerationMethod = false;
for (ExprChainedSpec chain : chainSpec) {
if (EnumMethodEnum.isEnumerationMethod(chain.getName())) {
hasEnumerationMethod = true;
break;
}
}
// determine if there is an implied binding, replace first chain element with evaluation node if there is
if (validationContext.getStreamTypeService().hasTableTypes() &&
validationContext.getTableService() != null &&
chainSpec.size() > 1 && chainSpec.get(0).isProperty()) {
Pair<ExprNode, List<ExprChainedSpec>> tableNode = validationContext.getTableService().getTableNodeChainable(validationContext.getStreamTypeService(), chainSpec, validationContext.getEngineImportService());
if (tableNode != null) {
ExprNode node = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.DOTNODE, tableNode.getFirst(), validationContext);
if (tableNode.getSecond().isEmpty()) {
return node;
}
chainSpec.clear();
chainSpec.addAll(tableNode.getSecond());
this.addChildNode(node);
}
}
// The root node expression may provide the input value:
// Such as "window(*).doIt(...)" or "(select * from Window).doIt()" or "prevwindow(sb).doIt(...)", in which case the expression to act on is a child expression
//
StreamTypeService streamTypeService = validationContext.getStreamTypeService();
if (this.getChildNodes().length != 0) {
// the root expression is the first child node
ExprNode rootNode = this.getChildNodes()[0];
ExprEvaluator rootNodeEvaluator = rootNode.getExprEvaluator();
// the root expression may also provide a lambda-function input (Iterator<EventBean>)
// Determine collection-type and evaluator if any for root node
ExprDotEnumerationSource enumSrc = ExprDotNodeUtility.getEnumerationSource(rootNode, validationContext.getStreamTypeService(), validationContext.getEventAdapterService(), validationContext.getStatementId(), hasEnumerationMethod, validationContext.isDisablePropertyExpressionEventCollCache());
EPType typeInfo;
if (enumSrc.getReturnType() == null) {
typeInfo = EPTypeHelper.singleValue(rootNodeEvaluator.getType()); // not a collection type, treat as scalar
} else {
typeInfo = enumSrc.getReturnType();
}
ExprDotNodeRealizedChain evals = ExprDotNodeUtility.getChainEvaluators(enumSrc.getStreamOfProviderIfApplicable(), typeInfo, chainSpec, validationContext, isDuckTyping, new ExprDotNodeFilterAnalyzerInputExpr());
exprEvaluator = new ExprDotEvalRootChild(hasEnumerationMethod, this, rootNodeEvaluator, enumSrc.getEnumeration(), typeInfo, evals.getChain(), evals.getChainWithUnpack(), false);
return null;
}
// 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(Locale.ENGLISH).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);
streamNumReferenced = propertyInfoPair.getFirst().getStreamNum();
}
return null;
}
// handle the case where the first chain spec element is a stream name.
ExprValidationException prefixedStreamNumException = null;
int prefixedStreamNumber = prefixedStreamName(chainSpec, validationContext.getStreamTypeService());
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);
streamNumReferenced = propertyInfoPair.getFirst().getStreamNum();
return null;
}
// 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 {
EPType typeInfo = EPTypeHelper.singleValue(type);
underlyingMethodChain = ExprDotNodeUtility.getChainEvaluators(prefixedStreamNumber, 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 {
EPType typeInfo = EPTypeHelper.singleEvent(eventType);
ExprDotNodeRealizedChain chain = ExprDotNodeUtility.getChainEvaluators(prefixedStreamNumber, typeInfo, remainderChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStream(prefixedStreamNumber));
eventTypeMethodChain = chain.getChainWithUnpack();
filterExprAnalyzerAffector = 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(this, prefixedStreamNumber, underlyingMethodChain);
streamNumReferenced = prefixedStreamNumber;
} else if (eventTypeMethodChain != null) {
exprEvaluator = new ExprDotEvalStreamEventBean(this, prefixedStreamNumber, eventTypeMethodChain);
streamNumReferenced = prefixedStreamNumber;
}
if (exprEvaluator != null) {
return null;
} else {
if (ExprDotNodeUtility.isDatetimeOrEnumMethod(remainderChain.get(0).getName())) {
prefixedStreamNumException = enumDatetimeEx;
} else {
prefixedStreamNumException = 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);
}
}
}
// 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];
EPType typeInfo;
ExprEvaluatorEnumeration enumerationEval = null;
EPType inputType;
ExprEvaluator rootNodeEvaluator = null;
EventPropertyGetter getter;
if (firstItem.getParameters().isEmpty()) {
getter = streamType.getGetter(propertyInfoPair.getFirst().getPropertyName());
ExprDotEnumerationSourceForProps propertyEval = ExprDotNodeUtility.getPropertyEnumerationSource(propertyInfoPair.getFirst().getPropertyName(), streamId, streamType, hasEnumerationMethod, validationContext.isDisablePropertyExpressionEventCollCache());
typeInfo = propertyEval.getReturnType();
enumerationEval = propertyEval.getEnumeration();
inputType = propertyEval.getReturnType();
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 = EPTypeHelper.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());
boolean rootIsEventBean = false;
try {
evals = ExprDotNodeUtility.getChainEvaluators(streamId, 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;
}
EPType fragmentTypeInfo;
if (fragment.isIndexed()) {
fragmentTypeInfo = EPTypeHelper.collectionOfEvents(fragment.getFragmentType());
} else {
fragmentTypeInfo = EPTypeHelper.singleEvent(fragment.getFragmentType());
}
rootIsEventBean = true;
evals = ExprDotNodeUtility.getChainEvaluators(propertyInfoPair.getFirst().getStreamNum(), fragmentTypeInfo, modifiedChain, validationContext, isDuckTyping, filterAnalyzerInputProp);
rootNodeEvaluator = new PropertyExprEvaluatorNonLambdaFragment(streamId, getter, fragment.getFragmentType().getUnderlyingType());
}
exprEvaluator = new ExprDotEvalRootChild(hasEnumerationMethod, this, rootNodeEvaluator, enumerationEval, inputType, evals.getChain(), evals.getChainWithUnpack(), !rootIsEventBean);
filterExprAnalyzerAffector = evals.getFilterAnalyzerDesc();
streamNumReferenced = propertyInfoPair.getFirst().getStreamNum();
rootPropertyName = propertyInfoPair.getFirst().getPropertyName();
return null;
}
// If variable then resolve as such
String contextNameVariable = validationContext.getVariableService().isContextVariable(firstItem.getName());
if (contextNameVariable != null) {
throw new ExprValidationException("Method invocation on context-specific variable is not supported");
}
VariableReader variableReader = validationContext.getVariableService().getReader(firstItem.getName(), EPStatementStartMethod.DEFAULT_AGENT_INSTANCE_ID);
if (variableReader != null) {
EPType typeInfo;
ExprDotStaticMethodWrap wrap;
if (variableReader.getVariableMetaData().getType().isArray()) {
typeInfo = EPTypeHelper.collectionOfSingleValue(variableReader.getVariableMetaData().getType().getComponentType());
wrap = new ExprDotStaticMethodWrapArrayScalar(variableReader.getVariableMetaData().getVariableName(), variableReader.getVariableMetaData().getType().getComponentType());
} else if (variableReader.getVariableMetaData().getEventType() != null) {
typeInfo = EPTypeHelper.singleEvent(variableReader.getVariableMetaData().getEventType());
wrap = null;
} else {
typeInfo = EPTypeHelper.singleValue(variableReader.getVariableMetaData().getType());
wrap = null;
}
ExprDotNodeRealizedChain evals = ExprDotNodeUtility.getChainEvaluators(null, typeInfo, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic());
exprEvaluator = new ExprDotEvalVariable(this, variableReader, wrap, evals.getChainWithUnpack());
return null;
}
// try resolve as enumeration class with value
Object enumconstant = JavaClassHelper.resolveIdentAsEnumConst(firstItem.getName(), validationContext.getEngineImportService(), false);
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.getEngineImportService(), validationContext.getEventAdapterService(), validationContext.getStatementId(), wildcardType != null, wildcardType, handler, methodSpec.getName(), validationContext.getTableService(), streamTypeService.getEngineURIQualifier());
// method resolved, hook up
modifiedChain.remove(0); // we identified this piece
ExprDotStaticMethodWrap optionalLambdaWrap = ExprDotStaticMethodWrapFactory.make(methodDesc.getReflectionMethod(), validationContext.getEventAdapterService(), modifiedChain, null);
EPType typeInfo = optionalLambdaWrap != null ? optionalLambdaWrap.getTypeInfo() : EPTypeHelper.singleValue(methodDesc.getFastMethod().getReturnType());
ExprDotNodeRealizedChain evals = ExprDotNodeUtility.getChainEvaluators(null, typeInfo, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic());
exprEvaluator = new ExprDotEvalStaticMethod(validationContext.getStatementName(), firstItem.getName(), methodDesc.getFastMethod(),
methodDesc.getChildEvals(), false, optionalLambdaWrap, evals.getChainWithUnpack(), false, enumconstant);
return null;
}
// if prefixed by a stream name, we are giving up
if (prefixedStreamNumException != null) {
throw prefixedStreamNumException;
}
// 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.getEngineImportService(), validationContext.getEventAdapterService(), validationContext.getStatementId(), allowWildcard, streamZeroType, new ExprNodeUtilResolveExceptionHandlerDefault(firstItem.getName() + "." + secondItem.getName(), false), secondItem.getName(), validationContext.getTableService(), streamTypeService.getEngineURIQualifier());
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, null);
EPType typeInfo = optionalLambdaWrap != null ? optionalLambdaWrap.getTypeInfo() : EPTypeHelper.singleValue(method.getReflectionMethod().getReturnType());
ExprDotNodeRealizedChain evals = ExprDotNodeUtility.getChainEvaluators(null, typeInfo, modifiedChain, validationContext, false, new ExprDotNodeFilterAnalyzerInputStatic());
exprEvaluator = new ExprDotEvalStaticMethod(validationContext.getStatementName(), firstItem.getName(), method.getFastMethod(), method.getChildEvals(), isConstantParameters, optionalLambdaWrap, evals.getChainWithUnpack(), false, null);
return null;
}
public FilterExprAnalyzerAffector getAffector(boolean isOuterJoin) {
return isOuterJoin ? null : filterExprAnalyzerAffector;
}
private ExprEvaluator getPropertyPairEvaluator(ExprEvaluator parameterEval, Pair<PropertyResolutionDescriptor, String> propertyInfoPair, ExprValidationContext validationContext)
throws ExprValidationException {
String propertyName = propertyInfoPair.getFirst().getPropertyName();
EventPropertyDescriptor propertyDesc = EventTypeUtility.getNestablePropertyDescriptor(propertyInfoPair.getFirst().getStreamEventType(), propertyName);
if (propertyDesc == null || (!propertyDesc.isMapped() && !propertyDesc.isIndexed())) {
throw new ExprValidationException("Unknown single-row function, aggregation function or mapped or indexed property named '" + propertyName + "' could not be resolved");
}
int streamNum = propertyInfoPair.getFirst().getStreamNum();
if (propertyDesc.isMapped()) {
if (parameterEval.getType() != String.class) {
throw new ExprValidationException("Parameter expression to mapped property '" + propertyDesc.getPropertyName() + "' is expected to return a string-type value but returns " + JavaClassHelper.getClassNameFullyQualPretty(parameterEval.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");
}
return new ExprDotEvalPropertyExprMapped(validationContext.getStatementName(), propertyDesc.getPropertyName(), streamNum, parameterEval, propertyDesc.getPropertyComponentType(), mappedGetter);
} else {
if (JavaClassHelper.getBoxedType(parameterEval.getType()) != Integer.class) {
throw new ExprValidationException("Parameter expression to indexed property '" + propertyDesc.getPropertyName() + "' is expected to return a Integer-type value but returns " + JavaClassHelper.getClassNameFullyQualPretty(parameterEval.getType()));
}
EventPropertyGetterIndexed indexedGetter = propertyInfoPair.getFirst().getStreamEventType().getGetterIndexed(propertyInfoPair.getFirst().getPropertyName());
if (indexedGetter == null) {
throw new ExprValidationException("Indexed property named '" + propertyName + "' failed to obtain getter-object");
}
return new ExprDotEvalPropertyExprIndexed(validationContext.getStatementName(), propertyDesc.getPropertyName(), streamNum, parameterEval, propertyDesc.getPropertyComponentType(), indexedGetter);
}
}
private int prefixedStreamName(List<ExprChainedSpec> chainSpec, StreamTypeService streamTypeService) {
if (chainSpec.size() < 1) {
return -1;
}
ExprChainedSpec spec = chainSpec.get(0);
if (spec.getParameters().size() > 0 && !spec.isProperty()) {
return -1;
}
return streamTypeService.getStreamNumForStreamName(spec.getName());
}
public void accept(ExprNodeVisitor visitor) {
super.accept(visitor);
ExprNodeUtility.acceptChain(visitor, chainSpec);
}
public void accept(ExprNodeVisitorWithParent visitor) {
super.accept(visitor);
ExprNodeUtility.acceptChain(visitor, chainSpec);
}
public void acceptChildnodes(ExprNodeVisitorWithParent visitor, ExprNode parent) {
super.acceptChildnodes(visitor, parent);
ExprNodeUtility.acceptChain(visitor, chainSpec, this);
}
public void replaceUnlistedChildNode(ExprNode nodeToReplace, ExprNode newNode) {
ExprNodeUtility.replaceChainChildNode(nodeToReplace, newNode, chainSpec);
}
public List<ExprChainedSpec> getChainSpec() {
return chainSpec;
}
public ExprEvaluator getExprEvaluator() {
return exprEvaluator;
}
public boolean isConstantResult() {
return isReturnsConstantResult;
}
public void toPrecedenceFreeEPL(StringWriter writer) {
if (this.getChildNodes().length != 0) {
writer.append(ExprNodeUtility.toExpressionStringMinPrecedenceSafe(this.getChildNodes()[0]));
}
ExprNodeUtility.toExpressionString(chainSpec, writer, this.getChildNodes().length != 0, null);
}
public ExprPrecedenceEnum getPrecedence() {
return ExprPrecedenceEnum.MINIMUM;
}
public Map<String, Object> getEventType() {
return null;
}
public boolean equalsNode(ExprNode node, boolean ignoreStreamPrefix) {
if (!(node instanceof ExprDotNodeImpl)) {
return false;
}
ExprDotNodeImpl other = (ExprDotNodeImpl) node;
if (other.chainSpec.size() != this.chainSpec.size()) {
return false;
}
for (int i = 0; i < chainSpec.size(); i++) {
if (!(this.chainSpec.get(i).equals(other.chainSpec.get(i)))) {
return false;
}
}
return true;
}
@Override
public List<ExprNode> getAdditionalNodes() {
return ExprNodeUtility.collectChainParameters(chainSpec);
}
public String isVariableOpGetName(VariableService variableService) {
VariableMetaData metaData = null;
if (chainSpec.size() > 0 && chainSpec.get(0).isProperty()) {
metaData = variableService.getVariableMetaData(chainSpec.get(0).getName());
}
return metaData == null ? null : metaData.getVariableName();
}
private ExprValidationException handleNotFound(String name) {
String appDotMethodDidYouMean = getAppDotMethodDidYouMean();
String message = "Unknown single-row function, expression declaration, script or aggregation function named '" + name + "' could not be resolved";
if (appDotMethodDidYouMean != null) {
message += " (did you mean '" + appDotMethodDidYouMean + "')";
}
return new ExprValidationException(message);
}
private String getAppDotMethodDidYouMean() {
String lhsName = chainSpec.get(0).getName().toLowerCase(Locale.ENGLISH);
if (lhsName.equals("rectangle")) {
return "rectangle.intersects";
}
if (lhsName.equals("point")) {
return "point.inside";
}
return null;
}
private ExprAppDotMethodImpl getAppDotMethod(boolean filterExpression) throws ExprValidationException {
if (chainSpec.size() < 2) {
return null;
}
String lhsName = chainSpec.get(0).getName().toLowerCase(Locale.ENGLISH);
String operationName = chainSpec.get(1).getName().toLowerCase(Locale.ENGLISH);
boolean pointInside = lhsName.equals("point") && operationName.equals("inside");
boolean rectangleIntersects = lhsName.equals("rectangle") && operationName.equals("intersects");
if (!pointInside && !rectangleIntersects) {
return null;
}
if (chainSpec.get(1).getParameters().size() != 1) {
throw getAppDocMethodException(lhsName, operationName);
}
ExprNode param = chainSpec.get(1).getParameters().get(0);
if (!(param instanceof ExprDotNode)) {
throw getAppDocMethodException(lhsName, operationName);
}
ExprDotNode compared = (ExprDotNode) chainSpec.get(1).getParameters().get(0);
if (compared.getChainSpec().size() != 1) {
throw getAppDocMethodException(lhsName, operationName);
}
String rhsName = compared.getChainSpec().get(0).getName().toLowerCase(Locale.ENGLISH);
boolean pointInsideRectangle = pointInside && rhsName.equals("rectangle");
boolean rectangleIntersectsRectangle = rectangleIntersects && rhsName.equals("rectangle");
if (!pointInsideRectangle && !rectangleIntersectsRectangle) {
throw getAppDocMethodException(lhsName, operationName);
}
List<ExprNode> lhsExpressions = chainSpec.get(0).getParameters();
ExprNode[] indexNamedParameter = null;
List<ExprNode> lhsExpressionsValues = new ArrayList<>();
for (ExprNode lhsExpression : lhsExpressions) {
if (lhsExpression instanceof ExprNamedParameterNode) {
ExprNamedParameterNode named = (ExprNamedParameterNode) lhsExpression;
if (named.getParameterName().toLowerCase(Locale.ENGLISH).equals(FILTERINDEX_NAMED_PARAMETER)) {
if (!filterExpression) {
throw new ExprValidationException("The '" + named.getParameterName() + "' named parameter can only be used in in filter expressions");
}
indexNamedParameter = named.getChildNodes();
} else {
throw new ExprValidationException(lhsName + " does not accept '" + named.getParameterName() + "' as a named parameter");
}
} else {
lhsExpressionsValues.add(lhsExpression);
}
}
ExprNode[] lhs = ExprNodeUtility.toArray(lhsExpressionsValues);
ExprNode[] rhs = ExprNodeUtility.toArray(compared.getChainSpec().get(0).getParameters());
EngineImportApplicationDotMethod predefined;
if (pointInsideRectangle) {
predefined = new EngineImportApplicationDotMethodPointInsideRectange(lhsName, lhs, operationName, rhsName, rhs, indexNamedParameter);
} else {
predefined = new EngineImportApplicationDotMethodRectangeIntersectsRectangle(lhsName, lhs, operationName, rhsName, rhs, indexNamedParameter);
}
return new ExprAppDotMethodImpl(predefined);
}
private ExprValidationException getAppDocMethodException(String lhsName, String operationName) {
return new ExprValidationException(lhsName + "." + operationName + " requires a single rectangle as parameter");
}
}