/* * ************************************************************************************* * Copyright (C) 2008 EsperTech, Inc. All rights reserved. * * http://esper.codehaus.org * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * * ************************************************************************************* */ package com.espertech.esper.event; import com.espertech.esper.client.*; import com.espertech.esper.epl.core.EngineImportException; import com.espertech.esper.epl.core.EngineImportService; import com.espertech.esper.epl.expression.ExprValidationException; import com.espertech.esper.epl.parse.ASTFilterSpecHelper; import com.espertech.esper.epl.spec.ColumnDesc; import com.espertech.esper.epl.spec.CreateSchemaDesc; 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.property.*; import com.espertech.esper.util.EventRepresentationUtil; import com.espertech.esper.util.JavaClassHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.util.*; public class EventTypeUtility { private static final Log log = LogFactory.getLog(EventTypeUtility.class); public static EventPropertyDescriptor getNestablePropertyDescriptor(EventType target, String propertyName) { EventPropertyDescriptor descriptor = target.getPropertyDescriptor(propertyName); if (descriptor != null) { return descriptor; } int index = ASTFilterSpecHelper.unescapedIndexOfDot(propertyName); if (index == -1) { return null; } // parse, can be an nested property Property property = PropertyParser.parse(propertyName, false); 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) 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() + "'"); } Class plain = JavaClassHelper.getClassForSimpleName(column.getType()); if (plain != null) { if (column.isArray()) { plain = Array.newInstance(plain, 0).getClass(); } typing.put(column.getName(), plain); } else { if (column.isArray()) { typing.put(column.getName(), column.getType() + "[]"); } else { typing.put(column.getName(), column.getType()); } } } 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; } 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("[]"); } /** * 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, Class> simplePropertyTypes = new LinkedHashMap<String, Class>(); Map<String, EventPropertyGetter> propertyGetters = new LinkedHashMap<String, EventPropertyGetter>(); Map<String, FragmentEventType> eventTypeFragments = new LinkedHashMap<String, FragmentEventType>(); Map<String, Object> nestableTypes = new LinkedHashMap<String, Object>(); // 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); } simplePropertyTypes.putAll(superType.simplePropertyTypes); propertyGetters.putAll(superType.propertyGetters); 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(); simplePropertyTypes.put(name, classType); propertyNameList.add(name); 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; if (isFragment) { FragmentEventType fragmentType = EventBeanUtility.createNativeFragmentType(classType, null, eventAdapterService); if (fragmentType != null) { nativeFragmentType = (BeanEventType) fragmentType.getFragmentType(); eventTypeFragments.put(name, fragmentType); } else { isFragment = false; } } else { eventTypeFragments.put(name, null); } propertyDescriptors.add(new EventPropertyDescriptor(name, classType, componentType, false, false, isArray, isMapped, isFragment)); EventPropertyGetter getter = factory.getGetterProperty(name, nativeFragmentType, eventAdapterService); propertyGetters.put(name, getter); continue; } // A null-type is also allowed if (entry.getValue() == null) { simplePropertyTypes.put(name, null); propertyNameList.add(name); EventPropertyGetter getter = factory.getGetterProperty(name, null, null); propertyGetters.put(name, getter); propertyDescriptors.add(new EventPropertyDescriptor(name, null, null, false, false, false, false, false)); eventTypeFragments.put(name, null); continue; } if (entry.getValue() instanceof Map) { // Add Map itself as a property simplePropertyTypes.put(name, Map.class); propertyNameList.add(name); EventPropertyGetter getter = factory.getGetterProperty(name, null, null); propertyGetters.put(name, getter); propertyDescriptors.add(new EventPropertyDescriptor(name, Map.class, null, false, false, false, true, false)); eventTypeFragments.put(name, null); continue; } if (entry.getValue() instanceof EventType) { // Add EventType itself as a property EventType eventType = (EventType) entry.getValue(); simplePropertyTypes.put(name, eventType.getUnderlyingType()); propertyNameList.add(name); EventPropertyGetter getter = factory.getGetterEventBean(name); propertyGetters.put(name, getter); propertyDescriptors.add(new EventPropertyDescriptor(name, eventType.getUnderlyingType(), null, false, false, false, false, true)); eventTypeFragments.put(name, new FragmentEventType(eventType, false, false)); 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); simplePropertyTypes.put(name, prototypeArray.getClass()); propertyNameList.add(name); EventPropertyGetter getter = factory.getGetterEventBeanArray(name, eventType); propertyGetters.put(name, getter); propertyDescriptors.add(new EventPropertyDescriptor(name, prototypeArray.getClass(), eventType.getUnderlyingType(), false, false, true, false, true)); eventTypeFragments.put(name, new FragmentEventType(eventType, true, false)); 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)) { 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(); if (isArray) { underlyingType = Array.newInstance(underlyingType, 0).getClass(); } simplePropertyTypes.put(name, underlyingType); propertyNameList.add(name); EventPropertyGetter getter; if (!isArray) { getter = factory.getGetterBeanNested(name, eventType, eventAdapterService); } else { getter = factory.getGetterBeanNestedArray(name, eventType, eventAdapterService); } propertyGetters.put(name, getter); propertyDescriptors.add(new EventPropertyDescriptor(name, underlyingType, null, false, false, isArray, false, true)); eventTypeFragments.put(name, new FragmentEventType(eventType, isArray, false)); continue; } generateExceptionNestedProp(name, entry.getValue()); } return new PropertySetDescriptor(propertyNameList, propertyDescriptors, simplePropertyTypes, propertyGetters, eventTypeFragments, 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, Class> simplePropertyTypes, Map<String, Object> nestableTypes, EventAdapterService eventAdapterService) { Class result = simplePropertyTypes.get(ASTFilterSpecHelper.unescapeDot(propertyName)); if (result != null) { return result; } // see if this is a nested property int index = ASTFilterSpecHelper.unescapedIndexOfDot(propertyName); if (index == -1) { // dynamic simple property if (propertyName.endsWith("?")) { return Object.class; } // parse, can be an indexed property Property property; try { property = PropertyParser.parse(propertyName, false); } catch (Exception ex) { // cannot parse property, return type return simplePropertyTypes.get(propertyName); } 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.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 = ASTFilterSpecHelper.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.parse(propertyMap, false); 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); } // handle eventtype[] in map else if (type instanceof EventType[]) { EventType innerType = ((EventType[]) type)[0]; return innerType.getPropertyType(propertyNested); } // handle array class in map case 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); return nestedEventType.getPropertyType(propertyNested); } } else if (property instanceof MappedProperty) { return null; // Since no type information is available for the property } else { return 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.parse(propertyNested, isRootedDynamic); return prop.getPropertyTypeMap(null, eventAdapterService); // we don't have a definition of the nested props } else if (nestedType instanceof Map) { Property prop = PropertyParser.parse(propertyNested, isRootedDynamic); Map nestedTypes = (Map) nestedType; return prop.getPropertyTypeMap(nestedTypes, eventAdapterService); } else if (nestedType instanceof Class) { Class simpleClass = (Class) nestedType; if (JavaClassHelper.isJavaBuiltinDataType(simpleClass)) { return null; } EventType nestedEventType = eventAdapterService.addBeanType(simpleClass.getName(), simpleClass, false, false, false); return nestedEventType.getPropertyType(propertyNested); } else if (nestedType instanceof EventType) { EventType innerType = (EventType) nestedType; return 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 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, EventPropertyGetter> propertyGetters, Map<String, EventPropertyGetter> propertyGetterCache, Map<String, Object> nestableTypes, EventAdapterService eventAdapterService, EventTypeNestableGetterFactory factory) { EventPropertyGetter cachedGetter = propertyGetterCache.get(propertyName); if (cachedGetter != null) { return cachedGetter; } String unescapePropName = ASTFilterSpecHelper.unescapeDot(propertyName); EventPropertyGetter getter = propertyGetters.get(unescapePropName); if (getter != null) { propertyGetterCache.put(propertyName, getter); return getter; } // see if this is a nested property int index = ASTFilterSpecHelper.unescapedIndexOfDot(propertyName); if (index == -1) { Property prop = PropertyParser.parse(propertyName, false); 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 = ASTFilterSpecHelper.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.parse(propertyMap, false); 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 { typeGetter = factory.getGetterNestedEntryBeanArray(indexedProp.getPropertyNameAtomic(), indexedProp.getIndex(), innerType.getGetter(propertyNested), 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 { return null; } } // The map contains another map, we resolve the property dynamically if (nestedType == Map.class) { Property prop = PropertyParser.parse(propertyNested, isRootedDynamic); 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.parse(propertyNested, isRootedDynamic); 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; EventType nestedEventType = eventAdapterService.addBeanType(simpleClass.getName(), simpleClass, false, false, false); final BeanEventPropertyGetter nestedGetter = (BeanEventPropertyGetter) nestedEventType.getGetter(propertyNested); if (nestedGetter == null) { return null; } Class nestedReturnType = nestedEventType.getPropertyType(propertyNested); // construct getter for nested property getter = factory.getGetterNestedPOJOProp(propertyMap, nestedGetter, eventAdapterService, nestedReturnType); 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 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()) { boolean useMap = EventRepresentationUtil.isMap(annotations, configSnapshot, spec.getAssignedType()); Map<String, Object> typing = EventTypeUtility.buildType(spec.getColumns(), eventAdapterService, spec.getCopyFrom()); Map<String, Object> compiledTyping = EventTypeUtility.compileMapTypeProperties(typing, eventAdapterService); ConfigurationEventTypeWithSupertype config; if (useMap) { config = new ConfigurationEventTypeMap(); } else { config = new ConfigurationEventTypeObjectArray(); } if (spec.getInherits() != null) { config.getSuperTypes().addAll(spec.getInherits()); } config.setStartTimestampPropertyName(spec.getStartTimestampProperty()); config.setEndTimestampPropertyName(spec.getEndTimestampProperty()); if (useMap) { if (isAnonymous) { eventType = eventAdapterService.createAnonymousMapType(spec.getSchemaName(), compiledTyping); } else { eventType = eventAdapterService.addNestableMapType(spec.getSchemaName(), compiledTyping, (ConfigurationEventTypeMap) config, false, false, true, false, false); } } else { if (isAnonymous) { eventType = eventAdapterService.createAnonymousObjectArrayType(spec.getSchemaName(), compiledTyping); } else { eventType = eventAdapterService.addNestableObjectArrayType(spec.getSchemaName(), compiledTyping, (ConfigurationEventTypeObjectArray) config, false, false, true, false, false); } } } 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); } 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); 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); } 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; } } }