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

net.jqwik.engine.support.OverriddenMethodAnnotationSupport Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
package net.jqwik.engine.support;

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

import org.junit.platform.commons.support.*;

/**
 * Adapted from https://stackoverflow.com/a/49164791/32352
 */
public final class OverriddenMethodAnnotationSupport {

	/**
	 * Returns the optional first element of the list returned by
	 * {@code getAnnotations}, or {@code null} if the
	 * list would be empty.
	 *
	 * @param  the type of the annotation to find.
	 * @param m   the method to begin the search from.
	 * @param t   the type of the annotation to find.
	 * @return optional of first annotation found of the specified type which
	 * is present on {@code m}, or present on any methods which
	 * {@code m} overrides.
	 * @throws NullPointerException if any argument is {@code null}.
	 */
	public static  Optional findDeclaredOrInheritedAnnotation(Method m, Class t) {
		List list = getAnnotations(m, t);
		return list.isEmpty() ? Optional.empty() : Optional.of(list.get(0));
	}

	/**
	 * Let {@code D} be the class or interface which declares the method
	 * {@code m}.
	 * 

* Returns a list of all of the annotations of the specified type * which are either present on {@code m}, or present on any methods * declared by a supertype of {@code D} which {@code m} overrides. *

* Annotations are listed in order of nearest proximity to {@code D}, * that is, assuming {@code D extends E} and {@code E extends F}, then * the returned list would contain annotations in the order of * {@code [D, E, F]}. A bit more formally, if {@code Sn} is the nth * superclass of {@code D} (where {@code n} is an integer starting at 0), * then the index of the annotation present on {@code Sn.m} is {@code n+1}, * assuming annotations are present on {@code m} for every class. *

* Annotations from methods declared by the superinterfaces of {@code D} * appear last in the list, in order of their declaration, * recursively. For example, if {@code class D implements X, Y} and * {@code interface X extends Z}, then annotations will appear in the * list in the order of {@code [D, X, Z, Y]}. * * @param the type of the annotation to find. * @param m the method to begin the search from. * @param t the type of the annotation to find. * @return a list of all of the annotations of the specified type * which are either present on {@code m}, or present on any * methods which {@code m} overrides. * @throws NullPointerException if any argument is {@code null}. */ private static List getAnnotations(Method m, Class t) { List list = new ArrayList<>(); Collections.addAll(list, allAnnotationsByType(m, t)); Class decl = m.getDeclaringClass(); for (Class supr = decl; (supr = supr.getSuperclass()) != null; ) { addAnnotations(list, m, t, supr); } for (Class face : getAllInterfaces(decl)) { addAnnotations(list, m, t, face); } return list; } private static Set> getAllInterfaces(Class c) { Set> set = new LinkedHashSet<>(); do { addAllInterfaces(set, c); } while ((c = c.getSuperclass()) != null); return set; } private static void addAllInterfaces(Set> set, Class c) { for (Class i : c.getInterfaces()) { if (set.add(i)) { addAllInterfaces(set, i); } } } private static void addAnnotations (List list, Method m, Class t, Class decl) { try { Method n = decl.getDeclaredMethod(m.getName(), m.getParameterTypes()); if (overrides(m, n)) { Collections.addAll(list, allAnnotationsByType(n, t)); } } catch (NoSuchMethodException x) { } } /** * Returns present and meta present annotations */ @SuppressWarnings("unchecked") private static A[] allAnnotationsByType(Method m, Class t) { // TODO: Does not work for repeatable annotations return (A[]) AnnotationSupport.findAnnotation(m, t).map(a -> new Annotation[]{a}).orElse(new Annotation[0]); } /** * @param a the method which may override {@code b}. * @param b the method which may be overridden by {@code a}. * @return {@code true} if {@code a} probably overrides {@code b} * and {@code false} otherwise. * @throws NullPointerException if any argument is {@code null}. */ private static boolean overrides(Method a, Method b) { if (!a.getName().equals(b.getName())) return false; Class classA = a.getDeclaringClass(); Class classB = b.getDeclaringClass(); if (classA.equals(classB)) return false; if (!classB.isAssignableFrom(classA)) return false; int modsA = a.getModifiers(); int modsB = b.getModifiers(); if (Modifier.isPrivate(modsA) || Modifier.isPrivate(modsB)) return false; if (Modifier.isStatic(modsA) || Modifier.isStatic(modsB)) return false; if (Modifier.isFinal(modsB)) return false; if (compareAccess(modsA, modsB) < 0) return false; if ((isPackageAccess(modsA) || isPackageAccess(modsB)) && !Objects.equals(classA.getPackage(), classB.getPackage())) return false; if (!b.getReturnType().isAssignableFrom(a.getReturnType())) return false; Class[] paramsA = a.getParameterTypes(); Class[] paramsB = b.getParameterTypes(); if (paramsA.length != paramsB.length) return false; for (int i = 0; i < paramsA.length; ++i) if (!paramsA[i].equals(paramsB[i])) return false; return true; } private static boolean isPackageAccess(int mods) { return (mods & ACCESS_MODIFIERS) == 0; } private static final int ACCESS_MODIFIERS = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE; private static final List ACCESS_ORDER = Arrays.asList( Modifier.PRIVATE, 0, Modifier.PROTECTED, Modifier.PUBLIC ); private static int compareAccess(int lhs, int rhs) { return Integer.compare( ACCESS_ORDER.indexOf(lhs & ACCESS_MODIFIERS), ACCESS_ORDER.indexOf(rhs & ACCESS_MODIFIERS) ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy