diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberDto.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberDto.java new file mode 100644 index 000000000..bf3ce6cd9 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberDto.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 MemberDto(Boolean isActive, Boolean premium) { + +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberEntity.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberEntity.java new file mode 100644 index 000000000..50a7b1ce0 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberEntity.java @@ -0,0 +1,31 @@ +/* + * 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 class MemberEntity { + + private Boolean isActive; + private Boolean premium; + + public Boolean getIsActive() { + return isActive; + } + + public void setIsActive(Boolean active) { + isActive = active; + } + + public Boolean getPremium() { + return premium; + } + + public void setPremium(Boolean premium) { + this.premium = premium; + } +} diff --git a/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberMapper.java b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberMapper.java new file mode 100644 index 000000000..3460aeed6 --- /dev/null +++ b/integrationtest/src/test/resources/recordsTest/src/main/java/org/mapstruct/itest/records/MemberMapper.java @@ -0,0 +1,24 @@ +/* + * 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; + +import org.mapstruct.Mapper; +import org.mapstruct.ReportingPolicy; +import org.mapstruct.factory.Mappers; + +/** + * @author Filip Hrisafov + */ +@Mapper(unmappedSourcePolicy = ReportingPolicy.ERROR) +public interface MemberMapper { + + MemberMapper INSTANCE = Mappers.getMapper( MemberMapper.class ); + + MemberEntity fromRecord(MemberDto record); + + MemberDto toRecord(MemberEntity entity); + +} 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 e3d055345..e98df8797 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 @@ -61,4 +61,26 @@ public class RecordsTest { assertThat( carDto.wheelPositions() ) .containsExactly( "left" ); } + + @Test + public void shouldMapMemberRecord() { + MemberEntity member = MemberMapper.INSTANCE.fromRecord( new MemberDto( true, false ) ); + + assertThat( member ).isNotNull(); + assertThat( member.getIsActive() ).isTrue(); + assertThat( member.getPremium() ).isFalse(); + } + + @Test + public void shouldMapIntoMemberRecord() { + MemberEntity entity = new MemberEntity(); + entity.setIsActive( false ); + entity.setPremium( true ); + + MemberDto value = MemberMapper.INSTANCE.toRecord( entity ); + + assertThat( value ).isNotNull(); + assertThat( value.isActive() ).isEqualTo( false ); + assertThat( value.premium() ).isEqualTo( true ); + } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java index caa7a9c62..bba33a276 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/common/Type.java @@ -576,14 +576,33 @@ public class Type extends ModelElement implements Comparable { */ public Map getPropertyReadAccessors() { if ( readAccessors == null ) { - List getterList = filters.getterMethodsIn( getAllMethods() ); Map modifiableGetters = new LinkedHashMap<>(); + + Map recordAccessors = filters.recordAccessorsIn( getRecordComponents() ); + modifiableGetters.putAll( recordAccessors ); + + List getterList = filters.getterMethodsIn( getAllMethods() ); for ( Accessor getter : getterList ) { + String simpleName = getter.getSimpleName(); + if ( recordAccessors.containsKey( simpleName ) ) { + // If there is already a record accessor that contains the simple name + // then it means that the getter is actually a record component. + // In that case we need to ignore it. + // e.g. record component named isActive. + // The DefaultAccessorNamingStrategy will return active as property name, + // but the property name is isActive, since it is a record + continue; + } String propertyName = getPropertyName( getter ); + + if ( recordAccessors.containsKey( propertyName ) ) { + // If there is already a record accessor, the property needs to be ignored + continue; + } if ( modifiableGetters.containsKey( propertyName ) ) { // In the DefaultAccessorNamingStrategy, this can only be the case for Booleans: isFoo() and // getFoo(); The latter is preferred. - if ( !getter.getSimpleName().startsWith( "is" ) ) { + if ( !simpleName.startsWith( "is" ) ) { modifiableGetters.put( propertyName, getter ); } @@ -593,11 +612,6 @@ public class Type extends ModelElement implements Comparable { } } - Map recordAccessors = filters.recordAccessorsIn( getRecordComponents() ); - for ( Map.Entry recordEntry : recordAccessors.entrySet() ) { - modifiableGetters.putIfAbsent( recordEntry.getKey(), recordEntry.getValue() ); - } - List fieldsList = filters.fieldsIn( getAllFields() ); for ( Accessor field : fieldsList ) { String propertyName = getPropertyName( field );