#2393 Use includeModel when generating GeneratedType

With this we make sure that the implementation type will have a correct import in case of a clash with another mapper named the same
This commit is contained in:
Filip Hrisafov 2021-04-05 12:44:55 +02:00
parent 85d3b310f7
commit c9199b7068
10 changed files with 224 additions and 43 deletions

View File

@ -8,7 +8,6 @@ package org.mapstruct.ap.internal.model;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.SortedSet; import java.util.SortedSet;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.Accessibility;
@ -74,7 +73,8 @@ public class Decorator extends GeneratedType {
hasDelegateConstructor ); hasDelegateConstructor );
String elementPackage = elementUtils.getPackageOf( mapperElement ).getQualifiedName().toString(); Type mapperType = typeFactory.getType( mapperElement );
String elementPackage = mapperType.getPackageName();
String packageName = implPackage.replace( Mapper.PACKAGE_NAME_PLACEHOLDER, elementPackage ); String packageName = implPackage.replace( Mapper.PACKAGE_NAME_PLACEHOLDER, elementPackage );
return new Decorator( return new Decorator(
@ -82,10 +82,8 @@ public class Decorator extends GeneratedType {
packageName, packageName,
implementationName, implementationName,
decoratorType, decoratorType,
elementPackage, mapperType,
mapperElement.getKind() == ElementKind.INTERFACE ? mapperElement.getSimpleName().toString() : null,
methods, methods,
Arrays.asList( new Field( typeFactory.getType( mapperElement ), "delegate", true ) ),
options, options,
versionInformation, versionInformation,
Accessibility.fromModifiers( mapperElement.getModifiers() ), Accessibility.fromModifiers( mapperElement.getModifiers() ),
@ -96,22 +94,22 @@ public class Decorator extends GeneratedType {
} }
private final Type decoratorType; private final Type decoratorType;
private final Type mapperType;
@SuppressWarnings( "checkstyle:parameternumber" ) @SuppressWarnings( "checkstyle:parameternumber" )
private Decorator(TypeFactory typeFactory, String packageName, String name, Type decoratorType, private Decorator(TypeFactory typeFactory, String packageName, String name, Type decoratorType,
String interfacePackage, String interfaceName, List<MappingMethod> methods, Type mapperType,
List<Field> fields, Options options, VersionInformation versionInformation, List<MappingMethod> methods,
Options options, VersionInformation versionInformation,
Accessibility accessibility, SortedSet<Type> extraImports, Accessibility accessibility, SortedSet<Type> extraImports,
DecoratorConstructor decoratorConstructor) { DecoratorConstructor decoratorConstructor) {
super( super(
typeFactory, typeFactory,
packageName, packageName,
name, name,
decoratorType.getName(), decoratorType,
interfacePackage,
interfaceName,
methods, methods,
fields, Arrays.asList( new Field( mapperType, "delegate", true ) ),
options, options,
versionInformation, versionInformation,
accessibility, accessibility,
@ -120,6 +118,7 @@ public class Decorator extends GeneratedType {
); );
this.decoratorType = decoratorType; this.decoratorType = decoratorType;
this.mapperType = mapperType;
} }
@Override @Override
@ -148,4 +147,8 @@ public class Decorator extends GeneratedType {
protected String getTemplateName() { protected String getTemplateName() {
return getTemplateNameForClass( GeneratedType.class ); return getTemplateNameForClass( GeneratedType.class );
} }
public Type getMapperType() {
return mapperType;
}
} }

View File

@ -80,9 +80,7 @@ public abstract class GeneratedType extends ModelElement {
private final String packageName; private final String packageName;
private final String name; private final String name;
private final String superClassName; private final Type mapperDefinitionType;
private final String interfacePackage;
private final String interfaceName;
private final List<Annotation> annotations; private final List<Annotation> annotations;
private final List<MappingMethod> methods; private final List<MappingMethod> methods;
@ -102,15 +100,13 @@ public abstract class GeneratedType extends ModelElement {
private final boolean generatedTypeAvailable; private final boolean generatedTypeAvailable;
// CHECKSTYLE:OFF // CHECKSTYLE:OFF
protected GeneratedType(TypeFactory typeFactory, String packageName, String name, String superClassName, protected GeneratedType(TypeFactory typeFactory, String packageName, String name,
String interfacePackage, String interfaceName, List<MappingMethod> methods, Type mapperDefinitionType, List<MappingMethod> methods,
List<Field> fields, Options options, VersionInformation versionInformation, List<Field> fields, Options options, VersionInformation versionInformation,
Accessibility accessibility, SortedSet<Type> extraImportedTypes, Constructor constructor) { Accessibility accessibility, SortedSet<Type> extraImportedTypes, Constructor constructor) {
this.packageName = packageName; this.packageName = packageName;
this.name = name; this.name = name;
this.superClassName = superClassName; this.mapperDefinitionType = mapperDefinitionType;
this.interfacePackage = interfacePackage;
this.interfaceName = interfaceName;
this.extraImportedTypes = extraImportedTypes; this.extraImportedTypes = extraImportedTypes;
this.annotations = new ArrayList<>(); this.annotations = new ArrayList<>();
@ -153,16 +149,8 @@ public abstract class GeneratedType extends ModelElement {
return name; return name;
} }
public String getSuperClassName() { public Type getMapperDefinitionType() {
return superClassName; return mapperDefinitionType;
}
public String getInterfacePackage() {
return interfacePackage;
}
public String getInterfaceName() {
return interfaceName;
} }
public List<Annotation> getAnnotations() { public List<Annotation> getAnnotations() {
@ -214,6 +202,8 @@ public abstract class GeneratedType extends ModelElement {
SortedSet<Type> importedTypes = new TreeSet<>(); SortedSet<Type> importedTypes = new TreeSet<>();
addIfImportRequired( importedTypes, generatedType ); addIfImportRequired( importedTypes, generatedType );
addIfImportRequired( importedTypes, mapperDefinitionType );
for ( MappingMethod mappingMethod : methods ) { for ( MappingMethod mappingMethod : methods ) {
for ( Type type : mappingMethod.getImportTypes() ) { for ( Type type : mappingMethod.getImportTypes() ) {
addIfImportRequired( importedTypes, type ); addIfImportRequired( importedTypes, type );

View File

@ -10,7 +10,6 @@ import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.internal.model.common.Accessibility; import org.mapstruct.ap.internal.model.common.Accessibility;
@ -90,13 +89,14 @@ public class Mapper extends GeneratedType {
if ( !fragments.isEmpty() ) { if ( !fragments.isEmpty() ) {
constructor = new NoArgumentConstructor( implementationName, fragments ); constructor = new NoArgumentConstructor( implementationName, fragments );
} }
Type definitionType = typeFactory.getType( element );
return new Mapper( return new Mapper(
typeFactory, typeFactory,
packageName, packageName,
implementationName, implementationName,
element.getKind() != ElementKind.INTERFACE ? element.getSimpleName().toString() : null, definitionType,
elementPackage,
element.getKind() == ElementKind.INTERFACE ? element.getSimpleName().toString() : null,
customPackage, customPackage,
customName, customName,
methods, methods,
@ -117,8 +117,9 @@ public class Mapper extends GeneratedType {
private Decorator decorator; private Decorator decorator;
@SuppressWarnings( "checkstyle:parameternumber" ) @SuppressWarnings( "checkstyle:parameternumber" )
private Mapper(TypeFactory typeFactory, String packageName, String name, String superClassName, private Mapper(TypeFactory typeFactory, String packageName, String name,
String interfacePackage, String interfaceName, boolean customPackage, boolean customImplName, Type mapperDefinitionType,
boolean customPackage, boolean customImplName,
List<MappingMethod> methods, Options options, VersionInformation versionInformation, List<MappingMethod> methods, Options options, VersionInformation versionInformation,
Accessibility accessibility, List<Field> fields, Constructor constructor, Accessibility accessibility, List<Field> fields, Constructor constructor,
Decorator decorator, SortedSet<Type> extraImportedTypes ) { Decorator decorator, SortedSet<Type> extraImportedTypes ) {
@ -127,9 +128,7 @@ public class Mapper extends GeneratedType {
typeFactory, typeFactory,
packageName, packageName,
name, name,
superClassName, mapperDefinitionType,
interfacePackage,
interfaceName,
methods, methods,
fields, fields,
options, options,

View File

@ -12,9 +12,11 @@ import javax.tools.FileObject;
import javax.tools.StandardLocation; import javax.tools.StandardLocation;
import org.mapstruct.ap.internal.gem.MappingConstantsGem; import org.mapstruct.ap.internal.gem.MappingConstantsGem;
import org.mapstruct.ap.internal.model.Decorator;
import org.mapstruct.ap.internal.model.GeneratedType; import org.mapstruct.ap.internal.model.GeneratedType;
import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.Mapper;
import org.mapstruct.ap.internal.model.ServicesEntry; import org.mapstruct.ap.internal.model.ServicesEntry;
import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.model.source.MapperOptions; import org.mapstruct.ap.internal.model.source.MapperOptions;
import org.mapstruct.ap.internal.writer.ModelWriter; import org.mapstruct.ap.internal.writer.ModelWriter;
@ -55,15 +57,28 @@ public class MapperServiceProcessor implements ModelElementProcessor<Mapper, Vo
private void writeToSourceFile(Filer filer, Mapper model) { private void writeToSourceFile(Filer filer, Mapper model) {
ModelWriter modelWriter = new ModelWriter(); ModelWriter modelWriter = new ModelWriter();
ServicesEntry servicesEntry = getServicesEntry( model.getDecorator() == null ? model : model.getDecorator() ); ServicesEntry servicesEntry = getServicesEntry( model );
createSourceFile( servicesEntry, modelWriter, filer ); createSourceFile( servicesEntry, modelWriter, filer );
} }
private ServicesEntry getServicesEntry(GeneratedType model) { private ServicesEntry getServicesEntry(Mapper mapper) {
String mapperName = model.getInterfaceName() != null ? model.getInterfaceName() : model.getSuperClassName(); if ( mapper.getDecorator() != null ) {
return getServicesEntry( mapper.getDecorator() );
}
return new ServicesEntry(model.getInterfacePackage(), mapperName, return getServicesEntry( mapper.getMapperDefinitionType(), mapper );
}
private ServicesEntry getServicesEntry(Decorator decorator) {
return getServicesEntry( decorator.getMapperType(), decorator );
}
private ServicesEntry getServicesEntry(Type mapperType, GeneratedType model) {
String mapperName = mapperType.getName();
String mapperPackageName = mapperType.getPackageName();
return new ServicesEntry(mapperPackageName, mapperName,
model.getPackageName(), model.getName()); model.getPackageName(), model.getName());
} }

View File

@ -24,7 +24,7 @@ import ${importedType};
<#list annotations as annotation> <#list annotations as annotation>
<#nt><@includeModel object=annotation/> <#nt><@includeModel object=annotation/>
</#list> </#list>
<#lt>${accessibility.keyword} class ${name}<#if superClassName??> extends ${superClassName}</#if><#if interfaceName??> implements ${interfaceName}</#if> { <#lt>${accessibility.keyword} class ${name} <#if mapperDefinitionType.interface>implements<#else>extends</#if> <@includeModel object=mapperDefinitionType/> {
<#list fields as field><#if field.used><#nt> <@includeModel object=field/> <#list fields as field><#if field.used><#nt> <@includeModel object=field/>
</#if></#list> </#if></#list>

View File

@ -0,0 +1,28 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2393;
/**
* @author Filip Hrisafov
*/
public class Address {
private final String city;
private final Country country;
public Address(String city, Country country) {
this.city = city;
this.country = country;
}
public String getCity() {
return city;
}
public Country getCountry() {
return country;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2393;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* @author Filip Hrisafov
*/
public class AddressDto {
private String city;
private CountryDto country;
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public CountryDto getCountry() {
return country;
}
public void setCountry(CountryDto country) {
this.country = country;
}
@Mapper(uses = CountryDto.Converter.class)
public interface Converter {
Converter INSTANCE = Mappers.getMapper( Converter.class );
AddressDto convert(Address address);
}
}

View File

@ -0,0 +1,22 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2393;
/**
* @author Filip Hrisafov
*/
public class Country {
private final String name;
public Country(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View File

@ -0,0 +1,42 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2393;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
/**
* @author Filip Hrisafov
*/
public class CountryDto {
private String name;
private String code;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Mapper
public interface Converter {
@Mapping(target = "code", constant = "UNKNOWN")
CountryDto convert(Country from);
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.ap.test.bugs._2393;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Filip Hrisafov
*/
@IssueKey("2393")
@RunWith(AnnotationProcessorTestRunner.class)
@WithClasses({
Address.class,
AddressDto.class,
Country.class,
CountryDto.class,
})
public class Issue2393Test {
@Test
public void shouldUseCorrectImport() {
AddressDto dto = AddressDto.Converter.INSTANCE.convert( new Address(
"Zurich",
new Country( "Switzerland" )
) );
assertThat( dto.getCity() ).isEqualTo( "Zurich" );
assertThat( dto.getCountry().getName() ).isEqualTo( "Switzerland" );
assertThat( dto.getCountry().getCode() ).isEqualTo( "UNKNOWN" );
}
}