/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.event.vaevent; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventPropertyGetter; import com.espertech.esper.client.EventType; import com.espertech.esper.client.PropertyAccessException; import com.espertech.esper.collection.MultiKey; import com.espertech.esper.collection.MultiKeyUntyped; import com.espertech.esper.util.JavaClassHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.*; /** * Utility for handling properties for the purpose of merging and versioning by revision event types. */ public class PropertyUtility { private static final Logger log = LoggerFactory.getLogger(PropertyUtility.class); /** * Returns a multi-key for an event and key property getters * * @param theEvent to get keys for * @param keyPropertyGetters getters to use * @return key */ public static Object getKeys(EventBean theEvent, EventPropertyGetter[] keyPropertyGetters) { if (keyPropertyGetters.length == 1) { return keyPropertyGetters[0].get(theEvent); } Object[] keys = new Object[keyPropertyGetters.length]; for (int i = 0; i < keys.length; i++) { keys[i] = keyPropertyGetters[i].get(theEvent); } return new MultiKeyUntyped(keys); } /** * From a list of property groups that include contributing event types, build a map * of contributing event types and their type descriptor. * * @param groups property groups * @param changesetProperties properties that change between groups * @param keyProperties key properties * @return map of event type and type information */ public static Map<EventType, RevisionTypeDesc> getPerType(PropertyGroupDesc[] groups, String[] changesetProperties, String[] keyProperties) { Map<EventType, RevisionTypeDesc> perType = new HashMap<EventType, RevisionTypeDesc>(); for (PropertyGroupDesc group : groups) { for (EventType type : group.getTypes().keySet()) { EventPropertyGetter[] changesetGetters = getGetters(type, changesetProperties); EventPropertyGetter[] keyGetters = getGetters(type, keyProperties); RevisionTypeDesc pair = new RevisionTypeDesc(keyGetters, changesetGetters, group); perType.put(type, pair); } } return perType; } /** * From a list of property groups that include multiple group numbers for each property, * make a map of group numbers per property. * * @param groups property groups * @return map of property name and group number */ public static Map<String, int[]> getGroupsPerProperty(PropertyGroupDesc[] groups) { Map<String, int[]> groupsNumsPerProp = new HashMap<String, int[]>(); for (PropertyGroupDesc group : groups) { for (String property : group.getProperties()) { int[] value = groupsNumsPerProp.get(property); if (value == null) { value = new int[1]; groupsNumsPerProp.put(property, value); value[0] = group.getGroupNum(); } else { int[] copy = new int[value.length + 1]; System.arraycopy(value, 0, copy, 0, value.length); copy[value.length] = group.getGroupNum(); Arrays.sort(copy); groupsNumsPerProp.put(property, copy); } } } return groupsNumsPerProp; } /** * Analyze multiple event types and determine common property sets that form property groups. * * @param allProperties property names to look at * @param deltaEventTypes all types contributing * @param names names of properies * @return groups */ public static PropertyGroupDesc[] analyzeGroups(String[] allProperties, EventType[] deltaEventTypes, String[] names) { if (deltaEventTypes.length != names.length) { throw new IllegalArgumentException("Delta event type number and name number of elements don't match"); } allProperties = copyAndSort(allProperties); Map<MultiKey<String>, PropertyGroupDesc> result = new LinkedHashMap<MultiKey<String>, PropertyGroupDesc>(); int currentGroupNum = 0; for (int i = 0; i < deltaEventTypes.length; i++) { MultiKey<String> props = getPropertiesContributed(deltaEventTypes[i], allProperties); if (props.getArray().length == 0) { log.warn("Event type named '" + names[i] + "' does not contribute (or override) any properties of the revision event type"); continue; } PropertyGroupDesc propertyGroup = result.get(props); Map<EventType, String> typesForGroup; if (propertyGroup == null) { typesForGroup = new HashMap<EventType, String>(); propertyGroup = new PropertyGroupDesc(currentGroupNum++, typesForGroup, props.getArray()); result.put(props, propertyGroup); } else { typesForGroup = propertyGroup.getTypes(); } typesForGroup.put(deltaEventTypes[i], names[i]); } Collection<PropertyGroupDesc> outColl = result.values(); PropertyGroupDesc[] array = outColl.toArray(new PropertyGroupDesc[outColl.size()]); if (log.isDebugEnabled()) { log.debug(".analyzeGroups " + Arrays.toString(array)); } return array; } private static MultiKey<String> getPropertiesContributed(EventType deltaEventType, String[] allPropertiesSorted) { TreeSet<String> props = new TreeSet<String>(); for (String property : deltaEventType.getPropertyNames()) { for (String propInAll : allPropertiesSorted) { if (propInAll.equals(property)) { props.add(property); break; } } } return new MultiKey<String>(props.toArray(new String[props.size()])); } /** * Copy an sort the input array. * * @param input to sort * @return sorted copied array */ protected static String[] copyAndSort(String[] input) { String[] result = new String[input.length]; System.arraycopy(input, 0, result, 0, input.length); Arrays.sort(result); return result; } /** * Return getters for property names. * * @param eventType type to get getters from * @param propertyNames names to get * @return getters */ public static EventPropertyGetter[] getGetters(EventType eventType, String[] propertyNames) { EventPropertyGetter[] getters = new EventPropertyGetter[propertyNames.length]; for (int i = 0; i < getters.length; i++) { getters[i] = eventType.getGetter(propertyNames[i]); } return getters; } /** * Remove from values all removeValues and build a unique sorted result array. * * @param values to consider * @param removeValues values to remove from values * @return sorted unique */ protected static String[] uniqueExclusiveSort(String[] values, String[] removeValues) { Set<String> unique = new HashSet<String>(); unique.addAll(Arrays.asList(values)); for (String removeValue : removeValues) { unique.remove(removeValue); } String[] uniqueArr = unique.toArray(new String[unique.size()]); Arrays.sort(uniqueArr); return uniqueArr; } public static PropertyAccessException getMismatchException(Method method, Object object, ClassCastException e) { return getMismatchException(method.getDeclaringClass(), object, e); } public static PropertyAccessException getMismatchException(Field field, Object object, ClassCastException e) { return getMismatchException(field.getDeclaringClass(), object, e); } public static PropertyAccessException getInvocationTargetException(Method method, InvocationTargetException e) { Class declaring = method.getDeclaringClass(); String message = "Failed to invoke method " + method.getName() + " on class " + JavaClassHelper.getClassNameFullyQualPretty(declaring) + ": " + e.getTargetException().getMessage(); throw new PropertyAccessException(message, e); } public static PropertyAccessException getIllegalAccessException(Field field, IllegalAccessException e) { return getAccessExceptionField(field, e); } public static PropertyAccessException getIllegalArgumentException(Field field, IllegalArgumentException e) { return getAccessExceptionField(field, e); } private static PropertyAccessException getAccessExceptionField(Field field, Exception e) { Class declaring = field.getDeclaringClass(); String message = "Failed to obtain field value for field " + field.getName() + " on class " + JavaClassHelper.getClassNameFullyQualPretty(declaring) + ": " + e.getMessage(); throw new PropertyAccessException(message, e); } private static PropertyAccessException getMismatchException(Class declared, Object object, ClassCastException e) { String classNameExpected = JavaClassHelper.getClassNameFullyQualPretty(declared); String classNameReceived; if (object != null) { classNameReceived = JavaClassHelper.getClassNameFullyQualPretty(object.getClass()); } else { classNameReceived = "null"; } if (classNameExpected.equals(classNameReceived)) { classNameExpected = JavaClassHelper.getClassNameFullyQualPrettyWithClassloader(declared); classNameReceived = object != null ? JavaClassHelper.getClassNameFullyQualPrettyWithClassloader(object.getClass()) : "null"; } String message = "Mismatched getter instance to event bean type, expected " + classNameExpected + " but received " + classNameReceived; throw new PropertyAccessException(message, e); } public static PropertyAccessException getIllegalAccessException(Method method, IllegalAccessException e) { return getAccessExceptionMethod(method, e); } public static PropertyAccessException getIllegalArgumentException(Method method, IllegalArgumentException e) { return getAccessExceptionMethod(method, e); } private static PropertyAccessException getAccessExceptionMethod(Method method, Exception e) { Class declaring = method.getDeclaringClass(); String message = "Failed to invoke method " + method.getName() + " on class " + JavaClassHelper.getClassNameFullyQualPretty(declaring) + ": " + e.getMessage(); throw new PropertyAccessException(message, e); } }