Use most specific getter when generating metadata

This commit makes sure to use the most specific getter if more than
one candidate exists.

Closes gh-24002
This commit is contained in:
Stephane Nicoll
2020-11-05 15:24:14 +01:00
parent 006d4bc36d
commit 59ea7c11f6
5 changed files with 118 additions and 26 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -91,10 +91,12 @@ class PropertyDescriptorResolver {
TypeElementMembers members) {
// First check if we have regular java bean properties there
Map<String, PropertyDescriptor<?>> candidates = new LinkedHashMap<>();
members.getPublicGetters().forEach((name, getter) -> {
members.getPublicGetters().forEach((name, getters) -> {
VariableElement field = members.getFields().get(name);
ExecutableElement getter = findMatchingGetter(members, getters, field);
TypeMirror propertyType = getter.getReturnType();
register(candidates, new JavaBeanPropertyDescriptor(type, factoryMethod, getter, name, propertyType,
members.getFields().get(name), members.getPublicSetter(name, propertyType)));
register(candidates, new JavaBeanPropertyDescriptor(type, factoryMethod, getter, name, propertyType, field,
members.getPublicSetter(name, propertyType)));
});
// Then check for Lombok ones
members.getFields().forEach((name, field) -> {
@@ -107,6 +109,14 @@ class PropertyDescriptorResolver {
return candidates.values().stream();
}
private ExecutableElement findMatchingGetter(TypeElementMembers members, List<ExecutableElement> candidates,
VariableElement field) {
if (candidates.size() > 1 && field != null) {
return members.getMatchingGetter(candidates, field.asType());
}
return candidates.get(0);
}
private void register(Map<String, PropertyDescriptor<?>> candidates, PropertyDescriptor<?> descriptor) {
if (!candidates.containsKey(descriptor.getName()) && isCandidate(descriptor)) {
candidates.put(descriptor.getName(), descriptor);

View File

@@ -22,6 +22,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
@@ -48,7 +49,7 @@ class TypeElementMembers {
private final Map<String, VariableElement> fields = new LinkedHashMap<>();
private final Map<String, ExecutableElement> publicGetters = new LinkedHashMap<>();
private final Map<String, List<ExecutableElement>> publicGetters = new LinkedHashMap<>();
private final Map<String, List<ExecutableElement>> publicSetters = new LinkedHashMap<>();
@@ -74,8 +75,14 @@ class TypeElementMembers {
private void processMethod(ExecutableElement method) {
if (isPublic(method)) {
String name = method.getSimpleName().toString();
if (isGetter(method) && !this.publicGetters.containsKey(getAccessorName(name))) {
this.publicGetters.put(getAccessorName(name), method);
if (isGetter(method)) {
String propertyName = getAccessorName(name);
List<ExecutableElement> matchingGetters = this.publicGetters.computeIfAbsent(propertyName,
(k) -> new ArrayList<>());
TypeMirror returnType = method.getReturnType();
if (getMatchingGetter(matchingGetters, returnType) == null) {
matchingGetters.add(method);
}
}
else if (isSetter(method)) {
String propertyName = getAccessorName(name);
@@ -95,10 +102,19 @@ class TypeElementMembers {
&& !modifiers.contains(Modifier.STATIC);
}
ExecutableElement getMatchingGetter(List<ExecutableElement> candidates, TypeMirror type) {
return getMatchingAccessor(candidates, type, ExecutableElement::getReturnType);
}
private ExecutableElement getMatchingSetter(List<ExecutableElement> candidates, TypeMirror type) {
return getMatchingAccessor(candidates, type, (candidate) -> candidate.getParameters().get(0).asType());
}
private ExecutableElement getMatchingAccessor(List<ExecutableElement> candidates, TypeMirror type,
Function<ExecutableElement, TypeMirror> typeExtractor) {
for (ExecutableElement candidate : candidates) {
TypeMirror paramType = candidate.getParameters().get(0).asType();
if (this.env.getTypeUtils().isSameType(paramType, type)) {
TypeMirror candidateType = typeExtractor.apply(candidate);
if (this.env.getTypeUtils().isSameType(candidateType, type)) {
return candidate;
}
}
@@ -151,35 +167,30 @@ class TypeElementMembers {
return Collections.unmodifiableMap(this.fields);
}
Map<String, ExecutableElement> getPublicGetters() {
Map<String, List<ExecutableElement>> getPublicGetters() {
return Collections.unmodifiableMap(this.publicGetters);
}
ExecutableElement getPublicGetter(String name, TypeMirror type) {
ExecutableElement candidate = this.publicGetters.get(name);
if (candidate != null) {
TypeMirror returnType = candidate.getReturnType();
if (this.env.getTypeUtils().isSameType(returnType, type)) {
return candidate;
}
TypeMirror alternative = this.env.getTypeUtils().getWrapperOrPrimitiveFor(type);
if (alternative != null && this.env.getTypeUtils().isSameType(returnType, alternative)) {
return candidate;
}
}
return null;
List<ExecutableElement> candidates = this.publicGetters.get(name);
return getPublicAccessor(candidates, type, (specificType) -> getMatchingGetter(candidates, specificType));
}
ExecutableElement getPublicSetter(String name, TypeMirror type) {
List<ExecutableElement> candidates = this.publicSetters.get(name);
return getPublicAccessor(candidates, type, (specificType) -> getMatchingSetter(candidates, specificType));
}
private ExecutableElement getPublicAccessor(List<ExecutableElement> candidates, TypeMirror type,
Function<TypeMirror, ExecutableElement> matchingAccessorExtractor) {
if (candidates != null) {
ExecutableElement matching = getMatchingSetter(candidates, type);
ExecutableElement matching = matchingAccessorExtractor.apply(type);
if (matching != null) {
return matching;
}
TypeMirror alternative = this.env.getTypeUtils().getWrapperOrPrimitiveFor(type);
if (alternative != null) {
return getMatchingSetter(candidates, alternative);
return matchingAccessorExtractor.apply(alternative);
}
}
return null;