#782 Add BuilderInfo to SPI

The implementors of the SPI should return all the required information
This commit is contained in:
Filip Hrisafov 2018-02-10 15:33:14 +01:00
parent d99a4cc217
commit 45abe9e35b
13 changed files with 304 additions and 98 deletions

View File

@ -52,7 +52,7 @@ import org.mapstruct.ap.internal.processor.ModelElementProcessor.ProcessorContex
import org.mapstruct.ap.internal.util.AnnotationProcessingException; import org.mapstruct.ap.internal.util.AnnotationProcessingException;
import org.mapstruct.ap.internal.util.AnnotationProcessorContext; import org.mapstruct.ap.internal.util.AnnotationProcessorContext;
import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.RoundContext;
import org.mapstruct.ap.internal.util.TypeHierarchyErroneousException; import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
/** /**
* A JSR 269 annotation {@link Processor} which generates the implementations for mapper interfaces (interfaces * A JSR 269 annotation {@link Processor} which generates the implementations for mapper interfaces (interfaces

View File

@ -40,8 +40,8 @@ import javax.tools.Diagnostic;
import org.mapstruct.ap.internal.model.PropertyMapping.ConstantMappingBuilder; import org.mapstruct.ap.internal.model.PropertyMapping.ConstantMappingBuilder;
import org.mapstruct.ap.internal.model.PropertyMapping.JavaExpressionMappingBuilder; import org.mapstruct.ap.internal.model.PropertyMapping.JavaExpressionMappingBuilder;
import org.mapstruct.ap.internal.model.PropertyMapping.PropertyMappingBuilder; import org.mapstruct.ap.internal.model.PropertyMapping.PropertyMappingBuilder;
import org.mapstruct.ap.internal.model.common.BuilderType;
import org.mapstruct.ap.internal.model.common.Parameter; import org.mapstruct.ap.internal.model.common.Parameter;
import org.mapstruct.ap.internal.model.common.ParameterBinding;
import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer;
import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder; import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder;
@ -244,33 +244,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
( (ForgedMethod) method ).addThrownTypes( factoryMethod.getThrownTypes() ); ( (ForgedMethod) method ).addThrownTypes( factoryMethod.getThrownTypes() );
} }
MethodReference finalizeMethod = null; MethodReference finalizeMethod = getFinalizeMethod(
if ( resultType == null ? method.getReturnType() : resultType );
!method.getReturnType().isVoid() &&
( resultType != null
&& !ctx.getTypeUtils().isAssignable(
resultType.getMappingType().getTypeMirror(),
resultType.getTypeMirror()
) ||
!ctx.getTypeUtils().isSameType(
method.getReturnType().getMappingType().getTypeMirror(),
method.getReturnType().getTypeMirror()
)
)
) {
finalizeMethod = MethodReference.forForgedMethod(
new ForgedMethod(
"build",
method.getReturnType(),
method.getReturnType(),
null,
null,
Collections.<Parameter>emptyList(),
null
),
Collections.<ParameterBinding>emptyList()
);
}
return new BeanMappingMethod( return new BeanMappingMethod(
method, method,
@ -285,6 +260,20 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
); );
} }
private MethodReference getFinalizeMethod(Type resultType) {
if ( method.getReturnType().isVoid() ||
resultType.getMappingType().isAssignableTo( resultType ) ) {
return null;
}
BuilderType builderType = resultType.getBuilderType();
if ( builderType == null ) {
// If the mapping type is assignable to the result type this should never happen
return null;
}
return MethodReference.forMethodCall( builderType.getBuildMethod() );
}
/** /**
* If there were nested defined targets that have not been handled. Then we need to process them at the end. * If there were nested defined targets that have not been handled. Then we need to process them at the end.
*/ */

View File

@ -118,7 +118,7 @@ public class MethodReference extends ModelElement implements Assignment {
this.name = method.getName(); this.name = method.getName();
} }
private MethodReference(String name, Type definingType) { private MethodReference(String name, Type definingType, boolean isStatic) {
this.name = name; this.name = name;
this.definingType = definingType; this.definingType = definingType;
this.sourceParameters = Collections.emptyList(); this.sourceParameters = Collections.emptyList();
@ -130,7 +130,7 @@ public class MethodReference extends ModelElement implements Assignment {
this.contextParam = null; this.contextParam = null;
this.parameterBindings = Collections.emptyList(); this.parameterBindings = Collections.emptyList();
this.providingParameter = null; this.providingParameter = null;
this.isStatic = true; this.isStatic = isStatic;
} }
public MapperReference getDeclaringMapper() { public MapperReference getDeclaringMapper() {
@ -335,6 +335,10 @@ public class MethodReference extends ModelElement implements Assignment {
} }
public static MethodReference forStaticBuilder(String builderCreationMethod, Type definingType) { public static MethodReference forStaticBuilder(String builderCreationMethod, Type definingType) {
return new MethodReference( builderCreationMethod, definingType ); return new MethodReference( builderCreationMethod, definingType, true );
}
public static MethodReference forMethodCall(String methodName) {
return new MethodReference( methodName, null, false );
} }
} }

View File

@ -0,0 +1,113 @@
/**
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.internal.model.common;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import org.mapstruct.ap.spi.BuilderInfo;
/**
* @author Filip Hrisafov
*/
public class BuilderType {
private final Type builder;
private final Type owner;
private final Type buildingType;
private final ExecutableElement builderCreationMethod;
private final ExecutableElement buildMethod;
private BuilderType(
Type builder,
Type owner,
Type buildingType,
ExecutableElement builderCreationMethod,
ExecutableElement buildMethod
) {
this.builder = builder;
this.owner = owner;
this.buildingType = buildingType;
this.builderCreationMethod = builderCreationMethod;
this.buildMethod = buildMethod;
}
public Type getBuilder() {
return builder;
}
public Type getOwner() {
return owner;
}
public Type getBuildingType() {
return buildingType;
}
public ExecutableElement getBuilderCreationMethod() {
return builderCreationMethod;
}
public String getBuildMethod() {
return buildMethod.getSimpleName().toString();
}
public BuilderInfo asBuilderInfo() {
return new BuilderInfo.Builder()
.builderCreationMethod( this.builderCreationMethod )
.buildMethod( this.buildMethod )
.build();
}
public static BuilderType create(BuilderInfo builderInfo, Type typeToBuild, TypeFactory typeFactory,
Types typeUtils) {
if ( builderInfo == null ) {
return null;
}
ExecutableElement buildMethod = builderInfo.getBuildMethod();
if ( !typeUtils.isAssignable( buildMethod.getReturnType(), typeToBuild.getTypeMirror() ) ) {
//TODO throw error
throw new IllegalArgumentException( "Build return type is not assignable" );
}
Type builder = typeFactory.getType( builderInfo.getBuilderCreationMethod().getReturnType() );
ExecutableElement builderCreationMethod = builderInfo.getBuilderCreationMethod();
Type owner;
TypeMirror builderCreationOwner = builderCreationMethod.getEnclosingElement().asType();
if ( typeUtils.isSameType( builderCreationOwner, typeToBuild.getTypeMirror() ) ) {
owner = typeToBuild;
}
else if ( typeUtils.isSameType( builder.getTypeMirror(), builderCreationOwner ) ) {
owner = builder;
}
else {
owner = typeFactory.getType( builderCreationOwner );
}
return new BuilderType(
builder,
owner,
typeToBuild,
builderCreationMethod,
buildMethod
);
}
}

View File

@ -49,6 +49,7 @@ import org.mapstruct.ap.internal.util.Filters;
import org.mapstruct.ap.internal.util.Nouns; import org.mapstruct.ap.internal.util.Nouns;
import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor; import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
import org.mapstruct.ap.spi.BuilderInfo;
/** /**
* Represents (a reference to) the type of a bean property, parameter etc. Types are managed per generated source file. * Represents (a reference to) the type of a bean property, parameter etc. Types are managed per generated source file.
@ -72,7 +73,7 @@ public class Type extends ModelElement implements Comparable<Type> {
private final ImplementationType implementationType; private final ImplementationType implementationType;
private final Type componentType; private final Type componentType;
private final Type builderType; private final BuilderType builderType;
private final String packageName; private final String packageName;
private final String name; private final String name;
@ -105,7 +106,7 @@ public class Type extends ModelElement implements Comparable<Type> {
public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory, public Type(Types typeUtils, Elements elementUtils, TypeFactory typeFactory,
TypeMirror typeMirror, TypeElement typeElement, TypeMirror typeMirror, TypeElement typeElement,
List<Type> typeParameters, ImplementationType implementationType, Type componentType, List<Type> typeParameters, ImplementationType implementationType, Type componentType,
Type builderType, BuilderInfo builderInfo,
String packageName, String name, String qualifiedName, String packageName, String name, String qualifiedName,
boolean isInterface, boolean isEnumType, boolean isIterableType, boolean isInterface, boolean isEnumType, boolean isIterableType,
boolean isCollectionType, boolean isMapType, boolean isStreamType, boolean isImported) { boolean isCollectionType, boolean isMapType, boolean isStreamType, boolean isImported) {
@ -119,7 +120,6 @@ public class Type extends ModelElement implements Comparable<Type> {
this.typeParameters = typeParameters; this.typeParameters = typeParameters;
this.componentType = componentType; this.componentType = componentType;
this.implementationType = implementationType; this.implementationType = implementationType;
this.builderType = builderType;
this.packageName = packageName; this.packageName = packageName;
this.name = name; this.name = name;
@ -149,6 +149,8 @@ public class Type extends ModelElement implements Comparable<Type> {
else { else {
enumConstants = Collections.emptyList(); enumConstants = Collections.emptyList();
} }
this.builderType = BuilderType.create( builderInfo, this, this.typeFactory, this.typeUtils );
} }
//CHECKSTYLE:ON //CHECKSTYLE:ON
@ -176,12 +178,12 @@ public class Type extends ModelElement implements Comparable<Type> {
return componentType; return componentType;
} }
public Type getBuilderType() { public BuilderType getBuilderType() {
return builderType; return builderType;
} }
public Type getMappingType() { public Type getMappingType() {
return builderType != null ? builderType : this; return builderType != null ? builderType.getBuilder() : this;
} }
public boolean isPrimitive() { public boolean isPrimitive() {
@ -366,7 +368,7 @@ public class Type extends ModelElement implements Comparable<Type> {
typeParameters, typeParameters,
implementationType, implementationType,
componentType, componentType,
builderType, builderType == null ? null : builderType.asBuilderInfo(),
packageName, packageName,
name, name,
qualifiedName, qualifiedName,

View File

@ -58,11 +58,12 @@ import org.mapstruct.ap.internal.util.Collections;
import org.mapstruct.ap.internal.util.JavaStreamConstants; import org.mapstruct.ap.internal.util.JavaStreamConstants;
import org.mapstruct.ap.internal.util.RoundContext; import org.mapstruct.ap.internal.util.RoundContext;
import org.mapstruct.ap.internal.util.Services; import org.mapstruct.ap.internal.util.Services;
import org.mapstruct.ap.internal.util.TypeHierarchyErroneousException;
import org.mapstruct.ap.internal.util.accessor.Accessor; import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor; import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
import org.mapstruct.ap.spi.BuilderInfo;
import org.mapstruct.ap.spi.BuilderProvider; import org.mapstruct.ap.spi.BuilderProvider;
import org.mapstruct.ap.spi.DefaultBuilderProvider; import org.mapstruct.ap.spi.DefaultBuilderProvider;
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
import static org.mapstruct.ap.internal.model.common.ImplementationType.withDefaultConstructor; import static org.mapstruct.ap.internal.model.common.ImplementationType.withDefaultConstructor;
import static org.mapstruct.ap.internal.model.common.ImplementationType.withInitialCapacity; import static org.mapstruct.ap.internal.model.common.ImplementationType.withInitialCapacity;
@ -170,7 +171,7 @@ public class TypeFactory {
} }
ImplementationType implementationType = getImplementationType( mirror ); ImplementationType implementationType = getImplementationType( mirror );
Type builderType = findBuilder( mirror ); BuilderInfo builderInfo = findBuilder( mirror );
boolean isIterableType = typeUtils.isSubtype( mirror, iterableType ); boolean isIterableType = typeUtils.isSubtype( mirror, iterableType );
boolean isCollectionType = typeUtils.isSubtype( mirror, collectionType ); boolean isCollectionType = typeUtils.isSubtype( mirror, collectionType );
@ -256,7 +257,7 @@ public class TypeFactory {
getTypeParameters( mirror, false ), getTypeParameters( mirror, false ),
implementationType, implementationType,
componentType, componentType,
builderType, builderInfo,
packageName, packageName,
name, name,
qualifiedName, qualifiedName,
@ -486,9 +487,8 @@ public class TypeFactory {
return null; return null;
} }
private Type findBuilder(TypeMirror type) { private BuilderInfo findBuilder(TypeMirror type) {
TypeMirror builder = BUILDER_PROVIDER.findBuilder( type, elementUtils, typeUtils ); return BUILDER_PROVIDER.findBuilderInfo( type, elementUtils, typeUtils );
return builder == null ? null : getType( builder );
} }
private TypeMirror getComponentType(TypeMirror mirror) { private TypeMirror getComponentType(TypeMirror mirror) {

View File

@ -22,13 +22,12 @@ import static java.util.Collections.singletonList;
import static org.mapstruct.ap.internal.util.Collections.first; import static org.mapstruct.ap.internal.util.Collections.first;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType; import javax.lang.model.type.ExecutableType;
@ -46,6 +45,7 @@ import org.mapstruct.ap.internal.model.MappingBuilderContext.MappingResolver;
import org.mapstruct.ap.internal.model.MethodReference; import org.mapstruct.ap.internal.model.MethodReference;
import org.mapstruct.ap.internal.model.VirtualMappingMethod; import org.mapstruct.ap.internal.model.VirtualMappingMethod;
import org.mapstruct.ap.internal.model.common.Assignment; import org.mapstruct.ap.internal.model.common.Assignment;
import org.mapstruct.ap.internal.model.common.BuilderType;
import org.mapstruct.ap.internal.model.common.ConversionContext; import org.mapstruct.ap.internal.model.common.ConversionContext;
import org.mapstruct.ap.internal.model.common.DefaultConversionContext; import org.mapstruct.ap.internal.model.common.DefaultConversionContext;
import org.mapstruct.ap.internal.model.common.FormattingParameters; import org.mapstruct.ap.internal.model.common.FormattingParameters;
@ -60,7 +60,6 @@ import org.mapstruct.ap.internal.model.source.selector.MethodSelectors;
import org.mapstruct.ap.internal.model.source.selector.SelectedMethod; import org.mapstruct.ap.internal.model.source.selector.SelectedMethod;
import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria; import org.mapstruct.ap.internal.model.source.selector.SelectionCriteria;
import org.mapstruct.ap.internal.util.Collections; import org.mapstruct.ap.internal.util.Collections;
import org.mapstruct.ap.internal.util.Executables;
import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.FormattingMessager;
import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Message;
import org.mapstruct.ap.internal.util.Strings; import org.mapstruct.ap.internal.util.Strings;
@ -143,7 +142,7 @@ public class MappingResolverImpl implements MappingResolver {
SelectionCriteria.forFactoryMethods( selectionParameters ) ); SelectionCriteria.forFactoryMethods( selectionParameters ) );
if (matchingFactoryMethods.isEmpty()) { if (matchingFactoryMethods.isEmpty()) {
return findBuilderFactoryMethod( mappingMethod, targetType ); return findBuilderFactoryMethod( targetType );
} }
if ( matchingFactoryMethods.size() > 1 ) { if ( matchingFactoryMethods.size() > 1 ) {
@ -166,37 +165,24 @@ public class MappingResolverImpl implements MappingResolver {
matchingFactoryMethod.getParameterBindings() ); matchingFactoryMethod.getParameterBindings() );
} }
private MethodReference findBuilderFactoryMethod(Method mappingMethod, Type targetType) { private MethodReference findBuilderFactoryMethod(Type targetType) {
if ( targetType.getBuilderType() == null ) { BuilderType builder = targetType.getBuilderType();
return null; if ( builder == null ) {
}
Type builderType = targetType.getBuilderType();
Type returnType = mappingMethod.getReturnType();
List<ExecutableElement> builderCreators = new ArrayList<ExecutableElement>();
for ( ExecutableElement executableElement : Executables.getAllEnclosedExecutableElements(
elementsUtils,
returnType.getTypeElement()
) ) {
if ( !executableElement.getModifiers().containsAll( Arrays.asList( Modifier.PUBLIC, Modifier.STATIC ) )
|| !executableElement.getParameters().isEmpty()
|| !typeUtils.isSameType( executableElement.getReturnType(), builderType.getTypeMirror() )) {
continue;
}
builderCreators.add( executableElement );
}
if ( builderCreators.size() == 1 ) {
return MethodReference.forStaticBuilder( first( builderCreators ).getSimpleName().toString(), targetType );
}
else if ( builderCreators.size() > 1 ) {
//error
return null; return null;
} }
// Find the default constructor, if it exists, and construct the FactoryMethod ExecutableElement builderCreationMethod = builder.getBuilderCreationMethod();
// We could also live with assuming it exists if ( builderCreationMethod.getKind() == ElementKind.CONSTRUCTOR ) {
return null; // If the builder creation method is a constructor it would be handled properly down the line
return null;
}
if ( !builder.getBuildingType().isAssignableTo( targetType ) ) {
//TODO print error message
return null;
}
return MethodReference.forStaticBuilder( builderCreationMethod.getSimpleName().toString(), builder.getOwner() );
} }
private MapperReference findMapperReference(Method method) { private MapperReference findMapperReference(Method method) {

View File

@ -47,6 +47,7 @@ import org.mapstruct.ap.internal.util.accessor.VariableElementAccessor;
import org.mapstruct.ap.spi.AccessorNamingStrategy; import org.mapstruct.ap.spi.AccessorNamingStrategy;
import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy; import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy;
import org.mapstruct.ap.spi.MethodType; import org.mapstruct.ap.spi.MethodType;
import org.mapstruct.ap.spi.TypeHierarchyErroneousException;
/** /**
* Provides functionality around {@link ExecutableElement}s. * Provides functionality around {@link ExecutableElement}s.

View File

@ -0,0 +1,68 @@
/**
* Copyright 2012-2017 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.spi;
import javax.lang.model.element.ExecutableElement;
/**
* @author Filip Hrisafov
*/
public class BuilderInfo {
private final ExecutableElement builderCreationMethod;
private final ExecutableElement buildMethod;
private BuilderInfo(ExecutableElement builderCreationMethod, ExecutableElement buildMethod) {
this.builderCreationMethod = builderCreationMethod;
this.buildMethod = buildMethod;
}
public ExecutableElement getBuilderCreationMethod() {
return builderCreationMethod;
}
public ExecutableElement getBuildMethod() {
return buildMethod;
}
public static class Builder {
private ExecutableElement builderCreationMethod;
private ExecutableElement buildMethod;
public Builder builderCreationMethod(ExecutableElement method) {
this.builderCreationMethod = method;
return this;
}
public Builder buildMethod(ExecutableElement method) {
this.buildMethod = method;
return this;
}
public BuilderInfo build() {
if ( builderCreationMethod == null ) {
throw new IllegalArgumentException( "Builder creation method is mandatory" );
}
else if ( buildMethod == null ) {
throw new IllegalArgumentException( "Build method is mandatory" );
}
return new BuilderInfo( builderCreationMethod, buildMethod );
}
}
}

View File

@ -29,5 +29,5 @@ import javax.lang.model.util.Types;
*/ */
public interface BuilderProvider { public interface BuilderProvider {
TypeMirror findBuilder(TypeMirror type, Elements elements, Types types); BuilderInfo findBuilderInfo(TypeMirror type, Elements elements, Types types);
} }

View File

@ -22,9 +22,9 @@ import java.util.List;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter; import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
@ -42,7 +42,19 @@ 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?\\..*" );
@Override @Override
public TypeMirror findBuilder(TypeMirror type, Elements elements, Types types) { public BuilderInfo findBuilderInfo(TypeMirror type, Elements elements, Types types) {
TypeElement typeElement = getTypeElement( type );
if ( typeElement == null ) {
return null;
}
return findBuilderInfo( typeElement, elements, types );
}
protected TypeElement getTypeElement(TypeMirror type) {
if ( type.getKind() == TypeKind.ERROR ) {
throw new TypeHierarchyErroneousException( type );
}
DeclaredType declaredType = type.accept( DeclaredType declaredType = type.accept(
new SimpleTypeVisitor6<DeclaredType, Void>() { new SimpleTypeVisitor6<DeclaredType, Void>() {
@Override @Override
@ -57,7 +69,7 @@ public class DefaultBuilderProvider implements BuilderProvider {
return null; return null;
} }
TypeElement typeElement = declaredType.asElement().accept( return declaredType.asElement().accept(
new SimpleElementVisitor6<TypeElement, Void>() { new SimpleElementVisitor6<TypeElement, Void>() {
@Override @Override
public TypeElement visitType(TypeElement e, Void p) { public TypeElement visitType(TypeElement e, Void p) {
@ -66,30 +78,63 @@ public class DefaultBuilderProvider implements BuilderProvider {
}, },
null null
); );
return findBuilder( typeElement, elements, types );
} }
protected TypeMirror findBuilder(TypeElement typeElement, Elements elements, Types types) { protected BuilderInfo findBuilderInfo(TypeElement typeElement, Elements elements, Types types) {
Name name = typeElement.getQualifiedName(); if ( shouldIgnore( typeElement ) ) {
if ( name.length() == 0 || JAVA_JAVAX_PACKAGE.matcher( name ).matches() ) {
return null; return null;
} }
List<ExecutableElement> methods = ElementFilter.methodsIn( typeElement.getEnclosedElements() );
List<ExecutableElement> methods = ElementFilter.methodsIn( typeElement.getEnclosedElements() );
for ( ExecutableElement method : methods ) { for ( ExecutableElement method : methods ) {
if ( isBuilderMethod( method ) ) { if ( isPossibleBuilderCreationMethod( method, typeElement, types ) ) {
return method.getReturnType(); TypeElement builderElement = getTypeElement( method.getReturnType() );
ExecutableElement buildMethod = findBuildMethod( builderElement, typeElement, types );
if ( buildMethod != null ) {
return new BuilderInfo.Builder()
.builderCreationMethod( method )
.buildMethod( buildMethod )
.build();
}
}
}
return findBuilderInfo( typeElement.getSuperclass(), elements, types );
}
protected boolean isPossibleBuilderCreationMethod(ExecutableElement method, TypeElement typeElement, Types types) {
return method.getParameters().isEmpty()
&& method.getModifiers().contains( Modifier.PUBLIC )
&& method.getModifiers().contains( Modifier.STATIC )
&& !types.isSameType( method.getReturnType(), typeElement.asType() );
}
protected ExecutableElement findBuildMethod(TypeElement builderElement, TypeElement typeElement, Types types) {
if ( shouldIgnore( builderElement ) ) {
return null;
}
List<ExecutableElement> builderMethods = ElementFilter.methodsIn( builderElement.getEnclosedElements() );
for ( ExecutableElement buildMethod : builderMethods ) {
if ( isBuildMethod( buildMethod, typeElement, types ) ) {
return buildMethod;
} }
} }
return findBuilder( typeElement.getSuperclass(), elements, types ); return findBuildMethod(
getTypeElement( builderElement.getSuperclass() ),
typeElement,
types
);
} }
protected boolean isBuilderMethod(ExecutableElement method) { protected boolean isBuildMethod(ExecutableElement buildMethod, TypeElement typeElement,
return method.getParameters().isEmpty() Types types) {
&& method.getSimpleName().toString().equals( "builder" ) return buildMethod.getParameters().isEmpty() &&
&& method.getModifiers().contains( Modifier.PUBLIC ) buildMethod.getModifiers().contains( Modifier.PUBLIC )
&& method.getModifiers().contains( Modifier.STATIC ); && types.isAssignable( buildMethod.getReturnType(), typeElement.asType() );
}
protected boolean shouldIgnore(TypeElement typeElement) {
return typeElement == null || JAVA_JAVAX_PACKAGE.matcher( typeElement.getQualifiedName() ).matches();
} }
} }

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap.internal.util; package org.mapstruct.ap.spi;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;

View File

@ -78,9 +78,7 @@
</#list> </#list>
<#if returnType.name != "void"> <#if returnType.name != "void">
<#if resultType.builderType??> <#if finalizeMethod??>
return ${resultName}.build();
<#elseif finalizeMethod??>
return ${resultName}.<@includeModel object=finalizeMethod />; return ${resultName}.<@includeModel object=finalizeMethod />;
<#else> <#else>
return ${resultName}; return ${resultName};