mercredi 25 octobre 2017

Overriding method level @annotation at runtime with reflection, works for class level

Problem: I am able to successfully override class level annotations using the following:

public static void alterClassAnnotation(Class classToLookFor, Class<? extends Annotation> annotationToAlter,Annotation annotationValue) {
        if (isJDK7OrLower()) {
            try {
                Field annotations = Class.class.getDeclaredField(ANNOTATIONS);
                annotations.setAccessible(true);
                Map<Class<? extends Annotation>, Annotation> map =
                    (Map<Class<? extends Annotation>, Annotation>) annotations.get(classToLookFor);
                map.put(annotationToAlter, annotationValue);
            } catch (Exception  e) {
                e.printStackTrace();
            }
        } else {
            try {
                //In JDK8 Class has a private method called annotationData().
                //We first need to invoke it to obtain a reference to AnnotationData class which is a private class
                Method method = Class.class.getDeclaredMethod(ANNOTATION_DATA, null);
                method.setAccessible(true);
                //Since AnnotationData is a private class we cannot create a direct reference to it. We will have to
                //manage with just Object
                Object annotationData = method.invoke(classToLookFor);
                //We now look for the map called "annotations" within AnnotationData object.
                Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
                annotations.setAccessible(true);
                Map<Class<? extends Annotation>, Annotation> map =
                    (Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData);
                map.put(annotationToAlter, annotationValue);
            } catch (Exception  e) {
                e.printStackTrace();
            }
        }
    }

I am confused with the reflection API, how can I modify this code in order to override the value of a method level paramater? I have 3 annotations, 2 of which can be set at class level so I'm overriding them successfully with this code, but how about if the annotation is against the method itself?

my implementation class of the interface:

import java.lang.annotation.Annotation;

import io.qameta.allure.junit4.DisplayName;
public class DynamicDisplayName implements DisplayName {

    private String value;

    public DynamicDisplayName(String value) {
        this.value = value;
    }

    @Override
    public Class<? extends Annotation> annotationType() {
        return null;
    }

    @Override
    public String value() {
        return value;
    }

}

I have successfully overridden Issue and TmsLink, here is my code (both of these interfaces function correctly at class level)

Issue issue = SaveCourse.class.getAnnotation(Issue.class);
DynamicIssue altered = new DynamicIssue(issueId);
AnnotationHelper.alterClassAnnotation(SaveCourse.class, Issue.class, altered);
issue = SaveCourse.class.getAnnotation(Issue.class);                 


DisplayName dname = SaveCourse.class.getAnnotation(DisplayName.class);
DynamicDisplayName dna = new DynamicDisplayName(displayName);
AnnotationHelper.alterClassAnnotation(SaveCourse.class, DisplayName.class, dna);
dname = SaveCourse.class.getAnnotation(DisplayName.class);

TmsLink tmslink = SaveCourse.class.getAnnotation(TmsLink.class);
DynamicTmsLink tmsaltered = new DynamicTmsLink(testCaseId);
AnnotationHelper.alterClassAnnotation(SaveCourse.class, TmsLink.class, tmsaltered);
tmslink = SaveCourse.class.getAnnotation(TmsLink.class);

The displayName interface:

/**
 * Used to change display name for test in the report.
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface DisplayName {

    String value();

}





Aucun commentaire:

Enregistrer un commentaire