Skip to content

Commit

Permalink
Added methods to output string representations of annotations to Anno…
Browse files Browse the repository at this point in the history
…tationMirrorWrapper.
  • Loading branch information
tobiasstamann committed Nov 5, 2023
1 parent ea6715d commit 240822a
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import io.toolisticon.aptk.tools.AnnotationUtils;
import io.toolisticon.aptk.tools.MessagerUtils;
import io.toolisticon.aptk.tools.TypeMirrorWrapper;
import io.toolisticon.aptk.tools.wrapper.ElementWrapper;
import io.toolisticon.cute.CompileTestBuilder;
import io.toolisticon.cute.PassIn;
import org.hamcrest.MatcherAssert;
Expand Down Expand Up @@ -64,6 +65,8 @@ public void aptkUnitTest(ProcessingEnvironment processingEnvironment, TypeElemen
MatcherAssert.assertThat(testAnnotationWrapper.annotationAttribute()._annotatedElement(), Matchers.is((Element)typeElement));




// single attribute values

MatcherAssert.assertThat(testAnnotationWrapper.charAttribute(), Matchers.is('X'));
Expand Down Expand Up @@ -152,4 +155,6 @@ public void aptkUnitTest(ProcessingEnvironment processingEnvironment, TypeElemen
.compilationShouldSucceed()
.executeTest();
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import io.toolisticon.aptk.tools.AnnotationUtils;
import io.toolisticon.aptk.tools.TypeMirrorWrapper;
import io.toolisticon.aptk.tools.TypeUtils;
import io.toolisticon.aptk.tools.wrapper.CompileMessageWriter;
import io.toolisticon.aptk.tools.wrapper.ElementWrapper;
!{if atw.customInterfaces != null}!{for customInterface : atw.customInterfaces}import ${customInterface.qualifiedName};
!{/for}!{/if}

Expand Down Expand Up @@ -377,12 +378,21 @@ ${state.visibilityModifier}class ${atw.simpleName}Wrapper !{if atw.customInterfa
!{else}
/**
* Gets the AnnotationMirror from passed element for this wrappers annotation type and creates a wrapper instance.
* @param element The element to read the annotations from
* @param annotatedElement The element to read the annotations from
* @return The wrapped AnnotationMirror if Element is annotated with this wrappers annotation type, otherwise null.
*/
${state.visibilityModifier}static List<${atw.simpleName}Wrapper> wrap(Element annotatedElement) {
Optional<List<AnnotationMirror>> repeatableAnnotations = AnnotationUtils.getRepeatableAnnotation(annotatedElement, ${atw.simpleName}.class);
return repeatableAnnotations.isPresent() ? repeatableAnnotations.get().stream().map(e -> wrap(annotatedElement, e)).collect(Collectors.toList()) : Collections.EMPTY_LIST;
}

/**
* Gets the AnnotationMirror from passed element for this wrappers annotation type and creates a wrapper instance.
* @param annotatedElement The element to read the annotations from
* @return The wrapped AnnotationMirror if Element is annotated with this wrappers annotation type, otherwise null.
*/
${state.visibilityModifier}static List<${atw.simpleName}Wrapper> wrap(Element element) {
Optional<List<AnnotationMirror>> repeatableAnnotations = AnnotationUtils.getRepeatableAnnotation(element, ${atw.simpleName}.class);
return repeatableAnnotations.isPresent() ? repeatableAnnotations.get().stream().map(e -> wrap(element, e)).collect(Collectors.toList()) : Collections.EMPTY_LIST;
${state.visibilityModifier}static List<${atw.simpleName}Wrapper> wrap(ElementWrapper annotatedElement) {
return ${atw.simpleName}Wrapper.wrap(annotatedElement.unwrap());
}
!{/if}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import javax.lang.model.type.DeclaredType;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -41,21 +43,30 @@ public AnnotationMirror unwrap() {

/**
* Get DeclaredType of wrapped annotation.
*
* @return the annotations declared type
*/
DeclaredType getAnnotationType() {
public DeclaredType getAnnotationType() {
return this.annotationMirror.getAnnotationType();
}

/**
* Get wrapped DeclaredType of wrapped annotation.
*
* @return the annotations declared type
*/
TypeMirrorWrapper getWrappedAnnotationType() {
public TypeMirrorWrapper getAnnotationTypeAsWrappedTypeMirror() {
return TypeMirrorWrapper.wrap(getAnnotationType());
}


/**
* Get wrapped DeclaredType's TypeElement of wrapped annotation.
*
* @return the annotations declared type as wrapped TypeELement
*/
public TypeElementWrapper getAnnotationTypeAsWrappedTypeElement() {
return getAnnotationTypeAsWrappedTypeMirror().getTypeElement().get();
}

/**
* Returns the "value" attribute
Expand Down Expand Up @@ -164,6 +175,15 @@ public Set<String> getAttributeNames() {
return this.annotationMirror.getAnnotationType().asElement().getEnclosedElements().stream().filter(e -> e.getKind() == ElementKind.METHOD).map(e -> e.getSimpleName().toString()).collect(Collectors.toSet());
}

/**
* Get all attribute names of the annotation.
*
* @return a list containing all attribute names in declaration order
*/
public List<String> getAttibuteNamesInDeclarationOrder() {
return this.annotationMirror.getAnnotationType().asElement().getEnclosedElements().stream().filter(e -> e.getKind() == ElementKind.METHOD).map(e -> e.getSimpleName().toString()).collect(Collectors.toList());
}

/**
* Check if AnnotationMirror has an attribute with passed name.
*
Expand All @@ -176,12 +196,66 @@ public boolean hasAttribute(String name) {


/**
* Returns the element corresponding to the wrapped AnnotationMirror.
* Returns the wrapped TypeMirror of the AnnotationMirrors annotation type.
*
* @return the wrapped TypeMirror corresponding to the wrapped TypeMirror
*/
public TypeMirrorWrapper asTypeMirror() {
return getAnnotationTypeAsWrappedTypeMirror();
}

/**
* Returns the wrapped TypeElement of the AnnotationMirrors annotation type.
*
* @return the wrapped TypeElement corresponding to the wrapped TypeMirror
*/
public TypeElementWrapper asElement() {
return this.getAnnotationTypeAsWrappedTypeElement();
}

/**
* Gets a string representation of the annotation.
*
* @return the TypeElement corresponding to this type
* @return
*/
public TypeMirrorWrapper asElement() {
return TypeMirrorWrapper.wrap(this.annotationMirror.getAnnotationType().asElement().asType());
public String getStringRepresentation() {

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("@").append(this.asElement().getSimpleName());

stringBuilder.append(getAttibuteNamesInDeclarationOrder().stream()
.map(e -> {
Optional<AnnotationValueWrapper> optionalAnnotationValueWrapper = getAttribute(e);
return optionalAnnotationValueWrapper.isPresent() ? e + " = " + getAnnotationAttributeValueStringRepresentation(optionalAnnotationValueWrapper.get()) : null;
})
.filter(Objects::nonNull)
.collect(Collectors.joining(", ", "(", ")")));

return stringBuilder.toString().replaceAll("[\"]", "\\\\\"");
}

public String getStringRepresentationWithDefaults() {

StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("@").append(this.asElement().getSimpleName());

stringBuilder.append(getAttibuteNamesInDeclarationOrder().stream()
.map(e -> e + " = " + getAnnotationAttributeValueStringRepresentation(getAttributeWithDefault(e)))
.collect(Collectors.joining(", ", "(", ")")));

return stringBuilder.toString().replaceAll("[\"]", "\\\\\"");
}

String getAnnotationAttributeValueStringRepresentation(AnnotationValueWrapper annotationValueWrapper) {
if (annotationValueWrapper.isArray()) {
return annotationValueWrapper.getArrayValue().stream().map(e -> getAnnotationAttributeValueStringRepresentation(e)).collect(Collectors.joining(", ", "{", "}"));
} else if (annotationValueWrapper.isEnum()) {
return annotationValueWrapper.getEnumValue().asType().getSimpleName() + "." + annotationValueWrapper.getEnumValue().getSimpleName();
} else if (annotationValueWrapper.isClass()) {
return annotationValueWrapper.getClassValue().getSimpleName() + ".class";
} else {
return annotationValueWrapper.unwrap().toString();
}
}

/**
Expand All @@ -201,6 +275,6 @@ public static AnnotationMirrorWrapper wrap(AnnotationMirror annotationMirror) {
* @return an array that contains the wrapped instances
*/
public static AnnotationMirrorWrapper[] wrap(AnnotationMirror[] annotationMirrors) {
return annotationMirrors != null ? Arrays.stream(annotationMirrors).map(e -> AnnotationMirrorWrapper.wrap(e)).toArray(AnnotationMirrorWrapper[]::new): null;
return annotationMirrors != null ? Arrays.stream(annotationMirrors).map(e -> AnnotationMirrorWrapper.wrap(e)).toArray(AnnotationMirrorWrapper[]::new) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Optional;

/**
Expand Down Expand Up @@ -124,18 +126,18 @@ public void test_getAttributeWithDefault() {
public void test_getAttributeWithDefault_passNullName() {

CompileTestBuilder.unitTest().<TypeElement>defineTestWithPassedInElement(MyTestClass.class, (processingEnvironment, element) -> {
try {
ToolingProvider.setTooling(processingEnvironment);
try {
ToolingProvider.setTooling(processingEnvironment);

AnnotationMirrorWrapper unit = ElementWrapper.wrap(element).getAnnotationMirror(MyTestAnnotation.class).get();
unit.getAttributeWithDefault(null);
AnnotationMirrorWrapper unit = ElementWrapper.wrap(element).getAnnotationMirror(MyTestAnnotation.class).get();
unit.getAttributeWithDefault(null);


} finally {
ToolingProvider.clearTooling();
}
} finally {
ToolingProvider.clearTooling();
}

}).expectedThrownException(IllegalArgumentException.class)
}).expectedThrownException(IllegalArgumentException.class)
.executeTest();

}
Expand All @@ -144,18 +146,18 @@ public void test_getAttributeWithDefault_passNullName() {
public void test_getAttributeWithDefault_invalidAttributeName() {

CompileTestBuilder.unitTest().<TypeElement>defineTestWithPassedInElement(MyTestClass.class, (processingEnvironment, element) -> {
try {
ToolingProvider.setTooling(processingEnvironment);
try {
ToolingProvider.setTooling(processingEnvironment);

AnnotationMirrorWrapper unit = ElementWrapper.wrap(element).getAnnotationMirror(MyTestAnnotation.class).get();
unit.getAttributeWithDefault("XYZ");
AnnotationMirrorWrapper unit = ElementWrapper.wrap(element).getAnnotationMirror(MyTestAnnotation.class).get();
unit.getAttributeWithDefault("XYZ");


} finally {
ToolingProvider.clearTooling();
}
} finally {
ToolingProvider.clearTooling();
}

}).expectedThrownException(IllegalArgumentException.class)
}).expectedThrownException(IllegalArgumentException.class)
.executeTest();

}
Expand Down Expand Up @@ -250,5 +252,98 @@ public void test_asElement() {

}

enum StringRepresentationTestEnum {
ENUM_VALUE,
DEFAULT_ENUM_VALUE;
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface StringRepresentationTestAnnotation {

String[] arrayValue() default {"1", "2"};

String stringValue() default "YYY";

long longValue() default 10L;

int intValue() default 11;

float floatValue() default 12.0f;

double doubleValue() default 13.0;

boolean booleanValue() default false;

StringRepresentationTestEnum enumValue() default StringRepresentationTestEnum.DEFAULT_ENUM_VALUE;

Class<?> classValue() default Long.class;

}


@PassIn
@StringRepresentationTestAnnotation(
arrayValue = {"4"},
stringValue = "XXX",
longValue = 1L,
intValue = 2,
floatValue = 3.0f,
doubleValue = 4.0,
booleanValue = true,
enumValue = StringRepresentationTestEnum.ENUM_VALUE,
classValue = String.class

)
static class StringRepresentationTest {

}

@PassIn
@StringRepresentationTestAnnotation()
static class StringRepresentationWithDefaultsTest {

}

@Test
public void test_stringRepresentation() {

CompileTestBuilder.unitTest().<TypeElement>defineTestWithPassedInElement(StringRepresentationTest.class, (processingEnvironment, element) -> {
try {
ToolingProvider.setTooling(processingEnvironment);

// By class
Optional<AnnotationMirrorWrapper> result = AnnotationMirrorWrapper.get(element, StringRepresentationTestAnnotation.class);
MatcherAssert.assertThat(result.get().getStringRepresentation(), Matchers.is("@StringRepresentationTestAnnotation(arrayValue = {\\\"4\\\"}, stringValue = \\\"XXX\\\", longValue = 1L, intValue = 2, floatValue = 3.0f, doubleValue = 4.0, booleanValue = true, enumValue = StringRepresentationTestEnum.ENUM_VALUE, classValue = String.class)"));


} finally {
ToolingProvider.clearTooling();
}

}).executeTest();

}


@Test
public void test_stringRepresentationWithDefaults() {

CompileTestBuilder.unitTest().<TypeElement>defineTestWithPassedInElement(StringRepresentationWithDefaultsTest.class, (processingEnvironment, element) -> {
try {
ToolingProvider.setTooling(processingEnvironment);

// By class
Optional<AnnotationMirrorWrapper> result = AnnotationMirrorWrapper.get(element, StringRepresentationTestAnnotation.class);
MatcherAssert.assertThat(result.get().getStringRepresentationWithDefaults(), Matchers.is("@StringRepresentationTestAnnotation(arrayValue = {\\\"1\\\", \\\"2\\\"}, stringValue = \\\"YYY\\\", longValue = 10L, intValue = 11, floatValue = 12.0f, doubleValue = 13.0, booleanValue = false, enumValue = StringRepresentationTestEnum.DEFAULT_ENUM_VALUE, classValue = Long.class)"));


} finally {
ToolingProvider.clearTooling();
}

}).executeTest();

}

}

0 comments on commit 240822a

Please sign in to comment.