-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Expected behavior
If I have a method annotated with @Condition that serves as a presence check for Java Optionals, and I have a separate mapping method that unboxes an Optional, the Condition method should not be treated as a valid mapper method.
Actual behavior
Conditional method, which needs to return boolean, clashes with mapping methods that return Boolean and whose input argument type is the same, e.g.
@Condition
boolean conditionalMethod(TypeA typeAObject) { }
clashes with
@Mapper
interface MyMapper {
boolean mappingMethod(TypeA typeAObject);
}
Specifically, this is true when attempting to use a Conditional method to check the if the Boolean wrapped in an Optional isPresent, because it will return a boolean, while the mapping method that unwraps the Boolean also does this.
Steps to reproduce the problem
MapStruct 1.5.3, Spring Boot 3.1.6
import org.mapstruct.*;
import java.util.Optional;
@Mapper
interface MyMapper {
default <T> T mapFromOptional(Optional<T> value) { return value.orElse((Object) null); }
@Condition
default boolean isOptionalPresent(Optional value) { return value.isPresent(); }
MyTargetDTO map(MySourceDTO sourceDto);
}
class MySourceDTO {
private Boolean isCondition;
public Optional<Boolean> getIsCondition() { return Optional.ofNullable(this.isCondition); }
}
class MyTargetDTO {
private String isCondition;
public Optional<String> getIsCondition() { return Optional.ofNullable(this.isCondition); }
public void setIsCondition(String isCondition) { this.isCondition = isCondition; }
}
Result:
Ambiguous 2step methods found, mapping Optional sourceDto to String. Found conversionY( methodX ( parameter ) ): conversionY: Boolean -->String, method(s)X: T mapFromOptional(Optional value); conversionY: boolean-->String, method(s)X: T mapFromOptional(Optional value);
It seems to me like MapStruct is taking the @Condition method, and using it as a valid mapping method, instead of using it only for Conditional Mapping presence checking.
If I understand correctly, the mapping steps would be:
- Call the @Mapper's map, to convert the
MySourceDTOtoMyTargetDTO. - Since the getter returns an
Optional<Boolean>, it needs to call the@Mapper'smapFromOptional, for thesourceDto.isConditionproperty.
This will resolve the mapping as default Boolean mapFromOptional(Optional<Boolean> value), which has a similar signature as default boolean isOptionalPresent(Optional value), so that must look ambiguous to MapStruct. However, the latter method is NOT a mapping method, merely a condition.
Methods annotated with @Condition should NOT be considered for mappings.
MapStruct Version
MapStruct 1.5.3