refactor: change TypeTransformerRegistry to use a Map instead of Arraylist - #5929
refactor: change TypeTransformerRegistry to use a Map instead of Arraylist#5929mokhairymahmoud wants to merge 3 commits into
Conversation
| @@ -31,7 +29,7 @@ | |||
|
|
|||
| public class TypeTransformerRegistryImpl implements TypeTransformerRegistry { | |||
| private final Map<String, Class<?>> aliases = new HashMap<>(); | |||
There was a problem hiding this comment.
it's been a while that I see this aliases unused field, since you're here, could you please delete it?
| @@ -0,0 +1,43 @@ | |||
| /* | |||
| * Copyright (c) 2022 - 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) | |||
| @Override | ||
| public void register(TypeTransformer<?, ?> transformer) { | ||
| this.transformers.add(transformer); | ||
| transformers.computeIfAbsent(transformer.getInputType(), key -> new HashMap<>()) |
There was a problem hiding this comment.
I think it could worth to add a warning log in case of transformer override
There was a problem hiding this comment.
Already did, thanks.
| private Optional<TypeTransformer<?, ?>> findTransformer(Object input, Class<?> outputType) { | ||
| var inputTypes = transformers.entrySet().stream() | ||
| .filter(entry -> entry.getKey().isInstance(input)) | ||
| .filter(entry -> entry.getValue().containsKey(outputType)) | ||
| .map(Map.Entry::getKey) | ||
| .toList(); | ||
|
|
||
| var mostSpecificInputTypes = inputTypes.stream() | ||
| .filter(candidate -> inputTypes.stream() | ||
| .noneMatch(other -> !candidate.equals(other) && candidate.isAssignableFrom(other))) | ||
| .toList(); | ||
|
|
||
| if (mostSpecificInputTypes.size() > 1) { | ||
| throw new EdcException(format("Ambiguous transformers registered for %s -> %s", input.getClass(), outputType)); | ||
| } | ||
|
|
||
| return mostSpecificInputTypes.stream() | ||
| .findFirst() | ||
| .map(inputType -> transformers.get(inputType).get(outputType)); | ||
| } |
There was a problem hiding this comment.
this logic is not wrong but I think it could be written in a more readable way - and slightly more performant, by avoiding multiple iterations over inputTypes, using streams reduce function:
private Optional<TypeTransformer<?, ?>> findTransformer(Object input, Class<?> outputType) {
return transformers.entrySet().stream()
.filter(entry -> entry.getKey().isInstance(input))
.filter(entry -> entry.getValue().containsKey(outputType))
.map(Map.Entry::getKey)
.map(Optional::of)
.reduce(Optional.empty(), (current, candidate) -> {
if (current.isEmpty()) {
return candidate;
}
if (candidate.get().isAssignableFrom(current.get())) {
return current;
}
if (current.get().isAssignableFrom(candidate.get())) {
return candidate;
}
throw new EdcException(format("Ambiguous transformers registered for %s -> %s", input.getClass(), outputType));
})
.map(transformers::get)
.map(it -> it.get(outputType));
}so the "most specific" logic emerges in the reduce function itself:
- set the first item as current
- is the candidate a supertype of current? ignore it
- is the candidate a subtype of current? set it as current
- neither of the two? it means that it's a type that's on a different hierarchy: throw exception
the reduce function could also be extracted and called findMostSpecificInputType
There was a problem hiding this comment.
Your implementation is cleaner so I applied it. thanks
d1f66f2 to
705fd59
Compare
What this PR changes/adds
This PR is to change the current TypeTransformerRegistry from an ArrayList to be a Map. This guarantees the existence of only one unique transformer type in the registry and make the transformer selection more deterministic. The PR is the implementation of this DR
Why it does that
The current way of tackling the typeTransformerRegistry is based on ArrayList with a selection mechanism based on findAny() which makes it indeterministic to select a specific transformer specially if there are two of the same type.
Linked Issue(s)
Related to #5921
Please be sure to take a look at the contributing guidelines and our etiquette for pull requests.