From c1feafef4c68053999b3d92be030ad41bd8843ba Mon Sep 17 00:00:00 2001 From: Filip Hrisafov Date: Sat, 29 Aug 2020 11:22:18 +0200 Subject: [PATCH] #2177 Mapping into a generic class / record with a typed constructor argument should work --- .../itest/records/CustomerMapper.java | 3 ++ .../itest/records/GenericRecord.java | 13 ++++++ .../mapstruct/itest/records/RecordsTest.java | 12 +++++ .../ap/internal/model/BeanMappingMethod.java | 27 +++++++++--- .../accessor/ParameterElementAccessor.java | 6 ++- .../ap/test/bugs/_2177/Issue2177Mapper.java | 44 +++++++++++++++++++ .../ap/test/bugs/_2177/Issue2177Test.java | 34 ++++++++++++++ 7 files changed, 131 insertions(+), 8 deletions(-) create mode 100644 integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/GenericRecord.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Mapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java index a3c7037f0..addb8c590 100644 --- a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/CustomerMapper.java @@ -24,4 +24,7 @@ public interface CustomerMapper { @InheritInverseConfiguration CustomerDto toRecord(CustomerEntity entity); + @Mapping(target = "value", source = "name") + GenericRecord toValue(CustomerEntity entity); + } diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/GenericRecord.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/GenericRecord.java new file mode 100644 index 000000000..9a39b5996 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/GenericRecord.java @@ -0,0 +1,13 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.itest.records; + +/** + * @author Filip Hrisafov + */ +public record GenericRecord(T value) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java index 824a763fc..b40468180 100644 --- a/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java +++ b/integrationtest/src/test/resources/recordsTest/src/test/java/org/mapstruct/itest/records/RecordsTest.java @@ -35,4 +35,16 @@ public class RecordsTest { assertThat( customer.name() ).isEqualTo( "Kermit" ); assertThat( customer.email() ).isEqualTo( "kermit@test.com" ); } + + @Test + public void shouldMapIntoGenericRecord() { + CustomerEntity entity = new CustomerEntity(); + entity.setName( "Kermit" ); + entity.setMail( "kermit@test.com" ); + + GenericRecord value = CustomerMapper.INSTANCE.toValue( entity ); + + assertThat( value ).isNotNull(); + assertThat( value.value() ).isEqualTo( "Kermit" ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 0062611e2..d6f06b49e 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -27,6 +27,7 @@ import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic; @@ -598,15 +599,21 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { List parameterBindings = new ArrayList<>( recordComponents.size() ); Map constructorAccessors = new LinkedHashMap<>(); for ( Element recordComponent : recordComponents ) { + TypeMirror recordComponentMirror = ctx.getTypeUtils() + .asMemberOf( (DeclaredType) type.getTypeMirror(), recordComponent ); String parameterName = recordComponent.getSimpleName().toString(); - Accessor accessor = createConstructorAccessor( recordComponent, parameterName ); + Accessor accessor = createConstructorAccessor( + recordComponent, + recordComponentMirror, + parameterName + ); constructorAccessors.put( parameterName, accessor ); parameterBindings.add( ParameterBinding.fromTypeAndName( - ctx.getTypeFactory().getType( recordComponent.asType() ), + ctx.getTypeFactory().getType( recordComponentMirror ), accessor.getSimpleName() ) ); } @@ -718,7 +725,11 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { for ( Parameter constructorParameter : constructorParameters ) { String parameterName = constructorParameter.getName(); Element parameterElement = constructorParameter.getElement(); - Accessor constructorAccessor = createConstructorAccessor( parameterElement, parameterName ); + Accessor constructorAccessor = createConstructorAccessor( + parameterElement, + constructorParameter.getType().getTypeMirror(), + parameterName + ); constructorAccessors.put( parameterName, constructorAccessor @@ -746,7 +757,11 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { String parameterName = constructorProperties.get( i ); Parameter constructorParameter = constructorParameters.get( i ); Element parameterElement = constructorParameter.getElement(); - Accessor constructorAccessor = createConstructorAccessor( parameterElement, parameterName ); + Accessor constructorAccessor = createConstructorAccessor( + parameterElement, + constructorParameter.getType().getTypeMirror(), + parameterName + ); constructorAccessors.put( parameterName, constructorAccessor @@ -761,13 +776,13 @@ public class BeanMappingMethod extends NormalTypeMappingMethod { } } - private Accessor createConstructorAccessor(Element element, String parameterName) { + private Accessor createConstructorAccessor(Element element, TypeMirror accessedType, String parameterName) { String safeParameterName = Strings.getSafeVariableName( parameterName, existingVariableNames ); existingVariableNames.add( safeParameterName ); - return new ParameterElementAccessor( element, safeParameterName ); + return new ParameterElementAccessor( element, accessedType, safeParameterName ); } private boolean hasDefaultAnnotationFromAnyPackage(Element element) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java index e1877cb0b..999137005 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/accessor/ParameterElementAccessor.java @@ -17,10 +17,12 @@ import javax.lang.model.type.TypeMirror; public class ParameterElementAccessor extends AbstractAccessor { protected final String name; + protected final TypeMirror accessedType; - public ParameterElementAccessor(Element element, String name) { + public ParameterElementAccessor(Element element, TypeMirror accessedType, String name) { super( element ); this.name = name; + this.accessedType = accessedType; } @Override @@ -30,7 +32,7 @@ public class ParameterElementAccessor extends AbstractAccessor { @Override public TypeMirror getAccessedType() { - return element.asType(); + return accessedType; } @Override diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Mapper.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Mapper.java new file mode 100644 index 000000000..bf96c6b5b --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Mapper.java @@ -0,0 +1,44 @@ +/* + * 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._2177; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper +public interface Issue2177Mapper { + + Issue2177Mapper INSTANCE = Mappers.getMapper( Issue2177Mapper.class ); + + Target map(Source source); + + class Source { + private final String value; + + public Source(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + class Target { + private final T value; + + public Target(T value) { + this.value = value; + } + + public T getValue() { + return value; + } + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java new file mode 100644 index 000000000..50c1d9f69 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/bugs/_2177/Issue2177Test.java @@ -0,0 +1,34 @@ +/* + * 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._2177; + +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("2177") +@RunWith(AnnotationProcessorTestRunner.class) +@WithClasses({ + Issue2177Mapper.class +}) +public class Issue2177Test { + + @Test + public void shouldCorrectlyUseGenericClassesWithConstructorMapping() { + + Issue2177Mapper.Target target = Issue2177Mapper.INSTANCE.map( new Issue2177Mapper.Source( "test" ) ); + + assertThat( target ).isNotNull(); + assertThat( target.getValue() ).isEqualTo( "test" ); + } +}