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:
Oliver Gierke
2011-06-07 15:49:26 -07:00
parent 3453806569
commit 351e37e4eb
4 changed files with 96 additions and 10 deletions

View File

@@ -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;

View File

@@ -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];
}
/*

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}