diff --git a/api/src/main/java/org/mapstruct/tools/gem/GemDefinition.java b/api/src/main/java/org/mapstruct/tools/gem/GemDefinition.java index 9325452..73a5abe 100644 --- a/api/src/main/java/org/mapstruct/tools/gem/GemDefinition.java +++ b/api/src/main/java/org/mapstruct/tools/gem/GemDefinition.java @@ -25,4 +25,14 @@ * @return the annotation for which a Gem should be generated */ Class value(); + + /** + * Specifies the name of the implementation class. The {@code } will be replaced by the + * interface/abstract class name. + *

+ * Defaults to postfixing the name with {@code Gem}: {@code Gem} + * + * @return The implementation name. + */ + String implementationName() default "Gem"; } diff --git a/processor/src/main/java/org/mapstruct/tools/gem/processor/GemInfo.java b/processor/src/main/java/org/mapstruct/tools/gem/processor/GemInfo.java index 565c644..34c93bf 100644 --- a/processor/src/main/java/org/mapstruct/tools/gem/processor/GemInfo.java +++ b/processor/src/main/java/org/mapstruct/tools/gem/processor/GemInfo.java @@ -26,10 +26,10 @@ public class GemInfo { private final List gemValueInfos; private final Element[] originatingElements; - public GemInfo(String gemPackageName, String annotationName, String annotationFqn, + public GemInfo(String gemPackageName, String gemName, String annotationName, String annotationFqn, List gemValueInfos, Element... originatingElements) { this.gemPackageName = gemPackageName; - this.gemName = annotationName + "Gem"; + this.gemName = gemName; this.annotationName = annotationName; this.annotationFqn = annotationFqn; this.gemValueInfos = gemValueInfos; diff --git a/processor/src/main/java/org/mapstruct/tools/gem/processor/GemProcessor.java b/processor/src/main/java/org/mapstruct/tools/gem/processor/GemProcessor.java index b294998..9b57c2e 100644 --- a/processor/src/main/java/org/mapstruct/tools/gem/processor/GemProcessor.java +++ b/processor/src/main/java/org/mapstruct/tools/gem/processor/GemProcessor.java @@ -95,8 +95,9 @@ private void addGemInfo(AnnotationMirror gemDefinitionMirror, Element definingEl // create gem info DeclaredType gemDeclaredType = util.getAnnotationValue( gemDefinitionMirror, "value", DeclaredType.class ); + String annotationName = util.getSimpleName( gemDeclaredType ); + String gemName = getGemName( gemDefinitionMirror, annotationName ); PackageElement pkg = processingEnv.getElementUtils().getPackageOf( definingElement ); - String gemName = util.getSimpleName( gemDeclaredType ); String gemFqn = util.getFullyQualifiedName( gemDeclaredType ); String gemPackage = pkg.getQualifiedName().toString(); @@ -108,6 +109,7 @@ private void addGemInfo(AnnotationMirror gemDefinitionMirror, Element definingEl GemInfo gemInfo = new GemInfo( gemPackage, gemName, + annotationName, gemFqn, gemValueInfos, definingElement, @@ -116,6 +118,14 @@ private void addGemInfo(AnnotationMirror gemDefinitionMirror, Element definingEl gemInfos.add( gemInfo ); } + private String getGemName(AnnotationMirror gemDefinitionMirror, String annotationName) { + String implementationName = util.getAnnotationValue( gemDefinitionMirror, "implementationName", String.class ); + if ( implementationName != null ) { + return implementationName.replace( "", annotationName ); + } + return annotationName + "Gem"; + } + private void postProcessGemInfo() { for ( GemInfo gemInfo : gemInfos ) { for ( GemValueInfo gemValueInfo : gemInfo.getGemValueInfos() ) { diff --git a/processor/src/test/java/org/mapstruct/tools/gem/processor/ProcessorTest.java b/processor/src/test/java/org/mapstruct/tools/gem/processor/ProcessorTest.java index b9773e1..3bd937c 100644 --- a/processor/src/test/java/org/mapstruct/tools/gem/processor/ProcessorTest.java +++ b/processor/src/test/java/org/mapstruct/tools/gem/processor/ProcessorTest.java @@ -42,6 +42,7 @@ void example() throws IOException { ); File generatedDir = compile( new GemProcessor(), src ); assertGeneratedFileContent( "BuilderGem", generatedDir ); + assertGeneratedFileContent( "CustomBuilderGem", generatedDir ); assertGeneratedFileContent( "SomeAnnotationGem", generatedDir ); assertGeneratedFileContent( "SomeAnnotationsGem", generatedDir ); assertGeneratedFileContent( "SomeArrayAnnotationGem", generatedDir ); @@ -145,6 +146,7 @@ private String getSource() { "@GemDefinition(value = SomeAnnotations.class)\n" + "@GemDefinition(value = SomeArrayAnnotation.class)\n" + "@GemDefinition(value = Builder.class)\n" + + "@GemDefinition(value = Builder.class, implementationName = \"CustomGem\")\n" + "public class GemGenerator {\n" + "}"; } diff --git a/processor/src/test/resources/fixtures/org/mapstruct/tools/gem/processor/CustomBuilderGem.java b/processor/src/test/resources/fixtures/org/mapstruct/tools/gem/processor/CustomBuilderGem.java new file mode 100644 index 0000000..d5c1a0d --- /dev/null +++ b/processor/src/test/resources/fixtures/org/mapstruct/tools/gem/processor/CustomBuilderGem.java @@ -0,0 +1,104 @@ +package org.mapstruct.tools.gem.processor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.AbstractAnnotationValueVisitor8; +import javax.lang.model.util.ElementFilter; +import org.mapstruct.tools.gem.Gem; +import org.mapstruct.tools.gem.GemValue; + + +public class CustomBuilderGem implements Gem { + + private final AnnotationMirror mirror; + + private CustomBuilderGem( BuilderImpl builder ) { + mirror = builder.mirror; + } + + @Override + public AnnotationMirror mirror( ) { + return mirror; + } + + @Override + public boolean isValid( ) { + return true; + } + + public static CustomBuilderGem instanceOn(Element element) { + return build( element, new BuilderImpl() ); + } + + public static CustomBuilderGem instanceOn(AnnotationMirror mirror ) { + return build( mirror, new BuilderImpl() ); + } + + public static T build(Element element, Builder_ builder) { + AnnotationMirror mirror = element.getAnnotationMirrors().stream() + .filter( a -> "org.mapstruct.tools.gem.test.Builder".contentEquals( ( ( TypeElement )a.getAnnotationType().asElement() ).getQualifiedName() ) ) + .findAny() + .orElse( null ); + return build( mirror, builder ); + } + + public static T build(AnnotationMirror mirror, Builder_ builder ) { + + // return fast + if ( mirror == null || builder == null ) { + return null; + } + builder.setMirror( mirror ); + return builder.build(); + } + + /** + * A builder that can be implemented by the user to define custom logic e.g. in the + * build method, prior to creating the annotation gem. + */ + public interface Builder_ { + + /** + * Sets the annotation mirror + * + * @param mirror the mirror which this gem represents + * + * @return the {@link Builder_} for this gem, representing {@link CustomBuilderGem} + */ + Builder_ setMirror( AnnotationMirror mirror ); + + /** + * The build method can be overriden in a custom custom implementation, which allows + * the user to define his own custom validation on the annotation. + * + * @return the representation of the annotation + */ + T build(); + } + + private static class BuilderImpl implements Builder_ { + + private AnnotationMirror mirror; + + public Builder_ setMirror( AnnotationMirror mirror ) { + this.mirror = mirror; + return this; + } + + public CustomBuilderGem build() { + return new CustomBuilderGem( this ); + } + } + +}