/*
***************************************************************************************
* 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.core;
import com.espertech.esper.client.EventPropertyGetter;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.PropertyAccessException;
import com.espertech.esper.client.annotation.Audit;
import com.espertech.esper.client.annotation.AuditEnum;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.epl.core.PropertyResolutionDescriptor;
import com.espertech.esper.epl.expression.table.ExprTableIdentNode;
import com.espertech.esper.epl.parse.ASTUtil;
import com.espertech.esper.event.property.PropertyParser;
import com.espertech.esper.filter.FilterSpecLookupable;
import java.io.StringWriter;
/**
* Represents an stream property identifier in a filter expressiun tree.
*/
public class ExprIdentNodeImpl extends ExprNodeBase implements ExprIdentNode {
private static final long serialVersionUID = 5882493771230745244L;
// select myprop from... is a simple property, no stream supplied
// select s0.myprop from... is a simple property with a stream supplied, or a nested property (cannot tell until resolved)
// select indexed[1] from ... is a indexed property
private final String unresolvedPropertyName;
private String streamOrPropertyName;
private String resolvedStreamName;
private String resolvedPropertyName;
private transient ExprIdentNodeEvaluator evaluator;
/**
* Ctor.
*
* @param unresolvedPropertyName is the event property name in unresolved form, ie. unvalidated against streams
*/
public ExprIdentNodeImpl(String unresolvedPropertyName) {
if (unresolvedPropertyName == null) {
throw new IllegalArgumentException("Property name is null");
}
this.unresolvedPropertyName = unresolvedPropertyName;
this.streamOrPropertyName = null;
}
/**
* Ctor.
*
* @param unresolvedPropertyName is the event property name in unresolved form, ie. unvalidated against streams
* @param streamOrPropertyName is the stream name, or if not a valid stream name a possible nested property name
* in one of the streams.
*/
public ExprIdentNodeImpl(String unresolvedPropertyName, String streamOrPropertyName) {
if (unresolvedPropertyName == null) {
throw new IllegalArgumentException("Property name is null");
}
if (streamOrPropertyName == null) {
throw new IllegalArgumentException("Stream (or property name) name is null");
}
this.unresolvedPropertyName = unresolvedPropertyName;
this.streamOrPropertyName = streamOrPropertyName;
}
public ExprIdentNodeImpl(EventType eventType, String propertyName, int streamNumber) {
unresolvedPropertyName = propertyName;
resolvedPropertyName = propertyName;
EventPropertyGetter propertyGetter = eventType.getGetter(propertyName);
if (propertyGetter == null) {
throw new IllegalArgumentException("Ident-node constructor could not locate property " + propertyName);
}
Class propertyType = eventType.getPropertyType(propertyName);
evaluator = new ExprIdentNodeEvaluatorImpl(streamNumber, propertyGetter, propertyType, this);
}
public ExprEvaluator getExprEvaluator() {
return evaluator;
}
/**
* For unit testing, returns unresolved property name.
*
* @return property name
*/
public String getUnresolvedPropertyName() {
return unresolvedPropertyName;
}
/**
* For unit testing, returns stream or property name candidate.
*
* @return stream name, or property name of a nested property of one of the streams
*/
public String getStreamOrPropertyName() {
return streamOrPropertyName;
}
/**
* Set name.
*
* @param streamOrPropertyName to use
*/
public void setStreamOrPropertyName(String streamOrPropertyName) {
this.streamOrPropertyName = streamOrPropertyName;
}
/**
* Returns the unresolved property name in it's complete form, including
* the stream name if there is one.
*
* @return property name
*/
public String getFullUnresolvedName() {
if (streamOrPropertyName == null) {
return unresolvedPropertyName;
} else {
return streamOrPropertyName + "." + unresolvedPropertyName;
}
}
public boolean getFilterLookupEligible() {
return evaluator.getStreamNum() == 0 && !(evaluator.isContextEvaluated());
}
public FilterSpecLookupable getFilterLookupable() {
return new FilterSpecLookupable(resolvedPropertyName, evaluator.getGetter(), evaluator.getType(), false);
}
public ExprNode validate(ExprValidationContext validationContext) throws ExprValidationException {
// rewrite expression into a table-access expression
if (validationContext.getStreamTypeService().hasTableTypes()) {
ExprTableIdentNode tableIdentNode = validationContext.getTableService().getTableIdentNode(validationContext.getStreamTypeService(), unresolvedPropertyName, streamOrPropertyName);
if (tableIdentNode != null) {
return tableIdentNode;
}
}
String unescapedPropertyName = PropertyParser.unescapeBacktick(unresolvedPropertyName);
Pair<PropertyResolutionDescriptor, String> propertyInfoPair = ExprIdentNodeUtil.getTypeFromStream(validationContext.getStreamTypeService(), unescapedPropertyName, streamOrPropertyName, false);
resolvedStreamName = propertyInfoPair.getSecond();
int streamNum = propertyInfoPair.getFirst().getStreamNum();
Class propertyType = propertyInfoPair.getFirst().getPropertyType();
resolvedPropertyName = propertyInfoPair.getFirst().getPropertyName();
EventPropertyGetter propertyGetter;
try {
propertyGetter = propertyInfoPair.getFirst().getStreamEventType().getGetter(resolvedPropertyName);
} catch (PropertyAccessException ex) {
throw new ExprValidationException("Property '" + unresolvedPropertyName + "' is not valid: " + ex.getMessage(), ex);
}
if (propertyGetter == null) {
throw new ExprValidationException("Property getter returned was invalid for property '" + unresolvedPropertyName + "'");
}
Audit audit = AuditEnum.PROPERTY.getAudit(validationContext.getAnnotations());
if (audit != null) {
evaluator = new ExprIdentNodeEvaluatorLogging(streamNum, propertyGetter, propertyType, this, resolvedPropertyName, validationContext.getStatementName(), validationContext.getStreamTypeService().getEngineURIQualifier());
} else {
evaluator = new ExprIdentNodeEvaluatorImpl(streamNum, propertyGetter, propertyType, this);
}
// if running in a context, take the property value from context
if (validationContext.getContextDescriptor() != null && !validationContext.isFilterExpression()) {
EventType fromType = validationContext.getStreamTypeService().getEventTypes()[streamNum];
String contextPropertyName = validationContext.getContextDescriptor().getContextPropertyRegistry().getPartitionContextPropertyName(fromType, resolvedPropertyName);
if (contextPropertyName != null) {
EventType contextType = validationContext.getContextDescriptor().getContextPropertyRegistry().getContextEventType();
evaluator = new ExprIdentNodeEvaluatorContext(streamNum, contextType.getPropertyType(contextPropertyName), contextType.getGetter(contextPropertyName));
}
}
return null;
}
public boolean isConstantResult() {
return false;
}
/**
* Returns stream id supplying the property value.
*
* @return stream number
*/
public int getStreamId() {
if (evaluator == null) {
throw new IllegalStateException("Identifier expression has not been validated");
}
return evaluator.getStreamNum();
}
public Integer getStreamReferencedIfAny() {
return getStreamId();
}
public String getRootPropertyNameIfAny() {
return getResolvedPropertyNameRoot();
}
public Class getType() {
if (evaluator == null) {
throw new IllegalStateException("Identifier expression has not been validated");
}
return evaluator.getType();
}
/**
* Returns stream name as resolved by lookup of property in streams.
*
* @return stream name
*/
public String getResolvedStreamName() {
if (resolvedStreamName == null) {
throw new IllegalStateException("Identifier node has not been validated");
}
return resolvedStreamName;
}
/**
* Return property name as resolved by lookup in streams.
*
* @return property name
*/
public String getResolvedPropertyName() {
if (resolvedPropertyName == null) {
throw new IllegalStateException("Identifier node has not been validated");
}
return resolvedPropertyName;
}
/**
* Returns the root of the resolved property name, if any.
*
* @return root
*/
public String getResolvedPropertyNameRoot() {
if (resolvedPropertyName == null) {
throw new IllegalStateException("Identifier node has not been validated");
}
if (resolvedPropertyName.indexOf('[') != -1) {
return resolvedPropertyName.substring(0, resolvedPropertyName.indexOf('['));
}
if (resolvedPropertyName.indexOf('(') != -1) {
return resolvedPropertyName.substring(0, resolvedPropertyName.indexOf('('));
}
if (resolvedPropertyName.indexOf('.') != -1) {
return resolvedPropertyName.substring(0, resolvedPropertyName.indexOf('.'));
}
return resolvedPropertyName;
}
public String toString() {
return "unresolvedPropertyName=" + unresolvedPropertyName +
" streamOrPropertyName=" + streamOrPropertyName +
" resolvedPropertyName=" + resolvedPropertyName;
}
public void toPrecedenceFreeEPL(StringWriter writer) {
toPrecedenceFreeEPL(writer, streamOrPropertyName, unresolvedPropertyName);
}
public static void toPrecedenceFreeEPL(StringWriter writer, String streamOrPropertyName, String unresolvedPropertyName) {
if (streamOrPropertyName != null) {
writer.append(ASTUtil.unescapeDot(streamOrPropertyName)).append('.');
}
writer.append(ASTUtil.unescapeDot(PropertyParser.unescapeBacktick(unresolvedPropertyName)));
}
public ExprPrecedenceEnum getPrecedence() {
return ExprPrecedenceEnum.UNARY;
}
public boolean equalsNode(ExprNode node, boolean ignoreStreamPrefix) {
if (!(node instanceof ExprIdentNode)) {
return false;
}
ExprIdentNode other = (ExprIdentNode) node;
if (ignoreStreamPrefix && resolvedPropertyName != null && other.getResolvedPropertyName() != null && resolvedPropertyName.equals(other.getResolvedPropertyName())) {
return true;
}
if (streamOrPropertyName != null ? !streamOrPropertyName.equals(other.getStreamOrPropertyName()) : other.getStreamOrPropertyName() != null)
return false;
if (unresolvedPropertyName != null ? !unresolvedPropertyName.equals(other.getUnresolvedPropertyName()) : other.getUnresolvedPropertyName() != null)
return false;
return true;
}
public ExprIdentNodeEvaluator getExprEvaluatorIdent() {
return evaluator;
}
}