/* *************************************************************************************** * 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.event; import com.espertech.esper.client.*; import com.espertech.esper.client.util.EventUnderlyingType; import com.espertech.esper.collection.Pair; import com.espertech.esper.epl.core.EngineImportException; import com.espertech.esper.epl.core.EngineImportService; import com.espertech.esper.epl.expression.core.ExprValidationException; import com.espertech.esper.epl.parse.ASTUtil; import com.espertech.esper.epl.spec.ColumnDesc; import com.espertech.esper.epl.spec.CreateSchemaDesc; import com.espertech.esper.event.arr.ObjectArrayEventType; import com.espertech.esper.event.avro.AvroSchemaEventType; import com.espertech.esper.event.bean.BeanEventPropertyGetter; import com.espertech.esper.event.bean.BeanEventType; import com.espertech.esper.event.map.MapEventPropertyGetter; import com.espertech.esper.event.map.MapEventType; import com.espertech.esper.event.property.*; import com.espertech.esper.util.EventRepresentationUtil; import com.espertech.esper.util.JavaClassHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.util.*; public class EventTypeUtility { private static final Logger log = LoggerFactory.getLogger(EventTypeUtility.class); public static EventType requireEventType(String aspectCamel, String aspectName, EventAdapterService eventAdapterService, String optionalEventTypeName) throws ExprValidationException { if (optionalEventTypeName == null) { throw new ExprValidationException(aspectCamel + " '" + aspectName + "' returns EventBean-array but does not provide the event type name"); } EventType eventType = eventAdapterService.getExistsTypeByName(optionalEventTypeName); if (eventType == null) { throw new ExprValidationException(aspectCamel + " '" + aspectName + "' returns event type '" + optionalEventTypeName + "' and the event type cannot be found"); } return eventType; } public static Pair<EventType[], Set<EventType>> getSuperTypesDepthFirst(ConfigurationEventTypeWithSupertype optionalConfig, EventUnderlyingType representation, Map<String, ? extends EventType> nameToTypeMap) { return getSuperTypesDepthFirst(optionalConfig == null ? null : optionalConfig.getSuperTypes(), representation, nameToTypeMap); } public static Pair<EventType[], Set<EventType>> getSuperTypesDepthFirst(Set<String> superTypesSet, EventUnderlyingType representation, Map<String, ? extends EventType> nameToTypeMap) throws EventAdapterException { if (superTypesSet == null || superTypesSet.isEmpty()) { return new Pair<>(null, null); } EventType[] superTypes = new EventType[superTypesSet.size()]; Set<EventType> deepSuperTypes = new LinkedHashSet<>(); int count = 0; for (String superName : superTypesSet) { EventType type = nameToTypeMap.get(superName); if (type == null) { throw new EventAdapterException("Supertype by name '" + superName + "' could not be found"); } if (representation == EventUnderlyingType.MAP) { if (!(type instanceof MapEventType)) { throw new EventAdapterException("Supertype by name '" + superName + "' is not a Map, expected a Map event type as a supertype"); } } else if (representation == EventUnderlyingType.OBJECTARRAY) { if (!(type instanceof ObjectArrayEventType)) { throw new EventAdapterException("Supertype by name '" + superName + "' is not an Object-array type, expected a Object-array event type as a supertype"); } } else if (representation == EventUnderlyingType.AVRO) { if (!(type instanceof AvroSchemaEventType)) { throw new EventAdapterException("Supertype by name '" + superName + "' is not an Avro type, expected a Avro event type as a supertype"); } } else { throw new IllegalStateException("Unrecognized enum " + representation); } superTypes[count++] = type; deepSuperTypes.add(type); addRecursiveSupertypes(deepSuperTypes, type); } List<EventType> superTypesListDepthFirst = new ArrayList<>(deepSuperTypes); Collections.reverse(superTypesListDepthFirst); return new Pair<EventType[], Set<EventType>>(superTypes, new LinkedHashSet<>(superTypesListDepthFirst)); } public static EventPropertyDescriptor getNestablePropertyDescriptor(EventType target, String propertyName) { EventPropertyDescriptor descriptor = target.getPropertyDescriptor(propertyName); if (descriptor != null) { return descriptor; } int index = ASTUtil.unescapedIndexOfDot(propertyName); if (index == -1) { return null; } // parse, can be an nested property Property property = PropertyParser.parseAndWalkLaxToSimple(propertyName); if (property instanceof PropertyBase) { return target.getPropertyDescriptor(((PropertyBase) property).getPropertyNameAtomic()); } if (!(property instanceof NestedProperty)) { return null; } NestedProperty nested = (NestedProperty) property; Deque<Property> properties = new ArrayDeque<Property>(nested.getProperties()); return getNestablePropertyDescriptor(target, properties); } public static EventPropertyDescriptor getNestablePropertyDescriptor(EventType target, Deque<Property> stack) { Property topProperty = stack.removeFirst(); if (stack.isEmpty()) { return target.getPropertyDescriptor(((PropertyBase) topProperty).getPropertyNameAtomic()); } if (!(topProperty instanceof SimpleProperty)) { return null; } SimpleProperty simple = (SimpleProperty) topProperty; FragmentEventType fragmentEventType = target.getFragmentType(simple.getPropertyNameAtomic()); if (fragmentEventType == null || fragmentEventType.getFragmentType() == null) { return null; } return getNestablePropertyDescriptor(fragmentEventType.getFragmentType(), stack); } public static LinkedHashMap<String, Object> buildType(List<ColumnDesc> columns, EventAdapterService eventAdapterService, Set<String> copyFrom, EngineImportService engineImportService) throws ExprValidationException { LinkedHashMap<String, Object> typing = new LinkedHashMap<String, Object>(); Set<String> columnNames = new HashSet<String>(); for (ColumnDesc column : columns) { boolean added = columnNames.add(column.getName()); if (!added) { throw new ExprValidationException("Duplicate column name '" + column.getName() + "'"); } Object columnType = buildType(column, engineImportService); typing.put(column.getName(), columnType); } if (copyFrom != null && !copyFrom.isEmpty()) { for (String copyFromName : copyFrom) { EventType type = eventAdapterService.getExistsTypeByName(copyFromName); if (type == null) { throw new ExprValidationException("Type by name '" + copyFromName + "' could not be located"); } mergeType(typing, type); } } return typing; } public static Object buildType(ColumnDesc column, EngineImportService engineImportService) throws ExprValidationException { if (column.getType() == null) { return null; } if (column.isPrimitiveArray()) { Class primitive = JavaClassHelper.getPrimitiveClassForName(column.getType()); if (primitive != null) { return Array.newInstance(primitive, 0).getClass(); } throw new ExprValidationException("Type '" + column.getType() + "' is not a primitive type"); } Class plain = JavaClassHelper.getClassForSimpleName(column.getType(), engineImportService.getClassForNameProvider()); if (plain != null) { if (column.isArray()) { plain = Array.newInstance(plain, 0).getClass(); } return plain; } // try imports first Class resolved = null; try { resolved = engineImportService.resolveClass(column.getType(), false); } catch (EngineImportException e) { // expected } // resolve from classpath when not found if (resolved == null) { try { resolved = JavaClassHelper.getClassForName(column.getType(), engineImportService.getClassForNameProvider()); } catch (ClassNotFoundException e) { // expected } } // Handle resolved classes here if (resolved != null) { if (column.isArray()) { resolved = Array.newInstance(resolved, 0).getClass(); } return resolved; } // Event types fall into here if (column.isArray()) { return column.getType() + "[]"; } return column.getType(); } private static void mergeType(Map<String, Object> typing, EventType typeToMerge) throws ExprValidationException { for (EventPropertyDescriptor prop : typeToMerge.getPropertyDescriptors()) { Object existing = typing.get(prop.getPropertyName()); if (!prop.isFragment()) { Class assigned = prop.getPropertyType(); if (existing != null && existing instanceof Class) { if (JavaClassHelper.getBoxedType((Class) existing) != JavaClassHelper.getBoxedType(assigned)) { throw new ExprValidationException("Type by name '" + typeToMerge.getName() + "' contributes property '" + prop.getPropertyName() + "' defined as '" + JavaClassHelper.getClassNameFullyQualPretty(assigned) + "' which overides the same property of type '" + JavaClassHelper.getClassNameFullyQualPretty((Class) existing) + "'"); } } typing.put(prop.getPropertyName(), prop.getPropertyType()); } else { if (existing != null) { throw new ExprValidationException("Property by name '" + prop.getPropertyName() + "' is defined twice by adding type '" + typeToMerge.getName() + "'"); } FragmentEventType fragment = typeToMerge.getFragmentType(prop.getPropertyName()); if (fragment == null) { continue; } if (fragment.isIndexed()) { typing.put(prop.getPropertyName(), new EventType[]{fragment.getFragmentType()}); } else { typing.put(prop.getPropertyName(), fragment.getFragmentType()); } } } } public static void validateTimestampProperties(EventType eventType, String startTimestampProperty, String endTimestampProperty) throws ConfigurationException { if (startTimestampProperty != null) { if (eventType.getGetter(startTimestampProperty) == null) { throw new ConfigurationException("Declared start timestamp property name '" + startTimestampProperty + "' was not found"); } Class type = eventType.getPropertyType(startTimestampProperty); if (!JavaClassHelper.isDatetimeClass(type)) { throw new ConfigurationException("Declared start timestamp property '" + startTimestampProperty + "' is expected to return a Date, Calendar or long-typed value but returns '" + type.getName() + "'"); } } if (endTimestampProperty != null) { if (startTimestampProperty == null) { throw new ConfigurationException("Declared end timestamp property requires that a start timestamp property is also declared"); } if (eventType.getGetter(endTimestampProperty) == null) { throw new ConfigurationException("Declared end timestamp property name '" + endTimestampProperty + "' was not found"); } Class type = eventType.getPropertyType(endTimestampProperty); if (!JavaClassHelper.isDatetimeClass(type)) { throw new ConfigurationException("Declared end timestamp property '" + endTimestampProperty + "' is expected to return a Date, Calendar or long-typed value but returns '" + type.getName() + "'"); } Class startType = eventType.getPropertyType(startTimestampProperty); if (JavaClassHelper.getBoxedType(startType) != JavaClassHelper.getBoxedType(type)) { throw new ConfigurationException("Declared end timestamp property '" + endTimestampProperty + "' is expected to have the same property type as the start-timestamp property '" + startTimestampProperty + "'"); } } } public static boolean isTypeOrSubTypeOf(EventType candidate, EventType superType) { if (candidate == superType) { return true; } if (candidate.getSuperTypes() != null) { for (Iterator<EventType> it = candidate.getDeepSuperTypes(); it.hasNext(); ) { if (it.next() == superType) { return true; } } } return false; } /** * Determine among the Map-type properties which properties are Bean-type event type names, * rewrites these as Class-type instead so that they are configured as native property and do not require wrapping, * but may require unwrapping. * * @param typing properties of map type * @param eventAdapterService event adapter service * @return compiled properties, same as original unless Bean-type event type names were specified. */ public static Map<String, Object> compileMapTypeProperties(Map<String, Object> typing, EventAdapterService eventAdapterService) { Map<String, Object> compiled = new LinkedHashMap<String, Object>(typing); for (Map.Entry<String, Object> specifiedEntry : typing.entrySet()) { Object typeSpec = specifiedEntry.getValue(); String nameSpec = specifiedEntry.getKey(); if (!(typeSpec instanceof String)) { continue; } String typeNameSpec = (String) typeSpec; boolean isArray = EventTypeUtility.isPropertyArray(typeNameSpec); if (isArray) { typeNameSpec = EventTypeUtility.getPropertyRemoveArray(typeNameSpec); } EventType eventType = eventAdapterService.getExistsTypeByName(typeNameSpec); if (eventType == null || !(eventType instanceof BeanEventType)) { continue; } BeanEventType beanEventType = (BeanEventType) eventType; Class underlyingType = beanEventType.getUnderlyingType(); if (isArray) { underlyingType = JavaClassHelper.getArrayType(underlyingType); } compiled.put(nameSpec, underlyingType); } return compiled; } /** * Returns true if the name indicates that the type is an array type. * * @param name the property name * @return true if array type */ public static boolean isPropertyArray(String name) { return name.trim().endsWith("[]"); } public static boolean isTypeOrSubTypeOf(String typeName, EventType sameTypeOrSubtype) { if (sameTypeOrSubtype.getName().equals(typeName)) { return true; } if (sameTypeOrSubtype.getSuperTypes() == null) { return false; } for (EventType superType : sameTypeOrSubtype.getSuperTypes()) { if (superType.getName().equals(typeName)) { return true; } } return false; } /** * Returns the property name without the array type extension, if present. * * @param name property name * @return property name with removed array extension name */ public static String getPropertyRemoveArray(String name) { return name.replaceAll("\\[", "").replaceAll("\\]", ""); } public static PropertySetDescriptor getNestableProperties(Map<String, Object> propertiesToAdd, EventAdapterService eventAdapterService, EventTypeNestableGetterFactory factory, EventType[] optionalSuperTypes) throws EPException { List<String> propertyNameList = new ArrayList<String>(); List<EventPropertyDescriptor> propertyDescriptors = new ArrayList<EventPropertyDescriptor>(); Map<String, Object> nestableTypes = new LinkedHashMap<String, Object>(); Map<String, PropertySetDescriptorItem> propertyItems = new HashMap<String, PropertySetDescriptorItem>(); // handle super-types first, such that the order of properties is well-defined from super-type to subtype if (optionalSuperTypes != null) { for (int i = 0; i < optionalSuperTypes.length; i++) { BaseNestableEventType superType = (BaseNestableEventType) optionalSuperTypes[i]; for (String propertyName : superType.getPropertyNames()) { if (nestableTypes.containsKey(propertyName)) { continue; } propertyNameList.add(propertyName); } for (EventPropertyDescriptor descriptor : superType.getPropertyDescriptors()) { if (nestableTypes.containsKey(descriptor.getPropertyName())) { continue; } propertyDescriptors.add(descriptor); } propertyItems.putAll(superType.propertyItems); nestableTypes.putAll(superType.nestableTypes); } } nestableTypes.putAll(propertiesToAdd); // Initialize getters and names array: at this time we do not care about nested types, // these are handled at the time someone is asking for them for (Map.Entry<String, Object> entry : propertiesToAdd.entrySet()) { if (!(entry.getKey() instanceof String)) { throw new EPException("Invalid type configuration: property name is not a String-type value"); } String name = entry.getKey(); // handle types that are String values if (entry.getValue() instanceof String) { String value = entry.getValue().toString().trim(); Class clazz = JavaClassHelper.getPrimitiveClassForName(value); if (clazz != null) { entry.setValue(clazz); } } if (entry.getValue() instanceof Class) { Class classType = (Class) entry.getValue(); boolean isArray = classType.isArray(); Class componentType = null; if (isArray) { componentType = classType.getComponentType(); } boolean isMapped = JavaClassHelper.isImplementsInterface(classType, Map.class); if (isMapped) { componentType = Object.class; // Cannot determine the type at runtime } boolean isFragment = JavaClassHelper.isFragmentableType(classType); BeanEventType nativeFragmentType = null; FragmentEventType fragmentType = null; if (isFragment) { fragmentType = EventBeanUtility.createNativeFragmentType(classType, null, eventAdapterService); if (fragmentType != null) { nativeFragmentType = (BeanEventType) fragmentType.getFragmentType(); } else { isFragment = false; } } EventPropertyGetter getter = factory.getGetterProperty(name, nativeFragmentType, eventAdapterService); EventPropertyDescriptor descriptor = new EventPropertyDescriptor(name, classType, componentType, false, false, isArray, isMapped, isFragment); PropertySetDescriptorItem item = new PropertySetDescriptorItem(descriptor, classType, getter, fragmentType); propertyNameList.add(name); propertyDescriptors.add(descriptor); propertyItems.put(name, item); continue; } // A null-type is also allowed if (entry.getValue() == null) { EventPropertyGetter getter = factory.getGetterProperty(name, null, null); EventPropertyDescriptor descriptor = new EventPropertyDescriptor(name, null, null, false, false, false, false, false); PropertySetDescriptorItem item = new PropertySetDescriptorItem(descriptor, null, getter, null); propertyNameList.add(name); propertyDescriptors.add(descriptor); propertyItems.put(name, item); continue; } // Add Map itself as a property if (entry.getValue() instanceof Map) { EventPropertyGetter getter = factory.getGetterProperty(name, null, null); EventPropertyDescriptor descriptor = new EventPropertyDescriptor(name, Map.class, null, false, false, false, true, false); PropertySetDescriptorItem item = new PropertySetDescriptorItem(descriptor, Map.class, getter, null); propertyNameList.add(name); propertyDescriptors.add(descriptor); propertyItems.put(name, item); continue; } if (entry.getValue() instanceof EventType) { // Add EventType itself as a property EventPropertyGetter getter = factory.getGetterEventBean(name); EventType eventType = (EventType) entry.getValue(); EventPropertyDescriptor descriptor = new EventPropertyDescriptor(name, eventType.getUnderlyingType(), null, false, false, false, false, true); FragmentEventType fragmentEventType = new FragmentEventType(eventType, false, false); PropertySetDescriptorItem item = new PropertySetDescriptorItem(descriptor, eventType.getUnderlyingType(), getter, fragmentEventType); propertyNameList.add(name); propertyDescriptors.add(descriptor); propertyItems.put(name, item); continue; } if (entry.getValue() instanceof EventType[]) { // Add EventType array itself as a property, type is expected to be first array element EventType eventType = ((EventType[]) entry.getValue())[0]; Object prototypeArray = Array.newInstance(eventType.getUnderlyingType(), 0); EventPropertyGetter getter = factory.getGetterEventBeanArray(name, eventType); EventPropertyDescriptor descriptor = new EventPropertyDescriptor(name, prototypeArray.getClass(), eventType.getUnderlyingType(), false, false, true, false, true); FragmentEventType fragmentEventType = new FragmentEventType(eventType, true, false); PropertySetDescriptorItem item = new PropertySetDescriptorItem(descriptor, prototypeArray.getClass(), getter, fragmentEventType); propertyNameList.add(name); propertyDescriptors.add(descriptor); propertyItems.put(name, item); continue; } if (entry.getValue() instanceof String) { String propertyName = entry.getValue().toString(); boolean isArray = EventTypeUtility.isPropertyArray(propertyName); if (isArray) { propertyName = EventTypeUtility.getPropertyRemoveArray(propertyName); } // Add EventType itself as a property EventType eventType = eventAdapterService.getExistsTypeByName(propertyName); if (!(eventType instanceof BaseNestableEventType) && !(eventType instanceof BeanEventType)) { throw new EPException("Nestable type configuration encountered an unexpected property type name '" + entry.getValue() + "' for property '" + name + "', expected java.lang.Class or java.util.Map or the name of a previously-declared Map or ObjectArray type"); } Class underlyingType = eventType.getUnderlyingType(); Class propertyComponentType = null; if (isArray) { propertyComponentType = underlyingType; if (underlyingType != Object[].class) { underlyingType = Array.newInstance(underlyingType, 0).getClass(); } } EventPropertyGetter getter; if (!isArray) { getter = factory.getGetterBeanNested(name, eventType, eventAdapterService); } else { getter = factory.getGetterBeanNestedArray(name, eventType, eventAdapterService); } EventPropertyDescriptor descriptor = new EventPropertyDescriptor(name, underlyingType, propertyComponentType, false, false, isArray, false, true); FragmentEventType fragmentEventType = new FragmentEventType(eventType, isArray, false); PropertySetDescriptorItem item = new PropertySetDescriptorItem(descriptor, underlyingType, getter, fragmentEventType); propertyNameList.add(name); propertyDescriptors.add(descriptor); propertyItems.put(name, item); continue; } generateExceptionNestedProp(name, entry.getValue()); } return new PropertySetDescriptor(propertyNameList, propertyDescriptors, propertyItems, nestableTypes); } private static void generateExceptionNestedProp(String name, Object value) throws EPException { String clazzName = (value == null) ? "null" : value.getClass().getSimpleName(); throw new EPException("Nestable type configuration encountered an unexpected property type of '" + clazzName + "' for property '" + name + "', expected java.lang.Class or java.util.Map or the name of a previously-declared Map or ObjectArray type"); } public static Class getNestablePropertyType(String propertyName, Map<String, PropertySetDescriptorItem> simplePropertyTypes, Map<String, Object> nestableTypes, EventAdapterService eventAdapterService) { PropertySetDescriptorItem item = simplePropertyTypes.get(ASTUtil.unescapeDot(propertyName)); if (item != null) { return item.getSimplePropertyType(); } // see if this is a nested property int index = ASTUtil.unescapedIndexOfDot(propertyName); if (index == -1) { // dynamic simple property if (propertyName.endsWith("?")) { return Object.class; } // parse, can be an indexed property Property property = PropertyParser.parseAndWalkLaxToSimple(propertyName); if (property instanceof SimpleProperty) { PropertySetDescriptorItem propitem = simplePropertyTypes.get(propertyName); if (propitem != null) { return propitem.getSimplePropertyType(); } return null; } if (property instanceof IndexedProperty) { IndexedProperty indexedProp = (IndexedProperty) property; Object type = nestableTypes.get(indexedProp.getPropertyNameAtomic()); if (type == null) { return null; } else if (type instanceof EventType[]) { return ((EventType[]) type)[0].getUnderlyingType(); } else if (type instanceof String) { String propTypeName = type.toString(); boolean isArray = EventTypeUtility.isPropertyArray(propTypeName); if (isArray) { propTypeName = EventTypeUtility.getPropertyRemoveArray(propTypeName); } EventType innerType = eventAdapterService.getExistsTypeByName(propTypeName); return innerType == null ? null : innerType.getUnderlyingType(); } if (!(type instanceof Class)) { return null; } if (!((Class) type).isArray()) { return null; } // its an array return ((Class) type).getComponentType(); } else if (property instanceof MappedProperty) { MappedProperty mappedProp = (MappedProperty) property; Object type = nestableTypes.get(mappedProp.getPropertyNameAtomic()); if (type == null) { return null; } if (type instanceof Class) { if (JavaClassHelper.isImplementsInterface((Class) type, Map.class)) { return Object.class; } } return null; } else { return null; } } // Map event types allow 2 types of properties inside: // - a property that is a Java object is interrogated via bean property getters and BeanEventType // - a property that is a Map itself is interrogated via map property getters // The property getters therefore act on // Take apart the nested property into a map key and a nested value class property name String propertyMap = ASTUtil.unescapeDot(propertyName.substring(0, index)); String propertyNested = propertyName.substring(index + 1, propertyName.length()); boolean isRootedDynamic = false; // If the property is dynamic, remove the ? since the property type is defined without if (propertyMap.endsWith("?")) { propertyMap = propertyMap.substring(0, propertyMap.length() - 1); isRootedDynamic = true; } Object nestedType = nestableTypes.get(propertyMap); if (nestedType == null) { // parse, can be an indexed property Property property = PropertyParser.parseAndWalkLaxToSimple(propertyMap); if (property instanceof IndexedProperty) { IndexedProperty indexedProp = (IndexedProperty) property; Object type = nestableTypes.get(indexedProp.getPropertyNameAtomic()); if (type == null) { return null; } // handle map-in-map case if (type instanceof String) { String propTypeName = type.toString(); boolean isArray = EventTypeUtility.isPropertyArray(propTypeName); if (isArray) { propTypeName = EventTypeUtility.getPropertyRemoveArray(propTypeName); } EventType innerType = eventAdapterService.getExistsTypeByName(propTypeName); if (!(innerType instanceof BaseNestableEventType)) { return null; } return innerType.getPropertyType(propertyNested); } else if (type instanceof EventType[]) { // handle eventtype[] in map EventType innerType = ((EventType[]) type)[0]; return innerType.getPropertyType(propertyNested); } else { // handle array class in map case if (!(type instanceof Class)) { return null; } if (!((Class) type).isArray()) { return null; } Class componentType = ((Class) type).getComponentType(); EventType nestedEventType = eventAdapterService.addBeanType(componentType.getName(), componentType, false, false, false); return nestedEventType.getPropertyType(propertyNested); } } else if (property instanceof MappedProperty) { return null; // Since no type information is available for the property } else { return isRootedDynamic ? Object.class : null; } } // If there is a map value in the map, return the Object value if this is a dynamic property if (nestedType == Map.class) { Property prop = PropertyParser.parseAndWalk(propertyNested, isRootedDynamic); return isRootedDynamic ? Object.class : prop.getPropertyTypeMap(null, eventAdapterService); // we don't have a definition of the nested props } else if (nestedType instanceof Map) { Property prop = PropertyParser.parseAndWalk(propertyNested, isRootedDynamic); Map nestedTypes = (Map) nestedType; return isRootedDynamic ? Object.class : prop.getPropertyTypeMap(nestedTypes, eventAdapterService); } else if (nestedType instanceof Class) { Class simpleClass = (Class) nestedType; if (JavaClassHelper.isJavaBuiltinDataType(simpleClass)) { return null; } if (simpleClass.isArray() && (JavaClassHelper.isJavaBuiltinDataType(simpleClass.getComponentType()) || simpleClass.getComponentType() == Object.class)) { return null; } EventType nestedEventType = eventAdapterService.addBeanType(simpleClass.getName(), simpleClass, false, false, false); return isRootedDynamic ? Object.class : nestedEventType.getPropertyType(propertyNested); } else if (nestedType instanceof EventType) { EventType innerType = (EventType) nestedType; return isRootedDynamic ? Object.class : innerType.getPropertyType(propertyNested); } else if (nestedType instanceof EventType[]) { return null; // requires indexed property } else if (nestedType instanceof String) { String nestedName = nestedType.toString(); boolean isArray = EventTypeUtility.isPropertyArray(nestedName); if (isArray) { nestedName = EventTypeUtility.getPropertyRemoveArray(nestedName); } EventType innerType = eventAdapterService.getExistsTypeByName(nestedName); if (!(innerType instanceof BaseNestableEventType)) { return null; } return isRootedDynamic ? Object.class : innerType.getPropertyType(propertyNested); } else { String message = "Nestable map type configuration encountered an unexpected value type of '" + nestedType.getClass() + " for property '" + propertyName + "', expected Class, Map.class or Map<String, Object> as value type"; throw new PropertyAccessException(message); } } public static EventPropertyGetter getNestableGetter(String propertyName, Map<String, PropertySetDescriptorItem> propertyGetters, Map<String, EventPropertyGetter> propertyGetterCache, Map<String, Object> nestableTypes, EventAdapterService eventAdapterService, EventTypeNestableGetterFactory factory, boolean isObjectArray) { EventPropertyGetter cachedGetter = propertyGetterCache.get(propertyName); if (cachedGetter != null) { return cachedGetter; } String unescapePropName = ASTUtil.unescapeDot(propertyName); PropertySetDescriptorItem item = propertyGetters.get(unescapePropName); if (item != null) { EventPropertyGetter getter = item.getPropertyGetter(); propertyGetterCache.put(propertyName, getter); return getter; } // see if this is a nested property int index = ASTUtil.unescapedIndexOfDot(propertyName); if (index == -1) { Property prop = PropertyParser.parseAndWalkLaxToSimple(propertyName); if (prop instanceof DynamicProperty) { EventPropertyGetter getterDyn = factory.getPropertyProvidedGetter(nestableTypes, propertyName, prop, eventAdapterService); propertyGetterCache.put(propertyName, getterDyn); return getterDyn; } else if (prop instanceof IndexedProperty) { IndexedProperty indexedProp = (IndexedProperty) prop; Object type = nestableTypes.get(indexedProp.getPropertyNameAtomic()); if (type == null) { return null; } else if (type instanceof EventType[]) { EventPropertyGetter getterArr = factory.getGetterIndexedEventBean(indexedProp.getPropertyNameAtomic(), indexedProp.getIndex()); propertyGetterCache.put(propertyName, getterArr); return getterArr; } else if (type instanceof String) { String nestedTypeName = type.toString(); boolean isArray = EventTypeUtility.isPropertyArray(nestedTypeName); if (isArray) { nestedTypeName = EventTypeUtility.getPropertyRemoveArray(nestedTypeName); } EventType innerType = eventAdapterService.getExistsTypeByName(nestedTypeName); if (!(innerType instanceof BaseNestableEventType)) { return null; } EventPropertyGetter typeGetter; if (!isArray) { typeGetter = factory.getGetterBeanNested(indexedProp.getPropertyNameAtomic(), innerType, eventAdapterService); } else { typeGetter = factory.getGetterIndexedUnderlyingArray(indexedProp.getPropertyNameAtomic(), indexedProp.getIndex(), eventAdapterService, innerType); } propertyGetterCache.put(propertyName, typeGetter); return typeGetter; } // handle map type name in map if (!(type instanceof Class)) { return null; } if (!((Class) type).isArray()) { return null; } // its an array Class componentType = ((Class) type).getComponentType(); EventPropertyGetter indexedGetter = factory.getGetterIndexedPOJO(indexedProp.getPropertyNameAtomic(), indexedProp.getIndex(), eventAdapterService, componentType); propertyGetterCache.put(propertyName, indexedGetter); return indexedGetter; } else if (prop instanceof MappedProperty) { MappedProperty mappedProp = (MappedProperty) prop; Object type = nestableTypes.get(mappedProp.getPropertyNameAtomic()); if (type == null) { return null; } if (type instanceof Class) { if (JavaClassHelper.isImplementsInterface((Class) type, Map.class)) { return factory.getGetterMappedProperty(mappedProp.getPropertyNameAtomic(), mappedProp.getKey()); } } return null; } else { return null; } } // Take apart the nested property into a map key and a nested value class property name String propertyMap = ASTUtil.unescapeDot(propertyName.substring(0, index)); String propertyNested = propertyName.substring(index + 1, propertyName.length()); boolean isRootedDynamic = false; // If the property is dynamic, remove the ? since the property type is defined without if (propertyMap.endsWith("?")) { propertyMap = propertyMap.substring(0, propertyMap.length() - 1); isRootedDynamic = true; } Object nestedType = nestableTypes.get(propertyMap); if (nestedType == null) { // parse, can be an indexed property Property property = PropertyParser.parseAndWalkLaxToSimple(propertyMap); if (property instanceof IndexedProperty) { IndexedProperty indexedProp = (IndexedProperty) property; Object type = nestableTypes.get(indexedProp.getPropertyNameAtomic()); if (type == null) { return null; } if (type instanceof String) { String nestedTypeName = type.toString(); boolean isArray = EventTypeUtility.isPropertyArray(nestedTypeName); if (isArray) { nestedTypeName = EventTypeUtility.getPropertyRemoveArray(nestedTypeName); } EventType innerType = eventAdapterService.getExistsTypeByName(nestedTypeName); if (!(innerType instanceof BaseNestableEventType)) { return null; } EventPropertyGetter typeGetter; if (!isArray) { typeGetter = factory.getGetterNestedEntryBean(propertyMap, innerType.getGetter(propertyNested), innerType, eventAdapterService); } else { EventPropertyGetter innerGetter = innerType.getGetter(propertyNested); if (innerGetter == null) { return null; } typeGetter = factory.getGetterNestedEntryBeanArray(indexedProp.getPropertyNameAtomic(), indexedProp.getIndex(), innerGetter, innerType, eventAdapterService); } propertyGetterCache.put(propertyName, typeGetter); return typeGetter; } else if (type instanceof EventType[]) { EventType componentType = ((EventType[]) type)[0]; final EventPropertyGetter nestedGetter = componentType.getGetter(propertyNested); if (nestedGetter == null) { return null; } EventPropertyGetter typeGetter = factory.getGetterIndexedEntryEventBeanArrayElement(indexedProp.getPropertyNameAtomic(), indexedProp.getIndex(), nestedGetter); propertyGetterCache.put(propertyName, typeGetter); return typeGetter; } else { if (!(type instanceof Class)) { return null; } if (!((Class) type).isArray()) { return null; } Class componentType = ((Class) type).getComponentType(); EventType nestedEventType = eventAdapterService.addBeanType(componentType.getName(), componentType, false, false, false); final BeanEventPropertyGetter nestedGetter = (BeanEventPropertyGetter) nestedEventType.getGetter(propertyNested); if (nestedGetter == null) { return null; } Class propertyTypeGetter = nestedEventType.getPropertyType(propertyNested); // construct getter for nested property EventPropertyGetter indexGetter = factory.getGetterIndexedEntryPOJO(indexedProp.getPropertyNameAtomic(), indexedProp.getIndex(), nestedGetter, eventAdapterService, propertyTypeGetter); propertyGetterCache.put(propertyName, indexGetter); return indexGetter; } } else if (property instanceof MappedProperty) { return null; // Since no type information is available for the property } else { if (isRootedDynamic) { Property prop = PropertyParser.parseAndWalk(propertyNested, true); if (!isObjectArray) { EventPropertyGetter getterNested = prop.getGetterMap(null, eventAdapterService); EventPropertyGetter dynamicGetter = factory.getGetterNestedPropertyProvidedGetterDynamic(nestableTypes, propertyMap, getterNested, eventAdapterService); propertyGetterCache.put(propertyName, dynamicGetter); return dynamicGetter; } return null; } return null; } } // The map contains another map, we resolve the property dynamically if (nestedType == Map.class) { Property prop = PropertyParser.parseAndWalkLaxToSimple(propertyNested); MapEventPropertyGetter getterNestedMap = prop.getGetterMap(null, eventAdapterService); if (getterNestedMap == null) { return null; } EventPropertyGetter mapGetter = factory.getGetterNestedMapProp(propertyMap, getterNestedMap); propertyGetterCache.put(propertyName, mapGetter); return mapGetter; } else if (nestedType instanceof Map) { Property prop = PropertyParser.parseAndWalkLaxToSimple(propertyNested); Map nestedTypes = (Map) nestedType; MapEventPropertyGetter getterNestedMap = prop.getGetterMap(nestedTypes, eventAdapterService); if (getterNestedMap == null) { return null; } EventPropertyGetter mapGetter = factory.getGetterNestedMapProp(propertyMap, getterNestedMap); propertyGetterCache.put(propertyName, mapGetter); return mapGetter; } else if (nestedType instanceof Class) { // ask the nested class to resolve the property Class simpleClass = (Class) nestedType; if (simpleClass.isArray()) { return null; } EventType nestedEventType = eventAdapterService.addBeanType(simpleClass.getName(), simpleClass, false, false, false); final BeanEventPropertyGetter nestedGetter = (BeanEventPropertyGetter) nestedEventType.getGetter(propertyNested); if (nestedGetter == null) { return null; } Class propertyType; Class propertyComponentType; EventPropertyDescriptor desc = nestedEventType.getPropertyDescriptor(propertyNested); if (desc == null) { propertyType = nestedEventType.getPropertyType(propertyNested); propertyComponentType = propertyType.isArray() ? propertyType.getComponentType() : JavaClassHelper.getGenericType(propertyType, 0); } else { propertyType = desc.getPropertyType(); propertyComponentType = desc.getPropertyComponentType(); } // construct getter for nested property EventPropertyGetter getter = factory.getGetterNestedPOJOProp(propertyMap, nestedGetter, eventAdapterService, propertyType, propertyComponentType); propertyGetterCache.put(propertyName, getter); return getter; } else if (nestedType instanceof EventType) { // ask the nested class to resolve the property EventType innerType = (EventType) nestedType; final EventPropertyGetter nestedGetter = innerType.getGetter(propertyNested); if (nestedGetter == null) { return null; } // construct getter for nested property EventPropertyGetter getter = factory.getGetterNestedEventBean(propertyMap, nestedGetter); propertyGetterCache.put(propertyName, getter); return getter; } else if (nestedType instanceof EventType[]) { EventType[] typeArray = (EventType[]) nestedType; EventPropertyGetter beanArrGetter = factory.getGetterEventBeanArray(propertyMap, typeArray[0]); propertyGetterCache.put(propertyName, beanArrGetter); return beanArrGetter; } else if (nestedType instanceof String) { String nestedName = nestedType.toString(); boolean isArray = EventTypeUtility.isPropertyArray(nestedName); if (isArray) { nestedName = EventTypeUtility.getPropertyRemoveArray(nestedName); } EventType innerType = eventAdapterService.getExistsTypeByName(nestedName); if (!(innerType instanceof BaseNestableEventType)) { return null; } EventPropertyGetter innerGetter = innerType.getGetter(propertyNested); if (innerGetter == null) { return null; } EventPropertyGetter outerGetter; if (!isArray) { outerGetter = factory.getGetterNestedEntryBean(propertyMap, innerGetter, innerType, eventAdapterService); } else { outerGetter = factory.getGetterNestedEntryBeanArray(propertyMap, 0, innerGetter, innerType, eventAdapterService); } propertyGetterCache.put(propertyName, outerGetter); return outerGetter; } else { String message = "Nestable type configuration encountered an unexpected value type of '" + nestedType.getClass() + " for property '" + propertyName + "', expected Class, Map.class or Map<String, Object> as value type"; throw new PropertyAccessException(message); } } public static LinkedHashMap<String, Object> validateObjectArrayDef(String[] propertyNames, Object[] propertyTypes) { if (propertyNames.length != propertyTypes.length) { throw new ConfigurationException("Number of property names and property types do not match, found " + propertyNames.length + " property names and " + propertyTypes.length + " property types"); } // validate property names for no-duplicates Set<String> propertyNamesSet = new HashSet<String>(); LinkedHashMap<String, Object> propertyTypesMap = new LinkedHashMap<String, Object>(); for (int i = 0; i < propertyNames.length; i++) { String propertyName = propertyNames[i]; if (propertyNamesSet.contains(propertyName)) { // duplicate prop check throw new ConfigurationException("Property '" + propertyName + "' is listed twice in the type definition"); } propertyNamesSet.add(propertyName); propertyTypesMap.put(propertyName, propertyTypes[i]); } return propertyTypesMap; } public static EventType createNonVariantType(boolean isAnonymous, CreateSchemaDesc spec, Annotation[] annotations, ConfigurationInformation configSnapshot, EventAdapterService eventAdapterService, EngineImportService engineImportService) throws ExprValidationException { if (spec.getAssignedType() == CreateSchemaDesc.AssignedType.VARIANT) { throw new IllegalStateException("Variant type is not allowed in this context"); } EventType eventType; if (spec.getTypes().isEmpty()) { EventUnderlyingType representation = EventRepresentationUtil.getRepresentation(annotations, configSnapshot, spec.getAssignedType()); Map<String, Object> typing = EventTypeUtility.buildType(spec.getColumns(), eventAdapterService, spec.getCopyFrom(), engineImportService); Map<String, Object> compiledTyping = EventTypeUtility.compileMapTypeProperties(typing, eventAdapterService); ConfigurationEventTypeWithSupertype config; if (representation == EventUnderlyingType.MAP) { config = new ConfigurationEventTypeMap(); } else if (representation == EventUnderlyingType.OBJECTARRAY) { config = new ConfigurationEventTypeObjectArray(); } else if (representation == EventUnderlyingType.AVRO) { config = new ConfigurationEventTypeAvro(); } else { throw new IllegalStateException("Unrecognized representation '" + representation + "'"); } if (spec.getInherits() != null) { config.getSuperTypes().addAll(spec.getInherits()); } config.setStartTimestampPropertyName(spec.getStartTimestampProperty()); config.setEndTimestampPropertyName(spec.getEndTimestampProperty()); if (representation == EventUnderlyingType.MAP) { if (isAnonymous) { eventType = eventAdapterService.createAnonymousMapType(spec.getSchemaName(), compiledTyping, true); } else { eventType = eventAdapterService.addNestableMapType(spec.getSchemaName(), compiledTyping, (ConfigurationEventTypeMap) config, false, false, true, false, false); } } else if (representation == EventUnderlyingType.OBJECTARRAY) { if (isAnonymous) { eventType = eventAdapterService.createAnonymousObjectArrayType(spec.getSchemaName(), compiledTyping); } else { eventType = eventAdapterService.addNestableObjectArrayType(spec.getSchemaName(), compiledTyping, (ConfigurationEventTypeObjectArray) config, false, false, true, false, false, false, null); } } else if (representation == EventUnderlyingType.AVRO) { if (isAnonymous) { eventType = eventAdapterService.createAnonymousAvroType(spec.getSchemaName(), compiledTyping, annotations, null, null); } else { eventType = eventAdapterService.addAvroType(spec.getSchemaName(), compiledTyping, false, false, true, false, false, annotations, (ConfigurationEventTypeAvro) config, null, null); } } else { throw new IllegalStateException("Unrecognized representation " + representation); } } else { // Java Object/Bean/POJO type definition if (spec.getCopyFrom() != null && !spec.getCopyFrom().isEmpty()) { throw new ExprValidationException("Copy-from types are not allowed with class-provided types"); } if (spec.getTypes().size() != 1) { throw new IllegalStateException("Multiple types provided"); } String typeName = spec.getTypes().iterator().next(); try { // use the existing configuration, if any, possibly adding the start and end timestamps ConfigurationEventTypeLegacy config = eventAdapterService.getClassLegacyConfigs(typeName); if (spec.getStartTimestampProperty() != null || spec.getEndTimestampProperty() != null) { if (config == null) { config = new ConfigurationEventTypeLegacy(); } config.setStartTimestampPropertyName(spec.getStartTimestampProperty()); config.setEndTimestampPropertyName(spec.getEndTimestampProperty()); eventAdapterService.setClassLegacyConfigs(Collections.singletonMap(typeName, config)); } if (isAnonymous) { String className = spec.getTypes().iterator().next(); Class clazz; try { clazz = engineImportService.resolveClass(className, false); } catch (EngineImportException e) { throw new ExprValidationException("Failed to resolve class '" + className + "': " + e.getMessage(), e); } eventType = eventAdapterService.createAnonymousBeanType(spec.getSchemaName(), clazz); } else { eventType = eventAdapterService.addBeanType(spec.getSchemaName(), spec.getTypes().iterator().next(), false, false, false, true); } } catch (EventAdapterException ex) { Class clazz; try { clazz = engineImportService.resolveClass(typeName, false); if (isAnonymous) { eventType = eventAdapterService.createAnonymousBeanType(spec.getSchemaName(), clazz); } else { eventType = eventAdapterService.addBeanType(spec.getSchemaName(), clazz, false, false, true); } } catch (EngineImportException e) { log.debug("Engine import failed to resolve event type '" + typeName + "'"); throw ex; } } } return eventType; } public static WriteablePropertyDescriptor findWritable(String propertyName, Set<WriteablePropertyDescriptor> writables) { for (WriteablePropertyDescriptor writable : writables) { if (writable.getPropertyName().equals(propertyName)) { return writable; } } return null; } public static TimestampPropertyDesc validatedDetermineTimestampProps(EventType type, String startProposed, String endProposed, EventType[] superTypes) throws EPException { // determine start&end timestamp as inherited String startTimestampPropertyName = startProposed; String endTimestampPropertyName = endProposed; if (superTypes != null && superTypes.length > 0) { for (EventType superType : superTypes) { if (superType.getStartTimestampPropertyName() != null) { if (startTimestampPropertyName != null && !startTimestampPropertyName.equals(superType.getStartTimestampPropertyName())) { throw getExceptionTimestampInherited("start", startTimestampPropertyName, superType.getStartTimestampPropertyName(), superType); } startTimestampPropertyName = superType.getStartTimestampPropertyName(); } if (superType.getEndTimestampPropertyName() != null) { if (endTimestampPropertyName != null && !endTimestampPropertyName.equals(superType.getEndTimestampPropertyName())) { throw getExceptionTimestampInherited("end", endTimestampPropertyName, superType.getEndTimestampPropertyName(), superType); } endTimestampPropertyName = superType.getEndTimestampPropertyName(); } } } validateTimestampProperties(type, startTimestampPropertyName, endTimestampPropertyName); return new TimestampPropertyDesc(startTimestampPropertyName, endTimestampPropertyName); } private static EPException getExceptionTimestampInherited(String tstype, String firstName, String secondName, EventType superType) { String message = "Event type declares " + tstype + " timestamp as property '" + firstName + "' however inherited event type '" + superType.getName() + "' declares " + tstype + " timestamp as property '" + secondName + "'"; return new EPException(message); } private static void addRecursiveSupertypes(Set<EventType> superTypes, EventType child) { if (child.getSuperTypes() != null) { for (int i = 0; i < child.getSuperTypes().length; i++) { superTypes.add(child.getSuperTypes()[i]); addRecursiveSupertypes(superTypes, child.getSuperTypes()[i]); } } } public static String disallowedAtTypeMessage() { return "The @type annotation is only allowed when the invocation target returns EventBean instances"; } public static class TimestampPropertyDesc { private final String start; private final String end; public TimestampPropertyDesc(String start, String end) { this.start = start; this.end = end; } public String getStart() { return start; } public String getEnd() { return end; } } }