Reputation: 5239
Let's say we have a structure like this:
class A {
@AMethodAnnotation("my-data")
public void myMethod() {
}
}
@MyClassAnnotation
class B extends A {
@Override
public void myMethod() {
}
}
Using annotation processing I'm trying to extract data from the annotation AMethodAnnotation
located on the method myMethod
inside class A
. class B
extends this class and overrides it's method myMethod
.
The twist is that I want data from methods with AMethodAnnotation
if the class it's inside has the annotation MyClassAnnotation
.
I'm getting the classes with annotation MyClassAnnotation
and looping through the enclosedElements
, there I can check if it has the Override
annotation, but I'm not sure how to get the method it's overriding, since that is where the AMethodAnnotation
is located with the data I want. ExecutableElement
does not appear to provide methods to get this.
for (Element classElement : roundEnv.getElementsAnnotatedWith(MyClassAnnotation.class)) {
// Make sure it's a class
if (classElement.getKind() != ElementKind.CLASS) {
continue;
}
// Loop through methods inside class
for (Element methodElement : classElement.getEnclosedElements()) {
// Make sure the element is a method & has a @Path annotation
if (methodElement.getKind() != ElementKind.METHOD) {
continue;
}
// If method has @Override annotation do stuff
}
}
Is there a way to get a reference to the method which is being overridden?
There is a way, you get the superclass of B
which is A
and you loop through the enclosedElements
in A
, then you would have to verify if the method name is the same, and if the parameters are the same and are in the same order. But I find this way to require a lot of checking, hence my question if there's a better way.
Upvotes: 2
Views: 531
Reputation: 5239
I wrote the following method based on the link @rmuller posted in the comments. There's extensive documentation for this method as seen in the Javadoc and the image below, in which it's more readable.
/**
* Will find the method which the method <strong>methodElement</strong> is overriding, if any.
* It does this by recursively traversing up the superclass tree of the
* <strong>classElement</strong> and checking if there are methods which override the
* <strong>methodElement</strong>. It will return after it finds the first method with the
* annotation <strong>annotation</strong>.
*
* @param originalClassElement The original class inside which the
* <strong>methodElement</strong> is located.
* @param classElement The class which represents the superclass while recursively
* looking up the tree, it should be equal to the
* <strong>originalClassElement</strong> when first calling this
* method.
* @param methodElement The method for which should be checked if it's overriding
* anything.
* @param annotation The annotation which must be matched before returning the result
* @return Will return the following, the list is written in order:
* <ul>
* <li>The method <strong>methodElement</strong> if <strong>methodElement</strong>
* already has an annotation of the type <strong>annotation</strong></li>
* <li>Null if the method <strong>methodElement</strong> does not have an @Override
* annotation</li>
* <li>Null if the class <strong>classElement</strong> does not have a superclass</li>
* <li>The method element which was found to have the annotation
* <strong>annotation</strong></li>
* </ul>
*/
public ExecutableElement getMethodOverride(TypeElement originalClassElement,
TypeElement classElement, ExecutableElement methodElement,
Class<? extends Annotation> annotation) {
if (methodElement.getAnnotation(annotation) != null) {
// The methodElement which was passed has the required annotation already
return methodElement;
}
if (methodElement.getAnnotation(Override.class) == null) {
// The methodElement which was passed does not have an @Override annotation and is
// therefore not overriding anything.
return null;
}
if (classElement.getSuperclass().getKind() == TypeKind.NONE) {
// Class has no superclass
return null;
}
for (Element elem : classElement.getEnclosedElements()) {
if (elem.getKind() != ElementKind.METHOD) {
// Not a method
continue;
}
// Check if the method inside the superclass overrids the method inside the original
// class
if (this.processingEnv.getElementUtils().overrides(methodElement,
(ExecutableElement) elem, classElement)) {
// Recursively go up the tree and check since this method might also override
// another method
return getMethodOverride(originalClassElement,
this.env.getElementUtils()
.getTypeElement(classElement.getSuperclass().toString()),
(ExecutableElement) elem, annotation);
}
}
// Recursively go up the tree and check there
return getMethodOverride(originalClassElement,
this.env.getElementUtils().getTypeElement(classElement.getSuperclass().toString()),
methodElement, annotation);
}
Upvotes: 2