package org.sql2o.reflection; import org.sql2o.tools.AbstractCache; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.*; import static java.beans.Introspector.decapitalize; import static java.lang.reflect.Modifier.isPrivate; import static java.lang.reflect.Modifier.isStatic; /** * User: dimzon * Date: 4/9/14 * Time: 1:10 AM */ // TODO: move introspection code from PojoMetadata to PojoIntrospector @SuppressWarnings("UnusedDeclaration") public class PojoIntrospector { private static final AbstractCache<Class<?>, Map<String, ReadableProperty>, Void> rpCache = new AbstractCache<Class<?>, Map<String, ReadableProperty>, Void>() { @Override protected Map<String, ReadableProperty> evaluate(Class<?> key, Void param) { return collectReadableProperties(key); } }; private static Map<String, ReadableProperty> collectReadableProperties(Class<?> cls) { Map<String, ReadableProperty> map = new HashMap<String, ReadableProperty>(); List<Class<?>> classList = classInheritanceHierarhy(cls, Object.class); for (Class<?> aClass : classList) { collectPropertyGetters(map, aClass); } for (Class<?> aClass : classList) { collectReadableFields(map, aClass); } return Collections.unmodifiableMap(map); } public static Map<String, ReadableProperty> readableProperties(Class<?> ofClass) { return rpCache.get(ofClass, null); } private static void collectReadableFields(Map<String, ReadableProperty> map, Class<?> cls) { for (final Field m : cls.getDeclaredFields()) { if (isStaticOrPrivate(m)) continue; String propName = m.getName(); if (map.containsKey(propName)) continue; Class<?> returnType = m.getType(); m.setAccessible(true); ReadableProperty rp = new ReadableProperty(propName, returnType) { @Override public Object get(Object instance) throws InvocationTargetException, IllegalAccessException { return m.get(instance); } }; map.put(propName, rp); } } private static boolean isStaticOrPrivate(Member m) { final int modifiers = m.getModifiers(); return isStatic(modifiers) || isPrivate(modifiers); } private static void collectPropertyGetters(Map<String, ReadableProperty> map, Class<?> cls) { for (final Method m : cls.getDeclaredMethods()) { if (isStatic(m.getModifiers())) continue; if (isPrivate(m.getModifiers())) continue; if (0 != m.getParameterTypes().length) continue; Class<?> returnType = m.getReturnType(); if (returnType == Void.TYPE || returnType == Void.class) continue; String name = m.getName(); String propName = null; if (name.startsWith("get") && name.length() > 3) { propName = decapitalize(name.substring(3)); } else if (name.startsWith("is") && name.length() > 2 && returnType == Boolean.TYPE) { propName = decapitalize(name.substring(2)); } if (propName == null) continue; if (map.containsKey(propName)) continue; m.setAccessible(true); ReadableProperty rp = new ReadableProperty(propName, returnType) { @Override public Object get(Object instance) throws InvocationTargetException, IllegalAccessException { return m.invoke(instance, (Object[]) null); } }; map.put(propName, rp); } } private static List<Class<?>> classInheritanceHierarhy(Class<?> cls, Class<Object> stopAt) { ArrayList<Class<?>> list = new ArrayList<Class<?>>(); while (cls != null && cls != stopAt) { list.add(cls); cls = cls.getSuperclass(); } return list; } public abstract static class ReadableProperty { public final String name; public final Class<?> type; private ReadableProperty(String name, Class<?> type) { this.name = name; this.type = type; } public abstract Object get(Object instance) throws InvocationTargetException, IllegalAccessException; } }