/************************************************************************************** * 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.property; import com.espertech.esper.client.EventPropertyGetter; import com.espertech.esper.client.EventType; import com.espertech.esper.client.PropertyAccessException; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.event.EventTypeUtility; import com.espertech.esper.event.arr.ObjectArrayEventPropertyGetter; import com.espertech.esper.event.bean.BeanEventType; import com.espertech.esper.event.bean.NestedPropertyGetter; import com.espertech.esper.event.map.MapEventPropertyGetter; import com.espertech.esper.event.map.MapNestedPropertyGetterMapOnly; import com.espertech.esper.event.map.MapNestedPropertyGetterMixedType; import com.espertech.esper.event.xml.*; import java.io.StringWriter; import java.util.*; /** * This class represents a nested property, each nesting level made up of a property instance that * can be of type indexed, mapped or simple itself. * <p> * The syntax for nested properties is as follows. * <pre> * a.n * a[1].n * a('1').n * </pre> */ public class NestedProperty implements Property { private List<Property> properties; /** * Ctor. * @param properties is the list of Property instances representing each nesting level */ public NestedProperty(List<Property> properties) { this.properties = properties; } /** * Returns the list of property instances making up the nesting levels. * @return list of Property instances */ public List<Property> getProperties() { return properties; } public boolean isDynamic() { for (Property property : properties) { if (property.isDynamic()) { return true; } } return false; } public EventPropertyGetter getGetter(BeanEventType eventType, EventAdapterService eventAdapterService) { List<EventPropertyGetter> getters = new LinkedList<EventPropertyGetter>(); Property lastProperty = null; for (Iterator<Property> it = properties.iterator(); it.hasNext();) { Property property = it.next(); lastProperty = property; EventPropertyGetter getter = property.getGetter(eventType, eventAdapterService); if (getter == null) { return null; } if (it.hasNext()) { Class clazz = property.getPropertyType(eventType, eventAdapterService); if (clazz == null) { // if the property is not valid, return null return null; } // Map cannot be used to further nest as the type cannot be determined if (clazz == Map.class) { return null; } if (clazz.isArray()) { return null; } eventType = eventAdapterService.getBeanEventTypeFactory().createBeanType(clazz.getName(), clazz, false, false, false); } getters.add(getter); } GenericPropertyDesc finalPropertyType = lastProperty.getPropertyTypeGeneric(eventType, eventAdapterService); return new NestedPropertyGetter(getters, eventAdapterService, finalPropertyType.getType(), finalPropertyType.getGeneric()); } public Class getPropertyType(BeanEventType eventType, EventAdapterService eventAdapterService) { Class result = null; for (Iterator<Property> it = properties.iterator(); it.hasNext();) { Property property = it.next(); result = property.getPropertyType(eventType, eventAdapterService); if (result == null) { // property not found, return null return null; } if (it.hasNext()) { // Map cannot be used to further nest as the type cannot be determined if (result == Map.class) { return null; } if (result.isArray()) { return null; } eventType = eventAdapterService.getBeanEventTypeFactory().createBeanType(result.getName(), result, false, false, false); } } return result; } public GenericPropertyDesc getPropertyTypeGeneric(BeanEventType eventType, EventAdapterService eventAdapterService) { GenericPropertyDesc result = null; for (Iterator<Property> it = properties.iterator(); it.hasNext();) { Property property = it.next(); result = property.getPropertyTypeGeneric(eventType, eventAdapterService); if (result == null) { // property not found, return null return null; } if (it.hasNext()) { // Map cannot be used to further nest as the type cannot be determined if (result.getType() == Map.class) { return null; } if (result.getType().isArray()) { return null; } eventType = eventAdapterService.getBeanEventTypeFactory().createBeanType(result.getType().getName(), result.getType(), false, false, false); } } return result; } public Class getPropertyTypeMap(Map optionalMapPropTypes, EventAdapterService eventAdapterService) { Map currentDictionary = optionalMapPropTypes; int count = 0; for (Iterator<Property> it = properties.iterator(); it.hasNext();) { count++; Property property = it.next(); PropertyBase theBase = (PropertyBase) property; String propertyName = theBase.getPropertyNameAtomic(); Object nestedType = null; if (currentDictionary != null) { nestedType = currentDictionary.get(propertyName); } if (nestedType == null) { if (property instanceof DynamicProperty) { return Object.class; } else { return null; } } if (!it.hasNext()) { if (nestedType instanceof Class) { return (Class) nestedType; } if (nestedType instanceof Map) { return Map.class; } } if (nestedType == Map.class) { return Object.class; } if (nestedType instanceof Class) { Class pojoClass = (Class) nestedType; if (!pojoClass.isArray()) { BeanEventType beanType = eventAdapterService.getBeanEventTypeFactory().createBeanType(pojoClass.getName(), pojoClass, false, false, false); String remainingProps = toPropertyEPL(properties, count); return beanType.getPropertyType(remainingProps); } else if (property instanceof IndexedProperty) { Class componentType = pojoClass.getComponentType(); BeanEventType beanType = eventAdapterService.getBeanEventTypeFactory().createBeanType(componentType.getName(), componentType, false, false, false); String remainingProps = toPropertyEPL(properties, count); return beanType.getPropertyType(remainingProps); } } if (nestedType instanceof String) // property type is the name of a map event type { String nestedName = nestedType.toString(); boolean isArray = EventTypeUtility.isPropertyArray(nestedName); if (isArray) { nestedName = EventTypeUtility.getPropertyRemoveArray(nestedName); } EventType innerType = eventAdapterService.getExistsTypeByName(nestedName); if (innerType == null) { return null; } String remainingProps = toPropertyEPL(properties, count); return innerType.getPropertyType(remainingProps); } else if (nestedType instanceof EventType) // property type is the name of a map event type { EventType innerType = (EventType) nestedType; String remainingProps = toPropertyEPL(properties, count); return innerType.getPropertyType(remainingProps); } else { if (!(nestedType instanceof Map)) { 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); } } currentDictionary = (Map) nestedType; } throw new IllegalStateException("Unexpected end of nested property"); } public MapEventPropertyGetter getGetterMap(Map optionalMapPropTypes, EventAdapterService eventAdapterService) { List<EventPropertyGetter> getters = new LinkedList<EventPropertyGetter>(); Map currentDictionary = optionalMapPropTypes; int count = 0; for (Iterator<Property> it = properties.iterator(); it.hasNext();) { count++; Property property = it.next(); // manufacture a getter for getting the item out of the map EventPropertyGetter getter = property.getGetterMap(currentDictionary, eventAdapterService); if (getter == null) { return null; } getters.add(getter); PropertyBase theBase = (PropertyBase) property; String propertyName = theBase.getPropertyNameAtomic(); // For the next property if there is one, check how to property type is defined if (!it.hasNext()) { continue; } if (currentDictionary != null) { // check the type that this property will return Object propertyReturnType = currentDictionary.get(propertyName); if (propertyReturnType == null) { currentDictionary = null; } if (propertyReturnType != null) { if (propertyReturnType instanceof Map) { currentDictionary = (Map) propertyReturnType; } else if (propertyReturnType == Map.class) { currentDictionary = null; } else if (propertyReturnType instanceof String) { String nestedName = propertyReturnType.toString(); boolean isArray = EventTypeUtility.isPropertyArray(nestedName); if (isArray) { nestedName = EventTypeUtility.getPropertyRemoveArray(nestedName); } EventType innerType = eventAdapterService.getExistsTypeByName(nestedName); if (innerType == null) { return null; } String remainingProps = toPropertyEPL(properties, count); EventPropertyGetter getterInner = innerType.getGetter(remainingProps); if (getterInner == null) { return null; } getters.add(getterInner); break; // the single Pojo getter handles the rest } else if (propertyReturnType instanceof EventType) { EventType innerType = (EventType) propertyReturnType; String remainingProps = toPropertyEPL(properties, count); EventPropertyGetter getterInner = innerType.getGetter(remainingProps); if (getterInner == null) { return null; } getters.add(getterInner); break; // the single Pojo getter handles the rest } else { // treat the return type of the map property as a POJO Class pojoClass = (Class) propertyReturnType; if (!pojoClass.isArray()) { BeanEventType beanType = eventAdapterService.getBeanEventTypeFactory().createBeanType(pojoClass.getName(), pojoClass, false, false, false); String remainingProps = toPropertyEPL(properties, count); EventPropertyGetter getterInner = beanType.getGetter(remainingProps); if (getterInner == null) { return null; } getters.add(getterInner); break; // the single Pojo getter handles the rest } else { Class componentType = pojoClass.getComponentType(); BeanEventType beanType = eventAdapterService.getBeanEventTypeFactory().createBeanType(componentType.getName(), componentType, false, false, false); String remainingProps = toPropertyEPL(properties, count); EventPropertyGetter getterInner = beanType.getGetter(remainingProps); if (getterInner == null) { return null; } getters.add(getterInner); break; // the single Pojo getter handles the rest } } } } } boolean hasNonmapGetters = false; for (int i = 0; i < getters.size(); i++) { if (!(getters.get(i) instanceof MapEventPropertyGetter)) { hasNonmapGetters = true; } } if (!hasNonmapGetters) { return new MapNestedPropertyGetterMapOnly(getters, eventAdapterService); } else { return new MapNestedPropertyGetterMixedType(getters, eventAdapterService); } } public void toPropertyEPL(StringWriter writer) { String delimiter = ""; for (Property property : properties) { writer.append(delimiter); property.toPropertyEPL(writer); delimiter = "."; } } public String[] toPropertyArray() { List<String> propertyNames = new ArrayList<String>(); for (Property property : properties) { String[] nested = property.toPropertyArray(); propertyNames.addAll(Arrays.asList(nested)); } return propertyNames.toArray(new String[propertyNames.size()]); } public EventPropertyGetter getGetterDOM() { List<EventPropertyGetter> getters = new LinkedList<EventPropertyGetter>(); for (Iterator<Property> it = properties.iterator(); it.hasNext();) { Property property = it.next(); EventPropertyGetter getter = property.getGetterDOM(); if (getter == null) { return null; } getters.add(getter); } return new DOMNestedPropertyGetter(getters, null); } public EventPropertyGetter getGetterDOM(SchemaElementComplex parentComplexProperty, EventAdapterService eventAdapterService, BaseXMLEventType eventType, String propertyExpression) { List<EventPropertyGetter> getters = new LinkedList<EventPropertyGetter>(); SchemaElementComplex complexElement = parentComplexProperty; for (Iterator<Property> it = properties.iterator(); it.hasNext();) { Property property = it.next(); EventPropertyGetter getter = property.getGetterDOM(complexElement, eventAdapterService, eventType, propertyExpression); if (getter == null) { return null; } if (it.hasNext()) { SchemaItem childSchemaItem = property.getPropertyTypeSchema(complexElement, eventAdapterService); if (childSchemaItem == null) { // if the property is not valid, return null return null; } if ((childSchemaItem instanceof SchemaItemAttribute) || (childSchemaItem instanceof SchemaElementSimple)) { return null; } complexElement = (SchemaElementComplex) childSchemaItem; if (complexElement.isArray()) { if ((property instanceof SimpleProperty) || (property instanceof DynamicSimpleProperty)) { return null; } } } getters.add(getter); } return new DOMNestedPropertyGetter(getters, new FragmentFactoryDOMGetter(eventAdapterService, eventType, propertyExpression)); } public SchemaItem getPropertyTypeSchema(SchemaElementComplex parentComplexProperty, EventAdapterService eventAdapterService) { Property lastProperty = null; SchemaElementComplex complexElement = parentComplexProperty; for (Iterator<Property> it = properties.iterator(); it.hasNext();) { Property property = it.next(); lastProperty = property; if (it.hasNext()) { SchemaItem childSchemaItem = property.getPropertyTypeSchema(complexElement, eventAdapterService); if (childSchemaItem == null) { // if the property is not valid, return null return null; } if ((childSchemaItem instanceof SchemaItemAttribute) || (childSchemaItem instanceof SchemaElementSimple)) { return null; } complexElement = (SchemaElementComplex) childSchemaItem; } } return lastProperty.getPropertyTypeSchema(complexElement, eventAdapterService); } public ObjectArrayEventPropertyGetter getGetterObjectArray(Map<String, Integer> indexPerProperty, Map<String, Object> nestableTypes, EventAdapterService eventAdapterService) { throw new UnsupportedOperationException("Object array nested property getter not implemented as not implicitly nestable"); } private static String toPropertyEPL(List<Property> property, int startFromIndex) { String delimiter = ""; StringWriter writer = new StringWriter(); for (int i = startFromIndex; i < property.size(); i++) { writer.append(delimiter); property.get(i).toPropertyEPL(writer); delimiter = "."; } return writer.getBuffer().toString(); } }