com.espertech.esper.event.bean.PropertyHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.espertech.esper.event.bean.PropertyHelper.java

Source

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

import com.espertech.esper.client.EventPropertyGetter;
import com.espertech.esper.event.EventAdapterService;
import com.espertech.esper.event.EventPropertyType;
import com.espertech.esper.event.WriteablePropertyDescriptor;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.beans.*;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.*;

/**
 * This class offers utililty methods around introspection and CGLIB interaction.
 */
public class PropertyHelper {
    /**
     * Return getter for the given method and CGLIB FastClass.
     * @param method to return getter for
     * @param fastClass is the CGLIB fast classs to make FastMethod for
     * @param eventAdapterService factory for event beans and event types
     * @return property getter
     */
    public static EventPropertyGetter getGetter(Method method, FastClass fastClass,
            EventAdapterService eventAdapterService) {
        // Get CGLib fast method handle
        FastMethod fastMethod = null;
        try {
            if (fastClass != null) {
                fastMethod = fastClass.getMethod(method);
            }
        } catch (Throwable ex) {
            log.warn(".getAccessors Unable to obtain CGLib fast method implementation, msg=" + ex.getMessage());
        }

        // Construct the appropriate property getter CGLib or reflect
        EventPropertyGetter getter;
        if (fastMethod != null) {
            getter = new CGLibPropertyGetter(method, fastMethod, eventAdapterService);
        } else {
            getter = new ReflectionPropMethodGetter(method, eventAdapterService);
        }

        return getter;
    }

    /**
     * Introspects the given class and returns event property descriptors for each property found
     * in the class itself, it's superclasses and all interfaces this class and the superclasses implements.
     * @param clazz is the Class to introspect
     * @return list of properties
     */
    public static List<InternalEventPropDescriptor> getProperties(Class clazz) {
        // Determine all interfaces implemented and the interface's parent interfaces if any
        Set<Class> propertyOrigClasses = new HashSet<Class>();
        getImplementedInterfaceParents(clazz, propertyOrigClasses);

        // Add class itself
        propertyOrigClasses.add(clazz);

        // Get the set of property names for all classes
        return getPropertiesForClasses(propertyOrigClasses);
    }

    /**
     * Introspects the given class and returns event property descriptors for each writable property found
     * in the class itself, it's superclasses and all interfaces this class and the superclasses implements.
     * @param clazz is the Class to introspect
     * @return list of properties
     */
    public static Set<WriteablePropertyDescriptor> getWritableProperties(Class clazz) {
        // Determine all interfaces implemented and the interface's parent interfaces if any
        Set<Class> propertyOrigClasses = new HashSet<Class>();
        getImplementedInterfaceParents(clazz, propertyOrigClasses);

        // Add class itself
        propertyOrigClasses.add(clazz);

        // Get the set of property names for all classes
        return getWritablePropertiesForClasses(propertyOrigClasses);
    }

    private static void getImplementedInterfaceParents(Class clazz, Set<Class> classesResult) {
        Class[] interfaces = clazz.getInterfaces();

        if (interfaces == null) {
            return;
        }

        for (int i = 0; i < interfaces.length; i++) {
            classesResult.add(interfaces[i]);
            getImplementedInterfaceParents(interfaces[i], classesResult);
        }
    }

    private static Set<WriteablePropertyDescriptor> getWritablePropertiesForClasses(Set<Class> propertyClasses) {
        Set<WriteablePropertyDescriptor> result = new HashSet<WriteablePropertyDescriptor>();

        for (Class clazz : propertyClasses) {
            addIntrospectPropertiesWritable(clazz, result);
        }

        return result;
    }

    private static List<InternalEventPropDescriptor> getPropertiesForClasses(Set<Class> propertyClasses) {
        List<InternalEventPropDescriptor> result = new LinkedList<InternalEventPropDescriptor>();

        for (Class clazz : propertyClasses) {
            addIntrospectProperties(clazz, result);
            addMappedProperties(clazz, result);
        }

        removeDuplicateProperties(result);
        removeJavaProperties(result);

        return result;
    }

    /**
     * Remove Java language specific properties from the given list of property descriptors.
     * @param properties is the list of property descriptors
     */
    protected static void removeJavaProperties(List<InternalEventPropDescriptor> properties) {
        List<InternalEventPropDescriptor> toRemove = new LinkedList<InternalEventPropDescriptor>();

        // add removed entries to separate list
        for (InternalEventPropDescriptor desc : properties) {
            if ((desc.getPropertyName().equals("class")) || (desc.getPropertyName().equals("getClass"))
                    || (desc.getPropertyName().equals("toString")) || (desc.getPropertyName().equals("hashCode"))) {
                toRemove.add(desc);
            }
        }

        // remove
        for (InternalEventPropDescriptor desc : toRemove) {
            properties.remove(desc);
        }
    }

    /**
     * Removed duplicate properties using the property name to find unique properties.
     * @param properties is a list of property descriptors
     */
    protected static void removeDuplicateProperties(List<InternalEventPropDescriptor> properties) {
        LinkedHashMap<String, InternalEventPropDescriptor> set = new LinkedHashMap<String, InternalEventPropDescriptor>();
        List<InternalEventPropDescriptor> toRemove = new LinkedList<InternalEventPropDescriptor>();

        // add duplicates to separate list
        for (InternalEventPropDescriptor desc : properties) {
            if (set.containsKey(desc.getPropertyName())) {
                toRemove.add(desc);
                continue;
            }
            set.put(desc.getPropertyName(), desc);
        }

        // remove duplicates
        for (InternalEventPropDescriptor desc : toRemove) {
            properties.remove(desc);
        }
    }

    /**
     * Adds to the given list of property descriptors the properties of the given class
     * using the Introspector to introspect properties. This also finds array and indexed properties.
     * @param clazz to introspect
     * @param result is the list to add to
     */
    protected static void addIntrospectProperties(Class clazz, List<InternalEventPropDescriptor> result) {
        PropertyDescriptor properties[] = introspect(clazz);
        for (int i = 0; i < properties.length; i++) {
            PropertyDescriptor property = properties[i];
            String propertyName = property.getName();
            Method readMethod = property.getReadMethod();

            EventPropertyType type = EventPropertyType.SIMPLE;
            if (property instanceof IndexedPropertyDescriptor) {
                readMethod = ((IndexedPropertyDescriptor) property).getIndexedReadMethod();
                type = EventPropertyType.INDEXED;
            }

            if (readMethod == null) {
                continue;
            }

            result.add(new InternalEventPropDescriptor(propertyName, readMethod, type));
        }
    }

    private static void addIntrospectPropertiesWritable(Class clazz, Set<WriteablePropertyDescriptor> result) {
        PropertyDescriptor properties[] = introspect(clazz);
        for (int i = 0; i < properties.length; i++) {
            PropertyDescriptor property = properties[i];
            String propertyName = property.getName();
            Method writeMethod = property.getWriteMethod();

            if (writeMethod == null) {
                continue;
            }

            result.add(
                    new WriteablePropertyDescriptor(propertyName, writeMethod.getParameterTypes()[0], writeMethod));
        }
    }

    /**
     * Adds to the given list of property descriptors the mapped properties, ie.
     * properties that have a getter method taking a single String value as a parameter.
     * @param clazz to introspect
     * @param result is the list to add to
     */
    protected static void addMappedProperties(Class clazz, List<InternalEventPropDescriptor> result) {
        Set<String> uniquePropertyNames = new HashSet<String>();
        Method[] methods = clazz.getMethods();

        for (int i = 0; i < methods.length; i++) {
            String methodName = methods[i].getName();
            if (!methodName.startsWith("get")) {
                continue;
            }

            String inferredName = methodName.substring(3, methodName.length());
            if (inferredName.length() == 0) {
                continue;
            }

            Class<?> parameterTypes[] = methods[i].getParameterTypes();
            if (parameterTypes.length != 1) {
                continue;
            }

            if (parameterTypes[0] != String.class) {
                continue;
            }

            String newInferredName = null;
            // Leave uppercase inferred names such as URL
            if (inferredName.length() >= 2) {
                if ((Character.isUpperCase(inferredName.charAt(0)))
                        && (Character.isUpperCase(inferredName.charAt(1)))) {
                    newInferredName = inferredName;
                }
            }
            // camelCase the inferred name
            if (newInferredName == null) {
                newInferredName = Character.toString(Character.toLowerCase(inferredName.charAt(0)));
                if (inferredName.length() > 1) {
                    newInferredName += inferredName.substring(1, inferredName.length());
                }
            }
            inferredName = newInferredName;

            // if the property inferred name already exists, don't supply it
            if (uniquePropertyNames.contains(inferredName)) {
                continue;
            }

            result.add(new InternalEventPropDescriptor(inferredName, methods[i], EventPropertyType.MAPPED));
            uniquePropertyNames.add(inferredName);
        }
    }

    /**
     * Using the Java Introspector class the method returns the property descriptors obtained through introspection.
     * @param clazz to introspect
     * @return array of property descriptors
     */
    protected static PropertyDescriptor[] introspect(Class clazz) {
        BeanInfo beanInfo;

        try {
            beanInfo = Introspector.getBeanInfo(clazz);
        } catch (IntrospectionException e) {
            return (new PropertyDescriptor[0]);
        }

        return beanInfo.getPropertyDescriptors();
    }

    public static String getGetterMethodName(String propertyName) {
        return getGetterSetterMethodName(propertyName, "get");
    }

    public static String getSetterMethodName(String propertyName) {
        return getGetterSetterMethodName(propertyName, "set");
    }

    public static String getIsMethodName(String propertyName) {
        return getGetterSetterMethodName(propertyName, "is");
    }

    private static String getGetterSetterMethodName(String propertyName, String operation) {
        StringWriter writer = new StringWriter();
        writer.write(operation);
        writer.write(Character.toUpperCase(propertyName.charAt(0)));
        writer.write(propertyName.substring(1));
        return writer.toString();
    }

    private static final Log log = LogFactory.getLog(PropertyHelper.class);
}