/**************************************************************************************
* 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.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.parse.ASTFilterSpecHelper;
import com.espertech.esper.event.property.PropertyParser;
import com.espertech.esper.filter.FilterSpecLookupable;
import java.util.Map;
/**
* 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);
}
public ExprEvaluator getExprEvaluator()
{
return evaluator;
}
public Map<String, Object> getEventType() {
return null;
}
/**
* 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());
}
public void validate(ExprValidationContext validationContext) throws ExprValidationException
{
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, resolvedPropertyName, validationContext.getStatementName(), validationContext.getStreamTypeService().getEngineURIQualifier());
}
else {
evaluator = new ExprIdentNodeEvaluatorImpl(streamNum, propertyGetter, propertyType);
}
// if running in a context, take the property value from context
if (validationContext.getContextDescriptor() != null) {
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));
}
}
}
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 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 String toExpressionString()
{
StringBuilder buffer = new StringBuilder();
if (streamOrPropertyName != null)
{
buffer.append(ASTFilterSpecHelper.unescapeDot(streamOrPropertyName)).append('.');
}
buffer.append(ASTFilterSpecHelper.unescapeDot(PropertyParser.unescapeBacktick(unresolvedPropertyName)));
return buffer.toString();
}
public boolean equalsNode(ExprNode node)
{
if (!(node instanceof ExprIdentNode))
{
return false;
}
ExprIdentNode other = (ExprIdentNode) node;
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;
}
}