Skip to content

DATACMNS-1026 ExtensionAwareEvaluationContextProvider now returns all overloaded methods as functions #217

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from

Conversation

schauder
Copy link
Contributor

All overloaded methods are now available in SPeL expressions.

The key of the Map used for collecting methods is now the name and the length of the argument list.

It is also actually a MultiValueMap to allow multiple overloaded methods.

Among methods with identical argument list from different sources in the same extension (extension, root object, aliases) the last one in the order in parens wins.

If there is more than one method for an application the following rules are applied:

If there is one method with exact matching types in the argument list it is used. Otherwise, an exception is thrown.

schauder added 2 commits May 17, 2017 10:06
…s all overloaded methods as functions.

All overloaded methods are now available in SPeL expressions.

The key of the Map used for collecting methods is now the name and the length of the argument list.

It is also actually a MultiValueMap to allow multiple overloaded methods.

Among methods with identical argument list from different sources in the same extension (extension, root object, aliases) the last one in the order in parens wins.

If there is more than one method for an application the following rules are applied:

If there is one method with exact matching types in the argument list it is used, otherwise an exception is thrown.
Minor improvements to Javadoc.
Copy link
Member

@odrotbohm odrotbohm left a comment

Choose a reason for hiding this comment

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

Cosmetics. Looks good beyond that.

}

Optional<Function> exactMatch = candidates.stream().filter(f -> f.supportsExact(argumentTypes)).findFirst();
if (exactMatch.isPresent()) {
Copy link
Member

Choose a reason for hiding this comment

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

return exactMatch.orElseThrow(…)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that, but then we return a Function instead of Optional<Function> and we end up sooner or later either rewrapping it in an Optional or doing null checks.

What we really need is
ifPresentOrElse but that is java9

I just removed the else to make it a little lighter on the eye.

Copy link
Member

@odrotbohm odrotbohm May 17, 2017

Choose a reason for hiding this comment

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

I see. That makes sense. Maybe inverting the check here is an option, too. if (!exactMatch.isPresent()) { throw … }.

I also wonder: we don't really detect multiple exact matches here, do we? It just barks if we don't find any exact match, right? Actually that renders the exception message wrong as it's only thrown if there's no match at all, not if there are multiple ones matching.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct: multiple exact matches are not possible at this point because we drop those when we add elements to Functions this ensures the behavior of overwriting of methods from different sources (extension, root object, and aliases).

But we only check for an exact match if we have more than one match (candidates). So we throw the exception if we have multiple candidates and can't disambiguate between them.

We could improve the message to:

There are multiple matching methods for arguments of type , but no exact match. Make sure to provide only one matching overload or one with exactly those types.

What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

But that's not what the code does is it? If we have multiple candidates and have multiple ones that potentially match exactly, we just pick the first one. Is that intended? I thought the idea was to also reject that case, wasn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There can't be multiple exact matches. We don't add them to the Map contained in functions in the first place. see the addAll methods.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, now that makes sense. Thanks!

}
}

@Data
Copy link
Member

Choose a reason for hiding this comment

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

Could this be @Value instead?

}

/**
* Checks wither this {@code Function} has the same signature as another {@code Function}.
Copy link
Member

Choose a reason for hiding this comment

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

"whether"

* @param keyFunction {@link Function} to create a key from an element of the {@link java.util.stream.Stream}
* @param valueFunction {@link Function} to create a value from an element of the {@link java.util.stream.Stream}
*/
public MultiValueMapCollector(Function<T, K> keyFunction, Function<T, V> valueFunction) {
Copy link
Member

Choose a reason for hiding this comment

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

@RequiredArgsConstructor? I guess a static factory method in StreamUtils would align nicely with the ones we have in place for unmodifiable lists and sets.

@Override
public BinaryOperator<MultiValueMap<K, V>> combiner() {

return (map1,map2) -> {
Copy link
Member

Choose a reason for hiding this comment

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

Space after comma.

* @author Jens Schauder
* @since 2.0
*/
class FunctionsMap {
Copy link
Member

Choose a reason for hiding this comment

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

What do you think of a plain Functions?

*/
public Map<String, Function> getFunctions() {
FunctionsMap getFunctions() {
Copy link
Member

Choose a reason for hiding this comment

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

Wonder if it makes sense to extract an interface here so that clients can't modify the instance anymore, i.e. return something that doesn't expose the addAll(…) methods anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is just in a private class anyway. A separate interface sounds like overkill to me in this case.

FunctionsMap functions = adapter.getFunctions();

Optional<Function> function = functions.get(name, argumentTypes);
return function.map(FunctionMethodExecutor::new);
Copy link
Member

Choose a reason for hiding this comment

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

Inline function?

.findFirst().map(Entry::getValue).map(FunctionMethodExecutor::new);
FunctionsMap functions = adapter.getFunctions();

Optional<Function> function = functions.get(name, argumentTypes);
Copy link
Member

Choose a reason for hiding this comment

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

Inline functions?

@Test // DATACMNS-1026
public void overloadedMethodsGetResolved() throws Exception {

provider = new ExtensionAwareEvaluationContextProvider(
Copy link
Member

Choose a reason for hiding this comment

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

Shall we extract that setup step? It's repeated in every test below.

Copy link
Member

@odrotbohm odrotbohm left a comment

Choose a reason for hiding this comment

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

Looks good to me, I'll take it from here.

@odrotbohm odrotbohm self-assigned this May 17, 2017
odrotbohm pushed a commit that referenced this pull request May 18, 2017
…ll overloaded methods as functions.

All overloaded methods are now available in SPeL expressions. Among methods with identical argument list from different sources in the same extension (extension, root object, aliases) the last one in the order in parens wins. If there is more than one method for an application the following rules are applied: if there is one method with exact matching types in the argument list it is used, otherwise an exception is thrown.

Original pull request: #217.
odrotbohm pushed a commit that referenced this pull request May 18, 2017
Minor improvements to Javadoc.

Original pull request: #217.
odrotbohm added a commit that referenced this pull request May 18, 2017
Minor refactorings here in there for clarity.

Original pull request: #217.
@odrotbohm odrotbohm closed this May 18, 2017
@odrotbohm odrotbohm deleted the issue/DATACMNS-1026 branch May 18, 2017 05:07
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.

2 participants