package io.ebeaninternal.server.deploy.parse; import io.ebean.annotation.Platform; import io.ebean.annotation.Formula; import io.ebean.annotation.Where; import io.ebean.config.NamingConvention; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * Provides some base methods for processing deployment annotations. All findAnnotation* methods * are capable to search for meta-annotations (annotation that has an other annotation) * *
search algorithm for ONE annotation:
*search algoritm for a Set<Annotation> works a litte bit different, as it does not stop * on the first match, but continues searching down to the last corner to find all annotations.
* *To prevent endless recursion, the search algoritm tracks all visited annotations
* *Supports also "java 1.6 repeatable containers" like{@link JoinColumn} / {@link JoinColumns}.
* *This means, searching for JoinColumn will find them also if they are inside a
* JoinColumns annotation
* Looks first at the field and then at the getter method. It searches for meta-annotations, but not * recursively in the class hierarchy. *
*
* If a repeatable annotation class is specified and the annotation is platform
* specific(see {@link #getPlatformMatchingAnnotation(Set, Platform)}), then the platform specific
* annotation is returned. Otherwise the first annotation is returned. Note that you need no longer
* handle "java 1.6 repeatable containers" like {@link JoinColumn} / {@link JoinColumns} yourself.
*
*/
protected
* Looks first at the field and then at the getter method. then at class level.
* (This is used for SequenceGenerator e.g.)
*
* Meta-annotations will be searched if the annotation is not directly present on the supplied element.
*
* Warning: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
* It also does not filter out platform dependent annotations!
*/
public static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start search for meta-annotations.
A ann = annotatedElement.getAnnotation(annotationType);
if (ann != null) {
return ann;
} else {
return findAnnotation(annotatedElement, annotationType, new HashSet<>());
}
}
/**
* Find a single {@link Annotation} of {@code annotationType} on the supplied class.
* Meta-annotations will be searched if the annotation is not directly present on
* the supplied element.
* Note: this method searches for annotations at class & superClass(es)!
*/
public static A findAnnotationRecursive(Class> clazz, Class annotationType) {
if (annotationType == null) {
return null;
}
// check if directly present, if not, start search for meta-annotations.
A ann = clazz.getAnnotation(annotationType);
if (ann != null) {
return ann;
} else {
while (clazz != null && clazz != Object.class) {
ann = findAnnotation(clazz, annotationType, new HashSet<>());
if (ann != null) {
return ann;
}
// no meta-annotation present at this class - traverse to superclass
clazz = clazz.getSuperclass();
}
return null;
}
}
/**
* Finds the first annotation of a type for this platform. (if annotation is platform specific, otherwise first
* found annotation is returned)
*/
public static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType, Platform platform) {
if (annotationType == null) {
return null;
}
Set anns = findAnnotations(annotatedElement, annotationType);
return getPlatformMatchingAnnotation(anns, platform);
}
/**
* Finds all annotations recusively for a class and its superclasses.
*/
public static Set findAnnotationsRecursive(Class> clazz, Class annotationType) {
if (annotationType == null) {
return null;
}
Set ret = new LinkedHashSet<>();
Set
* Meta-annotations will be searched if the annotation is not directly present on the supplied element.
*
* Warning: this method operates generically on annotated elements. In other words, this method
* does not execute specialized search algorithms for classes or methods. It only traverses through Annotations!
*/
public static Set findAnnotations(AnnotatedElement annotatedElement, Class annotationType) {
if (annotationType == null) {
return null;
}
Set ret = new LinkedHashSet<>();
findMetaAnnotations(annotatedElement, annotationType, ret, new HashSet<>());
return ret;
}
/**
* Perform the search algorithm avoiding endless recursion by tracking which
* annotations have already been visited.
*/
@SuppressWarnings("unchecked")
private static void findMetaAnnotations(AnnotatedElement annotatedElement, Class annotationType, Set ret, Set
* value() method for a possible containerAnnotation.
* Method is retuned only, if its signature is array of containingType.
*/
private static Method getRepeatableValueMethod(
Annotation containerAnnotation, Class containingType) {
Method method = valueMethods.get(containerAnnotation);
if (method == null) {
try {
method = containerAnnotation.annotationType().getMethod("value");
} catch (NoSuchMethodException e) {
method = nullMethod;
} catch (Exception e) {
throw new RuntimeException(e);
}
Method prev = valueMethods.putIfAbsent(containerAnnotation, method);
method = prev == null ? method : prev;
}
if (method != nullMethod) {
Class> retType = method.getReturnType();
if (retType.isArray() && retType.getComponentType() == containingType) {
return method;
}
}
return null;
}
/**
* Finds a suitable annotation from Set for this platform.
* To distinguish between platforms, annotation type T must define
* a method withthis signature:
* Class extends DatabasePlatform>[] platforms() default {};
*
*
* (This mechanism is currently used by {@link Where} and {@link Formula})
*/
public static ann[0]
* databasePlatform