/* *************************************************************************************** * 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.updatehelper; import com.espertech.esper.client.EventPropertyDescriptor; import com.espertech.esper.client.EventType; import com.espertech.esper.client.FragmentEventType; import com.espertech.esper.collection.Pair; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.expression.ops.ExprEqualsNode; import com.espertech.esper.epl.expression.visitor.ExprNodeIdentifierCollectVisitor; import com.espertech.esper.epl.spec.OnTriggerSetAssignment; import com.espertech.esper.event.*; import com.espertech.esper.util.TypeWidener; import com.espertech.esper.util.TypeWidenerCustomizer; import com.espertech.esper.util.TypeWidenerFactory; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class EventBeanUpdateHelperFactory { public static EventBeanUpdateHelper make(String updatedWindowOrTableName, EventTypeSPI eventTypeSPI, List<OnTriggerSetAssignment> assignments, String updatedAlias, EventType optionalTriggeringEventType, boolean isCopyOnWrite, String statementName, String engineURI, EventAdapterService eventAdapterService) throws ExprValidationException { List<EventBeanUpdateItem> updateItems = new ArrayList<EventBeanUpdateItem>(); List<String> properties = new ArrayList<String>(); TypeWidenerCustomizer typeWidenerCustomizer = eventAdapterService.getTypeWidenerCustomizer(eventTypeSPI); for (int i = 0; i < assignments.size(); i++) { OnTriggerSetAssignment assignment = assignments.get(i); EventBeanUpdateItem updateItem; // determine whether this is a "property=value" assignment, we use property setters in this case Pair<String, ExprNode> possibleAssignment = ExprNodeUtility.checkGetAssignmentToProp(assignment.getExpression()); // handle assignment "property = value" if (possibleAssignment != null) { String propertyName = possibleAssignment.getFirst(); EventPropertyDescriptor writableProperty = eventTypeSPI.getWritableProperty(propertyName); // check assignment to indexed or mapped property if (writableProperty == null) { Pair<String, EventPropertyDescriptor> nameWriteablePair = checkIndexedOrMappedProp(possibleAssignment.getFirst(), updatedWindowOrTableName, updatedAlias, eventTypeSPI); propertyName = nameWriteablePair.getFirst(); writableProperty = nameWriteablePair.getSecond(); } ExprEvaluator evaluator = possibleAssignment.getSecond().getExprEvaluator(); EventPropertyWriter writers = eventTypeSPI.getWriter(propertyName); boolean notNullableField = writableProperty.getPropertyType().isPrimitive(); properties.add(propertyName); TypeWidener widener = TypeWidenerFactory.getCheckPropertyAssignType(ExprNodeUtility.toExpressionStringMinPrecedenceSafe(possibleAssignment.getSecond()), possibleAssignment.getSecond().getExprEvaluator().getType(), writableProperty.getPropertyType(), propertyName, false, typeWidenerCustomizer, statementName, engineURI); // check event type assignment if (optionalTriggeringEventType != null && possibleAssignment.getSecond() instanceof ExprIdentNode) { ExprIdentNode node = (ExprIdentNode) possibleAssignment.getSecond(); FragmentEventType fragmentRHS = optionalTriggeringEventType.getFragmentType(node.getResolvedPropertyName()); FragmentEventType fragmentLHS = eventTypeSPI.getFragmentType(possibleAssignment.getFirst()); if (fragmentRHS != null && fragmentLHS != null && !EventTypeUtility.isTypeOrSubTypeOf(fragmentRHS.getFragmentType(), fragmentLHS.getFragmentType())) { throw new ExprValidationException("Invalid assignment to property '" + possibleAssignment.getFirst() + "' event type '" + fragmentLHS.getFragmentType().getName() + "' from event type '" + fragmentRHS.getFragmentType().getName() + "'"); } } updateItem = new EventBeanUpdateItem(evaluator, propertyName, writers, notNullableField, widener); } else { // handle non-assignment, i.e. UDF or other expression ExprEvaluator evaluator = assignment.getExpression().getExprEvaluator(); updateItem = new EventBeanUpdateItem(evaluator, null, null, false, null); } updateItems.add(updateItem); } // copy-on-write is the default event semantics as events are immutable EventBeanCopyMethod copyMethod; if (isCopyOnWrite) { // obtain copy method List<String> propertiesUniqueList = new ArrayList<String>(new HashSet<String>(properties)); String[] propertiesArray = propertiesUniqueList.toArray(new String[propertiesUniqueList.size()]); copyMethod = eventTypeSPI.getCopyMethod(propertiesArray); if (copyMethod == null) { throw new ExprValidationException("Event type does not support event bean copy"); } } else { // for in-place update, determine assignment expressions to use "initial" to access prior-change values // the copy-method is optional copyMethod = null; Set<String> propertiesInitialValue = determinePropertiesInitialValue(assignments); if (!propertiesInitialValue.isEmpty()) { String[] propertiesInitialValueArray = propertiesInitialValue.toArray(new String[propertiesInitialValue.size()]); copyMethod = eventTypeSPI.getCopyMethod(propertiesInitialValueArray); } } EventBeanUpdateItem[] updateItemsArray = updateItems.toArray(new EventBeanUpdateItem[updateItems.size()]); return new EventBeanUpdateHelper(copyMethod, updateItemsArray); } private static Set<String> determinePropertiesInitialValue(List<OnTriggerSetAssignment> assignments) { Set<String> props = new HashSet<String>(); ExprNodeIdentifierCollectVisitor visitor = new ExprNodeIdentifierCollectVisitor(); for (OnTriggerSetAssignment assignment : assignments) { if (assignment.getExpression() instanceof ExprEqualsNode) { assignment.getExpression().getChildNodes()[1].accept(visitor); } else { assignment.getExpression().accept(visitor); } for (ExprIdentNode node : visitor.getExprProperties()) { if (node.getStreamId() == 2) { props.add(node.getResolvedPropertyName()); } } } return props; } private static Pair<String, EventPropertyDescriptor> checkIndexedOrMappedProp(String propertyName, String updatedWindowOrTableName, String namedWindowAlias, EventTypeSPI eventTypeSPI) throws ExprValidationException { EventPropertyDescriptor writableProperty = null; int indexDot = propertyName.indexOf("."); if ((namedWindowAlias != null) && (indexDot != -1)) { String prefix = propertyName.substring(0, indexDot); String name = propertyName.substring(indexDot + 1); if (prefix.equals(namedWindowAlias)) { writableProperty = eventTypeSPI.getWritableProperty(name); propertyName = name; } } if (writableProperty == null && indexDot != -1) { String prefix = propertyName.substring(0, indexDot); String name = propertyName.substring(indexDot + 1); if (prefix.equals(updatedWindowOrTableName)) { writableProperty = eventTypeSPI.getWritableProperty(name); propertyName = name; } } if (writableProperty == null) { throw new ExprValidationException("Property '" + propertyName + "' is not available for write access"); } return new Pair<String, EventPropertyDescriptor>(propertyName, writableProperty); } }