/** * GRANITE DATA SERVICES * Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S. * * This file is part of the Granite Data Services Platform. * * Granite Data Services is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * Granite Data Services is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser * General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA, or see <http://www.gnu.org/licenses/>. */ package org.granite.messaging.reflect; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Reflection provider * * @author Franck WOLFF */ public class Reflection { protected static final int STATIC_TRANSIENT_MASK = Modifier.STATIC | Modifier.TRANSIENT; protected static final int STATIC_PRIVATE_PROTECTED_MASK = Modifier.STATIC | Modifier.PRIVATE | Modifier.PROTECTED; protected static final Property NULL_PROPERTY = new NullProperty(); protected final ClassLoader classLoader; protected final BypassConstructorAllocator instanceFactory; protected final Comparator<Property> lexicalPropertyComparator; protected final ConcurrentMap<Class<?>, ClassDescriptor> descriptorCache; protected final ConcurrentMap<SinglePropertyKey, Property> singlePropertyCache; public Reflection(ClassLoader classLoader) { this(classLoader, null); } public Reflection(ClassLoader classLoader, BypassConstructorAllocator instanceFactory) { this.classLoader = classLoader; if (instanceFactory != null) this.instanceFactory = instanceFactory; else { try { this.instanceFactory = new SunBypassConstructorAllocator(); } catch (Exception e) { throw new RuntimeException("Could not instantiate BypassConstructorAllocator", e); } } this.lexicalPropertyComparator = new Comparator<Property>() { public int compare(Property p1, Property p2) { return p1.getName().compareTo(p2.getName()); } }; this.descriptorCache = new ConcurrentHashMap<Class<?>, ClassDescriptor>(); this.singlePropertyCache = new ConcurrentHashMap<SinglePropertyKey, Property>(); } public ClassLoader getClassLoader() { return (classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader()); } public BypassConstructorAllocator getInstanceFactory() { return instanceFactory; } public Comparator<Property> getLexicalPropertyComparator() { return lexicalPropertyComparator; } public Class<?> loadClass(String className) throws ClassNotFoundException { return getClassLoader().loadClass(className); } @SuppressWarnings("unchecked") public <T> T newInstance(Class<T> cls) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException { ClassDescriptor desc = descriptorCache.get(cls); if (desc != null) return (T)desc.newInstance(); try { Constructor<T> constructor = cls.getConstructor(); return constructor.newInstance(); } catch (NoSuchMethodException e) { return (T)instanceFactory.newInstantiator(cls).newInstance(); } } @SuppressWarnings("unchecked") public <T> T newInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException { return newInstance((Class<T>)loadClass(className)); } public Property findSerializableProperty(Class<?> cls, String name) throws SecurityException { List<Property> properties = findSerializableProperties(cls); for (Property property : properties) { if (name.equals(property.getName())) return property; } return null; } public ClassDescriptor getDescriptor(Class<?> cls) { if (cls == null || cls == Object.class || !isRegularClass(cls)) return null; ClassDescriptor descriptor = descriptorCache.get(cls); if (descriptor == null) { descriptor = new ClassDescriptor(this, cls); ClassDescriptor previousDescriptor = descriptorCache.putIfAbsent(cls, descriptor); if (previousDescriptor != null) descriptor = previousDescriptor; } return descriptor; } public List<Property> findSerializableProperties(Class<?> cls) throws SecurityException { ClassDescriptor descriptor = getDescriptor(cls); if (descriptor == null) return Collections.emptyList(); return descriptor.getInheritedSerializableProperties(); } protected FieldProperty newFieldProperty(Field field) { return new SimpleFieldProperty(field); } protected MethodProperty newMethodProperty(Method getter, Method setter, String name) { return new SimpleMethodProperty(getter, setter, name); } public boolean isRegularClass(Class<?> cls) { return cls != Class.class && !cls.isAnnotation() && !cls.isArray() && !cls.isEnum() && !cls.isInterface() && !cls.isPrimitive(); } public Property findProperty(Class<?> cls, String name, Class<?> type) { NameTypePropertyKey key = new NameTypePropertyKey(cls, name, type); Property property = singlePropertyCache.get(key); if (property == null) { Field field = null; for (Class<?> c = cls; c != null && c != Object.class; c = c.getSuperclass()) { try { field = c.getDeclaredField(name); } catch (NoSuchFieldException e) { continue; } if (field.getType() != type) continue; field.setAccessible(true); break; } if (field == null) property = NULL_PROPERTY; else property = newFieldProperty(field); Property previous = singlePropertyCache.putIfAbsent(key, property); if (previous != null) property = previous; } return (property != NULL_PROPERTY ? property : null); } public Property findProperty(Class<?> cls, Class<? extends Annotation> annotationClass) { AnnotatedPropertyKey key = new AnnotatedPropertyKey(cls, annotationClass); Property property = singlePropertyCache.get(key); if (property == null) { boolean searchFields = false; boolean searchMethods = false; if (!annotationClass.isAnnotationPresent(Target.class)) searchFields = searchMethods = true; else { Target target = annotationClass.getAnnotation(Target.class); for (ElementType targetType : target.value()) { if (targetType == ElementType.FIELD) searchFields = true; else if (targetType == ElementType.METHOD) searchMethods = true; } } if (searchFields == false && searchMethods == false) return null; final int modifierMask = Modifier.PUBLIC | Modifier.STATIC; classLoop: for (Class<?> c = cls; c != null && c != Object.class; c = c.getSuperclass()) { if (searchMethods) { for (Method method : c.getDeclaredMethods()) { if ((method.getModifiers() & modifierMask) != Modifier.PUBLIC || !method.isAnnotationPresent(annotationClass)) continue; if (method.getReturnType() == Void.TYPE) { if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) { String name = method.getName().substring(3); if (name.length() == 0) continue; Method getter = null; try { getter = cls.getMethod("get" + name); } catch (NoSuchMethodException e) { try { getter = cls.getMethod("is" + name); } catch (Exception f) { } } if (getter != null && (getter.getModifiers() & Modifier.STATIC) != 0 && getter.getReturnType() != method.getParameterTypes()[0]) getter = null; if (getter == null) continue; name = name.substring(0, 1).toLowerCase() + name.substring(1); property = newMethodProperty(getter, method, name); break classLoop; } } else if (method.getParameterTypes().length == 0 && (method.getName().startsWith("get") || method.getName().startsWith("is"))) { String name; if (method.getName().startsWith("get")) name = method.getName().substring(3); else name = method.getName().substring(2); if (name.length() == 0) continue; Method setter = null; try { setter = cls.getMethod("set" + name); } catch (NoSuchMethodException e) { } if (setter != null && (setter.getModifiers() & Modifier.STATIC) != 0 && method.getReturnType() != setter.getParameterTypes()[0]) setter = null; name = name.substring(0, 1).toLowerCase() + name.substring(1); property = newMethodProperty(method, setter, name); break classLoop; } } } if (searchFields) { for (Field field : c.getDeclaredFields()) { if ((field.getModifiers() & Modifier.STATIC) == 0 && field.isAnnotationPresent(annotationClass)) { property = newFieldProperty(field); break classLoop; } } } } if (property == null) property = NULL_PROPERTY; Property previous = singlePropertyCache.putIfAbsent(key, property); if (previous != null) property = previous; } return (property != NULL_PROPERTY ? property : null); } protected static abstract class SinglePropertyKey { protected final Class<?> cls; public SinglePropertyKey(Class<?> cls) { this.cls = cls; } } protected static class AnnotatedPropertyKey extends SinglePropertyKey { private final Class<? extends Annotation> annotationClass; public AnnotatedPropertyKey(Class<?> cls, Class<? extends Annotation> annotationClass) { super(cls); this.annotationClass = annotationClass; } public Class<?> getCls() { return cls; } public Class<? extends Annotation> getAnnotationClass() { return annotationClass; } @Override public int hashCode() { return (31 * cls.hashCode()) + annotationClass.hashCode(); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof AnnotatedPropertyKey)) return false; AnnotatedPropertyKey key = (AnnotatedPropertyKey)obj; return cls.equals(key.cls) && annotationClass.equals(key.annotationClass); } } protected static class NameTypePropertyKey extends SinglePropertyKey { private final String name; private final Class<?> type; public NameTypePropertyKey(Class<?> cls, String name, Class<?> type) { super(cls); this.name = name; this.type = type; } public Class<?> getCls() { return cls; } public String getName() { return name; } public Class<?> getType() { return type; } @Override public int hashCode() { return (31 * (31 * cls.hashCode()) + name.hashCode()) + type.hashCode(); } @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof NameTypePropertyKey)) return false; NameTypePropertyKey key = (NameTypePropertyKey)obj; return cls.equals(key.cls) && name.equals(key.name) && type.equals(key.type); } } }