#3661 Use correct type for the Record component read accessors

This commit is contained in:
Filip Hrisafov 2024-09-14 01:17:45 +02:00 committed by GitHub
parent 12c9c6c1f0
commit 2686e852b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 265 additions and 29 deletions

View File

@ -131,6 +131,13 @@ public class MavenIntegrationTest {
void recordsCrossModuleTest() {
}
@ProcessorTest(baseDir = "recordsCrossModuleInterfaceTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})
@EnabledForJreRange(min = JRE.JAVA_17)
void recordsCrossModuleInterfaceTest() {
}
@ProcessorTest(baseDir = "expressionTextBlocksTest", processorTypes = {
ProcessorTest.ProcessorType.JAVAC
})

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>recordsCrossModuleInterfaceTest</artifactId>
<groupId>org.mapstruct</groupId>
<version>1.0.0</version>
</parent>
<artifactId>records-cross-module-1</artifactId>
</project>

View File

@ -0,0 +1,10 @@
/*
* 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.module1;
public interface NestedInterface {
String field();
}

View File

@ -0,0 +1,10 @@
/*
* 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.module1;
public interface RootInterface {
NestedInterface nested();
}

View File

@ -0,0 +1,11 @@
/*
* 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.module1;
public record SourceNestedRecord(
String field
) implements NestedInterface {
}

View File

@ -0,0 +1,11 @@
/*
* 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.module1;
public record SourceRootRecord(
SourceNestedRecord nested
) implements RootInterface {
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>recordsCrossModuleInterfaceTest</artifactId>
<groupId>org.mapstruct</groupId>
<version>1.0.0</version>
</parent>
<artifactId>records-cross-module-2</artifactId>
<dependencies>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>records-cross-module-1</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,18 @@
/*
* 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.module2;
import org.mapstruct.itest.records.module1.SourceRootRecord;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface RecordInterfaceIssueMapper {
RecordInterfaceIssueMapper INSTANCE = Mappers.getMapper(RecordInterfaceIssueMapper.class);
TargetRootRecord map(SourceRootRecord source);
}

View File

@ -0,0 +1,11 @@
/*
* 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.module2;
public record TargetNestedRecord(
String field
) {
}

View File

@ -0,0 +1,11 @@
/*
* 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.module2;
public record TargetRootRecord(
TargetNestedRecord nested
) {
}

View File

@ -0,0 +1,26 @@
/*
* 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.module2;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.mapstruct.itest.records.module1.SourceRootRecord;
import org.mapstruct.itest.records.module1.SourceNestedRecord;
public class RecordsTest {
@Test
public void shouldMap() {
SourceRootRecord source = new SourceRootRecord( new SourceNestedRecord( "test" ) );
TargetRootRecord target = RecordInterfaceIssueMapper.INSTANCE.map( source );
assertThat( target ).isNotNull();
assertThat( target.nested() ).isNotNull();
assertThat( target.nested().field() ).isEqualTo( "test" );
}
}

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright MapStruct Authors.
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-it-parent</artifactId>
<version>1.0.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>recordsCrossModuleInterfaceTest</artifactId>
<packaging>pom</packaging>
<modules>
<module>module-1</module>
<module>module-2</module>
</modules>
</project>

View File

@ -50,7 +50,7 @@ import org.mapstruct.ap.internal.util.Nouns;
import org.mapstruct.ap.internal.util.TypeUtils;
import org.mapstruct.ap.internal.util.accessor.Accessor;
import org.mapstruct.ap.internal.util.accessor.AccessorType;
import org.mapstruct.ap.internal.util.accessor.FieldElementAccessor;
import org.mapstruct.ap.internal.util.accessor.ElementAccessor;
import org.mapstruct.ap.internal.util.accessor.MapValueAccessor;
import org.mapstruct.ap.internal.util.accessor.PresenceCheckAccessor;
import org.mapstruct.ap.internal.util.accessor.ReadAccessor;
@ -1047,7 +1047,7 @@ public class Type extends ModelElement implements Comparable<Type> {
List<Accessor> setterMethods = getSetters();
List<Accessor> readAccessors = new ArrayList<>( getPropertyReadAccessors().values() );
// All the fields are also alternative accessors
readAccessors.addAll( filters.fieldsIn( getAllFields(), FieldElementAccessor::new ) );
readAccessors.addAll( filters.fieldsIn( getAllFields(), ElementAccessor::new ) );
// there could be a read accessor (field or method) for a list/map that is not present as setter.
// an accessor could substitute the setter in that case and act as setter.

View File

@ -39,22 +39,16 @@ import static org.mapstruct.ap.internal.util.accessor.AccessorType.SETTER;
public class Filters {
private static final Method RECORD_COMPONENTS_METHOD;
private static final Method RECORD_COMPONENT_ACCESSOR_METHOD;
static {
Method recordComponentsMethod;
Method recordComponentAccessorMethod;
try {
recordComponentsMethod = TypeElement.class.getMethod( "getRecordComponents" );
recordComponentAccessorMethod = Class.forName( "javax.lang.model.element.RecordComponentElement" )
.getMethod( "getAccessor" );
}
catch ( NoSuchMethodException | ClassNotFoundException e ) {
catch ( NoSuchMethodException e ) {
recordComponentsMethod = null;
recordComponentAccessorMethod = null;
}
RECORD_COMPONENTS_METHOD = recordComponentsMethod;
RECORD_COMPONENT_ACCESSOR_METHOD = recordComponentAccessorMethod;
}
private final AccessorNamingUtils accessorNaming;
@ -89,26 +83,19 @@ public class Filters {
}
public Map<String, ReadAccessor> recordAccessorsIn(Collection<Element> recordComponents) {
if ( RECORD_COMPONENT_ACCESSOR_METHOD == null ) {
if ( recordComponents.isEmpty() ) {
return java.util.Collections.emptyMap();
}
try {
Map<String, ReadAccessor> recordAccessors = new LinkedHashMap<>();
for ( Element recordComponent : recordComponents ) {
ExecutableElement recordExecutableElement =
(ExecutableElement) RECORD_COMPONENT_ACCESSOR_METHOD.invoke( recordComponent );
recordAccessors.put(
recordComponent.getSimpleName().toString(),
ReadAccessor.fromGetter( recordExecutableElement, getReturnType( recordExecutableElement ) )
ReadAccessor.fromRecordComponent( recordComponent )
);
}
return recordAccessors;
}
catch ( IllegalAccessException | InvocationTargetException e ) {
return java.util.Collections.emptyMap();
}
}
private TypeMirror getReturnType(ExecutableElement executableElement) {
return getWithinContext( executableElement ).getReturnType();

View File

@ -0,0 +1,45 @@
/*
* 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.internal.util.accessor;
import javax.lang.model.element.Element;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
/**
* An {@link Accessor} that wraps a {@link VariableElement}.
*
* @author Filip Hrisafov
*/
public class ElementAccessor extends AbstractAccessor<Element> {
private final AccessorType accessorType;
public ElementAccessor(VariableElement variableElement) {
this( variableElement, AccessorType.FIELD );
}
public ElementAccessor(Element element, AccessorType accessorType) {
super( element );
this.accessorType = accessorType;
}
@Override
public TypeMirror getAccessedType() {
return element.asType();
}
@Override
public String toString() {
return element.toString();
}
@Override
public AccessorType getAccessorType() {
return accessorType;
}
}

View File

@ -5,6 +5,7 @@
*/
package org.mapstruct.ap.internal.util.accessor;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
@ -17,7 +18,7 @@ public interface ReadAccessor extends Accessor {
String getReadValueSource();
static ReadAccessor fromField(VariableElement variableElement) {
return new ReadDelegateAccessor( new FieldElementAccessor( variableElement ) ) {
return new ReadDelegateAccessor( new ElementAccessor( variableElement ) ) {
@Override
public String getReadValueSource() {
return getSimpleName();
@ -25,6 +26,15 @@ public interface ReadAccessor extends Accessor {
};
}
static ReadAccessor fromRecordComponent(Element element) {
return new ReadDelegateAccessor( new ElementAccessor( element, AccessorType.GETTER ) ) {
@Override
public String getReadValueSource() {
return getSimpleName() + "()";
}
};
}
static ReadAccessor fromGetter(ExecutableElement element, TypeMirror accessedType) {
return new ReadDelegateAccessor( new ExecutableElementAccessor( element, accessedType, AccessorType.GETTER ) ) {
@Override

View File

@ -5,6 +5,7 @@
*/
package org.mapstruct.ap.internal.util.accessor;
import javax.lang.model.element.Element;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
@ -13,9 +14,9 @@ import javax.lang.model.type.TypeMirror;
*
* @author Filip Hrisafov
*/
public class FieldElementAccessor extends AbstractAccessor<VariableElement> {
public class RecordElementAccessor extends AbstractAccessor<Element> {
public FieldElementAccessor(VariableElement element) {
public RecordElementAccessor(Element element) {
super( element );
}
@ -31,7 +32,7 @@ public class FieldElementAccessor extends AbstractAccessor<VariableElement> {
@Override
public AccessorType getAccessorType() {
return AccessorType.FIELD;
return AccessorType.GETTER;
}
}