#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 {
return roundContext.getAnnotationProcessorContext()
.getBuilderProvider()
.findBuilderInfo( type, elementUtils, typeUtils );
.findBuilderInfo( type );
}
catch ( MoreThanOneBuilderCreationMethodException ex ) {
messager.printMessage(

View File

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

View File

@ -6,8 +6,6 @@
package org.mapstruct.ap.spi;
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
@ -16,13 +14,19 @@ import javax.lang.model.util.Types;
*/
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}.
*
* @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
*
* @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
* 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?\\..*" );
protected Elements elementUtils;
protected Types typeUtils;
@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 );
if ( typeElement == 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}.
* <p>
* The default implementation iterates over all the methods in {@code typeElement} and uses
* {@link DefaultBuilderProvider#isPossibleBuilderCreationMethod(ExecutableElement, TypeElement, Types)} and
* {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement, Types)} to create the
* {@link DefaultBuilderProvider#isPossibleBuilderCreationMethod(ExecutableElement, TypeElement)} and
* {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement)} to create the
* {@link BuilderInfo}.
* <p>
* The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the
@ -141,14 +150,11 @@ public class DefaultBuilderProvider implements BuilderProvider {
* thrown.
*
* @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
* {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement, Types)}
* {@link DefaultBuilderProvider#findBuildMethods(TypeElement, TypeElement)}
* @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 ) ) {
return null;
}
@ -156,9 +162,9 @@ public class DefaultBuilderProvider implements BuilderProvider {
List<ExecutableElement> methods = ElementFilter.methodsIn( typeElement.getEnclosedElements() );
List<BuilderInfo> builderInfo = new ArrayList<>();
for ( ExecutableElement method : methods ) {
if ( isPossibleBuilderCreationMethod( method, typeElement, types ) ) {
if ( isPossibleBuilderCreationMethod( method, typeElement ) ) {
TypeElement builderElement = getTypeElement( method.getReturnType() );
Collection<ExecutableElement> buildMethods = findBuildMethods( builderElement, typeElement, types );
Collection<ExecutableElement> buildMethods = findBuildMethods( builderElement, typeElement );
if ( !buildMethods.isEmpty() ) {
builderInfo.add( new BuilderInfo.Builder()
.builderCreationMethod( method )
@ -176,7 +182,7 @@ public class DefaultBuilderProvider implements BuilderProvider {
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 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
*/
protected boolean isPossibleBuilderCreationMethod(ExecutableElement method, TypeElement typeElement, Types types) {
protected boolean isPossibleBuilderCreationMethod(ExecutableElement method, TypeElement typeElement) {
return method.getParameters().isEmpty()
&& method.getModifiers().contains( Modifier.PUBLIC )
&& 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}.
* <p>
* 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}.
* <p>
* The default implementation uses {@link DefaultBuilderProvider#shouldIgnore(TypeElement)} to check if the
* {@code builderElement} should be ignored, i.e. not checked for build elements.
* <p>
* 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.
* @param builderElement the element for the builder
* @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
* {@code build}
*/
protected Collection<ExecutableElement> findBuildMethods(TypeElement builderElement, TypeElement typeElement,
Types types) {
protected Collection<ExecutableElement> findBuildMethods(TypeElement builderElement, TypeElement typeElement) {
if ( shouldIgnore( builderElement ) ) {
return Collections.emptyList();
}
@ -232,7 +233,7 @@ public class DefaultBuilderProvider implements BuilderProvider {
List<ExecutableElement> builderMethods = ElementFilter.methodsIn( builderElement.getEnclosedElements() );
List<ExecutableElement> buildMethods = new ArrayList<>();
for ( ExecutableElement buildMethod : builderMethods ) {
if ( isBuildMethod( buildMethod, typeElement, types ) ) {
if ( isBuildMethod( buildMethod, typeElement ) ) {
buildMethods.add( buildMethod );
}
}
@ -240,8 +241,7 @@ public class DefaultBuilderProvider implements BuilderProvider {
if ( buildMethods.isEmpty() ) {
return findBuildMethods(
getTypeElement( builderElement.getSuperclass() ),
typeElement,
types
typeElement
);
}
@ -260,15 +260,13 @@ public class DefaultBuilderProvider implements BuilderProvider {
*
* @param buildMethod the method that should be checked
* @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}
* otherwise
*/
protected boolean isBuildMethod(ExecutableElement buildMethod, TypeElement typeElement, Types types) {
protected boolean isBuildMethod(ExecutableElement buildMethod, TypeElement typeElement) {
return buildMethod.getParameters().isEmpty() &&
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.PackageElement;
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
@ -29,34 +27,32 @@ public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
private static final String IMMUTABLE_FQN = "org.immutables.value.Value.Immutable";
@Override
protected BuilderInfo findBuilderInfo(TypeElement typeElement, Elements elements, Types types) {
protected BuilderInfo findBuilderInfo(TypeElement typeElement) {
Name name = typeElement.getQualifiedName();
if ( name.length() == 0 || JAVA_JAVAX_PACKAGE.matcher( name ).matches() ) {
return null;
}
TypeElement immutableAnnotation = elements.getTypeElement( IMMUTABLE_FQN );
TypeElement immutableAnnotation = elementUtils.getTypeElement( IMMUTABLE_FQN );
if ( immutableAnnotation != null ) {
BuilderInfo info = findBuilderInfoForImmutables(
typeElement,
immutableAnnotation,
elements,
types
immutableAnnotation
);
if ( info != null ) {
return info;
}
}
return super.findBuilderInfo( typeElement, elements, types );
return super.findBuilderInfo( typeElement );
}
protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement,
TypeElement immutableAnnotation, Elements elements, Types types) {
for ( AnnotationMirror annotationMirror : elements.getAllAnnotationMirrors( typeElement ) ) {
if ( types.isSameType( annotationMirror.getAnnotationType(), immutableAnnotation.asType() ) ) {
TypeElement immutableElement = asImmutableElement( typeElement, elements );
TypeElement immutableAnnotation) {
for ( AnnotationMirror annotationMirror : elementUtils.getAllAnnotationMirrors( typeElement ) ) {
if ( typeUtils.isSameType( annotationMirror.getAnnotationType(), immutableAnnotation.asType() ) ) {
TypeElement immutableElement = asImmutableElement( typeElement );
if ( immutableElement != null ) {
return super.findBuilderInfo( immutableElement, elements, types );
return super.findBuilderInfo( immutableElement );
}
else {
// 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;
}
protected TypeElement asImmutableElement(TypeElement typeElement, Elements elements) {
protected TypeElement asImmutableElement(TypeElement typeElement) {
Element enclosingElement = typeElement.getEnclosingElement();
StringBuilder builderQualifiedName = new StringBuilder( typeElement.getQualifiedName().length() + 17 );
if ( enclosingElement.getKind() == ElementKind.PACKAGE ) {
@ -82,6 +78,6 @@ public class ImmutablesBuilderProvider extends DefaultBuilderProvider {
}
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[]
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
// end::documentation[]
@ -22,7 +20,7 @@ import javax.lang.model.util.Types;
public class NoOpBuilderProvider implements BuilderProvider {
@Override
public BuilderInfo findBuilderInfo(TypeMirror type, Elements elements, Types types) {
public BuilderInfo findBuilderInfo(TypeMirror type) {
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.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.mapstruct.ap.spi.BuilderInfo;
import org.mapstruct.ap.spi.BuilderProvider;
@ -17,22 +15,22 @@ import org.mapstruct.ap.spi.ImmutablesBuilderProvider;
public class Issue1569BuilderProvider extends ImmutablesBuilderProvider implements BuilderProvider {
@Override
protected BuilderInfo findBuilderInfo(TypeElement typeElement, Elements elements, Types types) {
protected BuilderInfo findBuilderInfo(TypeElement typeElement) {
Name name = typeElement.getQualifiedName();
if ( name.toString().endsWith( ".Item" ) ) {
BuilderInfo info = findBuilderInfoForImmutables( typeElement, elements, types );
BuilderInfo info = findBuilderInfoForImmutables( typeElement );
if ( info != null ) {
return info;
}
}
return super.findBuilderInfo( typeElement, elements, types );
return super.findBuilderInfo( typeElement );
}
protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement, Elements elements, Types types) {
TypeElement immutableElement = asImmutableElement( typeElement, elements );
protected BuilderInfo findBuilderInfoForImmutables(TypeElement typeElement) {
TypeElement immutableElement = asImmutableElement( typeElement );
if ( immutableElement != null ) {
return super.findBuilderInfo( immutableElement, elements, types );
return super.findBuilderInfo( immutableElement );
}
return null;
}