All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.ebeaninternal.server.deploy.parse.AnnotationBase Maven / Gradle / Ivy

There is a newer version: 15.8.1
Show newest version
package io.ebeaninternal.server.deploy.parse;

import io.ebean.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.
 */
public abstract class AnnotationBase {

  protected final DatabasePlatform databasePlatform;
  protected final Platform platform;
  protected final NamingConvention namingConvention;
  protected final DeployUtil util;

  protected AnnotationBase(DeployUtil util) {
    this.util = util;
    this.databasePlatform = util.getDbPlatform();
    this.platform = databasePlatform.getPlatform();
    this.namingConvention = util.getNamingConvention();
  }

  /**
   * read the deployment annotations.
   */
  public abstract void parse();

  /**
   * Checks string is null or empty .
   */
  protected boolean isEmpty(String s) {
    return s == null || s.trim().isEmpty();
  }

  /**
   * Return the annotation for the property.
   * 

* Looks first at the field and then at the getter method. *

*

* 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 must no longer * handle "java 1.6 repeatable containers" like {@link JoinColumn} / {@link JoinColumns} yourself. *

*

*/ protected T get(DeployBeanProperty prop, Class annClass) { T a = null; Field field = prop.getField(); if (field != null) { a = findAnnotation(field, annClass); } if (a == null) { Method method = prop.getReadMethod(); if (method != null) { a = findAnnotation(method, annClass); } } return a; } /** * Return all annotations for this property. Annotations are not filtered by platfrom and you'll get * really all annotations that are directly, indirectly or meta-present. */ protected Set getAll(DeployBeanProperty prop, Class annClass) { Set ret = null; Field field = prop.getField(); if (field != null) { ret = findAnnotations(field, annClass); } Method method = prop.getReadMethod(); if (method != null) { if (ret != null) { ret.addAll(findAnnotations(method, annClass)); } else { ret = findAnnotations(method, annClass); } } return ret; } /** * Return the annotation for the property. *

* Looks first at the field and then at the getter method. then at class level. *

*/ protected T find(DeployBeanProperty prop, Class annClass) { T a = get(prop, annClass); if (a == null) { a = findAnnotation(prop.getOwningType(), annClass, platform); } return a; } // this code is taken from the spring framework to find annotations recursively /** * Determine if the supplied {@link Annotation} is defined in the core JDK {@code java.lang.annotation} package. */ public static boolean isInJavaLangAnnotationPackage(Annotation annotation) { return annotation.annotationType().getName().startsWith("java.lang.annotation"); } /** * Find a single {@link Annotation} of {@code annotationType} on the supplied {@link AnnotatedElement}. *

* 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 recursive traversal 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 findAnnotation(Class clazz, Class annotationType) { if (annotationType == null) { return null; } // check if directly present, if not, start recursive traversal 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; } // not 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); } /** * Perform the search algorithm avoiding endless recursion by tracking which * annotations have already been visited. */ @SuppressWarnings("unchecked") private static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType, Set visited) { Annotation[] anns = annotatedElement.getAnnotations(); // directly annotatated or inherited for (Annotation ann : anns) { if (ann.annotationType() == annotationType) { return (A) ann; } } for (Annotation ann : anns) { if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { A annotation = findAnnotation(ann.annotationType(), annotationType, visited); if (annotation != null) { return annotation; } } } return null; } /** * Find all {@link Annotation}s of {@code annotationType} on the supplied {@link AnnotatedElement}. *

* 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 visited) { Annotation[] anns = annotatedElement.getAnnotations(); for (Annotation ann : anns) { if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { if (ann.annotationType() == annotationType) { ret.add((A) ann); } else { Method repeatableValueMethod = getRepeatableValueMethod(ann, annotationType); if (repeatableValueMethod != null) { try { A[] repeatedAnns = (A[]) repeatableValueMethod.invoke(ann); for (Annotation repeatedAnn : repeatedAnns) { ret.add((A) repeatedAnn); findMetaAnnotations(repeatedAnn.annotationType(), annotationType, ret, visited); } } catch (Exception e) { // catch all exceptions (thrown by invoke) throw new RuntimeException(e); } } else { findMetaAnnotations(ann.annotationType(), annotationType, ret, visited); } } } } } // caches for getRepeatableValueMethod private static Method getNullMethod() { try { return AnnotationBase.class.getDeclaredMethod("getNullMethod"); } catch (NoSuchMethodException e) { return null; } } private static final ConcurrentMap valueMethods = new ConcurrentHashMap(); private static final Method nullMethod = getNullMethod(); /** * Returns the 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 anns for this platform. * To distinguish between platforms, annotation type T must define * a method withthis signature: *

* Class[] platforms() default {}; *

* The finding rules are: *
    *
  1. Check if T has method "platforms" if not, return ann[0] *
  2. find the annotation that is defined for databasePlatform
  3. *
  4. otherwise return the annotation for default platform (platforms = {})
  5. *
  6. return null *
* (This mechanism is currently used by {@link Where} and {@link Formula}) */ public static T getPlatformMatchingAnnotation(Set anns, Platform matchPlatform) { if (anns.isEmpty()) { return null; } Method getPlatformsMethod = null; T fallback = null; for (T ann : anns) { try { if (getPlatformsMethod == null) { getPlatformsMethod = ann.getClass().getMethod("platforms"); } if (!Platform[].class.isAssignableFrom(getPlatformsMethod.getReturnType())) { return ann; } Platform[] platforms = (Platform[]) getPlatformsMethod.invoke(ann); if (platforms.length == 0) { fallback = ann; } else { for (Platform platform : platforms) { if (matchPlatform == platform) { return ann; } } } } catch (NoSuchMethodException e) { return ann; // not platform specific - return first one } catch (Exception e) { throw new RuntimeException(e); } } return fallback; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy