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.NULL_VALUE_ITERABLE_MAPPING_STRATEGY,
|
||||
MappingProcessor.NULL_VALUE_MAP_MAPPING_STRATEGY,
|
||||
MappingProcessor.DISABLE_LIFECYCLE_OVERLOAD_DEDUPLICATE_SELECTOR,
|
||||
})
|
||||
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 NULL_VALUE_ITERABLE_MAPPING_STRATEGY = "mapstruct.nullValueIterableMappingStrategy";
|
||||
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 String additionalSupportedOptionsError;
|
||||
@ -174,6 +177,8 @@ public class MappingProcessor extends AbstractProcessor {
|
||||
String nullValueIterableMappingStrategy = processingEnv.getOptions()
|
||||
.get( NULL_VALUE_ITERABLE_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(
|
||||
Boolean.parseBoolean( processingEnv.getOptions().get( SUPPRESS_GENERATOR_TIMESTAMP ) ),
|
||||
@ -189,7 +194,8 @@ public class MappingProcessor extends AbstractProcessor {
|
||||
NullValueMappingStrategyGem.valueOf( nullValueIterableMappingStrategy.toUpperCase( Locale.ROOT ) ) :
|
||||
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) {
|
||||
|
||||
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(
|
||||
callbackMethods,
|
||||
|
@ -5,12 +5,9 @@
|
||||
*/
|
||||
package org.mapstruct.ap.internal.model;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.element.ElementKind;
|
||||
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.util.Message;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
@ -126,7 +125,7 @@ public class ObjectFactoryMethodResolver {
|
||||
MappingBuilderContext ctx) {
|
||||
|
||||
MethodSelectors selectors =
|
||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager() );
|
||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getMessager(), null );
|
||||
|
||||
return selectors.getMatchingMethods(
|
||||
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||
|
@ -119,7 +119,8 @@ public final class PresenceCheckMethodResolver {
|
||||
MethodSelectors selectors = new MethodSelectors(
|
||||
ctx.getTypeUtils(),
|
||||
ctx.getElementUtils(),
|
||||
ctx.getMessager()
|
||||
ctx.getMessager(),
|
||||
null
|
||||
);
|
||||
|
||||
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 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.FormattingMessager;
|
||||
import org.mapstruct.ap.internal.util.TypeUtils;
|
||||
@ -24,20 +25,27 @@ public class MethodSelectors {
|
||||
private final List<MethodSelector> selectors;
|
||||
|
||||
public MethodSelectors(TypeUtils typeUtils, ElementUtils elementUtils,
|
||||
FormattingMessager messager) {
|
||||
selectors = Arrays.asList(
|
||||
FormattingMessager messager, Options options) {
|
||||
List<MethodSelector> selectorList = new ArrayList<>( Arrays.asList(
|
||||
new MethodFamilySelector(),
|
||||
new TypeSelector( messager ),
|
||||
new QualifierSelector( typeUtils, elementUtils ),
|
||||
new TargetTypeSelector( typeUtils ),
|
||||
new JavaxXmlElementDeclSelector( typeUtils ),
|
||||
new JakartaXmlElementDeclSelector( typeUtils ),
|
||||
new InheritanceSelector(),
|
||||
new InheritanceSelector()
|
||||
) );
|
||||
if ( options != null && !options.isDisableLifecycleOverloadDeduplicateSelector() ) {
|
||||
selectorList.add( new LifecycleOverloadDeduplicateSelector() );
|
||||
}
|
||||
|
||||
selectorList.addAll( Arrays.asList(
|
||||
new CreateOrUpdateSelector(),
|
||||
new SourceRhsSelector(),
|
||||
new FactoryParameterSelector(),
|
||||
new MostSpecificResultTypeSelector()
|
||||
);
|
||||
) );
|
||||
this.selectors = selectorList;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,6 +26,7 @@ public class Options {
|
||||
private final boolean verbose;
|
||||
private final NullValueMappingStrategyGem nullValueIterableMappingStrategy;
|
||||
private final NullValueMappingStrategyGem nullValueMapMappingStrategy;
|
||||
private final boolean disableLifecycleOverloadDeduplicateSelector;
|
||||
|
||||
//CHECKSTYLE:OFF
|
||||
public Options(boolean suppressGeneratorTimestamp, boolean suppressGeneratorVersionComment,
|
||||
@ -36,7 +37,8 @@ public class Options {
|
||||
boolean disableBuilders,
|
||||
boolean verbose,
|
||||
NullValueMappingStrategyGem nullValueIterableMappingStrategy,
|
||||
NullValueMappingStrategyGem nullValueMapMappingStrategy
|
||||
NullValueMappingStrategyGem nullValueMapMappingStrategy,
|
||||
boolean disableLifecycleOverloadDeduplicateSelector
|
||||
) {
|
||||
//CHECKSTYLE:ON
|
||||
this.suppressGeneratorTimestamp = suppressGeneratorTimestamp;
|
||||
@ -50,6 +52,7 @@ public class Options {
|
||||
this.verbose = verbose;
|
||||
this.nullValueIterableMappingStrategy = nullValueIterableMappingStrategy;
|
||||
this.nullValueMapMappingStrategy = nullValueMapMappingStrategy;
|
||||
this.disableLifecycleOverloadDeduplicateSelector = disableLifecycleOverloadDeduplicateSelector;
|
||||
}
|
||||
|
||||
public boolean isSuppressGeneratorTimestamp() {
|
||||
@ -95,4 +98,8 @@ public class Options {
|
||||
public NullValueMappingStrategyGem getNullValueMapMappingStrategy() {
|
||||
return nullValueMapMappingStrategy;
|
||||
}
|
||||
|
||||
public boolean isDisableLifecycleOverloadDeduplicateSelector() {
|
||||
return disableLifecycleOverloadDeduplicateSelector;
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ public class MappingResolverImpl implements MappingResolver {
|
||||
|
||||
this.conversions = new Conversions( typeFactory );
|
||||
this.builtInMethods = new BuiltInMappingMethods( typeFactory );
|
||||
this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, messager );
|
||||
this.methodSelectors = new MethodSelectors( typeUtils, elementUtils, messager, null );
|
||||
|
||||
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