/*
***************************************************************************************
* 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.property;
import com.espertech.esper.client.*;
import com.espertech.esper.core.service.StatementExtensionSvcContext;
import com.espertech.esper.epl.core.*;
import com.espertech.esper.epl.expression.core.*;
import com.espertech.esper.epl.named.NamedWindowMgmtService;
import com.espertech.esper.epl.spec.*;
import com.espertech.esper.epl.table.mgmt.TableService;
import com.espertech.esper.epl.variable.VariableService;
import com.espertech.esper.event.*;
import com.espertech.esper.schedule.TimeProvider;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.UuidGenerator;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* Factory for property evaluators.
*/
public class PropertyEvaluatorFactory {
public static PropertyEvaluator makeEvaluator(PropertyEvalSpec spec,
EventType sourceEventType,
String optionalSourceStreamName,
EventAdapterService eventAdapterService,
EngineImportService engineImportService,
final TimeProvider timeProvider,
VariableService variableService,
TableService tableService,
String engineURI,
int statementId,
String statementName,
Annotation[] annotations,
Collection<Integer> assignedTypeNumberStack,
ConfigurationInformation configuration,
NamedWindowMgmtService namedWindowMgmtService,
StatementExtensionSvcContext statementExtensionSvcContext)
throws ExprValidationException {
int length = spec.getAtoms().size();
ContainedEventEval[] containedEventEvals = new ContainedEventEval[length];
FragmentEventType[] fragmentEventTypes = new FragmentEventType[length];
EventType currentEventType = sourceEventType;
ExprEvaluator[] whereClauses = new ExprEvaluator[length];
List<EventType> streamEventTypes = new ArrayList<EventType>();
List<String> streamNames = new ArrayList<String>();
Map<String, Integer> streamNameAndNumber = new HashMap<String, Integer>();
List<String> expressionTexts = new ArrayList<String>();
ExprEvaluatorContext validateContext = new ExprEvaluatorContextTimeOnly(timeProvider);
streamEventTypes.add(sourceEventType);
streamNames.add(optionalSourceStreamName);
streamNameAndNumber.put(optionalSourceStreamName, 0);
expressionTexts.add(sourceEventType.getName());
List<SelectClauseElementCompiled> cumulativeSelectClause = new ArrayList<SelectClauseElementCompiled>();
for (int i = 0; i < length; i++) {
PropertyEvalAtom atom = spec.getAtoms().get(i);
ContainedEventEval containedEventEval = null;
String expressionText = null;
EventType streamEventType = null;
FragmentEventType fragmentEventType = null;
// Resolve directly as fragment event type if possible
if (atom.getSplitterExpression() instanceof ExprIdentNode) {
String propertyName = ((ExprIdentNode) atom.getSplitterExpression()).getFullUnresolvedName();
fragmentEventType = currentEventType.getFragmentType(propertyName);
if (fragmentEventType != null) {
EventPropertyGetter getter = currentEventType.getGetter(propertyName);
if (getter != null) {
containedEventEval = new ContainedEventEvalGetter(getter);
expressionText = propertyName;
streamEventType = fragmentEventType.getFragmentType();
}
}
}
// evaluate splitter expression
if (containedEventEval == null) {
ExprNodeUtility.validatePlainExpression(ExprNodeOrigin.CONTAINEDEVENT, atom.getSplitterExpression());
EventType[] availableTypes = streamEventTypes.toArray(new EventType[streamEventTypes.size()]);
String[] availableStreamNames = streamNames.toArray(new String[streamNames.size()]);
boolean[] isIStreamOnly = new boolean[streamNames.size()];
Arrays.fill(isIStreamOnly, true);
StreamTypeService streamTypeService = new StreamTypeServiceImpl(availableTypes, availableStreamNames, isIStreamOnly, engineURI, false);
ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, validateContext, eventAdapterService, statementName, statementId, annotations, null, false, false, true, false, null, false);
ExprNode validatedExprNode = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.CONTAINEDEVENT, atom.getSplitterExpression(), validationContext);
ExprEvaluator evaluator = validatedExprNode.getExprEvaluator();
// determine result type
if (atom.getOptionalResultEventType() == null) {
throw new ExprValidationException("Missing @type(name) declaration providing the event type name of the return type for expression '" +
ExprNodeUtility.toExpressionStringMinPrecedenceSafe(atom.getSplitterExpression()) + "'");
}
streamEventType = eventAdapterService.getExistsTypeByName(atom.getOptionalResultEventType());
if (streamEventType == null) {
throw new ExprValidationException("Event type by name '" + atom.getOptionalResultEventType() + "' could not be found");
}
Class returnType = evaluator.getType();
// when the expression returns an array, allow array values to become the column of the single-column event type
if (returnType.isArray() &&
streamEventType.getPropertyNames().length == 1 &&
JavaClassHelper.isSubclassOrImplementsInterface(JavaClassHelper.getBoxedType(returnType.getComponentType()), JavaClassHelper.getBoxedType(streamEventType.getPropertyType(streamEventType.getPropertyNames()[0])))) {
Set<WriteablePropertyDescriptor> writables = eventAdapterService.getWriteableProperties(streamEventType, false);
if (!writables.isEmpty()) {
try {
EventBeanManufacturer manufacturer = EventAdapterServiceHelper.getManufacturer(eventAdapterService, streamEventType, new WriteablePropertyDescriptor[]{writables.iterator().next()}, engineImportService, false, eventAdapterService.getEventAdapterAvroHandler());
containedEventEval = new ContainedEventEvalArrayToEvent(evaluator, manufacturer);
} catch (EventBeanManufactureException e) {
throw new ExprValidationException("Event type '" + streamEventType.getName() + "' cannot be populated: " + e.getMessage(), e);
}
} else {
throw new ExprValidationException("Event type '" + streamEventType.getName() + "' cannot be written to");
}
} else if (returnType.isArray() &&
returnType.getComponentType() == EventBean.class) {
containedEventEval = new ContainedEventEvalEventBeanArray(evaluator);
} else {
EventBeanFactory eventBeanFactory = EventAdapterServiceHelper.getFactoryForType(streamEventType, eventAdapterService);
// check expression result type against eventtype expected underlying type
if (returnType.isArray()) {
if (!JavaClassHelper.isSubclassOrImplementsInterface(returnType.getComponentType(), streamEventType.getUnderlyingType())) {
throw new ExprValidationException("Event type '" + streamEventType.getName() + "' underlying type " + streamEventType.getUnderlyingType().getName() +
" cannot be assigned a value of type " + JavaClassHelper.getClassNameFullyQualPretty(returnType));
}
} else if (JavaClassHelper.isImplementsInterface(returnType, Iterable.class)) {
// fine, assumed to return the right type
} else {
throw new ExprValidationException("Return type of expression '" + ExprNodeUtility.toExpressionStringMinPrecedenceSafe(atom.getSplitterExpression()) + "' is '" + returnType.getName() + "', expected an Iterable or array result");
}
containedEventEval = new ContainedEventEvalExprNode(evaluator, eventBeanFactory);
}
expressionText = ExprNodeUtility.toExpressionStringMinPrecedenceSafe(validatedExprNode);
fragmentEventType = new FragmentEventType(streamEventType, true, false);
}
// validate where clause, if any
streamEventTypes.add(streamEventType);
streamNames.add(atom.getOptionalAsName());
streamNameAndNumber.put(atom.getOptionalAsName(), i + 1);
expressionTexts.add(expressionText);
if (atom.getOptionalWhereClause() != null) {
EventType[] whereTypes = streamEventTypes.toArray(new EventType[streamEventTypes.size()]);
String[] whereStreamNames = streamNames.toArray(new String[streamNames.size()]);
boolean[] isIStreamOnly = new boolean[streamNames.size()];
Arrays.fill(isIStreamOnly, true);
StreamTypeService streamTypeService = new StreamTypeServiceImpl(whereTypes, whereStreamNames, isIStreamOnly, engineURI, false);
ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, validateContext, eventAdapterService, statementName, statementId, annotations, null, false, false, true, false, null, false);
whereClauses[i] = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.CONTAINEDEVENT, atom.getOptionalWhereClause(), validationContext).getExprEvaluator();
}
// validate select clause
if (atom.getOptionalSelectClause() != null) {
EventType[] whereTypes = streamEventTypes.toArray(new EventType[streamEventTypes.size()]);
String[] whereStreamNames = streamNames.toArray(new String[streamNames.size()]);
boolean[] isIStreamOnly = new boolean[streamNames.size()];
Arrays.fill(isIStreamOnly, true);
StreamTypeService streamTypeService = new StreamTypeServiceImpl(whereTypes, whereStreamNames, isIStreamOnly, engineURI, false);
ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, validateContext, eventAdapterService, statementName, statementId, annotations, null, false, false, true, false, null, false);
for (SelectClauseElementRaw raw : atom.getOptionalSelectClause().getSelectExprList()) {
if (raw instanceof SelectClauseStreamRawSpec) {
SelectClauseStreamRawSpec rawStreamSpec = (SelectClauseStreamRawSpec) raw;
if (!streamNames.contains(rawStreamSpec.getStreamName())) {
throw new ExprValidationException("Property rename '" + rawStreamSpec.getStreamName() + "' not found in path");
}
SelectClauseStreamCompiledSpec streamSpec = new SelectClauseStreamCompiledSpec(rawStreamSpec.getStreamName(), rawStreamSpec.getOptionalAsName());
int streamNumber = streamNameAndNumber.get(rawStreamSpec.getStreamName());
streamSpec.setStreamNumber(streamNumber);
cumulativeSelectClause.add(streamSpec);
} else if (raw instanceof SelectClauseExprRawSpec) {
SelectClauseExprRawSpec exprSpec = (SelectClauseExprRawSpec) raw;
ExprNode exprCompiled = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.CONTAINEDEVENT, exprSpec.getSelectExpression(), validationContext);
String resultName = exprSpec.getOptionalAsName();
if (resultName == null) {
resultName = ExprNodeUtility.toExpressionStringMinPrecedenceSafe(exprCompiled);
}
cumulativeSelectClause.add(new SelectClauseExprCompiledSpec(exprCompiled, resultName, exprSpec.getOptionalAsName(), exprSpec.isEvents()));
String isMinimal = ExprNodeUtility.isMinimalExpression(exprCompiled);
if (isMinimal != null) {
throw new ExprValidationException("Expression in a property-selection may not utilize " + isMinimal);
}
} else if (raw instanceof SelectClauseElementWildcard) {
// wildcards are stream selects: we assign a stream name (any) and add a stream wildcard select
String streamNameAtom = atom.getOptionalAsName();
if (streamNameAtom == null) {
streamNameAtom = UuidGenerator.generate();
}
SelectClauseStreamCompiledSpec streamSpec = new SelectClauseStreamCompiledSpec(streamNameAtom, atom.getOptionalAsName());
int streamNumber = i + 1;
streamSpec.setStreamNumber(streamNumber);
cumulativeSelectClause.add(streamSpec);
} else {
throw new IllegalStateException("Unknown select clause item:" + raw);
}
}
}
currentEventType = fragmentEventType.getFragmentType();
fragmentEventTypes[i] = fragmentEventType;
containedEventEvals[i] = containedEventEval;
}
if (cumulativeSelectClause.isEmpty()) {
if (length == 1) {
return new PropertyEvaluatorSimple(containedEventEvals[0], fragmentEventTypes[0], whereClauses[0], expressionTexts.get(0));
} else {
return new PropertyEvaluatorNested(containedEventEvals, fragmentEventTypes, whereClauses, expressionTexts);
}
} else {
PropertyEvaluatorAccumulative accumulative = new PropertyEvaluatorAccumulative(containedEventEvals, fragmentEventTypes, whereClauses, expressionTexts);
EventType[] whereTypes = streamEventTypes.toArray(new EventType[streamEventTypes.size()]);
String[] whereStreamNames = streamNames.toArray(new String[streamNames.size()]);
boolean[] isIStreamOnly = new boolean[streamNames.size()];
Arrays.fill(isIStreamOnly, true);
StreamTypeService streamTypeService = new StreamTypeServiceImpl(whereTypes, whereStreamNames, isIStreamOnly, engineURI, false);
SelectClauseElementCompiled[] cumulativeSelectArr = cumulativeSelectClause.toArray(new SelectClauseElementCompiled[cumulativeSelectClause.size()]);
SelectExprProcessor selectExpr = SelectExprProcessorFactory.getProcessor(assignedTypeNumberStack, cumulativeSelectArr, false, null, null, null, streamTypeService, eventAdapterService, null, null, null, engineImportService, validateContext, variableService, tableService, timeProvider, engineURI, statementId, statementName, annotations, null, configuration, null, namedWindowMgmtService, null, null, statementExtensionSvcContext);
return new PropertyEvaluatorSelect(selectExpr, accumulative);
}
}
}