Skip to content

Commit

Permalink
Allow using AnnotationCreator to represent nested annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
Ladicek committed May 5, 2022
1 parent 1048fce commit 9f16553
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 140 deletions.
68 changes: 67 additions & 1 deletion src/main/java/io/quarkus/gizmo/AnnotationCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,74 @@

package io.quarkus.gizmo;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public interface AnnotationCreator {
/**
* Returns an {@link AnnotationCreator} for annotation of given {@code annotationType}.
* <p>
* Useful for representing nested annotations (see {@link #addValue(String, Object) addValue}).
*
* @param annotationType class name of the annotation to create, must not be {@code null}
* @return an {@code AnnotationCreator}, never {@code null}
*/
static AnnotationCreator of(String annotationType) {
return of(annotationType, RetentionPolicy.RUNTIME);
}

void addValue(String name, Object value);
/**
* Returns an {@link AnnotationCreator} for annotation of given {@code annotationType}.
* <p>
* Useful for representing nested annotations (see {@link #addValue(String, Object) addValue}).
*
* @param annotationType type of the annotation to create, must not be {@code null}
* @return an {@code AnnotationCreator}, never {@code null}
*/
static AnnotationCreator of(Class<?> annotationType) {
Retention retention = annotationType.getAnnotation(Retention.class);
return of(annotationType.getName(), retention == null ? RetentionPolicy.SOURCE : retention.value());
}

/**
* Returns an {@link AnnotationCreator} for annotation of given {@code annotationType}
* which has given {@code retentionPolicy}.
* <p>
* Useful for representing nested annotations (see {@link #addValue(String, Object) addValue}).
*
* @param annotationType class name of the annotation to create, must not be {@code null}
* @param retentionPolicy retention policy of the annotation type
* @return an {@code AnnotationCreator}, never {@code null}
*/
static AnnotationCreator of(String annotationType, RetentionPolicy retentionPolicy) {
return new AnnotationCreatorImpl(annotationType, retentionPolicy);
}

/**
* Same as {@link #addValue(String, Object)}, but returns {@code this} to allow fluent usage.
*/
default AnnotationCreator add(String name, Object value) {
addValue(name, value);
return this;
}

/**
* Add a new annotation element with the given {@code name} and {@code value}. The name may be any permissible
* annotation element name (for example, {@code toString} or {@code annotationType} are invalid). The value may be:
* <ul>
* <li>primitive wrapper type</li>
* <li>{@link String}</li>
* <li>{@link Enum}</li>
* <li>{@link Class}</li>
* <li>nested annotation as an {@link AnnotationCreator} created using {@code AnnotationCreator.of()},
* or as a Jandex {@link org.jboss.jandex.AnnotationInstance AnnotationInstance}</li>
* <li>array of previously mentioned types</li>
* </ul>
* In addition to the types listed above, the value may also be a Jandex
* {@link org.jboss.jandex.AnnotationValue AnnotationValue}.
*
* @param name name of the annotation element to add
* @param value value of the annotation element to add
*/
void addValue(String name, Object value);
}
5 changes: 1 addition & 4 deletions src/main/java/io/quarkus/gizmo/AnnotationCreatorImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@

package io.quarkus.gizmo;

import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;

Expand All @@ -39,8 +37,7 @@ class AnnotationCreatorImpl implements AnnotationCreator {
@Override
public void addValue(String name, Object value) {
// TODO: Maybe typecheck value to ensure it matches the corresponding element type?
// TODO: If value is a Map, check if all its keys are elements for the annotation, and there are no
// missing required elements
// TODO: If value is a nested annotation, check for nonexistent or missing elements?
values.put(name, value);
}

Expand Down
25 changes: 7 additions & 18 deletions src/main/java/io/quarkus/gizmo/AnnotationUtils.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package io.quarkus.gizmo;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.Map;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Type;

final class AnnotationUtils {

Expand All @@ -28,24 +26,15 @@ static void visitAnnotationValue(AnnotationVisitor visitor, String key, Object v
visitAnnotationValue(nestedVisitor, annotationValue.name(), annotationValue);
}
nestedVisitor.visitEnd();
} else if (value instanceof Map) {
Map<String, Object> annotationValueMap = (Map<String, Object>) value;
if (!annotationValueMap.containsKey("annotationType")) {
throw new IllegalStateException("The annotationValueMap (" + annotationValueMap + ") does not have entry for " +
"required value \"annotationType\".");
} else if (value instanceof AnnotationCreator) {
if (!(value instanceof AnnotationCreatorImpl)) {
throw new IllegalArgumentException("Custom implementations of AnnotationCreator are not accepted");
}
Class<? extends Annotation> annotationType = (Class<? extends Annotation>) annotationValueMap.get("annotationType");
String descriptor = Type.getDescriptor(annotationType);
AnnotationCreatorImpl nested = (AnnotationCreatorImpl) value;
String descriptor = DescriptorUtils.objectToDescriptor(nested.getAnnotationType());
AnnotationVisitor nestedVisitor = visitor.visitAnnotation(key, descriptor);
for (Map.Entry<String, Object> annotationInstanceValueEntry : annotationValueMap.entrySet()) {
final String parameterName = annotationInstanceValueEntry.getKey();
final Object parameterValue = annotationInstanceValueEntry.getValue();

if (parameterName.equals("annotationType")) {
continue;
}

visitAnnotationValue(nestedVisitor, parameterName, parameterValue);
for (Map.Entry<String, Object> member : nested.getValues().entrySet()) {
visitAnnotationValue(nestedVisitor, member.getKey(), member.getValue());
}
nestedVisitor.visitEnd();
} else if (value instanceof AnnotationValue) {
Expand Down
Loading

0 comments on commit 9f16553

Please sign in to comment.