Kawai Wiki

Rev

Blame | Last modification | View Log | RSS feed

package org.kawai.helper;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;

/**
 * Test utility class that makes easy work of testing default behavior of getters and setters.
 */

public final class PropertyAsserter {

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

        private static final Map<Class, Object> TYPE_ARGUMENTS = new HashMap<Class, Object>();

        static {
                TYPE_ARGUMENTS.put(Collection.class, new ArrayList());
                TYPE_ARGUMENTS.put(List.class, new ArrayList());
                TYPE_ARGUMENTS.put(Set.class, new HashSet());
                TYPE_ARGUMENTS.put(SortedSet.class, new TreeSet());
                TYPE_ARGUMENTS.put(Map.class, new HashMap());
                TYPE_ARGUMENTS.put(SortedMap.class, new TreeMap());
                TYPE_ARGUMENTS.put(Boolean.class, true);
                TYPE_ARGUMENTS.put(Boolean.TYPE, true);
                TYPE_ARGUMENTS.put(Character.class, 'Z');
                TYPE_ARGUMENTS.put(Character.TYPE, 'Z');
                TYPE_ARGUMENTS.put(Byte.class, (byte) 10);
                TYPE_ARGUMENTS.put(Byte.TYPE, (byte) 10);
                TYPE_ARGUMENTS.put(Short.class, (short) 10);
                TYPE_ARGUMENTS.put(Short.TYPE, (short) 10);
                TYPE_ARGUMENTS.put(Integer.class, 10);
                TYPE_ARGUMENTS.put(Integer.TYPE, 10);
                TYPE_ARGUMENTS.put(Long.class, 10L);
                TYPE_ARGUMENTS.put(Long.TYPE, 10L);
                TYPE_ARGUMENTS.put(Float.class, 3.14159F);
                TYPE_ARGUMENTS.put(Float.TYPE, 3.14159F);
                TYPE_ARGUMENTS.put(Double.class, 3.14159);
                TYPE_ARGUMENTS.put(Double.TYPE, 3.14159);
                TYPE_ARGUMENTS.put(java.sql.Date.class, new java.sql.Date(new Date().getTime()));
                TYPE_ARGUMENTS.put(Timestamp.class, new Timestamp(new Date().getTime()));
                TYPE_ARGUMENTS.put(Calendar.class, Calendar.getInstance());
        }

        private static final Map<Class, Object> DEFAULT_TYPE_ARGUMENTS = Collections.unmodifiableMap(new HashMap<Class, Object>(TYPE_ARGUMENTS));

        private static final int TEST_ARRAY_SIZE = 10;

        private PropertyAsserter() {
        }

        public static void registerTypeAndDefaultArgument(Class type, Object defaultArgument) {
                TYPE_ARGUMENTS.put(type, defaultArgument);
        }

        public static void deregisterType(Class type) {
                TYPE_ARGUMENTS.remove(type);
        }

        public static void resetToDefaultTypes() {
                TYPE_ARGUMENTS.clear();
                TYPE_ARGUMENTS.putAll(DEFAULT_TYPE_ARGUMENTS);
        }

        public static Object defaultArgumentForType(Class type) {
                return TYPE_ARGUMENTS.get(type);
        }

        public static void assertBasicGetterSetterBehavior(Object target, String property) {
                assertBasicGetterSetterBehavior(target, property, null);
        }

        public static void assertBasicGetterSetterBehavior(Object target, String property, Object argument) {
                try {
                        PropertyDescriptor descriptor = new PropertyDescriptor(property, target.getClass());
                        Object arg = argument;
                        Class type = descriptor.getPropertyType();
                        if (arg == null) {
                                if (type.isArray()) {
                                        arg = Array.newInstance(type.getComponentType(), new int[] {TEST_ARRAY_SIZE });
                                } else if (type.isEnum()) {
                                        arg = type.getEnumConstants()[0];
                                } else if (TYPE_ARGUMENTS.containsKey(type)) {
                                        arg = TYPE_ARGUMENTS.get(type);
                                } else {
                                        arg = invokeDefaultConstructorEvenIfPrivate(type);
                                }
                        }

                        Method writeMethod = descriptor.getWriteMethod();
                        Method readMethod = descriptor.getReadMethod();

                        writeMethod.invoke(target, arg);
                        Object propertyValue = readMethod.invoke(target);
                        if (type.isPrimitive()) {
                                assertEquals(property + " getter/setter failed test", arg, propertyValue);
                        } else {
                                assertSame(property + " getter/setter failed test", arg, propertyValue);
                        }
                } catch (IntrospectionException e) {
                        String msg = "Error creating PropertyDescriptor for property [" + property + "]. Do you have a getter and a setter?";
                        log.error(msg, e);
                        fail(msg);
                } catch (IllegalAccessException e) {
                        String msg = "Error accessing property. Are the getter and setter both accessible?";
                        log.error(msg, e);
                        fail(msg);
                } catch (InvocationTargetException e) {
                        String msg = "Error invoking method on target";
                        log.error(msg, e);
                        fail(msg);
                }
        }

        private static Object invokeDefaultConstructorEvenIfPrivate(Class type) {
                try {
                        Constructor ctor = type.getDeclaredConstructor();
                        ctor.setAccessible(true);
                        return ctor.newInstance();
                } catch (Exception ex) {
                        throw new RuntimeException("Could not invoke default constructor on type " + type, ex);
                }
        }

        public static void assertBasicGetterSetterBehavior(Object target, Map<String, Object> properties) {
                Set<Map.Entry<String, Object>> entries = properties.entrySet();
                for (Map.Entry<String, Object> entry : entries) {
                        assertBasicGetterSetterBehavior(target, entry.getKey(), entry.getValue());
                }
        }

        public static void assertBasicGetterSetterBehavior(Object target, String... propertyNames) {
                Map<String, Object> properties = new LinkedHashMap<String, Object>();
                for (String propertyName : propertyNames) {
                        properties.put(propertyName, null);
                }
                assertBasicGetterSetterBehavior(target, properties);
        }

        public static void assertBasicGetterSetterBehavior(Object target) {
                assertBasicGetterSetterBehaviorWithBlacklist(target);
        }

        public static void assertBasicGetterSetterBehaviorWithBlacklist(Object target, String... propertyNames) {
                List<String> blacklist = Arrays.asList(propertyNames);
                try {
                        BeanInfo beanInfo = Introspector.getBeanInfo(target.getClass());
                        PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
                        for (PropertyDescriptor descriptor : descriptors) {
                                if (descriptor.getWriteMethod() == null) {
                                        continue;
                                }
                                if (!blacklist.contains(descriptor.getDisplayName())) {
                                        assertBasicGetterSetterBehavior(target, descriptor.getDisplayName());
                                }
                        }
                } catch (IntrospectionException e) {
                        fail("Failed while introspecting target " + target.getClass());
                }
        }

}