Fallback to StringUtils.uncapitalize(…) when looking up property paths.
Naming restrictions for property paths used in query method names requite capitalization of the first letter regardless whether the property name uses a second-letter uppercase form (zIndex -> ZIndex, qCode -> QCode). In such cases, Introspector.decapitalize(…) shortcuts to non-decapitalization as it checks the second letter casing. This leads to the case that the property name cannot be resolved, assuming proper property naming (getzIndex(), zIndex()). Falling back to StringUtils.uncapitalize() allows catching such properties. Closes: #1851 Original Pull Request: #2940
This commit is contained in:
committed by
Christoph Strobl
parent
aac6b9dd7d
commit
e04f0b0deb
@@ -50,7 +50,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
|
||||
private static final Pattern SPLITTER = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", DELIMITERS));
|
||||
private static final Pattern SPLITTER_FOR_QUOTED = Pattern.compile("(?:[%s]?([%s]*?[^%s]+))".replaceAll("%s", "\\."));
|
||||
private static final Pattern NESTED_PROPERTY_PATTERN = Pattern.compile("\\p{Lu}[\\p{Ll}\\p{Nd}]*$");
|
||||
private static final Map<Key, PropertyPath> cache = new ConcurrentReferenceHashMap<>();
|
||||
private static final Map<Property, PropertyPath> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
private final TypeInformation<?> owningType;
|
||||
private final String name;
|
||||
@@ -83,19 +83,31 @@ public class PropertyPath implements Streamable<PropertyPath> {
|
||||
Assert.notNull(owningType, "Owning type must not be null");
|
||||
Assert.notNull(base, "Previously found properties must not be null");
|
||||
|
||||
String propertyName = Introspector.decapitalize(name);
|
||||
TypeInformation<?> propertyType = owningType.getProperty(propertyName);
|
||||
String decapitalized = Introspector.decapitalize(name);
|
||||
Property property = lookupProperty(owningType, decapitalized);
|
||||
|
||||
if (propertyType == null) {
|
||||
throw new PropertyReferenceException(propertyName, owningType, base);
|
||||
if (property == null) {
|
||||
property = lookupProperty(owningType, StringUtils.uncapitalize(name));
|
||||
}
|
||||
|
||||
if (property == null) {
|
||||
throw new PropertyReferenceException(decapitalized, owningType, base);
|
||||
}
|
||||
|
||||
this.owningType = owningType;
|
||||
this.typeInformation = propertyType;
|
||||
this.isCollection = propertyType.isCollectionLike();
|
||||
this.name = propertyName;
|
||||
this.actualTypeInformation = propertyType.getActualType() == null ? propertyType
|
||||
: propertyType.getRequiredActualType();
|
||||
this.name = property.path();
|
||||
this.typeInformation = property.type();
|
||||
this.isCollection = this.typeInformation.isCollectionLike();
|
||||
this.actualTypeInformation = this.typeInformation.getActualType() == null ? this.typeInformation
|
||||
: this.typeInformation.getRequiredActualType();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Property lookupProperty(TypeInformation<?> owningType, String name) {
|
||||
|
||||
TypeInformation<?> propertyType = owningType.getProperty(name);
|
||||
|
||||
return propertyType != null ? new Property(propertyType, name) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +363,7 @@ public class PropertyPath implements Streamable<PropertyPath> {
|
||||
Assert.hasText(source, "Source must not be null or empty");
|
||||
Assert.notNull(type, "TypeInformation must not be null or empty");
|
||||
|
||||
return cache.computeIfAbsent(new Key(type, source), it -> {
|
||||
return cache.computeIfAbsent(new Property(type, source), it -> {
|
||||
|
||||
List<String> iteratorSource = new ArrayList<>();
|
||||
|
||||
@@ -487,5 +499,6 @@ public class PropertyPath implements Streamable<PropertyPath> {
|
||||
return String.format("%s.%s", owningType.getType().getSimpleName(), toDotPath());
|
||||
}
|
||||
|
||||
private record Key(TypeInformation<?> type, String path) {};
|
||||
private record Property(TypeInformation<?> type, String path) {
|
||||
};
|
||||
}
|
||||
|
||||
@@ -370,8 +370,8 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
|
||||
var field = ReflectionUtils.findField(rawType, fieldname);
|
||||
|
||||
return field != null ? Optional.of(TypeInformation.of(ResolvableType.forField(field, resolvableType)))
|
||||
: Optional.ofNullable(BeanUtils.getPropertyDescriptor(rawType, fieldname)).map(it -> from(it, rawType))
|
||||
.map(TypeInformation::of);
|
||||
: Optional.ofNullable(BeanUtils.getPropertyDescriptor(rawType, fieldname))
|
||||
.filter(it -> it.getName().equals(fieldname)).map(it -> from(it, rawType)).map(TypeInformation::of);
|
||||
}
|
||||
|
||||
private ResolvableType from(PropertyDescriptor descriptor, Class<?> rawType) {
|
||||
|
||||
Reference in New Issue
Block a user