/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2012 Servoy BV
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option) any
later version.
This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.scripting.annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.servoy.base.scripting.annotations.ServoyClientSupport;
import com.servoy.j2db.documentation.ClientSupport;
import com.servoy.j2db.scripting.IScriptable;
import com.servoy.j2db.scripting.annotations.AnnotationManager.IAnnotatedClass;
import com.servoy.j2db.scripting.annotations.AnnotationManager.IAnnotatedField;
import com.servoy.j2db.scripting.annotations.AnnotationManager.IAnnotatedMethod;
/**
* Handles annotations form classes, caches annotations defined for the class itself and for the interfaces it implements.
*
* Annotations have to defined at element (method or field) level, or at interface or class level.
* When an annotation is defined at class or interface, all elements in that class are annotated, but not all methods from classes inheriting from that interface or class.
*
* @author rgansevles
*
*/
public class AnnotationManagerReflection
{
private static AnnotationManagerReflection INSTANCE = new AnnotationManagerReflection();
private final AnnotationManager<Annotation, Class< ? extends Annotation>> annotationManager = new AnnotationManager<Annotation, Class< ? extends Annotation>>();
private final ConcurrentMap<Class, ReflectionAnnotatedClass> reflectionAnnotatedClassCache = new ConcurrentHashMap<Class, ReflectionAnnotatedClass>();
private AnnotationManagerReflection()
{
}
ReflectionAnnotatedClass getReflectionAnnotatedClass(Class< ? > cls)
{
ReflectionAnnotatedClass reflectionAnnotatedClass = reflectionAnnotatedClassCache.get(cls);
if (reflectionAnnotatedClass == null)
{
reflectionAnnotatedClass = new ReflectionAnnotatedClass(cls);
ReflectionAnnotatedClass old = reflectionAnnotatedClassCache.putIfAbsent(cls, reflectionAnnotatedClass);
if (old != null) reflectionAnnotatedClass = old;
}
return reflectionAnnotatedClass;
}
public static AnnotationManagerReflection getInstance()
{
return INSTANCE;
}
public <T extends Annotation> boolean isAnnotationPresent(Method method, Class< ? > originalClass, Class<T> annotationClass)
{
return getAnnotation(method, originalClass, annotationClass) != null;
}
@SuppressWarnings("unchecked")
public <T extends Annotation> T getAnnotation(Method method, Class< ? > originalClass, Class<T> annotationClass)
{
if (method == null) return null;
return (T)annotationManager.getAnnotation(new ReflectionAnnotatedMethod(method), getReflectionAnnotatedClass(originalClass), annotationClass);
}
@SuppressWarnings("unchecked")
public <T extends Annotation> T getAnnotation(Field field, Class<T> annotationClass)
{
if (field == null) return null;
return (T)annotationManager.getAnnotation(new ReflectionAnnotatedField(field), annotationClass);
}
@SuppressWarnings("unchecked")
public <T extends Annotation> T getAnnotation(Class< ? > targetClass, Class<T> annotationClass)
{
if (targetClass == null) return null;
return (T)annotationManager.getAnnotation(getReflectionAnnotatedClass(targetClass), annotationClass);
}
public boolean hasSupportForClientType(Method method, Class< ? > originalClass, ClientSupport clientType, ClientSupport defaultClientType)
{
return getClientSupport(method, originalClass, defaultClientType).hasSupport(clientType);
}
public boolean hasSupportForClientType(Class< ? extends IScriptable> cls, ClientSupport clientType, ClientSupport defaultClientType)
{
return getClientSupport(cls, defaultClientType).hasSupport(clientType);
}
public boolean hasSupportForClientType(Field field, ClientSupport clientType, ClientSupport defaultClientType)
{
return getClientSupport(field, defaultClientType).hasSupport(clientType);
}
public ClientSupport getClientSupport(Method method, Class< ? > originalClass, ClientSupport defaultClientType)
{
ServoyClientSupport csp = getAnnotation(method, originalClass, ServoyClientSupport.class);
return csp == null ? defaultClientType : ClientSupport.fromAnnotation(csp);
}
public ClientSupport getClientSupport(Class< ? > cls, ClientSupport defaultClientType)
{
ServoyClientSupport csp = getAnnotation(cls, ServoyClientSupport.class);
return csp == null ? defaultClientType : ClientSupport.fromAnnotation(csp);
}
public ClientSupport getClientSupport(Field field, ClientSupport defaultClientType)
{
ServoyClientSupport csp = getAnnotation(field, ServoyClientSupport.class);
return csp == null ? defaultClientType : ClientSupport.fromAnnotation(csp);
}
public static void flushCachedItems()
{
INSTANCE = null;
}
/////////////// Wrapper classes ///////////////////
private class ReflectionAnnotatedClass implements IAnnotatedClass<Annotation, Class< ? extends Annotation>>
{
private final Class< ? > originalClass;
private final ConcurrentMap<Object, ReflectionAnnotatedMethod> methodCache = new ConcurrentHashMap<Object, AnnotationManagerReflection.ReflectionAnnotatedMethod>();
private final ReflectionAnnotatedMethod NULL = new ReflectionAnnotatedMethod(null);
private ReflectionAnnotatedClass(Class< ? > originalClass)
{
this.originalClass = originalClass;
}
public Class< ? > getOriginalClass()
{
return originalClass;
}
@Override
public Annotation getAnnotation(Class< ? extends Annotation> searchedAnnotation)
{
return originalClass.getAnnotation(searchedAnnotation);
}
@Override
public IAnnotatedClass<Annotation, Class< ? extends Annotation>> getSuperclass()
{
Class< ? > superclass = originalClass.getSuperclass();
return superclass == null || superclass == Object.class ? null : getReflectionAnnotatedClass(superclass);
}
@Override
public IAnnotatedMethod<Annotation, Class< ? extends Annotation>> getMethod(Object signature)
{
try
{
ReflectionAnnotatedMethod reflectionAnnotatedMethod = methodCache.get(signature);
if (reflectionAnnotatedMethod == null)
{
reflectionAnnotatedMethod = new ReflectionAnnotatedMethod(originalClass.getMethod(((ReflectionSignature)signature).getName(),
((ReflectionSignature)signature).getParameterTypes()));
ReflectionAnnotatedMethod old = methodCache.putIfAbsent(signature, reflectionAnnotatedMethod);
if (old != null) reflectionAnnotatedMethod = old;
}
return reflectionAnnotatedMethod == NULL ? null : reflectionAnnotatedMethod;
}
catch (NoSuchMethodException e)
{
methodCache.put(signature, NULL);
return null;
}
}
@Override
public IAnnotatedField<Annotation, Class< ? extends Annotation>> getField(String name)
{
try
{
return new ReflectionAnnotatedField(originalClass.getField(name));
}
catch (NoSuchFieldException e)
{
return null;
}
}
@Override
public Collection<IAnnotatedClass<Annotation, Class< ? extends Annotation>>> getInterfaces()
{
Class< ? >[] intfs = originalClass.getInterfaces();
IAnnotatedClass<Annotation, Class< ? extends Annotation>>[] interfaces = new ReflectionAnnotatedClass[intfs.length];
for (int i = 0; i < intfs.length; i++)
{
interfaces[i] = getReflectionAnnotatedClass(intfs[i]);
}
return Arrays.asList(interfaces);
}
@Override
public boolean isAssignableFrom(IAnnotatedClass<Annotation, Class< ? extends Annotation>> declaringClass)
{
return originalClass.isAssignableFrom(((ReflectionAnnotatedClass)declaringClass).getOriginalClass());
}
@Override
public int hashCode()
{
return originalClass.hashCode();
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
ReflectionAnnotatedClass other = (ReflectionAnnotatedClass)obj;
if (originalClass == null)
{
if (other.originalClass != null) return false;
}
else if (!originalClass.equals(other.originalClass)) return false;
return true;
}
@Override
public String toString()
{
return originalClass.toString();
}
}
private class ReflectionAnnotatedField implements IAnnotatedField<Annotation, Class< ? extends Annotation>>
{
private final Field field;
private ReflectionAnnotatedField(Field field)
{
this.field = field;
}
@Override
public Annotation getAnnotation(Class< ? extends Annotation> searchedAnnotation)
{
return field.getAnnotation(searchedAnnotation);
}
@Override
public String getName()
{
return field.getName();
}
@Override
public IAnnotatedClass<Annotation, Class< ? extends Annotation>> getDeclaringClass()
{
return getReflectionAnnotatedClass(field.getDeclaringClass());
}
@Override
public int hashCode()
{
return field.hashCode();
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
ReflectionAnnotatedField other = (ReflectionAnnotatedField)obj;
if (field == null)
{
if (other.field != null) return false;
}
else if (!field.equals(other.field)) return false;
return true;
}
@Override
public String toString()
{
return field.toString();
}
}
private class ReflectionAnnotatedMethod implements IAnnotatedMethod<Annotation, Class< ? extends Annotation>>
{
private final Method method;
private ReflectionAnnotatedMethod(Method method)
{
this.method = method;
}
@Override
public Annotation getAnnotation(Class< ? extends Annotation> searchedAnnotation)
{
return method.getAnnotation(searchedAnnotation);
}
@Override
public ReflectionSignature getSignature()
{
return new ReflectionSignature(method.getName(), method.getParameterTypes());
}
@Override
public IAnnotatedClass<Annotation, Class< ? extends Annotation>> getDeclaringClass()
{
return getReflectionAnnotatedClass(method.getDeclaringClass());
}
@Override
public int hashCode()
{
return method.hashCode();
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
ReflectionAnnotatedMethod other = (ReflectionAnnotatedMethod)obj;
if (method == null)
{
if (other.method != null) return false;
}
else if (!method.equals(other.method)) return false;
return true;
}
@Override
public String toString()
{
return method.toString();
}
}
private static class ReflectionSignature
{
private final String name;
private final Class< ? >[] parameterTypes;
private ReflectionSignature(String name, Class< ? >[] parameterTypes)
{
this.name = name;
this.parameterTypes = parameterTypes;
}
public String getName()
{
return name;
}
public Class< ? >[] getParameterTypes()
{
return parameterTypes;
}
@Override
public String toString()
{
return name + '(' + Arrays.toString(parameterTypes) + ')';
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (obj == this) return true;
if (obj instanceof ReflectionSignature)
{
ReflectionSignature rs = (ReflectionSignature)obj;
return rs.name.equals(name) && Arrays.equals(rs.parameterTypes, parameterTypes);
}
return false;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return name.hashCode();
}
}
}