Package com.espertech.esper.event.map

Source Code of com.espertech.esper.event.map.MapEventType

/**************************************************************************************
* 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.map;

import com.espertech.esper.client.*;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.epl.parse.ASTFilterSpecHelper;
import com.espertech.esper.event.*;
import com.espertech.esper.event.bean.BeanEventType;
import com.espertech.esper.event.property.*;
import com.espertech.esper.util.GraphUtil;
import com.espertech.esper.util.JavaClassHelper;

import java.lang.reflect.Array;
import java.util.*;

/**
* Implementation of the {@link EventType} interface for handling plain Maps containing name value pairs.
*/
public class MapEventType implements EventTypeSPI
{
    private final EventTypeMetadata metadata;
    private final String typeName;
    private final EventAdapterService eventAdapterService;
    private final EventType[] optionalSuperTypes;
    private final Set<EventType> optionalDeepSupertypes;
    private final int eventTypeId;

    // Simple (not-nested) properties are stored here
    private String[] propertyNames;       // Cache an array of property names so not to construct one frequently
    private EventPropertyDescriptor[] propertyDescriptors;
    private Map<String, EventPropertyDescriptor> propertyDescriptorMap;

    private final Map<String, FragmentEventType> simpleFragmentTypes;     // Mapping of property name (fragment-only) and type
    private final Map<String, Class> simplePropertyTypes;     // Mapping of property name (simple-only) and type
    private final Map<String, MapEventPropertyGetter> propertyGetters;   // Mapping of simple property name and getters
    private final Map<String, MapEventPropertyGetter> propertyGetterCache; // Mapping of all property names and getters

    // Nestable definition of Map contents is here
    private Map<String, Object> nestableTypes;  // Deep definition of the map-type, containing nested maps and objects
    private Map<String, Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter>> propertyWriters;
    private EventPropertyDescriptor[] writablePropertyDescriptors;

    private String startTimstampPropertyName;
    private String endTimestampPropertyName;

    /**
     * Constructor takes a type name, map of property names and types, for
     * use with nestable Map events.
     * @param typeName is the event type name used to distinquish map types that have the same property types,
     * empty string for anonymous maps, or for insert-into statements generating map events
     * the stream name
     * @param propertyTypes is pairs of property name and type
     * @param eventAdapterService is required for access to objects properties within map values
     * @param optionalSuperTypes the supertypes to this type if any, or null if there are no supertypes
     * @param optionalDeepSupertypes the deep supertypes to this type if any, or null if there are no deep supertypes
     * @param metadata event type metadata
     */
    public MapEventType(EventTypeMetadata metadata,
                        String typeName,
                        int eventTypeId,
                        EventAdapterService eventAdapterService,
                        Map<String, Object> propertyTypes,
                        EventType[] optionalSuperTypes,
                        Set<EventType> optionalDeepSupertypes,
                        ConfigurationEventTypeMap configMapType
                        )
    {
        this.metadata = metadata;
        this.eventTypeId = eventTypeId;
        this.typeName = typeName;
        this.eventAdapterService = eventAdapterService;

        this.nestableTypes = new LinkedHashMap<String, Object>();
        this.nestableTypes.putAll(propertyTypes);

        this.optionalSuperTypes = optionalSuperTypes;
        if (optionalDeepSupertypes == null)
        {
            this.optionalDeepSupertypes = Collections.emptySet();
        }
        else
        {
            this.optionalDeepSupertypes = optionalDeepSupertypes;
        }

        // determine property set and prepare getters
        PropertySetDescriptor propertySet = getNestableMapProperties(propertyTypes, eventAdapterService);
       
        List<String> propertyNameList = propertySet.getPropertyNameList();
        propertyNames = propertyNameList.toArray(new String[propertyNameList.size()]);
        propertyGetters = propertySet.getPropertyGetters();
        propertyGetterCache = new HashMap<String, MapEventPropertyGetter>();
        simplePropertyTypes = propertySet.getSimplePropertyTypes();
        simpleFragmentTypes = propertySet.getSimpleFragmentTypes();
        propertyDescriptors = propertySet.getPropertyDescriptors().toArray(new EventPropertyDescriptor[propertySet.getPropertyDescriptors().size()]);
        propertyDescriptorMap = new HashMap<String, EventPropertyDescriptor>();
        for (EventPropertyDescriptor desc : propertyDescriptors)
        {
            propertyDescriptorMap.put(desc.getPropertyName(), desc);                               
        }

        // Copy parent properties to child
        copySuperTypes();

        if (configMapType != null) {
            startTimstampPropertyName = configMapType.getStartTimestampPropertyName();
            endTimestampPropertyName = configMapType.getEndTimestampPropertyName();
            EventTypeUtility.validateTimestampProperties(this, startTimstampPropertyName, endTimestampPropertyName);
        }
    }

    public String getName()
    {
        return typeName;
    }

    public EventBeanReader getReader()
    {
        return new MapEventBeanReader(this);
    }

    public int getEventTypeId() {
        return eventTypeId;
    }

    public String getStartTimestampPropertyName() {
        return startTimstampPropertyName;
    }

    public String getEndTimestampPropertyName() {
        return endTimestampPropertyName;
    }

    public final Class getPropertyType(String propertyName)
    {
        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 = null;
            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 MapEventType))
                    {
                        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 MapEventType))
            {
                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 final Class getUnderlyingType()
    {
        return java.util.Map.class;
    }

    public MapEventPropertyGetter getGetter(final String propertyName)
    {
        MapEventPropertyGetter cachedGetter = propertyGetterCache.get(propertyName);
        if (cachedGetter != null)
        {
            return cachedGetter;
        }

        String unescapePropName = ASTFilterSpecHelper.unescapeDot(propertyName);
        MapEventPropertyGetter 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)
            {
                MapEventPropertyGetter getterDyn = prop.getGetterMap(null, 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[])
                {
                    MapEventPropertyGetter getterArr = new MapEventBeanArrayIndexedPropertyGetter(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 MapEventType))
                    {
                        return null;
                    }
                    MapEventPropertyGetter typeGetter;
                    if (!isArray)
                    {
                        typeGetter = new MapMaptypedUndPropertyGetter(indexedProp.getPropertyNameAtomic(), eventAdapterService, innerType);
                    }
                    else
                    {
                        typeGetter = new MapArrayMaptypedUndPropertyGetter(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();
                MapEventPropertyGetter indexedGetter = new MapArrayPOJOEntryIndexedPropertyGetter(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 new MapMappedPropertyGetter(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 MapEventType))
                    {
                        return null;
                    }
                    MapEventPropertyGetter typeGetter;
                    if (!isArray)
                    {
                        typeGetter = new MapMaptypedEntryPropertyGetter(propertyMap, innerType.getGetter(propertyNested), (MapEventType) innerType, eventAdapterService);
                    }
                    else
                    {
                        typeGetter = new MapArrayMaptypedEntryPropertyGetter(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;
                    }
                    MapEventPropertyGetter typeGetter = new MapEventBeanArrayIndexedElementPropertyGetter(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 EventPropertyGetter nestedGetter = nestedEventType.getGetter(propertyNested);
                    if (nestedGetter == null)
                    {
                        return null;
                    }
                    Class propertyTypeGetter = nestedEventType.getPropertyType(propertyNested);
                    // construct getter for nested property
                    MapEventPropertyGetter indexGetter = new MapArrayPOJOBeanEntryIndexedPropertyGetter(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);
            EventPropertyGetter getterNestedMap = prop.getGetterMap(null, eventAdapterService);
            if (getterNestedMap == null)
            {
                return null;
            }
            MapEventPropertyGetter mapGetter = new MapPropertyGetter(propertyMap, getterNestedMap);
            propertyGetterCache.put(propertyName, mapGetter);
            return mapGetter;
        }
        else if (nestedType instanceof Map)
        {
            Property prop = PropertyParser.parse(propertyNested, isRootedDynamic);
            Map nestedTypes = (Map) nestedType;
            EventPropertyGetter getterNestedMap = prop.getGetterMap(nestedTypes, eventAdapterService);
            if (getterNestedMap == null)
            {
                return null;
            }
            MapEventPropertyGetter mapGetter = new MapPropertyGetter(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 EventPropertyGetter nestedGetter = nestedEventType.getGetter(propertyNested);
            if (nestedGetter == null)
            {
                return null;
            }
            Class nestedReturnType = nestedEventType.getPropertyType(propertyNested);

            // construct getter for nested property
            getter = new MapPOJOEntryPropertyGetter(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 = new MapEventBeanEntryPropertyGetter(propertyMap, nestedGetter);
            propertyGetterCache.put(propertyName, getter);
            return getter;
        }
        else if (nestedType instanceof EventType[])
        {
            EventType[] typeArray = (EventType[]) nestedType;
            Class underlying = typeArray[0].getUnderlyingType();
            MapEventPropertyGetter beanArrGetter = new MapEventBeanArrayPropertyGetter(propertyMap, underlying);
            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 MapEventType))
            {
                return null;
            }
            EventPropertyGetter innerGetter = innerType.getGetter(propertyNested);
            if (innerGetter == null)
            {
                return null;
            }
            MapEventPropertyGetter maptypeGetter;
            if (!isArray)
            {
                maptypeGetter = new MapMaptypedEntryPropertyGetter(propertyMap, innerGetter, (MapEventType) innerType, eventAdapterService);
            }
            else
            {
                maptypeGetter = new MapArrayMaptypedEntryPropertyGetter(propertyMap, 0, innerGetter, innerType, eventAdapterService);
            }
            propertyGetterCache.put(propertyName, maptypeGetter);
            return maptypeGetter;
        }
        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 EventPropertyGetterMapped getGetterMapped(String mappedPropertyName) {
        EventPropertyDescriptor desc = this.propertyDescriptorMap.get(mappedPropertyName);
        if (desc == null || !desc.isMapped()) {
            return null;
        }
        MappedProperty mappedProperty = new MappedProperty(mappedPropertyName);
        return mappedProperty.getGetterMap(this.nestableTypes, this.eventAdapterService);
    }

    public EventPropertyGetterIndexed getGetterIndexed(String indexedPropertyName) {
        EventPropertyDescriptor desc = this.propertyDescriptorMap.get(indexedPropertyName);
        if (desc == null || !desc.isIndexed()) {
            return null;
        }
        IndexedProperty indexedProperty = new IndexedProperty(indexedPropertyName);
        return indexedProperty.getGetterMap(this.nestableTypes, this.eventAdapterService);
    }

    /**
     * Returns the value of the given property, allowing nested property names.
     * @param propertyName is the name of the property
     * @param values is the map to get the value from
     * @return property value
     */
    public Object getValue(String propertyName, Map values)
    {
        // if a known type, return value
        if (simplePropertyTypes.get(ASTFilterSpecHelper.unescapeDot(propertyName)) != null)
        {
            return values.get(ASTFilterSpecHelper.unescapeDot(propertyName));
        }

        // see if this is a nested property
        int index = ASTFilterSpecHelper.unescapedIndexOfDot(propertyName);
        if (index == -1)
        {
            return null;
        }

        // Take apart the nested property into a map key and a nested value class property name
        final String propertyMap = ASTFilterSpecHelper.unescapeDot(propertyName.substring(0, index));
        String propertyNested = propertyName.substring(index + 1, propertyName.length());

        Class result = simplePropertyTypes.get(propertyMap);
        if (result == null)
        {
            return null;
        }

        // ask the nested class to resolve the property
        EventType nestedType = eventAdapterService.addBeanType(result.getName(), result, false, false, false);
        final EventPropertyGetter nestedGetter = nestedType.getGetter(propertyNested);
        if (nestedGetter == null)
        {
            return null;
        }

        // Wrap object
        Object value = values.get(propertyMap);
        if (value == null)
        {
            return null;
        }
        EventBean event = MapEventType.this.eventAdapterService.adapterForBean(value);
        return nestedGetter.get(event);
    }

    public String[] getPropertyNames()
    {
        return propertyNames;
    }

    public boolean isProperty(String propertyName)
    {
        Class propertyType = getPropertyType(propertyName);
        if (propertyType == null)
        {
            // Could be a native null type, such as "insert into A select null as field..."
            if (simplePropertyTypes.containsKey(ASTFilterSpecHelper.unescapeDot(propertyName)))
            {
                return true;
            }
        }
        return propertyType != null;
    }

    public EventType[] getSuperTypes()
    {
        return optionalSuperTypes;
    }

    public Iterator<EventType> getDeepSuperTypes()
    {
        return optionalDeepSupertypes.iterator();
    }

    public String toString()
    {
        return "MapEventType " +
                "typeName=" + typeName +
                " propertyNames=" + Arrays.toString(propertyNames);
    }

    /**
     * Returns the name-type map of map properties, each value in the map
     * can be a Class or a Map<String, Object> (for nested maps).
     * @return is the property name and types
     */
    public Map<String, Object> getTypes()
    {
        return this.nestableTypes;
    }

    /**
     * Adds additional properties that do not yet exist on the given type.
     * <p.
     * Ignores properties already present. Allows nesting.
     * @param typeMap properties to add
     * @param eventAdapterService for resolving further map event types that are property types
     */
    public void addAdditionalProperties(Map<String, Object> typeMap, EventAdapterService eventAdapterService)
    {
        // merge type graphs
        nestableTypes = GraphUtil.mergeNestableMap(nestableTypes, typeMap);

        // construct getters and types for each property (new or old)
        PropertySetDescriptor propertySet = getNestableMapProperties(typeMap, eventAdapterService);

        // add each new descriptor
        List<EventPropertyDescriptor> newPropertyDescriptors = new ArrayList<EventPropertyDescriptor>();
        for (EventPropertyDescriptor propertyDesc : propertySet.getPropertyDescriptors())
        {
            if (propertyGetters.containsKey(propertyDesc.getPropertyName()))  // not a new property
            {
                continue;
            }
            newPropertyDescriptors.add(propertyDesc);
        }

        // add each that is not already present
        List<String> newPropertyNames = new ArrayList<String>();
        for (String propertyName : propertySet.getPropertyNameList())
        {
            if (propertyGetters.containsKey(propertyName))  // not a new property
            {
                continue;
            }
            newPropertyNames.add(propertyName);
            propertyGetters.put(propertyName, propertySet.getPropertyGetters().get(propertyName));
            simplePropertyTypes.put(propertyName, propertySet.getSimplePropertyTypes().get(propertyName));
        }

        // expand property name array
        String[] allPropertyNames = new String[propertyNames.length + newPropertyNames.size()];
        System.arraycopy(propertyNames, 0, allPropertyNames, 0, propertyNames.length);
        int count = propertyNames.length;
        for (String newProperty : newPropertyNames)
        {
            allPropertyNames[count++] = newProperty;
        }
        propertyNames = allPropertyNames;

        // expand descriptor array
        EventPropertyDescriptor[] allPropertyDescriptors = new EventPropertyDescriptor[propertyDescriptors.length + newPropertyNames.size()];
        System.arraycopy(propertyDescriptors, 0, allPropertyDescriptors, 0, propertyDescriptors.length);
        count = propertyDescriptors.length;
        for (EventPropertyDescriptor desc : newPropertyDescriptors)
        {
            allPropertyDescriptors[count++] = desc;
        }
        propertyDescriptors = allPropertyDescriptors;
    }

    public EventPropertyDescriptor[] getPropertyDescriptors()
    {
        return propertyDescriptors;
    }

    /**
     * Compares two sets of properties and determines if they are the same, allowing for
     * boxed/unboxed types, and nested map types.
     * @param setOne is the first set of properties
     * @param setTwo is the second set of properties
     * @param otherName name of the type compared to
     * @return null if the property set is equivalent or message if not
     */
    public static String isDeepEqualsProperties(String otherName, Map<String, Object> setOne, Map<String, Object> setTwo)
    {
        // Should have the same number of properties
        if (setOne.size() != setTwo.size())
        {
            return "Type by name '" + otherName + "' expects " + setOne.size() + " properties but receives " + setTwo.size() + " properties";
        }

        // Compare property by property
        for (Map.Entry<String, Object> entry : setOne.entrySet())
        {
            String propName = entry.getKey();
            Object setTwoType = setTwo.get(entry.getKey());
            boolean setTwoTypeFound = setTwo.containsKey(entry.getKey());
            Object setOneType = entry.getValue();

            // allow null for nested event types
            if ((setOneType instanceof String || setOneType instanceof EventType) && setTwoType == null) {
                continue;
            }
            if ((setTwoType instanceof String || setTwoType instanceof EventType) && setOneType == null) {
                continue;
            }
            if (!setTwoTypeFound) {
                return "The property '" + propName + "' is not provided but required";
            }
            if (setTwoType == null)
            {
                continue;
            }
            if (((setTwoType == null) && (setOneType != null)) ||
                 (setTwoType != null) && (setOneType == null))
            {
                return "Type by name '" + otherName + "' in property '" + propName + "' incompatible with null-type or property name not found in target";
            }

            if ((setTwoType instanceof Class) && (setOneType instanceof Class))
            {
                Class boxedOther = JavaClassHelper.getBoxedType((Class)setTwoType);
                Class boxedThis = JavaClassHelper.getBoxedType((Class)setOneType);
                if (!boxedOther.equals(boxedThis))
                {
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected " + boxedThis + " but receives " + boxedOther;
                }
            }
            else if ((setTwoType instanceof BeanEventType) && (setOneType instanceof Class))
            {
                Class boxedOther = JavaClassHelper.getBoxedType(((BeanEventType)setTwoType).getUnderlyingType());
                Class boxedThis = JavaClassHelper.getBoxedType((Class)setOneType);
                if (!boxedOther.equals(boxedThis))
                {
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected " + boxedThis + " but receives " + boxedOther;
                }
            }
            else if (setTwoType instanceof EventType[] && ((EventType[])setTwoType)[0] instanceof BeanEventType && setOneType instanceof Class && ((Class) setOneType).isArray())
            {
                Class boxedOther = JavaClassHelper.getBoxedType((((EventType[])setTwoType)[0]).getUnderlyingType());
                Class boxedThis = JavaClassHelper.getBoxedType(((Class)setOneType).getComponentType());
                if (!boxedOther.equals(boxedThis))
                {
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected " + boxedThis + " but receives " + boxedOther;
                }
            }
            else if ((setTwoType instanceof Map) && (setOneType instanceof Map))
            {
                String messageIsDeepEquals = isDeepEqualsProperties(propName, (Map<String, Object>)setOneType, (Map<String, Object>)setTwoType);
                if (messageIsDeepEquals != null)
                {
                    return messageIsDeepEquals;
                }
            }
            else if ((setTwoType instanceof EventType) && (setOneType instanceof EventType))
            {
                boolean mismatch;
                if (setTwoType instanceof EventTypeSPI && setOneType instanceof EventTypeSPI) {
                    mismatch = !((EventTypeSPI) setOneType).equalsCompareType((EventTypeSPI) setTwoType);
                }
                else {
                    mismatch = !setOneType.equals(setTwoType);
                }
                if (mismatch) {
                    EventType setOneEventType = (EventType) setOneType;
                    EventType setTwoEventType = (EventType) setTwoType;
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneEventType.getName() + "' but receives event type '" + setTwoEventType.getName() + "'";
                }
            }
            else if ((setTwoType instanceof String) && (setOneType instanceof EventType))
            {
                if (!((EventType) setOneType).getName().equals(setTwoType))
                {
                    EventType setOneEventType = (EventType) setOneType;
                    String setTwoEventType = (String) setTwoType;
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneEventType.getName() + "' but receives event type '" + setTwoEventType + "'";
                }
            }
            else if ((setTwoType instanceof EventType) && (setOneType instanceof String))
            {
                if (!((EventType) setTwoType).getName().equals(setOneType))
                {
                    String setOneEventType = (String) setOneType;
                    EventType setTwoEventType = (EventType) setTwoType;
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneEventType + "' but receives event type '" + setTwoEventType.getName() + "'";
                }
            }
            else if ((setTwoType instanceof String) && (setOneType instanceof String))
            {
                if (!setTwoType.equals(setOneType))
                {
                    String setOneEventType = (String) setOneType;
                    String setTwoEventType = (String) setTwoType;
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneEventType + "' but receives event type '" + setTwoEventType + "'";
                }
            }
            else if ((setTwoType instanceof EventType[]) && (setOneType instanceof String))
            {
                EventType[] setTwoTypeArr = (EventType[]) setTwoType;
                EventType setTwoFragmentType = setTwoTypeArr[0];
                String setOneTypeString = (String)setOneType;
                if (!(setOneTypeString.endsWith("[]"))) {
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneType + "' but receives event type '" + setTwoFragmentType.getName() + "[]'";
                }
                String setOneTypeNoArray = (setOneTypeString).replaceAll("\\[\\]", "");
                if (!(setTwoFragmentType.getName().equals(setOneTypeNoArray)))
                {
                    return "Type by name '" + otherName + "' in property '" + propName + "' expected event type '" + setOneTypeNoArray + "[]' but receives event type '" + setTwoFragmentType.getName() + "'";
                }
            }
            else
            {
                String typeOne = getTypeName(setOneType);
                String typeTwo = getTypeName(setTwoType);
                if (typeOne.equals(typeTwo)) {
                    continue;
                }
                return "Type by name '" + otherName + "' in property '" + propName + "' expected " + typeOne + " but receives " + typeTwo;
            }
        }

        return null;
    }

    private static String getTypeName(Object type)
    {
        if (type == null)
        {
            return "null";
        }
        if (type instanceof Class)
        {
            return ((Class) type).getName();
        }
        if (type instanceof EventType)
        {
            return "event type '" + ((EventType)type).getName() + "'";
        }
        if (type instanceof String) {
            Class boxedType = JavaClassHelper.getBoxedType(JavaClassHelper.getPrimitiveClassForName((String)type));
            if (boxedType != null) {
                return boxedType.getName();
            }
        }
        return type.getClass().getName();
    }

    private static void generateExceptionNestedProp(String name, Object value) throws EPException
    {
        String clazzName = (value == null) ? "null" : value.getClass().getSimpleName();
        throw new EPException("Nestable map 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 type");
    }

    private void copySuperTypes()
    {
        if (optionalSuperTypes != null)
        {
            Set<String> allProperties = new LinkedHashSet<String>(Arrays.asList(propertyNames));
            Map<String, EventPropertyDescriptor> allDescriptors = new LinkedHashMap<String, EventPropertyDescriptor>();
            for (EventPropertyDescriptor current : propertyDescriptors)
            {
               allDescriptors.put(current.getPropertyName(), current);
            }
            for (int i = 0; i < optionalSuperTypes.length; i++)
            {
                allProperties.addAll(Arrays.asList(optionalSuperTypes[i].getPropertyNames()));
                MapEventType mapSuperType = (MapEventType) optionalSuperTypes[i];
                simplePropertyTypes.putAll(mapSuperType.simplePropertyTypes);
                propertyGetters.putAll(mapSuperType.propertyGetters);
                nestableTypes.putAll(mapSuperType.nestableTypes);
                for (EventPropertyDescriptor desc : optionalSuperTypes[i].getPropertyDescriptors())
                {
                    allDescriptors.put(desc.getPropertyName(), desc);
                }
            }
            propertyNames = allProperties.toArray(new String[allProperties.size()]);
            Collection<EventPropertyDescriptor> descs = allDescriptors.values();
            propertyDescriptors = descs.toArray(new EventPropertyDescriptor[descs.size()]);
            for (EventPropertyDescriptor desc : propertyDescriptors)
            {
                propertyDescriptorMap.put(desc.getPropertyName(), desc);
            }           
        }
    }

    private static PropertySetDescriptor getNestableMapProperties(Map<String, Object> propertiesToAdd, EventAdapterService eventAdapterService)
            throws EPException
    {
        List<String> propertyNameList = new ArrayList<String>();
        List<EventPropertyDescriptor> propertyDescriptors = new ArrayList<EventPropertyDescriptor>();
        Map<String, Class> simplePropertyTypes = new HashMap<String, Class>();
        Map<String, MapEventPropertyGetter> propertyGetters = new HashMap<String, MapEventPropertyGetter>();
        Map<String, FragmentEventType> eventTypeFragments = new HashMap<String, FragmentEventType>();

        // 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 map 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 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, false, isFragment));

                MapEventPropertyGetter getter = new MapEntryPropertyGetter(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);
                MapEventPropertyGetter getter = new MapEntryPropertyGetter(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);
                MapEventPropertyGetter getter = new MapEntryPropertyGetter(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);
                MapEventPropertyGetter getter = new MapEventBeanPropertyGetter(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);
                MapEventPropertyGetter getter = new MapEventBeanArrayPropertyGetter(name, eventType.getUnderlyingType());
                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 MapEventType))
                {
                    throw new EPException("Nestable map 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 type");
                }

                Class underlyingType = eventType.getUnderlyingType();
                if (isArray)
                {
                    underlyingType = Array.newInstance(underlyingType, 0).getClass();
                }
                simplePropertyTypes.put(name, underlyingType);
                propertyNameList.add(name);
                MapEventPropertyGetter getter;
                if (!isArray)
                {
                    getter = new MapMaptypedPropertyGetter(name, eventType, eventAdapterService);
                }
                else
                {
                    getter = new MapMaptypedArrayPropertyGetter(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);
    }

    public EventPropertyDescriptor getPropertyDescriptor(String propertyName)
    {
        return propertyDescriptorMap.get(propertyName);
    }   

    public EventTypeMetadata getMetadata()
    {
        return metadata;
    }

    public FragmentEventType getFragmentType(String propertyName)
    {
        if (simpleFragmentTypes.containsKey(propertyName))  // may contain null values
        {
            return simpleFragmentTypes.get(propertyName);
        }

        // see if this is a nested property
        int index = ASTFilterSpecHelper.unescapedIndexOfDot(propertyName);
        if (index == -1)
        {
            // dynamic simple property
            if (propertyName.endsWith("?"))
            {
                return null;
            }

            // parse, can be an indexed property
            Property property = PropertyParser.parse(propertyName, false);
            if (property instanceof IndexedProperty)
            {
                IndexedProperty indexedProp = (IndexedProperty) property;
                Object type = nestableTypes.get(indexedProp.getPropertyNameAtomic());
                if (type == null)
                {
                    return null;
                }
                else if (type instanceof EventType[])
                {
                    EventType eventType = ((EventType[]) type)[0];
                    return new FragmentEventType(eventType, false, false);
                }
                else if (type instanceof String)
                {
                    String propTypeName = type.toString();
                    boolean isArray = EventTypeUtility.isPropertyArray(propTypeName);
                    if (!isArray) {
                        return null;
                    }
                    propTypeName = EventTypeUtility.getPropertyRemoveArray(propTypeName);
                    EventType innerType = eventAdapterService.getExistsTypeByName(propTypeName);
                    if (!(innerType instanceof MapEventType))
                    {
                        return null;
                    }
                    return new FragmentEventType(innerType, false, false)// false since an index is present
                }
                if (!(type instanceof Class))
                {
                    return null;
                }
                if (!((Class) type).isArray())
                {
                    return null;
                }
                // its an array
                return EventBeanUtility.createNativeFragmentType(((Class)type).getComponentType(), null, eventAdapterService);
            }
            else if (property instanceof MappedProperty)
            {
                // No type information available for the inner event
                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());

        // If the property is dynamic, it cannot be a fragment
        if (propertyMap.endsWith("?"))
        {
            return null;
        }

        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 MapEventType))
                    {
                        return null;
                    }
                    return innerType.getFragmentType(propertyNested);
                }
                // handle eventtype[] in map
                else if (type instanceof EventType[])
                {
                    EventType innerType = ((EventType[]) type)[0];
                    return innerType.getFragmentType(propertyNested);
                }
                // handle array class in map case
                else
                {
                    if (!(type instanceof Class))
                    {
                        return null;
                    }
                    if (!((Class) type).isArray())
                    {
                        return null;
                    }
                    FragmentEventType fragmentParent = EventBeanUtility.createNativeFragmentType((Class) type, null, eventAdapterService);
                    if (fragmentParent == null)
                    {
                        return null;
                    }
                    return fragmentParent.getFragmentType().getFragmentType(propertyNested);
                }
            }
            else if (property instanceof MappedProperty)
            {
                // No type information available for the property's map value
                return null;
            }
            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)
        {
            return null;
        }
        else if (nestedType instanceof Map)
        {
            return null;
        }
        else if (nestedType instanceof Class)
        {
            Class simpleClass = (Class) nestedType;
            if (!JavaClassHelper.isFragmentableType(simpleClass))
            {
                return null;
            }
            EventType nestedEventType = eventAdapterService.getBeanEventTypeFactory().createBeanTypeDefaultName(simpleClass);
            return nestedEventType.getFragmentType(propertyNested);
        }
        else if (nestedType instanceof EventType)
        {
            EventType innerType = (EventType) nestedType;
            return innerType.getFragmentType(propertyNested);
        }
        else if (nestedType instanceof EventType[])
        {
            EventType[] innerType = (EventType[]) nestedType;
            return innerType[0].getFragmentType(propertyNested);
        }
        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 MapEventType))
            {
                return null;
            }
            return innerType.getFragmentType(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);
        }
    }

    /**
     * Returns a message if the type, compared to this type, is not compatible in regards to the property numbers
     * and types.
     * @param otherType to compare to
     * @return message
     */
    public String getEqualsMessage(EventType otherType)
    {
        if (!(otherType instanceof MapEventType))
        {
            return "Type by name '" + otherType.getName() + "' is not a compatible type (target type underlying is '" + otherType.getUnderlyingType().getSimpleName() + "')";
        }

        MapEventType other = (MapEventType) otherType;

        if ((metadata.getTypeClass() != EventTypeMetadata.TypeClass.ANONYMOUS) && (!other.typeName.equals(this.typeName)))
        {
            return "Type by name '" + otherType.getName() + "' is not the same name";
        }

        return isDeepEqualsProperties(otherType.getName(), other.nestableTypes, this.nestableTypes);
    }

    public MapEventBeanPropertyWriter getWriter(String propertyName)
    {
        if (writablePropertyDescriptors == null)
        {
            initializeWriters();
        }
        Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter> pair = propertyWriters.get(propertyName);
        if (pair != null) {
            return pair.getSecond();
        }

        Property property = PropertyParser.parse(propertyName, false);
        if (property instanceof MappedProperty) {
            MappedProperty mapProp = (MappedProperty) property;
            return new MapEventBeanPropertyWriterMapProp(mapProp.getPropertyNameAtomic(), mapProp.getKey());
        }

        if (property instanceof IndexedProperty) {
            IndexedProperty indexedProp = (IndexedProperty) property;
            return new MapEventBeanPropertyWriterIndexedProp(indexedProp.getPropertyNameAtomic(), indexedProp.getIndex());
        }

        return null;
    }

    public EventPropertyDescriptor getWritableProperty(String propertyName)
    {
        if (writablePropertyDescriptors == null)
        {
            initializeWriters();
        }
        Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter> pair = propertyWriters.get(propertyName);
        if (pair != null) {
            return pair.getFirst();
        }

        Property property = PropertyParser.parse(propertyName, false);
        if (property instanceof MappedProperty) {
            EventPropertyWriter writer = getWriter(propertyName);
            if (writer == null) {
                return null;
            }
            MappedProperty mapProp = (MappedProperty) property;
            return new EventPropertyDescriptor(mapProp.getPropertyNameAtomic(), Object.class, null, false, true, false, true, false);
        }
        if (property instanceof IndexedProperty) {
            EventPropertyWriter writer = getWriter(propertyName);
            if (writer == null) {
                return null;
            }
            IndexedProperty indexedProp = (IndexedProperty) property;
            return new EventPropertyDescriptor(indexedProp.getPropertyNameAtomic(), Object.class, null, true, false, true, false, false);
        }
        return null;
    }

    public EventPropertyDescriptor[] getWriteableProperties()
    {
        if (writablePropertyDescriptors == null)
        {
            initializeWriters();
        }
        return writablePropertyDescriptors;
    }

    private void initializeWriters()
    {
        List<EventPropertyDescriptor> writeableProps = new ArrayList<EventPropertyDescriptor>();
        Map<String, Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter>> propertWritersMap = new HashMap<String, Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter>>();
        for (EventPropertyDescriptor prop : propertyDescriptors)
        {
            if (prop.isFragment() || prop.isIndexed() || prop.isMapped())
            {
                continue;
            }
            writeableProps.add(prop);
            final String propertyName = prop.getPropertyName();
            MapEventBeanPropertyWriter eventPropertyWriter = new MapEventBeanPropertyWriter(propertyName);
            propertWritersMap.put(propertyName, new Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter>(prop, eventPropertyWriter));
        }

        propertyWriters = propertWritersMap;
        writablePropertyDescriptors = writeableProps.toArray(new EventPropertyDescriptor[writeableProps.size()]);
    }

    public EventBeanWriter getWriter(String[] properties)
    {
        if (writablePropertyDescriptors == null)
        {
            initializeWriters();
        }

        boolean allSimpleProps = true;
        MapEventBeanPropertyWriter[] writers = new MapEventBeanPropertyWriter[properties.length];
        for (int i = 0; i < properties.length; i++)
        {
            Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter> writerPair = propertyWriters.get(properties[i]);
            if (writerPair != null) {
                writers[i] = writerPair.getSecond();
            }
            else {
                writers[i] = getWriter(properties[i]);
                if (writers[i] == null) {
                    return null;
                }
                allSimpleProps = false;
            }
        }

        if (allSimpleProps) {
            return new MapEventBeanWriterSimpleProps(properties);
        }
        else {
            return new MapEventBeanWriterPerProp(writers);
        }
    }

    public EventBeanCopyMethod getCopyMethod(String[] properties)
    {
        Set<String> mapPropertiesToCopy = new HashSet<String>();
        Set<String> arrayPropertiesToCopy = new HashSet<String>();
        for (int i = 0; i < properties.length; i++) {
            Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter> writerPair = propertyWriters.get(properties[i]);
            if (propertyWriters.containsKey(properties[i])) {
                continue;
            }
            Property prop = PropertyParser.parse(properties[i], false);
            if (prop instanceof MappedProperty) {
                MappedProperty mappedProperty = (MappedProperty) prop;
                mapPropertiesToCopy.add(mappedProperty.getPropertyNameAtomic());
            }
            if (prop instanceof IndexedProperty) {
                IndexedProperty indexedProperty = (IndexedProperty) prop;
                arrayPropertiesToCopy.add(indexedProperty.getPropertyNameAtomic());
            }
        }

        if (mapPropertiesToCopy.isEmpty() && arrayPropertiesToCopy.isEmpty()) {
            return new MapEventBeanCopyMethod(this, eventAdapterService);
        }
        else {
            return new MapEventBeanCopyMethodWithArrayMap(this, eventAdapterService, mapPropertiesToCopy, arrayPropertiesToCopy);
        }
    }

    public boolean equalsCompareType(EventType otherEventType) {
        if (this == otherEventType)
        {
            return true;
        }

        String message = getEqualsMessage(otherEventType);
        return message == null;
    }

    /**
     * Descriptor of a property set.
     */
    public static class PropertySetDescriptor
    {
        private final List<String> propertyNameList;
        private final List<EventPropertyDescriptor> propertyDescriptors;
        private final Map<String, Class> simplePropertyTypes;
        private final Map<String, MapEventPropertyGetter> propertyGetters;
        private final Map<String, FragmentEventType> simpleFragmentTypes;

        /**
         * Ctor.
         * @param propertyNameList property name list
         * @param simplePropertyTypes property types
         * @param propertyDescriptors property descriptors
         * @param propertyGetters property getters
         * @param simpleFragmentTypes fragment types per property
         */
        public PropertySetDescriptor(List<String> propertyNameList, List<EventPropertyDescriptor> propertyDescriptors, Map<String, Class> simplePropertyTypes, Map<String, MapEventPropertyGetter> propertyGetters, Map<String, FragmentEventType> simpleFragmentTypes)
        {
            this.propertyNameList = propertyNameList;
            this.propertyDescriptors = propertyDescriptors;
            this.simplePropertyTypes = simplePropertyTypes;
            this.propertyGetters = propertyGetters;
            this.simpleFragmentTypes = simpleFragmentTypes;
        }

        /**
         * Returns map of property name and class.
         * @return property name and class
         */
        public Map<String, Class> getSimplePropertyTypes()
        {
            return simplePropertyTypes;
        }

        /**
         * Returns map of property name and getter.
         * @return property name and getter
         */
        public Map<String, MapEventPropertyGetter> getPropertyGetters()
        {
            return propertyGetters;
        }

        /**
         * Returns property name list.
         * @return property name list
         */
        public List<String> getPropertyNameList()
        {
            return propertyNameList;
        }

        /**
         * Returns the property descriptors.
         * @return property descriptors
         */
        public List<EventPropertyDescriptor> getPropertyDescriptors()
        {
            return propertyDescriptors;
        }

        /**
         * Returns the property fragment types.
         * @return fragment types.
         */
        public Map<String, FragmentEventType> getSimpleFragmentTypes()
        {
            return simpleFragmentTypes;
        }
    }
}
TOP

Related Classes of com.espertech.esper.event.map.MapEventType

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.