DATACMNS-45 - Improved property handling in TypeDiscoverer and Property.
TypeDiscoverer now looks up property type using a PropertyDescriptor if no field with the given name can be found. Property now handles _-prefixed properties as well.
This commit is contained in:
@@ -15,8 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.repository.query.parser;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -32,8 +33,9 @@ import org.springframework.util.StringUtils;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class Property {
|
||||
|
||||
private static String ERROR_TEMPLATE = "No property %s found for type %s";
|
||||
|
||||
private static final Pattern SPLITTER = Pattern.compile("(?:_?(_*?[^_]+))");
|
||||
private static final String ERROR_TEMPLATE = "No property %s found for type %s";
|
||||
|
||||
private final String name;
|
||||
private final TypeInformation<?> type;
|
||||
@@ -228,8 +230,15 @@ public class Property {
|
||||
}
|
||||
|
||||
private static Property from(String source, TypeInformation<?> type) {
|
||||
|
||||
List<String> iteratorSource = new ArrayList<String>();
|
||||
Matcher matcher = SPLITTER.matcher("_" + source);
|
||||
|
||||
while (matcher.find()) {
|
||||
iteratorSource.add(matcher.group(1));
|
||||
}
|
||||
|
||||
Iterator<String> parts = Arrays.asList(source.split("_")).iterator();
|
||||
Iterator<String> parts = iteratorSource.iterator();
|
||||
|
||||
Property result = null;
|
||||
Property current = null;
|
||||
|
||||
@@ -17,9 +17,11 @@ package org.springframework.data.util;
|
||||
|
||||
import static org.springframework.util.ObjectUtils.*;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
@@ -28,6 +30,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -159,15 +162,51 @@ class TypeDiscoverer<S> implements TypeInformation<S> {
|
||||
return info == null ? null : info.getProperty(fieldname.substring(separatorIndex + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link TypeInformation} for the given atomic field. Will inspect fields first and return the type of a
|
||||
* field if available. Otherwise it will fall back to a {@link PropertyDescriptor}.
|
||||
*
|
||||
* @see #getGenericType(PropertyDescriptor)
|
||||
* @param fieldname
|
||||
* @return
|
||||
*/
|
||||
private TypeInformation<?> getPropertyInformation(String fieldname) {
|
||||
|
||||
Class<?> type = getType();
|
||||
Field field = ReflectionUtils.findField(type, fieldname);
|
||||
|
||||
Field field = ReflectionUtils.findField(getType(), fieldname);
|
||||
if (field != null) {
|
||||
return createInfo(field.getGenericType());
|
||||
}
|
||||
|
||||
PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(type, fieldname);
|
||||
|
||||
if (field == null) {
|
||||
return descriptor == null ? null : createInfo(getGenericType(descriptor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the generic type for the given {@link PropertyDescriptor}. Will inspect its read method
|
||||
* followed by the first parameter of the write method.
|
||||
*
|
||||
* @param descriptor must not be {@literal null}
|
||||
* @return
|
||||
*/
|
||||
private static Type getGenericType(PropertyDescriptor descriptor) {
|
||||
|
||||
Method method = descriptor.getReadMethod();
|
||||
|
||||
if (method != null) {
|
||||
return method.getGenericReturnType();
|
||||
}
|
||||
|
||||
method = descriptor.getWriteMethod();
|
||||
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return createInfo(field.getGenericType());
|
||||
|
||||
Type[] parameterTypes = method.getGenericParameterTypes();
|
||||
return parameterTypes.length == 0 ? null : parameterTypes[0];
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -71,7 +71,7 @@ public class PropertyUnitTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void prfersExplicitPaths() throws Exception {
|
||||
public void prefersExplicitPaths() throws Exception {
|
||||
|
||||
Property reference = Property.from("user_name", Sample.class);
|
||||
assertThat(reference.getName(), is("user"));
|
||||
@@ -130,9 +130,24 @@ public class PropertyUnitTests {
|
||||
Property from = Property.from("barUserName", Sample.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATACMNS-45
|
||||
*/
|
||||
@Test
|
||||
public void handlesEmptyUnderscoresCorrectly() {
|
||||
|
||||
Property property = Property.from("_foo", Sample2.class);
|
||||
assertThat(property.getName(), is("_foo"));
|
||||
assertThat(property.getType(), is(typeCompatibleWith(Foo.class)));
|
||||
|
||||
property = Property.from("_foo__email", Sample2.class);
|
||||
assertThat(property.toDotPath(), is("_foo._email"));
|
||||
}
|
||||
|
||||
private class Foo {
|
||||
|
||||
String userName;
|
||||
String _email;
|
||||
}
|
||||
|
||||
private class Bar {
|
||||
@@ -159,5 +174,6 @@ public class PropertyUnitTests {
|
||||
|
||||
private String userNameWhatever;
|
||||
private FooBar user;
|
||||
private Foo _foo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springframework.data.util;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Calendar;
|
||||
@@ -134,6 +134,20 @@ public class ClassTypeInformationUnitTests {
|
||||
assertFalse(first.getProperty("wrapped").equals(second.getProperty("wrapped")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handlesPropertyFieldMismatchCorrectly() {
|
||||
|
||||
TypeInformation<PropertyGetter> from = ClassTypeInformation.from(PropertyGetter.class);
|
||||
|
||||
TypeInformation<?> property = from.getProperty("_name");
|
||||
assertThat(property, is(notNullValue()));
|
||||
assertThat(property.getType(), is(typeCompatibleWith(String.class)));
|
||||
|
||||
property = from.getProperty("name");
|
||||
assertThat(property, is(notNullValue()));
|
||||
assertThat(property.getType(), is(typeCompatibleWith(byte[].class)));
|
||||
}
|
||||
|
||||
static class StringMapContainer extends MapContainer<String> {
|
||||
|
||||
}
|
||||
@@ -199,4 +213,12 @@ public class ClassTypeInformationUnitTests {
|
||||
static class AnotherConcreteWrapper extends GenericWrapper<Long> {
|
||||
|
||||
}
|
||||
|
||||
static class PropertyGetter {
|
||||
private String _name;
|
||||
|
||||
public byte[] getName() {
|
||||
return _name.getBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user