/*
***************************************************************************************
* 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.core;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventPropertyDescriptor;
import com.espertech.esper.client.EventType;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.epl.expression.core.ExprEvaluator;
import com.espertech.esper.epl.expression.core.ExprEvaluatorContext;
import com.espertech.esper.epl.expression.core.ExprValidationException;
import com.espertech.esper.epl.spec.InsertIntoDesc;
import com.espertech.esper.event.*;
import com.espertech.esper.event.arr.ObjectArrayEventType;
import com.espertech.esper.event.avro.AvroSchemaEventType;
import com.espertech.esper.event.bean.BeanEventType;
import com.espertech.esper.event.bean.EventBeanManufacturerCtor;
import com.espertech.esper.event.bean.InstanceManufacturerUtil;
import com.espertech.esper.event.map.MapEventType;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.TypeWidener;
import com.espertech.esper.util.TypeWidenerCustomizer;
import com.espertech.esper.util.TypeWidenerFactory;
import net.sf.cglib.reflect.FastConstructor;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SelectExprInsertEventBeanFactory {
public static SelectExprProcessor getInsertUnderlyingNonJoin(EventAdapterService eventAdapterService,
EventType eventType,
boolean isUsingWildcard,
StreamTypeService typeService,
ExprEvaluator[] expressionNodes,
String[] columnNames,
Object[] expressionReturnTypes,
EngineImportService engineImportService,
InsertIntoDesc insertIntoDesc,
String[] columnNamesAsProvided,
boolean allowNestableTargetFragmentTypes,
String statementName)
throws ExprValidationException {
// handle single-column coercion to underlying, i.e. "insert into MapDefinedEvent select doSomethingReturnMap() from MyEvent"
if (expressionReturnTypes.length == 1 &&
expressionReturnTypes[0] instanceof Class &&
(eventType instanceof BaseNestableEventType || eventType instanceof AvroSchemaEventType) &&
JavaClassHelper.isSubclassOrImplementsInterface((Class) expressionReturnTypes[0], eventType.getUnderlyingType()) &&
insertIntoDesc.getColumnNames().isEmpty() &&
columnNamesAsProvided[0] == null) {
if (eventType instanceof MapEventType) {
return new SelectExprInsertNativeExpressionCoerceMap(eventType, expressionNodes[0], eventAdapterService);
} else if (eventType instanceof ObjectArrayEventType) {
return new SelectExprInsertNativeExpressionCoerceObjectArray(eventType, expressionNodes[0], eventAdapterService);
} else if (eventType instanceof AvroSchemaEventType) {
return new SelectExprInsertNativeExpressionCoerceAvro(eventType, expressionNodes[0], eventAdapterService);
} else {
throw new IllegalStateException("Unrecognied event type " + eventType);
}
}
// handle special case where the target type has no properties and there is a single "null" value selected
if (eventType.getPropertyDescriptors().length == 0 &&
columnNames.length == 1 &&
columnNames[0].equals("null") &&
expressionReturnTypes[0] == null &&
!isUsingWildcard) {
EventBeanManufacturer eventManufacturer;
try {
eventManufacturer = eventAdapterService.getManufacturer(eventType, new WriteablePropertyDescriptor[0], engineImportService, true);
} catch (EventBeanManufactureException e) {
throw new ExprValidationException(e.getMessage(), e);
}
return new SelectExprInsertNativeNoEval(eventType, eventManufacturer);
}
// handle writing to defined columns
Set<WriteablePropertyDescriptor> writableProps = eventAdapterService.getWriteableProperties(eventType, false);
boolean isEligible = checkEligible(eventType, writableProps, allowNestableTargetFragmentTypes);
if (!isEligible) {
return null;
}
try {
return initializeSetterManufactor(eventType, writableProps, isUsingWildcard, typeService, expressionNodes, columnNames, expressionReturnTypes, engineImportService, eventAdapterService, statementName);
} catch (ExprValidationException ex) {
if (!(eventType instanceof BeanEventType)) {
throw ex;
}
// Try constructor injection
try {
return initializeCtorInjection((BeanEventType) eventType, expressionNodes, expressionReturnTypes, engineImportService, eventAdapterService);
} catch (ExprValidationException ctorEx) {
if (writableProps.isEmpty()) {
throw ctorEx;
}
throw ex;
}
}
}
public static SelectExprProcessor getInsertUnderlyingJoinWildcard(EventAdapterService eventAdapterService, EventType eventType,
String[] streamNames, EventType[] streamTypes, EngineImportService engineImportService, String statementName, String engineURI)
throws ExprValidationException {
Set<WriteablePropertyDescriptor> writableProps = eventAdapterService.getWriteableProperties(eventType, false);
boolean isEligible = checkEligible(eventType, writableProps, false);
if (!isEligible) {
return null;
}
try {
return initializeJoinWildcardInternal(eventType, writableProps, streamNames, streamTypes, engineImportService, eventAdapterService, statementName, engineURI);
} catch (ExprValidationException ex) {
if (!(eventType instanceof BeanEventType)) {
throw ex;
}
// Try constructor injection
try {
ExprEvaluator[] evaluators = new ExprEvaluator[streamTypes.length];
Object[] resultTypes = new Object[streamTypes.length];
for (int i = 0; i < streamTypes.length; i++) {
evaluators[i] = new ExprEvaluatorJoinWildcard(i, streamTypes[i].getUnderlyingType());
resultTypes[i] = evaluators[i].getType();
}
return initializeCtorInjection((BeanEventType) eventType, evaluators, resultTypes, engineImportService, eventAdapterService);
} catch (ExprValidationException ctorEx) {
if (writableProps.isEmpty()) {
throw ctorEx;
}
throw ex;
}
}
}
private static boolean checkEligible(EventType eventType, Set<WriteablePropertyDescriptor> writableProps, boolean allowNestableTargetFragmentTypes) {
if (writableProps == null) {
return false; // no writable properties, not a writable type, proceed
}
// For map event types this class does not handle fragment inserts; all fragments are required however and must be explicit
if (!allowNestableTargetFragmentTypes && (eventType instanceof BaseNestableEventType || eventType instanceof AvroSchemaEventType)) {
for (EventPropertyDescriptor prop : eventType.getPropertyDescriptors()) {
if (prop.isFragment()) {
return false;
}
}
}
return true;
}
private static SelectExprProcessor initializeSetterManufactor(EventType eventType, Set<WriteablePropertyDescriptor> writables, boolean isUsingWildcard, StreamTypeService typeService, ExprEvaluator[] expressionNodes, String[] columnNames, Object[] expressionReturnTypes, EngineImportService engineImportService, EventAdapterService eventAdapterService, String statementName)
throws ExprValidationException {
TypeWidenerCustomizer typeWidenerCustomizer = eventAdapterService.getTypeWidenerCustomizer(eventType);
List<WriteablePropertyDescriptor> writablePropertiesList = new ArrayList<WriteablePropertyDescriptor>();
List<ExprEvaluator> evaluatorsList = new ArrayList<ExprEvaluator>();
List<TypeWidener> widenersList = new ArrayList<TypeWidener>();
// loop over all columns selected, if any
for (int i = 0; i < columnNames.length; i++) {
WriteablePropertyDescriptor selectedWritable = null;
TypeWidener widener = null;
ExprEvaluator evaluator = expressionNodes[i];
for (WriteablePropertyDescriptor desc : writables) {
if (!desc.getPropertyName().equals(columnNames[i])) {
continue;
}
Object columnType = expressionReturnTypes[i];
if (columnType == null) {
TypeWidenerFactory.getCheckPropertyAssignType(columnNames[i], null, desc.getType(), desc.getPropertyName(), false, typeWidenerCustomizer, statementName, typeService.getEngineURIQualifier());
} else if (columnType instanceof EventType) {
EventType columnEventType = (EventType) columnType;
final Class returnType = columnEventType.getUnderlyingType();
widener = TypeWidenerFactory.getCheckPropertyAssignType(columnNames[i], columnEventType.getUnderlyingType(), desc.getType(), desc.getPropertyName(), false, typeWidenerCustomizer, statementName, typeService.getEngineURIQualifier());
// handle evaluator returning an event
if (JavaClassHelper.isSubclassOrImplementsInterface(returnType, desc.getType())) {
selectedWritable = desc;
widener = new TypeWidener() {
public Object widen(Object input) {
if (input instanceof EventBean) {
return ((EventBean) input).getUnderlying();
}
return input;
}
};
continue;
}
// find stream
int streamNum = 0;
for (int j = 0; j < typeService.getEventTypes().length; j++) {
if (typeService.getEventTypes()[j] == columnEventType) {
streamNum = j;
break;
}
}
final int streamNumEval = streamNum;
evaluator = new ExprEvaluator() {
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
EventBean theEvent = eventsPerStream[streamNumEval];
if (theEvent != null) {
return theEvent.getUnderlying();
}
return null;
}
public Class getType() {
return returnType;
}
};
} else if (columnType instanceof EventType[]) {
// handle case where the select-clause contains an fragment array
EventType columnEventType = ((EventType[]) columnType)[0];
final Class componentReturnType = columnEventType.getUnderlyingType();
final Class arrayReturnType = Array.newInstance(componentReturnType, 0).getClass();
boolean allowObjectArrayToCollectionConversion = eventType instanceof AvroSchemaEventType;
widener = TypeWidenerFactory.getCheckPropertyAssignType(columnNames[i], arrayReturnType, desc.getType(), desc.getPropertyName(), allowObjectArrayToCollectionConversion, typeWidenerCustomizer, statementName, typeService.getEngineURIQualifier());
final ExprEvaluator inner = evaluator;
evaluator = new ExprEvaluator() {
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
Object result = inner.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (!(result instanceof EventBean[])) {
return null;
}
EventBean[] events = (EventBean[]) result;
Object values = Array.newInstance(componentReturnType, events.length);
for (int i = 0; i < events.length; i++) {
Array.set(values, i, events[i].getUnderlying());
}
return values;
}
public Class getType() {
return componentReturnType;
}
};
} else if (!(columnType instanceof Class)) {
String message = "Invalid assignment of column '" + columnNames[i] +
"' of type '" + columnType +
"' to event property '" + desc.getPropertyName() +
"' typed as '" + desc.getType().getName() +
"', column and parameter types mismatch";
throw new ExprValidationException(message);
} else {
widener = TypeWidenerFactory.getCheckPropertyAssignType(columnNames[i], (Class) columnType, desc.getType(), desc.getPropertyName(), false, typeWidenerCustomizer, statementName, typeService.getEngineURIQualifier());
}
selectedWritable = desc;
break;
}
if (selectedWritable == null) {
String message = "Column '" + columnNames[i] +
"' could not be assigned to any of the properties of the underlying type (missing column names, event property, setter method or constructor?)";
throw new ExprValidationException(message);
}
// add
writablePropertiesList.add(selectedWritable);
evaluatorsList.add(evaluator);
widenersList.add(widener);
}
// handle wildcard
if (isUsingWildcard) {
EventType sourceType = typeService.getEventTypes()[0];
for (EventPropertyDescriptor eventPropDescriptor : sourceType.getPropertyDescriptors()) {
if (eventPropDescriptor.isRequiresIndex() || (eventPropDescriptor.isRequiresMapkey())) {
continue;
}
WriteablePropertyDescriptor selectedWritable = null;
TypeWidener widener = null;
ExprEvaluator evaluator = null;
for (WriteablePropertyDescriptor writableDesc : writables) {
if (!writableDesc.getPropertyName().equals(eventPropDescriptor.getPropertyName())) {
continue;
}
widener = TypeWidenerFactory.getCheckPropertyAssignType(eventPropDescriptor.getPropertyName(), eventPropDescriptor.getPropertyType(), writableDesc.getType(), writableDesc.getPropertyName(), false, typeWidenerCustomizer, statementName, typeService.getEngineURIQualifier());
selectedWritable = writableDesc;
final String propertyName = eventPropDescriptor.getPropertyName();
final Class propertyType = eventPropDescriptor.getPropertyType();
evaluator = new ExprEvaluator() {
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
EventBean theEvent = eventsPerStream[0];
if (theEvent != null) {
return theEvent.get(propertyName);
}
return null;
}
public Class getType() {
return propertyType;
}
};
break;
}
if (selectedWritable == null) {
String message = "Event property '" + eventPropDescriptor.getPropertyName() +
"' could not be assigned to any of the properties of the underlying type (missing column names, event property, setter method or constructor?)";
throw new ExprValidationException(message);
}
writablePropertiesList.add(selectedWritable);
evaluatorsList.add(evaluator);
widenersList.add(widener);
}
}
// assign
WriteablePropertyDescriptor[] writableProperties = writablePropertiesList.toArray(new WriteablePropertyDescriptor[writablePropertiesList.size()]);
ExprEvaluator[] exprEvaluators = evaluatorsList.toArray(new ExprEvaluator[evaluatorsList.size()]);
TypeWidener[] wideners = widenersList.toArray(new TypeWidener[widenersList.size()]);
EventBeanManufacturer eventManufacturer;
try {
eventManufacturer = eventAdapterService.getManufacturer(eventType, writableProperties, engineImportService, false);
} catch (EventBeanManufactureException e) {
throw new ExprValidationException(e.getMessage(), e);
}
return new SelectExprInsertNativeWidening(eventType, eventManufacturer, exprEvaluators, wideners);
}
private static SelectExprProcessor initializeCtorInjection(BeanEventType beanEventType, ExprEvaluator[] exprEvaluators, Object[] expressionReturnTypes, EngineImportService engineImportService, EventAdapterService eventAdapterService)
throws ExprValidationException {
Pair<FastConstructor, ExprEvaluator[]> pair = InstanceManufacturerUtil.getManufacturer(beanEventType.getUnderlyingType(), engineImportService, exprEvaluators, expressionReturnTypes);
EventBeanManufacturerCtor eventManufacturer = new EventBeanManufacturerCtor(pair.getFirst(), beanEventType, eventAdapterService);
return new SelectExprInsertNativeNoWiden(beanEventType, eventManufacturer, pair.getSecond());
}
private static SelectExprProcessor initializeJoinWildcardInternal(EventType eventType, Set<WriteablePropertyDescriptor> writables, String[] streamNames, EventType[] streamTypes, EngineImportService engineImportService, EventAdapterService eventAdapterService, String statementName, String engineURI)
throws ExprValidationException {
TypeWidenerCustomizer typeWidenerCustomizer = eventAdapterService.getTypeWidenerCustomizer(eventType);
List<WriteablePropertyDescriptor> writablePropertiesList = new ArrayList<WriteablePropertyDescriptor>();
List<ExprEvaluator> evaluatorsList = new ArrayList<ExprEvaluator>();
List<TypeWidener> widenersList = new ArrayList<TypeWidener>();
// loop over all columns selected, if any
for (int i = 0; i < streamNames.length; i++) {
WriteablePropertyDescriptor selectedWritable = null;
TypeWidener widener = null;
for (WriteablePropertyDescriptor desc : writables) {
if (!desc.getPropertyName().equals(streamNames[i])) {
continue;
}
widener = TypeWidenerFactory.getCheckPropertyAssignType(streamNames[i], streamTypes[i].getUnderlyingType(), desc.getType(), desc.getPropertyName(), false, typeWidenerCustomizer, statementName, engineURI);
selectedWritable = desc;
break;
}
if (selectedWritable == null) {
String message = "Stream underlying object for stream '" + streamNames[i] +
"' could not be assigned to any of the properties of the underlying type (missing column names, event property or setter method?)";
throw new ExprValidationException(message);
}
final int streamNum = i;
final Class returnType = streamTypes[streamNum].getUnderlyingType();
ExprEvaluator evaluator = new ExprEvaluator() {
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
EventBean theEvent = eventsPerStream[streamNum];
if (theEvent != null) {
return theEvent.getUnderlying();
}
return null;
}
public Class getType() {
return returnType;
}
};
// add
writablePropertiesList.add(selectedWritable);
evaluatorsList.add(evaluator);
widenersList.add(widener);
}
// assign
WriteablePropertyDescriptor[] writableProperties = writablePropertiesList.toArray(new WriteablePropertyDescriptor[writablePropertiesList.size()]);
ExprEvaluator[] exprEvaluators = evaluatorsList.toArray(new ExprEvaluator[evaluatorsList.size()]);
TypeWidener[] wideners = widenersList.toArray(new TypeWidener[widenersList.size()]);
EventBeanManufacturer eventManufacturer;
try {
eventManufacturer = eventAdapterService.getManufacturer(eventType, writableProperties, engineImportService, false);
} catch (EventBeanManufactureException e) {
throw new ExprValidationException(e.getMessage(), e);
}
return new SelectExprInsertNativeWidening(eventType, eventManufacturer, exprEvaluators, wideners);
}
public abstract static class SelectExprInsertNativeExpressionCoerceBase implements SelectExprProcessor {
protected final EventType eventType;
protected final ExprEvaluator exprEvaluator;
protected final EventAdapterService eventAdapterService;
protected SelectExprInsertNativeExpressionCoerceBase(EventType eventType, ExprEvaluator exprEvaluator, EventAdapterService eventAdapterService) {
this.eventType = eventType;
this.exprEvaluator = exprEvaluator;
this.eventAdapterService = eventAdapterService;
}
public EventType getResultEventType() {
return eventType;
}
}
public static class SelectExprInsertNativeExpressionCoerceMap extends SelectExprInsertNativeExpressionCoerceBase {
protected SelectExprInsertNativeExpressionCoerceMap(EventType eventType, ExprEvaluator exprEvaluator, EventAdapterService eventAdapterService) {
super(eventType, exprEvaluator, eventAdapterService);
}
public EventBean process(EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) {
Object result = exprEvaluator.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (result == null) {
return null;
}
return eventAdapterService.adapterForTypedMap((Map) result, eventType);
}
}
public static class SelectExprInsertNativeExpressionCoerceAvro extends SelectExprInsertNativeExpressionCoerceBase {
protected SelectExprInsertNativeExpressionCoerceAvro(EventType eventType, ExprEvaluator exprEvaluator, EventAdapterService eventAdapterService) {
super(eventType, exprEvaluator, eventAdapterService);
}
public EventBean process(EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) {
Object result = exprEvaluator.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (result == null) {
return null;
}
return eventAdapterService.adapterForTypedAvro(result, eventType);
}
}
public static class SelectExprInsertNativeExpressionCoerceObjectArray extends SelectExprInsertNativeExpressionCoerceBase {
protected SelectExprInsertNativeExpressionCoerceObjectArray(EventType eventType, ExprEvaluator exprEvaluator, EventAdapterService eventAdapterService) {
super(eventType, exprEvaluator, eventAdapterService);
}
public EventBean process(EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) {
Object result = exprEvaluator.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (result == null) {
return null;
}
return eventAdapterService.adapterForTypedObjectArray((Object[]) result, eventType);
}
}
public static class SelectExprInsertNativeExpressionCoerceNative extends SelectExprInsertNativeExpressionCoerceBase {
protected SelectExprInsertNativeExpressionCoerceNative(EventType eventType, ExprEvaluator exprEvaluator, EventAdapterService eventAdapterService) {
super(eventType, exprEvaluator, eventAdapterService);
}
public EventBean process(EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) {
Object result = exprEvaluator.evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if (result == null) {
return null;
}
return eventAdapterService.adapterForTypedBean(result, eventType);
}
}
public abstract static class SelectExprInsertNativeBase implements SelectExprProcessor {
private final EventType eventType;
protected final EventBeanManufacturer eventManufacturer;
protected final ExprEvaluator[] exprEvaluators;
protected SelectExprInsertNativeBase(EventType eventType, EventBeanManufacturer eventManufacturer, ExprEvaluator[] exprEvaluators) {
this.eventType = eventType;
this.eventManufacturer = eventManufacturer;
this.exprEvaluators = exprEvaluators;
}
public EventType getResultEventType() {
return eventType;
}
}
public static class SelectExprInsertNativeWidening extends SelectExprInsertNativeBase {
private final TypeWidener[] wideners;
public SelectExprInsertNativeWidening(EventType eventType, EventBeanManufacturer eventManufacturer, ExprEvaluator[] exprEvaluators, TypeWidener[] wideners) {
super(eventType, eventManufacturer, exprEvaluators);
this.wideners = wideners;
}
public EventBean process(EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) {
Object[] values = new Object[exprEvaluators.length];
for (int i = 0; i < exprEvaluators.length; i++) {
Object evalResult = exprEvaluators[i].evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
if ((evalResult != null) && (wideners[i] != null)) {
evalResult = wideners[i].widen(evalResult);
}
values[i] = evalResult;
}
return eventManufacturer.make(values);
}
}
public static class SelectExprInsertNativeNoWiden extends SelectExprInsertNativeBase {
public SelectExprInsertNativeNoWiden(EventType eventType, EventBeanManufacturer eventManufacturer, ExprEvaluator[] exprEvaluators) {
super(eventType, eventManufacturer, exprEvaluators);
}
public EventBean process(EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) {
Object[] values = new Object[exprEvaluators.length];
for (int i = 0; i < exprEvaluators.length; i++) {
Object evalResult = exprEvaluators[i].evaluate(eventsPerStream, isNewData, exprEvaluatorContext);
values[i] = evalResult;
}
return eventManufacturer.make(values);
}
}
public static class SelectExprInsertNativeNoEval implements SelectExprProcessor {
private final static Object[] EMPTY_PROPS = new Object[0];
private final EventType eventType;
private final EventBeanManufacturer eventManufacturer;
public SelectExprInsertNativeNoEval(EventType eventType, EventBeanManufacturer eventManufacturer) {
this.eventType = eventType;
this.eventManufacturer = eventManufacturer;
}
public EventBean process(EventBean[] eventsPerStream, boolean isNewData, boolean isSynthesize, ExprEvaluatorContext exprEvaluatorContext) {
return eventManufacturer.make(EMPTY_PROPS);
}
public EventType getResultEventType() {
return eventType;
}
}
public static class ExprEvaluatorJoinWildcard implements ExprEvaluator {
private final int streamNum;
private final Class returnType;
public ExprEvaluatorJoinWildcard(int streamNum, Class returnType) {
this.streamNum = streamNum;
this.returnType = returnType;
}
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) {
EventBean bean = eventsPerStream[streamNum];
if (bean == null) {
return null;
}
return bean.getUnderlying();
}
public Class getType() {
return returnType;
}
}
}