Skip to content

Commit

Permalink
ArC AnnotationsTransformer - add more specific builders
Browse files Browse the repository at this point in the history
- and add the convenient doTransform() method that applies
  transformation automatically
- also mention the builders in the docs
  • Loading branch information
mkouba committed Mar 24, 2023
1 parent 125f884 commit b766fea
Show file tree
Hide file tree
Showing 5 changed files with 323 additions and 102 deletions.
14 changes: 14 additions & 0 deletions docs/src/main/asciidoc/cdi-integration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,20 @@ AnnotationsTransformerBuildItem transform() {

NOTE: Keep in mind that annotation transformers must be produced _before_ the bean discovery starts.

You can also use a convenient builder-like API to create transformer instances.
The example above can be rewritten like:

Builder Example
[source,java]
----
@BuildStep
AnnotationsTransformerBuildItem transform() {
return new AnnotationsTransformerBuildItem(AnnotationsTransformer.appliedToClass()
.whenClass(c -> c.name().toString().equals("org.acme.Bar"))
.doTransform(t -> t.add(MyInterceptorBinding.class)));
}
----

Build steps can query the transformed annotations for a given annotation target via the `TransformedAnnotationsBuildItem`.

.`TransformedAnnotationsBuildItem` Example
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationTarget.Kind;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.MethodInfo;

/**
* Allows a build-time extension to override the annotations that exist on bean classes.
Expand Down Expand Up @@ -43,11 +47,38 @@ default boolean appliesTo(Kind kind) {
/**
*
* @return a new builder instance
* @see #appliedToMethod()
* @see #appliedToField()
* @see #appliedToClass()
*/
static Builder builder() {
return new Builder();
}

/**
*
* @return a new builder to transform methods
*/
static MethodTransformerBuilder appliedToMethod() {
return new MethodTransformerBuilder();
}

/**
*
* @return a new builder to transform fields
*/
static FieldTransformerBuilder appliedToField() {
return new FieldTransformerBuilder();
}

/**
*
* @return a new builder to transform class
*/
static ClassTransformerBuilder appliedToClass() {
return new ClassTransformerBuilder();
}

/**
* A transformation context.
*/
Expand Down Expand Up @@ -85,17 +116,11 @@ default boolean isMethod() {
}

/**
* A convenient builder.
* A common {@link AnnotationsTransformer} builder.
*/
static final class Builder {
public final static class Builder extends AbstractBuilder<Builder> {

private int priority;
private Predicate<Kind> appliesTo;
private Predicate<TransformationContext> predicate;

private Builder() {
this.priority = DEFAULT_PRIORITY;
}
protected Predicate<Kind> appliesTo;

/**
*
Expand All @@ -118,14 +143,100 @@ public Builder appliesTo(Predicate<Kind> appliesTo) {
return this;
}

@Override
public boolean test(Kind kind) {
return appliesTo == null || appliesTo.test(kind);
}

}

public final static class MethodTransformerBuilder extends AbstractBuilder<MethodTransformerBuilder> {

/**
* The method must meet the given condition.
*
* @param condition
* @return self
*/
public MethodTransformerBuilder whenMethod(Predicate<MethodInfo> condition) {
return when(wrap(condition, MethodTransformerBuilder::extract));
}

@Override
public boolean test(Kind kind) {
return kind == Kind.METHOD;
}

private static MethodInfo extract(TransformationContext ctx) {
return ctx.getTarget().asMethod();
}

}

public final static class FieldTransformerBuilder extends AbstractBuilder<FieldTransformerBuilder> {

/**
* The field must meet the given condition.
*
* @param condition
* @return self
*/
public FieldTransformerBuilder whenField(Predicate<FieldInfo> condition) {
return when(wrap(condition, FieldTransformerBuilder::extract));
}

@Override
public boolean test(Kind kind) {
return kind == Kind.FIELD;
}

private static FieldInfo extract(TransformationContext ctx) {
return ctx.getTarget().asField();
}

}

public final static class ClassTransformerBuilder extends AbstractBuilder<ClassTransformerBuilder> {

/**
* The class must meet the given condition.
*
* @param condition
* @return self
*/
public ClassTransformerBuilder whenClass(Predicate<ClassInfo> condition) {
return when(wrap(condition, ClassTransformerBuilder::extract));
}

@Override
public boolean test(Kind kind) {
return kind == Kind.CLASS;
}

private static ClassInfo extract(TransformationContext ctx) {
return ctx.getTarget().asClass();
}

}

public abstract static class AbstractBuilder<THIS extends AbstractBuilder<THIS>>
implements Predicate<Kind> {

protected int priority;
protected Predicate<TransformationContext> predicate;

private AbstractBuilder() {
this.priority = DEFAULT_PRIORITY;
}

/**
*
* @param priority
* @return self
*/
public Builder priority(int priority) {
public THIS priority(int priority) {
this.priority = priority;
return this;
return self();
}

/**
Expand All @@ -134,7 +245,7 @@ public Builder priority(int priority) {
* @param annotationNames
* @return self
*/
public Builder whenContainsAll(List<DotName> annotationNames) {
public THIS whenContainsAll(List<DotName> annotationNames) {
return when(context -> {
for (DotName annotationName : annotationNames) {
if (!Annotations.contains(context.getAnnotations(), annotationName)) {
Expand All @@ -151,7 +262,7 @@ public Builder whenContainsAll(List<DotName> annotationNames) {
* @param annotationNames
* @return self
*/
public Builder whenContainsAll(DotName... annotationNames) {
public THIS whenContainsAll(DotName... annotationNames) {
return whenContainsAll(List.of(annotationNames));
}

Expand All @@ -162,7 +273,7 @@ public Builder whenContainsAll(DotName... annotationNames) {
* @return self
*/
@SafeVarargs
public final Builder whenContainsAll(Class<? extends Annotation>... annotationNames) {
public final THIS whenContainsAll(Class<? extends Annotation>... annotationNames) {
return whenContainsAll(
Arrays.stream(annotationNames).map(a -> DotName.createSimple(a.getName())).collect(Collectors.toList()));
}
Expand All @@ -173,7 +284,7 @@ public final Builder whenContainsAll(Class<? extends Annotation>... annotationNa
* @param annotationNames
* @return self
*/
public Builder whenContainsAny(List<DotName> annotationNames) {
public THIS whenContainsAny(List<DotName> annotationNames) {
return when(context -> Annotations.containsAny(context.getAnnotations(), annotationNames));
}

Expand All @@ -183,7 +294,7 @@ public Builder whenContainsAny(List<DotName> annotationNames) {
* @param annotationNames
* @return self
*/
public Builder whenContainsAny(DotName... annotationNames) {
public THIS whenContainsAny(DotName... annotationNames) {
return whenContainsAny(List.of(annotationNames));
}

Expand All @@ -194,7 +305,7 @@ public Builder whenContainsAny(DotName... annotationNames) {
* @return self
*/
@SafeVarargs
public final Builder whenContainsAny(Class<? extends Annotation>... annotationNames) {
public final THIS whenContainsAny(Class<? extends Annotation>... annotationNames) {
return whenContainsAny(
Arrays.stream(annotationNames).map(a -> DotName.createSimple(a.getName())).collect(Collectors.toList()));
}
Expand All @@ -205,7 +316,7 @@ public final Builder whenContainsAny(Class<? extends Annotation>... annotationNa
* @param annotationNames
* @return self
*/
public Builder whenContainsNone(List<DotName> annotationNames) {
public THIS whenContainsNone(List<DotName> annotationNames) {
return when(context -> !Annotations.containsAny(context.getAnnotations(), annotationNames));
}

Expand All @@ -215,7 +326,7 @@ public Builder whenContainsNone(List<DotName> annotationNames) {
* @param annotationNames
* @return self
*/
public Builder whenContainsNone(DotName... annotationNames) {
public THIS whenContainsNone(DotName... annotationNames) {
return whenContainsNone(List.of(annotationNames));
}

Expand All @@ -226,7 +337,7 @@ public Builder whenContainsNone(DotName... annotationNames) {
* @return self
*/
@SafeVarargs
public final Builder whenContainsNone(Class<? extends Annotation>... annotationNames) {
public final THIS whenContainsNone(Class<? extends Annotation>... annotationNames) {
return whenContainsNone(
Arrays.stream(annotationNames).map(a -> DotName.createSimple(a.getName())).collect(Collectors.toList()));
}
Expand All @@ -238,23 +349,44 @@ public final Builder whenContainsNone(Class<? extends Annotation>... annotationN
* @param predicate
* @return self
*/
public Builder when(Predicate<TransformationContext> when) {
public THIS when(Predicate<TransformationContext> when) {
if (predicate == null) {
predicate = when;
} else {
predicate = predicate.and(when);
}
return this;
return self();
}

/**
* The transformation logic is performed only if all conditions are met.
* <p>
* Unlike in {@link #transform(Consumer)} the transformation is always applied if conditions are met, i.e.
* {@link Transformation#done()} is called automatically.
*
* @param consumer
* @return a new annotation transformer
*/
public AnnotationsTransformer doTransform(Consumer<Transformation> consumer) {
return transform(new Consumer<TransformationContext>() {

@Override
public void accept(TransformationContext context) {
Transformation transformation = context.transform();
consumer.accept(transformation);
transformation.done();
}
});

}

/**
* The given transformation logic is only performed if all conditions added via {@link #when(Predicate)} are met.
* The transformation logic is performed only if all conditions are met.
*
* @param consumer
* @return a new annotation transformer
*/
public AnnotationsTransformer transform(Consumer<TransformationContext> consumer) {
Predicate<Kind> appliesTo = this.appliesTo;
int priority = this.priority;
Consumer<TransformationContext> transform = Objects.requireNonNull(consumer);
Predicate<TransformationContext> predicate = this.predicate;
Expand All @@ -267,7 +399,7 @@ public int getPriority() {

@Override
public boolean appliesTo(Kind kind) {
return appliesTo != null ? appliesTo.test(kind) : true;
return test(kind);
}

@Override
Expand All @@ -280,6 +412,22 @@ public void transform(TransformationContext context) {
};
}

@SuppressWarnings("unchecked")
protected THIS self() {
return (THIS) this;
}

protected <TARGET> Predicate<TransformationContext> wrap(Predicate<TARGET> condition,
Function<TransformationContext, TARGET> extractor) {
return new Predicate<TransformationContext>() {

@Override
public boolean test(TransformationContext ctx) {
return condition.test(extractor.apply(ctx));
}
};
}

}

}

0 comments on commit b766fea

Please sign in to comment.