Skip to content

Conversation

@filiphr
Copy link
Member

@filiphr filiphr commented Mar 3, 2024

This commit does a backwards incompatible change in regard to how presence check methods are used when only the source parameter is needed for the presence check. e.g.

@Condition
public static boolean shouldMap(Employee employee) {
    // ...
}

is now only applied to source parameters or a property of type Employee. The method is no longer applied to all the parameters of a mapping method with a source parameter Employee. If you still want to do that then the method should be adapted

@Condition
public static boolean shouldMap(Employee employee, @TargetPropertyName String targetPropertyName) {
    // ...
}

Instead of the @TargetPropertyName it can be @SourcePropertyName.

Fixes #2610
Fixes #3459
Fixes #3270

NB: Perhaps we need to add some more tests for #3459 and #3270, I wanted to first see if this idea is worth pursuing.

An alternative that we could do about the use of the right parameter would be if we introduce something like @SourceParameter and / or @TargetProperty. This people can explicitly decide if they want to apply a conditional method only on a source parameter or only on a target property. The 2 new annotations might also be used in other places

@filiphr filiphr requested a review from thunderhook March 10, 2024 08:13
@thunderhook
Copy link
Contributor

thunderhook commented Mar 10, 2024

Sorry, I haven't had the time and mental capacity to think this through.
This is tricky, thanks for your effort in trying to tackle this issues.

Users would now have to provide the @SourcePropertyName or @TargetPropertyName just to get the condition to work (maybe like it used to).
So if the property name is unused (which it most likely will be), the IDE and other static code analysis tools will warn the user about the unused parameter. For example, we are currently improving our code quality with SonarQube, and false positives like this are a nuisance.
It also feels like a workaround.

So I would therefore prefer the second approach with the specific annotation, as you said "something like @SourceParameter and / or @TargetProperty".

Whichever way we go, users will still wonder why this is needed (e.g. "why do I have to add something extra?"). So this should be well documented in the docs and release notes.
But I also think that this backwards incompatible change needs to be done, and that this is the right time to do it.

I just played with your branch and saw that a condition written in the old way is simply not used. Is it possible to throw a compile error (or a warning)? (You have better insight into the underlying code)
This would help users migrate the code, rather than a condition that is just not picked up. This can lead to a lot of pain if the mapping code is not thoroughly tested.

@filiphr
Copy link
Member Author

filiphr commented Mar 14, 2024

Sorry, I haven't had the time and mental complexity to think this through.
This is tricky, thanks for your effort in trying to tackle this issues.

No rush, I've been working on this one for a while, it took me a bit of time to wrap my head properly around it as well. Thanks a lot for going through it.

So I would therefore prefer the second approach with the specific annotation, as you said "something like @SourceParameter and / or @TargetProperty".

That was my gut feeling as well, or perhaps have a different annotation that would be applicable for source parameters condition check.

I just played with your branch and saw that a condition written in the old way is simply not used.

I didn't like that at all as well. I'll look into an alternative for this

@filiphr
Copy link
Member Author

filiphr commented Mar 17, 2024

@thunderhook, I gave another go at this. Basically, there is a new @SourceCondition (open to better naming suggestions) that should be used for source parameter checks. Let me know what you think about this approach

@thunderhook
Copy link
Contributor

I really like this approach.

One thing I noticed while playing with the branch: Is there anything against a condition method being a @SourceCondition and a @Condition at the same time? I do not see why this should not be allowed. Maybe there are some scenarios where this is useful.

With the current implementation, the method won't be used if you add a @Condition there:
https://github.com/filiphr/mapstruct/blob/efc374163242119df2b27d83f412a0ce699b7bf9/processor/src/test/java/org/mapstruct/ap/test/conditional/basic/ConditionalMethodForSourceBeanMapper.java#L22

I haven't tried it explicitly, but I don't think it would be picked up as a presence check method either.

Otherwise, I really like it!

@filiphr
Copy link
Member Author

filiphr commented Mar 24, 2024

You are indeed right. It won't be caught due to the MethodFamilySelector

method.getMethod().isObjectFactory() == criteria.isObjectFactoryRequired()
                && method.getMethod().isLifecycleCallbackMethod() == criteria.isLifecycleCallbackRequired()
                && method.getMethod().isPresenceCheck() == criteria.isPresenceCheckRequired()
                && method.getMethod().isSourceParameterCheck() == criteria.isSourceParameterCheckRequired()

I'll make sure that it works in that case as well.

I should also add some error handlings:

  • Using @SourceCondition with @TargetPropertyName or @SourcePropertyName should lead to a compile error

An alternative to the @SourceCondition could be something like:

public enum ConditionStrategy {
    PROPERTIES,
    SOURCE_PARAMETERS,
}

then the condition would be

@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.CLASS)
public @interface Condition {

    ConditionStrategy[] appliesTo() default ConditionStrategy.PROPERTIES;

}

it can then be used

  • @Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS) - when we want to apply a condition only on source parameters
  • @Condition(appliesTo = { ConditionStrategy.PROPERTIES, ConditionStrategy.SOURCE_PARAMETERS}) - when it applied both to properties and source parameters

I am not sure which approach I like more. What do you think @thunderhook

@thunderhook
Copy link
Contributor

thunderhook commented Mar 24, 2024

I haven't fully thought this through, but I think the second approach might be more flexible when introducing conditions for other scenarios. Like collection items #1610 or TARGET_PARAMETERS. Don't know if there are other scenarios.

If there are other strategies added, then it might make sense to provide a ConditionStrategy.ALL.

Another idea might be to support both approaches. Where @SourceCondition is a meta-annotation like:

@Condition( appliesTo = ConditionStrategy.SOURCE_PARAMETERS )
public @interface SourceCondition {

}

This would allow users to write their own Condition meta annotations, like:

@Condition( appliesTo = { ConditionStrategy.SOURCE_PARAMETERS, ... } )
public @interface MyOwnEntityCondition {

}

However, this may cause problems if multiple annotations are set. Should it cause a compile error? If not, should they be combined? What has higher priority?

@SourceCondition
@Condition(appliesTo = { ConditionStrategy.PROPERTIES, ConditionStrategy.SOURCE_PARAMETERS})
Target map(Source source);

So maybe it's better to stick to one of these approaches rather than a combination of the two.

@filiphr
Copy link
Member Author

filiphr commented Mar 24, 2024

Really good points @thunderhook. It would indeed work nicely for #1610 as well. That would basically be an additional ConditionStrategy#ITERABLE_ELEMENTS

Another idea might be to support both approaches. Where @SourceCondition is a meta-annotation like:

I liked this even more. We could indeed provide @SourceParameterCondition or @SourceCondition that would be meta annotated with @Condition(appliesTo = ConditionStrategy.SOURCE_PARAMETERS). Users can define their own annotations if they want to as well.

However, this may cause problems if multiple annotations are set. Should it cause a compile error? If not, should they be combined? What has higher priority?

I don't think that this would cause problems. We would basically just merge all of the applicable appliesTo into a single set.

Thanks a lot for the feedback @thunderhook, I'll try to rework this in that direction :).

@filiphr
Copy link
Member Author

filiphr commented Apr 27, 2024

@thunderhook I finally found the time to go over what we discussed last time. Can you please have a look at this and let me know what you think

Copy link
Contributor

@thunderhook thunderhook left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@filiphr I really like it. Just some small nitpicks.

The documentation can also be improved in another branch, if you want to merge it sooner than later.

filiphr added 5 commits April 28, 2024 20:37
…f source parameter in presence check method

This commit does a backwards incompatible change in regard to how presence check methods are used when only the source parameter is needed for the presence check.
e.g.

```java
@condition
public static boolean shouldMap(Employee employee) {
    // ...
}
```

is now only applied to source parameters or a property of type `Employee`.
The method is no longer applied to all the parameters of a mapping method with a source parameter `Employee`.
If you still want to do that then the method should be adapted

```java
@condition
public static boolean shouldMap(Employee employee, @TargetPropertyName String targetPropertyName) {
    // ...
}
```

Instead of the `@TargetPropertyName` it can be `@SourcePropertyName`.
@filiphr filiphr force-pushed the 2610-source-parameter-presenc-check branch from c82d218 to 64862c3 Compare April 28, 2024 19:03
@filiphr
Copy link
Member Author

filiphr commented Apr 28, 2024

Thanks a lot for the review @thunderhook. All your comments make a lot of sense. I've added some docs, added a new test and also added the class to the <code> tag. Do you think that the doc is enough?

Copy link
Contributor

@thunderhook thunderhook left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@filiphr Yes, I think this is enough. Thanks for your hard work on this. 👍 Merge at your own will!

@filiphr filiphr merged commit 0a2a0aa into mapstruct:main Apr 29, 2024
@filiphr filiphr deleted the 2610-source-parameter-presenc-check branch April 29, 2024 06:05
@filiphr
Copy link
Member Author

filiphr commented Apr 29, 2024

Thanks for the review @thunderhook

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Method with @Condition acts strange when applying to source @Condition badly implemented Support conditional mapping for source parameters

2 participants