class BeanUtil {

    public static BeanProperty findBeanProperty(Class type, String propertyName, boolean caseSensitive) throws IllegalAccessException {
        // bean property we'd return if found
        BeanProperty beanProperty = null;

        // class we'll start our search from
        Class classType = type;

        //
        // search for field first - loop thru all classes
        //
        while (classType != null && !classType.equals(Object.class)) {
            // search all declared fields
            for (Field f : classType.getDeclaredFields()) {
                // grab this field's name
                String fieldName = f.getName();
                // check if its the property we're searching for
                if ((!caseSensitive && fieldName.equalsIgnoreCase(propertyName)) || (caseSensitive && fieldName.equals(propertyName))) {
                    // success, we found the method we're looking for
                    Class fieldType = f.getType();
                    // make sure its accessible
                    f.setAccessible(true);
                    // create the BeanProperty we're going to use
                    beanProperty = new BeanProperty(propertyName, fieldType, f);
                    // done with this loop, break out of it
                    break;
                }
            }
            // move onto the superclass
            classType = classType.getSuperclass();
        }

        String capitalizedName = StringUtil.capitalize(propertyName);
        String getMethodName = "get" + capitalizedName;
        String setMethodName = "set" + capitalizedName;
        String addMethodName = "add" + capitalizedName;

        
        // if we get here and the beanProperty does not exist, then we need
        // to search thru the hierarchy of classes for a setter or getter
        // method that matches the name
        if (beanProperty == null) {
            // class we'll start our search from
            classType = type;
            while (classType != null && !classType.equals(Object.class)) {
                // search all declared fields
                for (Method m : classType.getDeclaredMethods()) {
                    // grab this methods's name
                    String methodName = m.getName();
                    // is this a getter we've been looking for?
                    if ((!caseSensitive && methodName.equalsIgnoreCase(getMethodName)) || (caseSensitive && methodName.equals(getMethodName))) {
                        // check to make sure this accepts no arguments
                        if (m.getParameterTypes().length == 0) {
                            // success, we found the getter method
                            beanProperty = new BeanProperty(propertyName, m.getReturnType(), null);
                            m.setAccessible(true);
                            beanProperty.getMethod = m;
                            break;
                        }
                    // is this a setter we've been looking for?
                    } else if ((!caseSensitive && methodName.equalsIgnoreCase(setMethodName)) || (caseSensitive && methodName.equals(setMethodName))) {
                        // check to make sure this accepts 1 argument
                        if (m.getParameterTypes().length == 1) {
                            // success, we found the setter method
                            beanProperty = new BeanProperty(propertyName, m.getParameterTypes()[0], null);
                            m.setAccessible(true);
                            beanProperty.setMethod = m;
                            break;
                        }
                    // is this a adder we've been looking for?
                    } else if ((!caseSensitive && methodName.equalsIgnoreCase(addMethodName)) || (caseSensitive && methodName.equals(addMethodName))) {
                        // check to make sure this accepts 1 argument
                        if (m.getParameterTypes().length == 1) {
                            // success, we found the adder method
                            beanProperty = new BeanProperty(propertyName, m.getParameterTypes()[0], null);
                            m.setAccessible(true);
                            beanProperty.addMethod = m;
                            break;
                        }
                    }
                }
                // move onto the superclass
                classType = classType.getSuperclass();
            }
        }

        // if we get here and beanProperty exists, then we need to search for
        // whether its getter/setter methods also exist
        if (beanProperty != null) {
            if (beanProperty.getMethod == null) {
                try {
                    // look for a "getter" method
                    Method getMethod = ClassUtil.getMethod(type, getMethodName, beanProperty.getType(), null, caseSensitive);
                    // set to accessible?
                    getMethod.setAccessible(true);
                    beanProperty.getMethod = getMethod;
                } catch (NoSuchMethodException e) {
                    // its okay if this happens, ignore
                }
            }

            if (beanProperty.setMethod == null) {
                try {
                    // look for a "setter" method
                    Method setMethod = ClassUtil.getMethod(type, setMethodName, null, beanProperty.getType(), caseSensitive);
                    // set to accessible?
                    setMethod.setAccessible(true);
                    beanProperty.setMethod = setMethod;
                } catch (NoSuchMethodException e) {
                    // its okay if this happens, ignore
                }
            }

            if (beanProperty.addMethod == null) {
                try {
                    // look for a "adder" method
                    Method addMethod = ClassUtil.getMethod(type, addMethodName, null, beanProperty.getType(), caseSensitive);
                    // set to accessible?
                    addMethod.setAccessible(true);
                    beanProperty.addMethod = addMethod;
                } catch (NoSuchMethodException e) {
                    // its okay if this happens, ignore
                }
            }
        }

        return beanProperty;
    }

}