#1640 Add init method with MapStructProcessingEnvironment to the BuilderProvider for initializing the Elements and Types

This commit is contained in:
Filip Hrisafov 2018-11-03 11:36:33 +01:00
parent 1266796921
commit 3ff4ebd60a
7 changed files with 57 additions and 62 deletions

View File

@ -522,7 +522,7 @@ public class TypeFactory {
try { try {
return roundContext.getAnnotationProcessorContext() return roundContext.getAnnotationProcessorContext()
.getBuilderProvider() .getBuilderProvider()
.findBuilderInfo( type, elementUtils, typeUtils ); .findBuilderInfo( type );
} }
catch ( MoreThanOneBuilderCreationMethodException ex ) { catch ( MoreThanOneBuilderCreationMethodException ex ) {
messager.printMessage( messager.printMessage(

View File

@ -76,6 +76,7 @@ public class AnnotationProcessorContext implements MapStructProcessingEnvironmen
this.accessorNamingStrategy = Services.get( AccessorNamingStrategy.class, defaultAccessorNamingStrategy ); this.accessorNamingStrategy = Services.get( AccessorNamingStrategy.class, defaultAccessorNamingStrategy );
this.accessorNamingStrategy.init( this ); this.accessorNamingStrategy.init( this );
this.builderProvider = Services.get( BuilderProvider.class, defaultBuilderProvider ); this.builderProvider = Services.get( BuilderProvider.class, defaultBuilderProvider );
this.builderProvider.init( this );
this.accessorNaming = new AccessorNamingUtils( this.accessorNamingStrategy ); this.accessorNaming = new AccessorNamingUtils( this.accessorNamingStrategy );
this.initialized = true; this.initialized = true;
} }

View File

@ -6,8 +6,6 @@
package org.mapstruct.ap.spi; package org.mapstruct.ap.spi;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/** /**
* A service provider interface that is used to detect types that require a builder for mapping. This interface could * A service provider interface that is used to detect types that require a builder for mapping. This interface could
@ -16,13 +14,19 @@ import javax.lang.model.util.Types;
*/ */
public interface BuilderProvider { public interface BuilderProvider {
/**
* Initializes the builder provider with the MapStruct processing environment.
*
* @param processingEnvironment environment for facilities
*/
default void init(MapStructProcessingEnvironment processingEnvironment) {
}
/** /**
* Find the builder information, if any, for the {@code type}. * Find the builder information, if any, for the {@code type}.
* *
* @param type the type for which a builder should be found * @param type the type for which a builder should be found
* @param elements the util elements that can be used for operations on program elements
* @param types the util types that can be used for operations on {@link TypeMirror}(s)
*
* @return the builder info for the {@code type} if it exists, or {@code null} if there is no builder * @return the builder info for the {@code type} if it exists, or {@code null} if there is no builder
* *
* @throws TypeHierarchyErroneousException if the type that needs to be visited is not ready yet, this signals the * @throws TypeHierarchyErroneousException if the type that needs to be visited is not ready yet, this signals the
@ -30,5 +34,5 @@ public interface BuilderProvider {
* @throws MoreThanOneBuilderCreationMethodException if {@code type} has more than one method that can create the * @throws MoreThanOneBuilderCreationMethodException if {@code type} has more than one method that can create the
* builder * builder
*/ */
BuilderInfo findBuilderInfo(TypeMirror type, Elements elements, Types types); BuilderInfo findBuilderInfo(TypeMirror type);
} }

View File

@ -77,14 +77,23 @@ public class DefaultBuilderProvider implements BuilderProvider {
private static final Pattern JAVA_JAVAX_PACKAGE = Pattern.compile( "^javax?\\..*" ); private static final Pattern JAVA_JAVAX_PACKAGE = Pattern.compile( "^javax?\\..*" );
protected Elements elementUtils;
protected Types typeUtils;
@Override @Override
public BuilderInfo findBuilderInfo(TypeMirror type, Elements elements, Types types) { public void init(MapStructProcessingEnvironment processingEnvironment) {
this.elementUtils = processingEnvironment.getElementUtils();
this.typeUtils = processingEnvironment.getTypeUtils();
}
@Override
public BuilderInfo findBuilderInfo(TypeMirror type) {
TypeElement typeElement = getTypeElement( type ); TypeElement typeElement = getTypeElement( type );
if ( typeElement == null ) { if ( typeElement == null ) {
return null; return null;
} }
return findBuilderInfo( typeElement, elements, types ); return findBuilderInfo( typeElement );
} }
/** /**
@ -130,8 +139,8 @@ public class DefaultBuilderProvider implements BuilderProvider {
* Find the {@link BuilderInfo} for the given {@code typeElement}. * Find the {@link BuilderInfo} for the given {@code typeElement}.
* <p> * <p>
* The default implementation iterates over all the methods in {@code typeElement} and uses * The default implementation iterates over all the methods in {@code typeElement} and uses
* {@link DefaultBuilderProvider#isPossibleBuilderCreationMethod(ExecutableElement, TypeElement, Types)} and * {@link DefaultBuilderProvider#isPossibleBuilderCreationMethod(ExecutableElement, TypeElement)} and
* {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement, Types)} to create the * {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement)} to create the
* {@link BuilderInfo}. * {@link BuilderInfo}.
* <p> * <p>
* The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the * The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the
@ -141,14 +150,11 @@ public class DefaultBuilderProvider implements BuilderProvider {
* thrown. * thrown.
* *
* @param typeElement the type element for which a builder searched * @param typeElement the type element for which a builder searched
* @param elements the util elements that can be used for operating on the type element
* @param types the util types that can be used for operation on {@link TypeMirror}(s)
*
* @return the {@link BuilderInfo} or {@code null} if no builder was found for the type * @return the {@link BuilderInfo} or {@code null} if no builder was found for the type
* {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement, Types)} * {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement)}
* @throws MoreThanOneBuilderCreationMethodException if there are multiple builder creation methods * @throws MoreThanOneBuilderCreationMethodException if there are multiple builder creation methods
*/ */
protected BuilderInfo findBuilderInfo(TypeElement typeElement, Elements elements, Types types) { protected BuilderInfo findBuilderInfo(TypeElement typeElement) {
if ( shouldIgnore( typeElement ) ) { if ( shouldIgnore( typeElement ) ) {
return null; return null;
} }
@ -156,9 +162,9 @@ public class DefaultBuilderProvider implements BuilderProvider {
List<ExecutableElement> methods = ElementFilter.methodsIn( typeElement.getEnclosedElements() ); List<ExecutableElement> methods = ElementFilter.methodsIn( typeElement.getEnclosedElements() );
List<BuilderInfo> builderInfo = new ArrayList<>(); List<BuilderInfo> builderInfo = new ArrayList<>();
for ( ExecutableElement method : methods ) { for ( ExecutableElement method : methods ) {
if ( isPossibleBuilderCreationMethod( method, typeElement, types ) ) { if ( isPossibleBuilderCreationMethod( method, typeElement ) ) {
TypeElement builderElement = getTypeElement( method.getReturnType() ); TypeElement builderElement = getTypeElement( method.getReturnType() );
Collection<ExecutableElement> buildMethods = findBuildMethods( builderElement, typeElement, types ); Collection<ExecutableElement> buildMethods = findBuildMethods( builderElement, typeElement );
if ( !buildMethods.isEmpty() ) { if ( !buildMethods.isEmpty() ) {
builderInfo.add( new BuilderInfo.Builder() builderInfo.add( new BuilderInfo.Builder()
.builderCreationMethod( method ) .builderCreationMethod( method )
@ -176,7 +182,7 @@ public class DefaultBuilderProvider implements BuilderProvider {
throw new MoreThanOneBuilderCreationMethodException( typeElement.asType(), builderInfo ); throw new MoreThanOneBuilderCreationMethodException( typeElement.asType(), builderInfo );
} }
return findBuilderInfo( typeElement.getSuperclass(), elements, types ); return findBuilderInfo( typeElement.getSuperclass() );
} }
/** /**
@ -192,39 +198,34 @@ public class DefaultBuilderProvider implements BuilderProvider {
* *
* @param method The method that needs to be checked * @param method The method that needs to be checked
* @param typeElement the enclosing element of the method, i.e. the type in which the method is located in * @param typeElement the enclosing element of the method, i.e. the type in which the method is located in
* @param types the util types that can be used for operations on {@link TypeMirror}(s)
*
* @return {@code true} if the {@code method} is a possible builder creation method, {@code false} otherwise * @return {@code true} if the {@code method} is a possible builder creation method, {@code false} otherwise
*/ */
protected boolean isPossibleBuilderCreationMethod(ExecutableElement method, TypeElement typeElement, Types types) { protected boolean isPossibleBuilderCreationMethod(ExecutableElement method, TypeElement typeElement) {
return method.getParameters().isEmpty() return method.getParameters().isEmpty()
&& method.getModifiers().contains( Modifier.PUBLIC ) && method.getModifiers().contains( Modifier.PUBLIC )
&& method.getModifiers().contains( Modifier.STATIC ) && method.getModifiers().contains( Modifier.STATIC )
&& !types.isSameType( method.getReturnType(), typeElement.asType() ); && !typeUtils.isSameType( method.getReturnType(), typeElement.asType() );
} }
/** /**
* Searches for a build method for {@code typeElement} within the {@code builderElement}. * Searches for a build method for {@code typeElement} within the {@code builderElement}.
* <p> * <p>
* The default implementation iterates over each method in {@code builderElement} and uses * The default implementation iterates over each method in {@code builderElement} and uses
* {@link DefaultBuilderProvider#isBuildMethod(ExecutableElement, TypeElement, Types)} to check if the method is a * {@link DefaultBuilderProvider#isBuildMethod(ExecutableElement, TypeElement)} to check if the method is a
* build method for {@code typeElement}. * build method for {@code typeElement}.
* <p> * <p>
* The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the * The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the
* {@code builderElement} should be ignored, i.e. not checked for build elements. * {@code builderElement} should be ignored, i.e. not checked for build elements.
* <p> * <p>
* If there are multiple methods that satisfy * If there are multiple methods that satisfy
* {@link DefaultBuilderProvider#isBuildMethod(ExecutableElement, TypeElement, Types)} and one of those methods * {@link DefaultBuilderProvider#isBuildMethod(ExecutableElement, TypeElement)} and one of those methods
* is names {@code build} that that method would be considered as a build method. * is names {@code build} that that method would be considered as a build method.
* @param builderElement the element for the builder * @param builderElement the element for the builder
* @param typeElement the element for the type that is being built * @param typeElement the element for the type that is being built
* @param types the util types tat can be used for operations on {@link TypeMirror}(s)
*
* @return the build method for the {@code typeElement} if it exists, or {@code null} if it does not * @return the build method for the {@code typeElement} if it exists, or {@code null} if it does not
* {@code build} * {@code build}
*/ */
protected Collection<ExecutableElement> findBuildMethods(TypeElement builderElement, TypeElement typeElement, protected Collection<ExecutableElement> findBuildMethods(TypeElement builderElement, TypeElement typeElement) {
Types types) {
if ( shouldIgnore( builderElement ) ) { if ( shouldIgnore( builderElement ) ) {
return Collections.emptyList(); return Collections.emptyList();
} }
@ -232,7 +233,7 @@ public class DefaultBuilderProvider implements BuilderProvider {
List<ExecutableElement> builderMethods = ElementFilter.methodsIn( builderElement.getEnclosedElements() ); List<ExecutableElement> builderMethods = ElementFilter.methodsIn( builderElement.getEnclosedElements() );
List<ExecutableElement> buildMethods = new ArrayList<>(); List<ExecutableElement> buildMethods = new ArrayList<>();
for ( ExecutableElement buildMethod : builderMethods ) { for ( ExecutableElement buildMethod : builderMethods ) {
if ( isBuildMethod( buildMethod, typeElement, types ) ) { if ( isBuildMethod( buildMethod, typeElement ) ) {
buildMethods.add( buildMethod ); buildMethods.add( buildMethod );
} }
} }
@ -240,8 +241,7 @@ public class DefaultBuilderProvider implements BuilderProvider {
if ( buildMethods.isEmpty() ) { if ( buildMethods.isEmpty() ) {
return findBuildMethods( return findBuildMethods(
getTypeElement( builderElement.getSuperclass() ), getTypeElement( builderElement.getSuperclass() ),
typeElement, typeElement
types
); );
} }
@ -260,15 +260,13 @@ public class DefaultBuilderProvider implements BuilderProvider {
* *
* @param buildMethod the method that should be checked * @param buildMethod the method that should be checked
* @param typeElement the type element that needs to be built * @param typeElement the type element that needs to be built
* @param types the util types that can be used for operations on {@link TypeMirror}(s)
*
* @return {@code true} if the {@code buildMethod} is a build method for {@code typeElement}, {@code false} * @return {@code true} if the {@code buildMethod} is a build method for {@code typeElement}, {@code false}
* otherwise * otherwise
*/ */
protected boolean isBuildMethod(ExecutableElement buildMethod, TypeElement typeElement, Types types) { protected boolean isBuildMethod(ExecutableElement buildMethod, TypeElement typeElement) {
return buildMethod.getParameters().isEmpty() && return buildMethod.getParameters().isEmpty() &&
buildMethod.getModifiers().contains( Modifier.PUBLIC ) buildMethod.getModifiers().contains( Modifier.PUBLIC )
&& types.isAssignable( buildMethod.getReturnType(), typeElement.asType() ); && typeUtils.isAssignable( buildMethod.getReturnType(), typeElement.asType() );
} }
/** /**

View File

@ -12,8 +12,6 @@ import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Name; import javax.lang.model.element.Name;
import javax.lang.model.element.PackageElement; import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
/** /**
* Builder provider for Immutables. A custom provider is needed because Immutables creates an implementation of an * Builder provider for Immutables. A custom provider is needed because Immutables creates an implementation of an
@ -29,34 +27,32 @@ public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
private static final String IMMUTABLE_FQN = "org.immutables.value.Value.Immutable"; private static final String IMMUTABLE_FQN = "org.immutables.value.Value.Immutable";
@Override @Override
protected BuilderInfo findBuilderInfo(TypeElement typeElement, Elements elements, Types types) { protected BuilderInfo findBuilderInfo(TypeElement typeElement) {
Name name = typeElement.getQualifiedName(); Name name = typeElement.getQualifiedName();
if ( name.length() == 0 || JAVA_JAVAX_PACKAGE.matcher( name ).matches() ) { if ( name.length() == 0 || JAVA_JAVAX_PACKAGE.matcher( name ).matches() ) {
return null; return null;
} }
TypeElement immutableAnnotation = elements.getTypeElement( IMMUTABLE_FQN ); TypeElement immutableAnnotation = elementUtils.getTypeElement( IMMUTABLE_FQN );
if ( immutableAnnotation != null ) { if ( immutableAnnotation != null ) {
BuilderInfo info = findBuilderInfoForImmutables( BuilderInfo info = findBuilderInfoForImmutables(
typeElement, typeElement,
immutableAnnotation, immutableAnnotation
elements,
types
); );
if ( info != null ) { if ( info != null ) {
return info; return info;
} }
} }
return super.findBuilderInfo( typeElement, elements, types ); return super.findBuilderInfo( typeElement );
} }
protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement, protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement,
TypeElement immutableAnnotation, Elements elements, Types types) { TypeElement immutableAnnotation) {
for ( AnnotationMirror annotationMirror : elements.getAllAnnotationMirrors( typeElement ) ) { for ( AnnotationMirror annotationMirror : elementUtils.getAllAnnotationMirrors( typeElement ) ) {
if ( types.isSameType( annotationMirror.getAnnotationType(), immutableAnnotation.asType() ) ) { if ( typeUtils.isSameType( annotationMirror.getAnnotationType(), immutableAnnotation.asType() ) ) {
TypeElement immutableElement = asImmutableElement( typeElement, elements ); TypeElement immutableElement = asImmutableElement( typeElement );
if ( immutableElement != null ) { if ( immutableElement != null ) {
return super.findBuilderInfo( immutableElement, elements, types ); return super.findBuilderInfo( immutableElement );
} }
else { else {
// Immutables processor has not run yet. Trigger a postpone to the next round for MapStruct // Immutables processor has not run yet. Trigger a postpone to the next round for MapStruct
@ -67,7 +63,7 @@ public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
return null; return null;
} }
protected TypeElement asImmutableElement(TypeElement typeElement, Elements elements) { protected TypeElement asImmutableElement(TypeElement typeElement) {
Element enclosingElement = typeElement.getEnclosingElement(); Element enclosingElement = typeElement.getEnclosingElement();
StringBuilder builderQualifiedName = new StringBuilder( typeElement.getQualifiedName().length() + 17 ); StringBuilder builderQualifiedName = new StringBuilder( typeElement.getQualifiedName().length() + 17 );
if ( enclosingElement.getKind() == ElementKind.PACKAGE ) { if ( enclosingElement.getKind() == ElementKind.PACKAGE ) {
@ -82,6 +78,6 @@ public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
} }
builderQualifiedName.append( "Immutable" ).append( typeElement.getSimpleName() ); builderQualifiedName.append( "Immutable" ).append( typeElement.getSimpleName() );
return elements.getTypeElement( builderQualifiedName ); return elementUtils.getTypeElement( builderQualifiedName );
} }
} }

View File

@ -8,8 +8,6 @@ package org.mapstruct.ap.spi;
// tag::documentation[] // tag::documentation[]
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
// end::documentation[] // end::documentation[]
@ -22,7 +20,7 @@ import javax.lang.model.util.Types;
public class NoOpBuilderProvider implements BuilderProvider { public class NoOpBuilderProvider implements BuilderProvider {
@Override @Override
public BuilderInfo findBuilderInfo(TypeMirror type, Elements elements, Types types) { public BuilderInfo findBuilderInfo(TypeMirror type) {
return null; return null;
} }
} }

View File

@ -7,8 +7,6 @@ package org.mapstruct.ap.test.bugs._1596;
import javax.lang.model.element.Name; import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.spi.BuilderInfo; import org.mapstruct.ap.spi.BuilderInfo;
import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.BuilderProvider;
@ -17,22 +15,22 @@ import org.mapstruct.ap.spi.ImmutablesBuilderProvider;
public class Issue1569BuilderProvider extends ImmutablesBuilderProvider implements BuilderProvider { public class Issue1569BuilderProvider extends ImmutablesBuilderProvider implements BuilderProvider {
@Override @Override
protected BuilderInfo findBuilderInfo(TypeElement typeElement, Elements elements, Types types) { protected BuilderInfo findBuilderInfo(TypeElement typeElement) {
Name name = typeElement.getQualifiedName(); Name name = typeElement.getQualifiedName();
if ( name.toString().endsWith( ".Item" ) ) { if ( name.toString().endsWith( ".Item" ) ) {
BuilderInfo info = findBuilderInfoForImmutables( typeElement, elements, types ); BuilderInfo info = findBuilderInfoForImmutables( typeElement );
if ( info != null ) { if ( info != null ) {
return info; return info;
} }
} }
return super.findBuilderInfo( typeElement, elements, types ); return super.findBuilderInfo( typeElement );
} }
protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement, Elements elements, Types types) { protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement) {
TypeElement immutableElement = asImmutableElement( typeElement, elements ); TypeElement immutableElement = asImmutableElement( typeElement );
if ( immutableElement != null ) { if ( immutableElement != null ) {
return super.findBuilderInfo( immutableElement, elements, types ); return super.findBuilderInfo( immutableElement );
} }
return null; return null;
} }