mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#3849: Resolve duplicate invocation of overloaded lifecycle methods with inheritance
Add compiler option `mapstruct.disableLifecycleOverloadDeduplicateSelector` to disable the deduplication if needed. Signed-off-by: TangYang <tangyang9464@163.com>
This commit is contained in:
parent
3a5c70224d
commit
0badba7003
@ -95,6 +95,7 @@ import static javax.lang.model.element.ElementKind.CLASS;
|
|||||||
MappingProcessor.VERBOSE,
|
MappingProcessor.VERBOSE,
|
||||||
MappingProcessor.NULL_VALUE_ITERABLE_MAPPING_STRATEGY,
|
MappingProcessor.NULL_VALUE_ITERABLE_MAPPING_STRATEGY,
|
||||||
MappingProcessor.NULL_VALUE_MAP_MAPPING_STRATEGY,
|
MappingProcessor.NULL_VALUE_MAP_MAPPING_STRATEGY,
|
||||||
|
MappingProcessor.DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR,
|
||||||
})
|
})
|
||||||
public class MappingProcessor extends AbstractProcessor {
|
public class MappingProcessor extends AbstractProcessor {
|
||||||
|
|
||||||
@ -115,6 +116,8 @@ public class MappingProcessor extends AbstractProcessor {
|
|||||||
protected static final String VERBOSE = "mapstruct.verbose";
|
protected static final String VERBOSE = "mapstruct.verbose";
|
||||||
protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = "mapstruct.nullValueIterableMappingStrategy";
|
protected static final String NULL_VALUE_ITERABLE_MAPPING_STRATEGY = "mapstruct.nullValueIterableMappingStrategy";
|
||||||
protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = "mapstruct.nullValueMapMappingStrategy";
|
protected static final String NULL_VALUE_MAP_MAPPING_STRATEGY = "mapstruct.nullValueMapMappingStrategy";
|
||||||
|
protected static final String DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR =
|
||||||
|
"mapstruct.disableLifecycleOverloadDeduplicateSelector";
|
||||||
|
|
||||||
private final Set<String> additionalSupportedOptions;
|
private final Set<String> additionalSupportedOptions;
|
||||||
private final String additionalSupportedOptionsError;
|
private final String additionalSupportedOptionsError;
|
||||||
@ -174,6 +177,8 @@ public class MappingProcessor extends AbstractProcessor {
|
|||||||
String nullValueIterableMappingStrategy = processingEnv.getOptions()
|
String nullValueIterableMappingStrategy = processingEnv.getOptions()
|
||||||
.get( NULL_VALUE_ITERABLE_MAPPING_STRATEGY );
|
.get( NULL_VALUE_ITERABLE_MAPPING_STRATEGY );
|
||||||
String nullValueMapMappingStrategy = processingEnv.getOptions().get( NULL_VALUE_MAP_MAPPING_STRATEGY );
|
String nullValueMapMappingStrategy = processingEnv.getOptions().get( NULL_VALUE_MAP_MAPPING_STRATEGY );
|
||||||
|
String disableLifecycleOverloadDeduplicateSelector = processingEnv.getOptions()
|
||||||
|
.get( DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR );
|
||||||
|
|
||||||
return new Options(
|
return new Options(
|
||||||
Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ),
|
Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ),
|
||||||
@ -189,7 +194,8 @@ public class MappingProcessor extends AbstractProcessor {
|
|||||||
NullValueMappingStrategyGem.valueOf( nullValueIterableMappingStrategy.toUpperCase( Locale.ROOT ) ) :
|
NullValueMappingStrategyGem.valueOf( nullValueIterableMappingStrategy.toUpperCase( Locale.ROOT ) ) :
|
||||||
null,
|
null,
|
||||||
nullValueMapMappingStrategy != null ?
|
nullValueMapMappingStrategy != null ?
|
||||||
NullValueMappingStrategyGem.valueOf( nullValueMapMappingStrategy.toUpperCase( Locale.ROOT ) ) : null
|
NullValueMappingStrategyGem.valueOf( nullValueMapMappingStrategy.toUpperCase( Locale.ROOT ) ) : null,
|
||||||
|
Boolean.parseBoolean( disableLifecycleOverloadDeduplicateSelector )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ public final class LifecycleMethodResolver {
|
|||||||
MappingBuilderContext ctx, Set<String> existingVariableNames) {
|
MappingBuilderContext ctx, Set<String> existingVariableNames) {
|
||||||
|
|
||||||
MethodSelectors selectors =
|
MethodSelectors selectors =
|
||||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() );
|
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager(), ctx.getOptions() );
|
||||||
|
|
||||||
List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
|
List<SelectedMethod<SourceMethod>> matchingMethods = selectors.getMatchingMethods(
|
||||||
callbackMethods,
|
callbackMethods,
|
||||||
|
@ -5,12 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.mapstruct.ap.internal.model;
|
package org.mapstruct.ap.internal.model;
|
||||||
|
|
||||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.lang.model.element.ElementKind;
|
import javax.lang.model.element.ElementKind;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
|
||||||
@ -26,6 +23,8 @@ import org.mapstruct.ap.internal.model.source.selector.SelectedMethod;
|
|||||||
import org.mapstruct.ap.internal.model.source.selector.SelectionContext;
|
import org.mapstruct.ap.internal.model.source.selector.SelectionContext;
|
||||||
import org.mapstruct.ap.internal.util.Message;
|
import org.mapstruct.ap.internal.util.Message;
|
||||||
|
|
||||||
|
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Sjaak Derksen
|
* @author Sjaak Derksen
|
||||||
@ -126,7 +125,7 @@ public class ObjectFactoryMethodResolver {
|
|||||||
MappingBuilderContext ctx) {
|
MappingBuilderContext ctx) {
|
||||||
|
|
||||||
MethodSelectors selectors =
|
MethodSelectors selectors =
|
||||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() );
|
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager(), null );
|
||||||
|
|
||||||
return selectors.getMatchingMethods(
|
return selectors.getMatchingMethods(
|
||||||
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||||
|
@ -119,7 +119,8 @@ public final class PresenceCheckMethodResolver {
|
|||||||
MethodSelectors selectors = new MethodSelectors(
|
MethodSelectors selectors = new MethodSelectors(
|
||||||
ctx.getTypeUtils(),
|
ctx.getTypeUtils(),
|
||||||
ctx.getElementUtils(),
|
ctx.getElementUtils(),
|
||||||
ctx.getMessager()
|
ctx.getMessager(),
|
||||||
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
return selectors.getMatchingMethods(
|
return selectors.getMatchingMethods(
|
||||||
|
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* 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.model.source.selector;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||||
|
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||||
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selector for deduplicating overloaded lifecycle callback methods
|
||||||
|
* whose parameter signatures differ only by type hierarchy.
|
||||||
|
* <p>
|
||||||
|
* In the context of lifecycle callback method selection
|
||||||
|
* (such as @BeforeMapping or @AfterMapping), it is possible to have multiple overloaded methods
|
||||||
|
* whose parameter lists are structurally identical except for the specific types,
|
||||||
|
* where those types are related by inheritance (e.g., one parameter is a superclass or subclass of another).
|
||||||
|
* <p>
|
||||||
|
* This selector groups such methods by their effective parameter signature
|
||||||
|
* (ignoring differences only in type hierarchy), and, within each group,
|
||||||
|
* retains only the method whose parameter types have the closest inheritance distance
|
||||||
|
* to the actual invocation types.
|
||||||
|
* This ensures that, for each group of nearly identical overloads,
|
||||||
|
* only the most specific and appropriate method is selected.
|
||||||
|
* <p>
|
||||||
|
* <b>Example (see Issue3849Test):</b>
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* @AfterMapping
|
||||||
|
* default void afterMapping(Parent source, @MappingTarget ParentDto target) { ... }
|
||||||
|
* @AfterMapping
|
||||||
|
* default void afterMapping(Parent source, @MappingTarget ChildDto target) { ... }
|
||||||
|
* }</pre>
|
||||||
|
* When mapping a Child to a ChildDto,
|
||||||
|
* only the method with ChildDto is selected, even though both methods match by signature
|
||||||
|
* except for the target type's inheritance relationship.
|
||||||
|
*/
|
||||||
|
public class LifecycleOverloadDeduplicateSelector implements MethodSelector {
|
||||||
|
@Override
|
||||||
|
public <T extends Method> List<SelectedMethod<T>> getMatchingMethods(List<SelectedMethod<T>> methods,
|
||||||
|
SelectionContext context) {
|
||||||
|
if ( !context.getSelectionCriteria().isLifecycleCallbackRequired() || methods.size() <= 1 ) {
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
Collection<List<SelectedMethod<T>>> methodSignatureGroups =
|
||||||
|
methods.stream()
|
||||||
|
.collect( Collectors.groupingBy(
|
||||||
|
LifecycleOverloadDeduplicateSelector::buildSignatureKey,
|
||||||
|
LinkedHashMap::new,
|
||||||
|
Collectors.toList()
|
||||||
|
) )
|
||||||
|
.values();
|
||||||
|
List<SelectedMethod<T>> deduplicatedMethods = new ArrayList<>( methods.size() );
|
||||||
|
for ( List<SelectedMethod<T>> signatureGroup : methodSignatureGroups ) {
|
||||||
|
if ( signatureGroup.size() == 1 ) {
|
||||||
|
deduplicatedMethods.add( signatureGroup.get( 0 ) );
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
SelectedMethod<T> bestInheritanceMethod = signatureGroup.get( 0 );
|
||||||
|
for ( int i = 1; i < signatureGroup.size(); i++ ) {
|
||||||
|
SelectedMethod<T> candidateMethod = signatureGroup.get( i );
|
||||||
|
if ( isInheritanceBetter( candidateMethod, bestInheritanceMethod ) ) {
|
||||||
|
bestInheritanceMethod = candidateMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deduplicatedMethods.add( bestInheritanceMethod );
|
||||||
|
}
|
||||||
|
return deduplicatedMethods;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a grouping key for a method based on its defining type,
|
||||||
|
* method name, and a detailed breakdown of each parameter binding.
|
||||||
|
* <p>
|
||||||
|
* The key consists of:
|
||||||
|
* <ul>
|
||||||
|
* <li>The type that defines the method</li>
|
||||||
|
* <li>The method name</li>
|
||||||
|
* <li>parameter bindings</li>
|
||||||
|
* </ul>
|
||||||
|
* This ensures that methods are grouped together only if all these aspects match,
|
||||||
|
* except for differences in type hierarchy, which are handled separately.
|
||||||
|
*/
|
||||||
|
private static <T extends Method> List<Object> buildSignatureKey(SelectedMethod<T> method) {
|
||||||
|
List<Object> key = new ArrayList<>();
|
||||||
|
key.add( method.getMethod().getDefiningType() );
|
||||||
|
key.add( method.getMethod().getName() );
|
||||||
|
for ( ParameterBinding binding : method.getParameterBindings() ) {
|
||||||
|
key.add( binding.getType() );
|
||||||
|
key.add( binding.getVariableName() );
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare the inheritance distance of parameters between two methods to determine if candidateMethod is better.
|
||||||
|
* Compares parameters in order, returns as soon as a better one is found.
|
||||||
|
*/
|
||||||
|
private <T extends Method> boolean isInheritanceBetter(SelectedMethod<T> candidateMethod,
|
||||||
|
SelectedMethod<T> currentBestMethod) {
|
||||||
|
List<ParameterBinding> candidateBindings = candidateMethod.getParameterBindings();
|
||||||
|
List<ParameterBinding> bestBindings = currentBestMethod.getParameterBindings();
|
||||||
|
List<Parameter> candidateParams = candidateMethod.getMethod().getParameters();
|
||||||
|
List<Parameter> bestParams = currentBestMethod.getMethod().getParameters();
|
||||||
|
int paramCount = candidateBindings.size();
|
||||||
|
|
||||||
|
for ( int i = 0; i < paramCount; i++ ) {
|
||||||
|
int candidateDistance = candidateBindings.get( i )
|
||||||
|
.getType()
|
||||||
|
.distanceTo( candidateParams.get( i ).getType() );
|
||||||
|
int bestDistance = bestBindings.get( i ).getType().distanceTo( bestParams.get( i ).getType() );
|
||||||
|
if ( candidateDistance < bestDistance ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ( candidateDistance > bestDistance ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,7 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.mapstruct.ap.internal.model.source.Method;
|
import org.mapstruct.ap.internal.model.source.Method;
|
||||||
|
import org.mapstruct.ap.internal.option.Options;
|
||||||
import org.mapstruct.ap.internal.util.ElementUtils;
|
import org.mapstruct.ap.internal.util.ElementUtils;
|
||||||
import org.mapstruct.ap.internal.util.FormattingMessager;
|
import org.mapstruct.ap.internal.util.FormattingMessager;
|
||||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||||
@ -24,20 +25,27 @@ public class MethodSelectors {
|
|||||||
private final List<MethodSelector> selectors;
|
private final List<MethodSelector> selectors;
|
||||||
|
|
||||||
public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils,
|
public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils,
|
||||||
FormattingMessager messager) {
|
FormattingMessager messager, Options options) {
|
||||||
selectors = Arrays.asList(
|
List<MethodSelector> selectorList = new ArrayList<>( Arrays.asList(
|
||||||
new MethodFamilySelector(),
|
new MethodFamilySelector(),
|
||||||
new TypeSelector( messager ),
|
new TypeSelector( messager ),
|
||||||
new QualifierSelector( typeUtils, elementUtils ),
|
new QualifierSelector( typeUtils, elementUtils ),
|
||||||
new TargetTypeSelector( typeUtils ),
|
new TargetTypeSelector( typeUtils ),
|
||||||
new JavaxXmlElementDeclSelector( typeUtils ),
|
new JavaxXmlElementDeclSelector( typeUtils ),
|
||||||
new JakartaXmlElementDeclSelector( typeUtils ),
|
new JakartaXmlElementDeclSelector( typeUtils ),
|
||||||
new InheritanceSelector(),
|
new InheritanceSelector()
|
||||||
|
) );
|
||||||
|
if ( options != null && !options.isDisableLifecycleOverloadDeduplicateSelector() ) {
|
||||||
|
selectorList.add( new LifecycleOverloadDeduplicateSelector() );
|
||||||
|
}
|
||||||
|
|
||||||
|
selectorList.addAll( Arrays.asList(
|
||||||
new CreateOrUpdateSelector(),
|
new CreateOrUpdateSelector(),
|
||||||
new SourceRhsSelector(),
|
new SourceRhsSelector(),
|
||||||
new FactoryParameterSelector(),
|
new FactoryParameterSelector(),
|
||||||
new MostSpecificResultTypeSelector()
|
new MostSpecificResultTypeSelector()
|
||||||
);
|
) );
|
||||||
|
this.selectors = selectorList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,6 +26,7 @@ public class Options {
|
|||||||
private final boolean verbose;
|
private final boolean verbose;
|
||||||
private final NullValueMappingStrategyGem nullValueIterableMappingStrategy;
|
private final NullValueMappingStrategyGem nullValueIterableMappingStrategy;
|
||||||
private final NullValueMappingStrategyGem nullValueMapMappingStrategy;
|
private final NullValueMappingStrategyGem nullValueMapMappingStrategy;
|
||||||
|
private final boolean disableLifecycleOverloadDeduplicateSelector;
|
||||||
|
|
||||||
//CHECKSTYLE:OFF
|
//CHECKSTYLE:OFF
|
||||||
public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment,
|
public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment,
|
||||||
@ -36,7 +37,8 @@ public class Options {
|
|||||||
boolean disableBuilders,
|
boolean disableBuilders,
|
||||||
boolean verbose,
|
boolean verbose,
|
||||||
NullValueMappingStrategyGem nullValueIterableMappingStrategy,
|
NullValueMappingStrategyGem nullValueIterableMappingStrategy,
|
||||||
NullValueMappingStrategyGem nullValueMapMappingStrategy
|
NullValueMappingStrategyGem nullValueMapMappingStrategy,
|
||||||
|
boolean disableLifecycleOverloadDeduplicateSelector
|
||||||
) {
|
) {
|
||||||
//CHECKSTYLE:ON
|
//CHECKSTYLE:ON
|
||||||
this.suppressGeneratorTimestamp = suppressGeneratorTimestamp;
|
this.suppressGeneratorTimestamp = suppressGeneratorTimestamp;
|
||||||
@ -50,6 +52,7 @@ public class Options {
|
|||||||
this.verbose = verbose;
|
this.verbose = verbose;
|
||||||
this.nullValueIterableMappingStrategy = nullValueIterableMappingStrategy;
|
this.nullValueIterableMappingStrategy = nullValueIterableMappingStrategy;
|
||||||
this.nullValueMapMappingStrategy = nullValueMapMappingStrategy;
|
this.nullValueMapMappingStrategy = nullValueMapMappingStrategy;
|
||||||
|
this.disableLifecycleOverloadDeduplicateSelector = disableLifecycleOverloadDeduplicateSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuppressGeneratorTimestamp() {
|
public boolean isSuppressGeneratorTimestamp() {
|
||||||
@ -95,4 +98,8 @@ public class Options {
|
|||||||
public NullValueMappingStrategyGem getNullValueMapMappingStrategy() {
|
public NullValueMappingStrategyGem getNullValueMapMappingStrategy() {
|
||||||
return nullValueMapMappingStrategy;
|
return nullValueMapMappingStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDisableLifecycleOverloadDeduplicateSelector() {
|
||||||
|
return disableLifecycleOverloadDeduplicateSelector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ public class MappingResolverImpl implements MappingResolver {
|
|||||||
|
|
||||||
this.conversions = new Conversions( typeFactory );
|
this.conversions = new Conversions( typeFactory );
|
||||||
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
||||||
this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, messager );
|
this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, messager, null );
|
||||||
|
|
||||||
this.verboseLogging = verboseLogging;
|
this.verboseLogging = verboseLogging;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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._3849;
|
||||||
|
|
||||||
|
public class Child extends Parent {
|
||||||
|
public Child() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* 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._3849;
|
||||||
|
|
||||||
|
public class ChildDto extends ParentDto {
|
||||||
|
public ChildDto(String value) {
|
||||||
|
super( value );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
super.setValue( value );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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._3849;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.BeforeMapping;
|
||||||
|
import org.mapstruct.Context;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface DeduplicateBySourceMapper {
|
||||||
|
|
||||||
|
DeduplicateBySourceMapper INSTANCE = Mappers.getMapper( DeduplicateBySourceMapper.class );
|
||||||
|
List<String> INVOKED_METHODS = new ArrayList<>();
|
||||||
|
|
||||||
|
ParentDto mapParent(Parent source, @Context MappingContext context);
|
||||||
|
|
||||||
|
ParentDto mapChild(Child source, @Context MappingContext context);
|
||||||
|
|
||||||
|
class MappingContext {
|
||||||
|
@BeforeMapping
|
||||||
|
void deduplicateBySourceForBefore(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingParentSourceInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
void deduplicateBySourceForBefore(Child sourceChild, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingChildSourceInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void deduplicateBySource(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingParentSourceInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void deduplicateBySource(Child sourceChild, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingChildSourceInOtherClass" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
default void deduplicateBySourceForBefore(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingParentSource" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
default void deduplicateBySourceForBefore(Child sourceChild, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingChildSource" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default void deduplicateBySource(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingParentSource" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default void deduplicateBySource(Child sourceChild, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingChildSource" );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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._3849;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.BeforeMapping;
|
||||||
|
import org.mapstruct.Context;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface DeduplicateByTargetMapper {
|
||||||
|
|
||||||
|
DeduplicateByTargetMapper INSTANCE = Mappers.getMapper( DeduplicateByTargetMapper.class );
|
||||||
|
List<String> INVOKED_METHODS = new ArrayList<>();
|
||||||
|
|
||||||
|
ParentDto mapParent(Parent source, @Context MappingContext context);
|
||||||
|
|
||||||
|
ChildDto mapChild(Parent source, @Context MappingContext context);
|
||||||
|
|
||||||
|
class MappingContext {
|
||||||
|
@BeforeMapping
|
||||||
|
void deduplicateByTargetForBefore(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingParentTargetInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
void deduplicateByTargetForBefore(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingChildTargetInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void deduplicateByTarget(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingParentTargetInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void deduplicateByTarget(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingChildTargetInOtherClass" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
default void deduplicateByTargetForBefore(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingParentTarget" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
default void deduplicateByTargetForBefore(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingChildTarget" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default void deduplicateByTarget(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingParentTarget" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default void deduplicateByTarget(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingChildTarget" );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* 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._3849;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.BeforeMapping;
|
||||||
|
import org.mapstruct.Context;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface DeduplicateForCompileArgsMapper {
|
||||||
|
|
||||||
|
DeduplicateForCompileArgsMapper INSTANCE = Mappers.getMapper( DeduplicateForCompileArgsMapper.class );
|
||||||
|
List<String> INVOKED_METHODS = new ArrayList<>();
|
||||||
|
|
||||||
|
ParentDto mapParent(Parent source, @Context MappingContext context);
|
||||||
|
|
||||||
|
ChildDto mapChild(Parent source, @Context MappingContext context);
|
||||||
|
|
||||||
|
class MappingContext {
|
||||||
|
@BeforeMapping
|
||||||
|
void deduplicateByTargetForBefore(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingParentTargetInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
void deduplicateByTargetForBefore(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingChildTargetInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void deduplicateByTarget(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingParentTargetInOtherClass" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
void deduplicateByTarget(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingChildTargetInOtherClass" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
default void deduplicateByTargetForBefore(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingParentTarget" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
default void deduplicateByTargetForBefore(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingChildTarget" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default void deduplicateByTarget(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingParentTarget" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default void deduplicateByTarget(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingChildTarget" );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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._3849;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.mapstruct.AfterMapping;
|
||||||
|
import org.mapstruct.BeforeMapping;
|
||||||
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.MappingTarget;
|
||||||
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
|
@Mapper
|
||||||
|
public interface DeduplicateGenericMapper {
|
||||||
|
|
||||||
|
DeduplicateGenericMapper INSTANCE = Mappers.getMapper( DeduplicateGenericMapper.class );
|
||||||
|
List<String> INVOKED_METHODS = new ArrayList<>();
|
||||||
|
|
||||||
|
ParentDto mapParent(Parent source);
|
||||||
|
|
||||||
|
ChildDto mapChild(Parent source);
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
default <T extends ParentDto> void deduplicateBefore(Parent source, @MappingTarget T target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingParentGeneric" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeMapping
|
||||||
|
default void deduplicateBefore(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "beforeMappingChild" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default <T> void deduplicate(Parent source, @MappingTarget T target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingGeneric" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default void deduplicate(Parent source, @MappingTarget ParentDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingParent" );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterMapping
|
||||||
|
default void deduplicate(Parent source, @MappingTarget ChildDto target) {
|
||||||
|
INVOKED_METHODS.add( "afterMappingChild" );
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* 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._3849;
|
||||||
|
|
||||||
|
import org.mapstruct.ap.testutil.IssueKey;
|
||||||
|
import org.mapstruct.ap.testutil.ProcessorTest;
|
||||||
|
import org.mapstruct.ap.testutil.WithClasses;
|
||||||
|
import org.mapstruct.ap.testutil.compilation.annotation.ProcessorOption;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
@IssueKey("3849")
|
||||||
|
@WithClasses({
|
||||||
|
Parent.class,
|
||||||
|
ParentDto.class,
|
||||||
|
Child.class,
|
||||||
|
ChildDto.class
|
||||||
|
})
|
||||||
|
public class Issue3849Test {
|
||||||
|
|
||||||
|
@ProcessorOption(name = "mapstruct.disableLifecycleOverloadDeduplicateSelector", value = "true")
|
||||||
|
@ProcessorTest()
|
||||||
|
@WithClasses(DeduplicateForCompileArgsMapper.class)
|
||||||
|
void lifecycleMappingOverloadSelectorDisableCompileArgs() {
|
||||||
|
Child child = new Child();
|
||||||
|
Parent parent = new Parent();
|
||||||
|
|
||||||
|
DeduplicateForCompileArgsMapper.MappingContext mappingContext =
|
||||||
|
new DeduplicateForCompileArgsMapper.MappingContext();
|
||||||
|
ParentDto parentDto = DeduplicateForCompileArgsMapper.INSTANCE.mapParent( parent, mappingContext );
|
||||||
|
assertThat( DeduplicateForCompileArgsMapper.INVOKED_METHODS )
|
||||||
|
.containsExactly(
|
||||||
|
"beforeMappingParentTargetInOtherClass",
|
||||||
|
"beforeMappingParentTarget",
|
||||||
|
"afterMappingParentTargetInOtherClass",
|
||||||
|
"afterMappingParentTarget"
|
||||||
|
);
|
||||||
|
|
||||||
|
DeduplicateForCompileArgsMapper.INVOKED_METHODS.clear();
|
||||||
|
|
||||||
|
ParentDto childDto = DeduplicateForCompileArgsMapper.INSTANCE.mapChild( child, mappingContext );
|
||||||
|
|
||||||
|
assertThat( DeduplicateForCompileArgsMapper.INVOKED_METHODS )
|
||||||
|
.containsExactly(
|
||||||
|
"beforeMappingChildTargetInOtherClass",
|
||||||
|
"beforeMappingChildTargetInOtherClass",
|
||||||
|
"beforeMappingChildTarget",
|
||||||
|
"beforeMappingChildTarget",
|
||||||
|
"afterMappingChildTargetInOtherClass",
|
||||||
|
"afterMappingChildTargetInOtherClass",
|
||||||
|
"afterMappingChildTarget",
|
||||||
|
"afterMappingChildTarget"
|
||||||
|
);
|
||||||
|
|
||||||
|
DeduplicateForCompileArgsMapper.INVOKED_METHODS.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProcessorTest()
|
||||||
|
@WithClasses( DeduplicateByTargetMapper.class )
|
||||||
|
void lifecycleMappingOverloadByTarget() {
|
||||||
|
Child child = new Child();
|
||||||
|
Parent parent = new Parent();
|
||||||
|
|
||||||
|
DeduplicateByTargetMapper.MappingContext mappingContext = new DeduplicateByTargetMapper.MappingContext();
|
||||||
|
ParentDto parentDto = DeduplicateByTargetMapper.INSTANCE.mapParent( parent, mappingContext );
|
||||||
|
assertThat( DeduplicateByTargetMapper.INVOKED_METHODS )
|
||||||
|
.containsExactly(
|
||||||
|
"beforeMappingParentTargetInOtherClass",
|
||||||
|
"beforeMappingParentTarget",
|
||||||
|
"afterMappingParentTargetInOtherClass",
|
||||||
|
"afterMappingParentTarget"
|
||||||
|
);
|
||||||
|
|
||||||
|
DeduplicateByTargetMapper.INVOKED_METHODS.clear();
|
||||||
|
|
||||||
|
ParentDto childDto = DeduplicateByTargetMapper.INSTANCE.mapChild( child, mappingContext );
|
||||||
|
|
||||||
|
assertThat( DeduplicateByTargetMapper.INVOKED_METHODS )
|
||||||
|
.containsExactly(
|
||||||
|
"beforeMappingChildTargetInOtherClass",
|
||||||
|
"beforeMappingChildTarget",
|
||||||
|
"afterMappingChildTargetInOtherClass",
|
||||||
|
"afterMappingChildTarget"
|
||||||
|
);
|
||||||
|
|
||||||
|
DeduplicateByTargetMapper.INVOKED_METHODS.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
@WithClasses( DeduplicateBySourceMapper.class )
|
||||||
|
void lifecycleMappingOverloadBySource() {
|
||||||
|
Child child = new Child();
|
||||||
|
Parent parent = new Parent();
|
||||||
|
|
||||||
|
DeduplicateBySourceMapper.MappingContext mappingContext = new DeduplicateBySourceMapper.MappingContext();
|
||||||
|
ParentDto parentDto = DeduplicateBySourceMapper.INSTANCE.mapParent( parent, mappingContext );
|
||||||
|
assertThat( DeduplicateBySourceMapper.INVOKED_METHODS )
|
||||||
|
.containsExactly(
|
||||||
|
"beforeMappingParentSourceInOtherClass",
|
||||||
|
"beforeMappingParentSource",
|
||||||
|
"afterMappingParentSourceInOtherClass",
|
||||||
|
"afterMappingParentSource"
|
||||||
|
);
|
||||||
|
|
||||||
|
DeduplicateBySourceMapper.INVOKED_METHODS.clear();
|
||||||
|
|
||||||
|
ParentDto childDto = DeduplicateBySourceMapper.INSTANCE.mapChild( child, mappingContext );
|
||||||
|
|
||||||
|
assertThat( DeduplicateBySourceMapper.INVOKED_METHODS )
|
||||||
|
.containsExactly(
|
||||||
|
"beforeMappingChildSourceInOtherClass",
|
||||||
|
"beforeMappingChildSource",
|
||||||
|
"afterMappingChildSourceInOtherClass",
|
||||||
|
"afterMappingChildSource"
|
||||||
|
);
|
||||||
|
|
||||||
|
DeduplicateBySourceMapper.INVOKED_METHODS.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProcessorTest
|
||||||
|
@WithClasses(DeduplicateGenericMapper.class)
|
||||||
|
void lifecycleMappingOverloadForGeneric() {
|
||||||
|
Child child = new Child();
|
||||||
|
Parent parent = new Parent();
|
||||||
|
|
||||||
|
ParentDto parentDto = DeduplicateGenericMapper.INSTANCE.mapParent( parent );
|
||||||
|
assertThat( DeduplicateGenericMapper.INVOKED_METHODS )
|
||||||
|
.containsExactly(
|
||||||
|
"beforeMappingParentGeneric",
|
||||||
|
"afterMappingParent"
|
||||||
|
);
|
||||||
|
|
||||||
|
DeduplicateGenericMapper.INVOKED_METHODS.clear();
|
||||||
|
|
||||||
|
ParentDto childDto = DeduplicateGenericMapper.INSTANCE.mapChild( child );
|
||||||
|
|
||||||
|
assertThat( DeduplicateGenericMapper.INVOKED_METHODS )
|
||||||
|
.containsExactly(
|
||||||
|
"beforeMappingChild",
|
||||||
|
"afterMappingChild"
|
||||||
|
);
|
||||||
|
|
||||||
|
DeduplicateGenericMapper.INVOKED_METHODS.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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._3849;
|
||||||
|
|
||||||
|
public class Parent {
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public Parent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
@ -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._3849;
|
||||||
|
|
||||||
|
public class ParentDto {
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public ParentDto(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user