mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#3125: Allow subclassmapping inheritance for methods with identical signature
Co-authored-by: Ben Zegveld <Ben.Zegveld@gmail.com>
This commit is contained in:
parent
1ab5db6a27
commit
2f78d3f4e2
@ -5,12 +5,15 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model.source;
|
||||
|
||||
import static org.mapstruct.ap.internal.model.source.MappingOptions.getMappingTargetNamesBy;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
|
||||
import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem;
|
||||
@ -20,8 +23,6 @@ import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
|
||||
import static org.mapstruct.ap.internal.model.source.MappingOptions.getMappingTargetNamesBy;
|
||||
|
||||
/**
|
||||
* Encapsulates all options specifiable on a mapping method
|
||||
*
|
||||
@ -205,12 +206,19 @@ public class MappingMethodOptions {
|
||||
}
|
||||
|
||||
if ( isInverse ) {
|
||||
// normal inheritence of subclass mappings will result runtime in infinite loops.
|
||||
List<SubclassMappingOptions> inheritedMappings = SubclassMappingOptions.copyForInverseInheritance(
|
||||
templateOptions.getSubclassMappings(),
|
||||
getBeanMapping() );
|
||||
addAllNonRedefined( sourceMethod, annotationMirror, inheritedMappings );
|
||||
}
|
||||
else if ( methodsHaveIdenticalSignature( templateMethod, sourceMethod ) ) {
|
||||
List<SubclassMappingOptions> inheritedMappings =
|
||||
SubclassMappingOptions
|
||||
.copyForInheritance(
|
||||
templateOptions.getSubclassMappings(),
|
||||
getBeanMapping() );
|
||||
addAllNonRedefined( sourceMethod, annotationMirror, inheritedMappings );
|
||||
}
|
||||
|
||||
Set<MappingOptions> newMappings = new LinkedHashSet<>();
|
||||
for ( MappingOptions mapping : templateOptions.getMappings() ) {
|
||||
@ -232,6 +240,28 @@ public class MappingMethodOptions {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean methodsHaveIdenticalSignature(SourceMethod templateMethod, SourceMethod sourceMethod) {
|
||||
return parametersAreOfIdenticalTypeAndOrder( templateMethod, sourceMethod )
|
||||
&& resultTypeIsTheSame( templateMethod, sourceMethod );
|
||||
}
|
||||
|
||||
private boolean parametersAreOfIdenticalTypeAndOrder(SourceMethod templateMethod, SourceMethod sourceMethod) {
|
||||
if (templateMethod.getParameters().size() != sourceMethod.getParameters().size()) {
|
||||
return false;
|
||||
}
|
||||
for ( int i = 0; i < templateMethod.getParameters().size(); i++ ) {
|
||||
if (!templateMethod.getParameters().get( i ).getType().equals(
|
||||
sourceMethod.getParameters().get( i ).getType() ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean resultTypeIsTheSame(SourceMethod templateMethod, SourceMethod sourceMethod) {
|
||||
return templateMethod.getResultType().equals( sourceMethod.getResultType() );
|
||||
}
|
||||
|
||||
private void addAllNonRedefined(SourceMethod sourceMethod, AnnotationMirror annotationMirror,
|
||||
List<SubclassMappingOptions> inheritedMappings) {
|
||||
Set<SubclassMappingOptions> redefinedSubclassMappings = new HashSet<>( subclassMappings );
|
||||
|
@ -202,6 +202,23 @@ public class SubclassMappingOptions extends DelegatingOptions {
|
||||
) ).collect( Collectors.toCollection( ArrayList::new ) );
|
||||
}
|
||||
|
||||
public static List<SubclassMappingOptions> copyForInheritance(Set<SubclassMappingOptions> subclassMappings,
|
||||
BeanMappingOptions beanMappingOptions) {
|
||||
// we are not interested in keeping it unique at this point.
|
||||
List<SubclassMappingOptions> mappings = new ArrayList<>();
|
||||
for ( SubclassMappingOptions subclassMapping : subclassMappings ) {
|
||||
mappings.add(
|
||||
new SubclassMappingOptions(
|
||||
subclassMapping.source,
|
||||
subclassMapping.target,
|
||||
subclassMapping.typeUtils,
|
||||
beanMappingOptions,
|
||||
subclassMapping.selectionParameters,
|
||||
subclassMapping.subclassMapping ) );
|
||||
}
|
||||
return mappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( obj == null || !( obj instanceof SubclassMappingOptions ) ) {
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.subclassmapping;
|
||||
|
||||
import org.mapstruct.InheritConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.mapstruct.SubclassMapping;
|
||||
import org.mapstruct.ap.test.subclassmapping.mappables.Bike;
|
||||
import org.mapstruct.ap.test.subclassmapping.mappables.BikeDto;
|
||||
import org.mapstruct.ap.test.subclassmapping.mappables.Car;
|
||||
import org.mapstruct.ap.test.subclassmapping.mappables.CarDto;
|
||||
import org.mapstruct.ap.test.subclassmapping.mappables.HatchBack;
|
||||
import org.mapstruct.ap.test.subclassmapping.mappables.Vehicle;
|
||||
import org.mapstruct.ap.test.subclassmapping.mappables.VehicleDto;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper( unmappedTargetPolicy = ReportingPolicy.IGNORE )
|
||||
public interface InheritedSubclassMapper {
|
||||
InheritedSubclassMapper INSTANCE = Mappers.getMapper( InheritedSubclassMapper.class );
|
||||
|
||||
@SubclassMapping( source = HatchBack.class, target = CarDto.class )
|
||||
@SubclassMapping( source = Car.class, target = CarDto.class )
|
||||
@SubclassMapping( source = Bike.class, target = BikeDto.class )
|
||||
@Mapping( source = "vehicleManufacturingCompany", target = "maker" )
|
||||
VehicleDto map(Vehicle vehicle);
|
||||
|
||||
@InheritConfiguration( name = "map" )
|
||||
VehicleDto mapInherited(Vehicle dto);
|
||||
|
||||
@SubclassMapping( source = Bike.class, target = CarDto.class )
|
||||
@InheritConfiguration( name = "map" )
|
||||
VehicleDto mapInheritedOverride(Vehicle dto);
|
||||
}
|
@ -130,7 +130,7 @@ public class SubclassMappingTest {
|
||||
HatchBack.class,
|
||||
InverseOrderSubclassMapper.class
|
||||
} )
|
||||
void subclassMappingOverridesInverseInheritsMapping() {
|
||||
void subclassMappingOverridesInverseInheritedMapping() {
|
||||
VehicleCollectionDto vehicleDtos = new VehicleCollectionDto();
|
||||
CarDto carDto = new CarDto();
|
||||
carDto.setMaker( "BenZ" );
|
||||
@ -143,6 +143,24 @@ public class SubclassMappingTest {
|
||||
.containsExactly( Car.class );
|
||||
}
|
||||
|
||||
@IssueKey( "3125" )
|
||||
@ProcessorTest
|
||||
@WithClasses( {
|
||||
HatchBack.class,
|
||||
InheritedSubclassMapper.class
|
||||
} )
|
||||
void subclassMappingOverridesInheritedMapping() {
|
||||
Vehicle bike = new Bike();
|
||||
|
||||
VehicleDto result = InheritedSubclassMapper.INSTANCE.map( bike );
|
||||
VehicleDto resultInherited = InheritedSubclassMapper.INSTANCE.mapInherited( bike );
|
||||
VehicleDto resultOverride = InheritedSubclassMapper.INSTANCE.mapInheritedOverride( bike );
|
||||
|
||||
assertThat( result ).isInstanceOf( BikeDto.class );
|
||||
assertThat( resultInherited ).isInstanceOf( BikeDto.class );
|
||||
assertThat( resultOverride ).isInstanceOf( CarDto.class );
|
||||
}
|
||||
|
||||
@ProcessorTest
|
||||
@WithClasses( { SubclassCompositeMapper.class, CompositeSubclassMappingAnnotation.class })
|
||||
void mappingIsDoneUsingSubclassMappingWithCompositeMapping() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user