Skip to content

@Condition treated as ambiguous mapping for methods returning Boolean/boolean #3565

Description

@hmzuleta

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:

  1. Call the @Mapper's map, to convert the MySourceDTO to MyTargetDTO.
  2. Since the getter returns an Optional<Boolean>, it needs to call the @Mapper's mapFromOptional, for the sourceDto.isCondition property.

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions