broke out pkg private classes from TypeDescriptor to improve manageability and testability

This commit is contained in:
Keith Donald 2011-06-03 03:09:05 +00:00
parent 07f985ac91
commit a1b7af5c9c
8 changed files with 593 additions and 475 deletions

View File

@ -0,0 +1,106 @@
/*
* Copyright 2002-2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Map;
abstract class AbstractDescriptor {
private final Class<?> type;
public AbstractDescriptor(Class<?> type) {
this.type = type;
}
public Class<?> getType() {
return type;
}
public TypeDescriptor getElementType() {
if (isCollection()) {
Class<?> elementType = wildcard(getCollectionElementClass());
return new TypeDescriptor(nested(elementType, 0));
} else if (isArray()) {
Class<?> elementType = getType().getComponentType();
return new TypeDescriptor(nested(elementType, 0));
} else {
return TypeDescriptor.NULL;
}
}
public TypeDescriptor getMapKeyType() {
if (isMap()) {
Class<?> keyType = wildcard(getMapKeyClass());
return new TypeDescriptor(nested(keyType, 0));
} else {
return TypeDescriptor.NULL;
}
}
public TypeDescriptor getMapValueType() {
if (isMap()) {
Class<?> valueType = wildcard(getMapValueClass());
return new TypeDescriptor(nested(valueType, 1));
} else {
return TypeDescriptor.NULL;
}
}
public abstract Annotation[] getAnnotations();
public AbstractDescriptor nested() {
if (isCollection()) {
return nested(wildcard(getCollectionElementClass()), 0);
} else if (isArray()) {
return nested(getType().getComponentType(), 0);
} else if (isMap()) {
return nested(wildcard(getMapValueClass()), 1);
} else {
throw new IllegalStateException("Not a collection, array, or map: cannot resolve nested value types");
}
}
// subclassing hooks
protected abstract Class<?> getCollectionElementClass();
protected abstract Class<?> getMapKeyClass();
protected abstract Class<?> getMapValueClass();
protected abstract AbstractDescriptor nested(Class<?> type, int typeIndex);
// internal helpers
private boolean isCollection() {
return Collection.class.isAssignableFrom(getType());
}
private boolean isArray() {
return getType().isArray();
}
private boolean isMap() {
return Map.class.isAssignableFrom(getType());
}
private Class<?> wildcard(Class<?> type) {
return type != null ? type : Object.class;
}
}

View File

@ -0,0 +1,152 @@
/*
* Copyright 2002-2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
class BeanPropertyDescriptor extends AbstractDescriptor {
private final Class<?> beanClass;
private final PropertyDescriptor property;
private final MethodParameter methodParameter;
private final Annotation[] annotations;
public BeanPropertyDescriptor(Class<?> beanClass, PropertyDescriptor property) {
super(property.getPropertyType());
this.beanClass = beanClass;
this.property = property;
this.methodParameter = resolveMethodParameter();
this.annotations = resolveAnnotations();
}
@Override
public Annotation[] getAnnotations() {
return annotations;
}
@Override
protected Class<?> getCollectionElementClass() {
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
}
@Override
protected Class<?> getMapKeyClass() {
return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter);
}
@Override
protected Class<?> getMapValueClass() {
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
MethodParameter methodParameter = new MethodParameter(this.methodParameter);
methodParameter.increaseNestingLevel();
methodParameter.setTypeIndexForCurrentLevel(typeIndex);
return new BeanPropertyDescriptor(type, beanClass, property, methodParameter, annotations);
}
// internal
private MethodParameter resolveMethodParameter() {
MethodParameter parameter = parameterForPropertyMethod();
// needed to resolve generic property types that parameterized by sub-classes e.g. T getFoo();
GenericTypeResolver.resolveParameterType(parameter, beanClass);
return parameter;
}
private MethodParameter parameterForPropertyMethod() {
if (property.getReadMethod() != null) {
return new MethodParameter(property.getReadMethod(), -1);
} else if (property.getWriteMethod() != null) {
return new MethodParameter(property.getWriteMethod(), 0);
} else {
throw new IllegalArgumentException("Property is neither readable or writeable");
}
}
private Annotation[] resolveAnnotations() {
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
Method readMethod = this.property.getReadMethod();
if (readMethod != null) {
for (Annotation ann : readMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Method writeMethod = this.property.getWriteMethod();
if (writeMethod != null) {
for (Annotation ann : writeMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Field field = getField();
if (field != null) {
for (Annotation ann : field.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
return annMap.values().toArray(new Annotation[annMap.size()]);
}
private Field getField() {
String name = this.property.getName();
if (!StringUtils.hasLength(name)) {
return null;
}
Class<?> declaringClass = declaringClass();
Field field = ReflectionUtils.findField(declaringClass, name);
if (field == null) {
// Same lenient fallback checking as in CachedIntrospectionResults...
field = ReflectionUtils.findField(declaringClass, name.substring(0, 1).toLowerCase() + name.substring(1));
if (field == null) {
field = ReflectionUtils.findField(declaringClass, name.substring(0, 1).toUpperCase() + name.substring(1));
}
}
return field;
}
private Class<?> declaringClass() {
if (this.property.getReadMethod() != null) {
return this.property.getReadMethod().getDeclaringClass();
} else {
return this.property.getWriteMethod().getDeclaringClass();
}
}
private BeanPropertyDescriptor(Class<?> type, Class<?> beanClass, java.beans.PropertyDescriptor propertyDescriptor, MethodParameter methodParameter, Annotation[] annotations) {
super(type);
this.beanClass = beanClass;
this.property = propertyDescriptor;
this.methodParameter = methodParameter;
this.annotations = annotations;
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2002-2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert;
import java.lang.annotation.Annotation;
class ClassDescriptor extends AbstractDescriptor {
ClassDescriptor(Class<?> type) {
super(type);
}
@Override
public Annotation[] getAnnotations() {
return TypeDescriptor.EMPTY_ANNOTATION_ARRAY;
}
@Override
protected Class<?> getCollectionElementClass() {
return Object.class;
}
@Override
protected Class<?> getMapKeyClass() {
return Object.class;
}
@Override
protected Class<?> getMapValueClass() {
return Object.class;
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
return new ClassDescriptor(type);
}
}

View File

@ -0,0 +1,116 @@
/*
* Copyright 2002-2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
class CommonElement {
private final Class<?> type;
private final Object value;
public CommonElement(Class<?> type, Object value) {
this.type = type;
this.value = value;
}
public Class<?> getType() {
return type;
}
public Object getValue() {
return value;
}
public TypeDescriptor toTypeDescriptor() {
if (type == null) {
return TypeDescriptor.NULL;
} else if (value instanceof Collection<?>) {
Collection<?> collection = (Collection<?>) value;
return new TypeDescriptor(type, typeDescriptor(collection));
}
else if (value instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) value;
return new TypeDescriptor(type, typeDescriptor(map.keySet()), typeDescriptor(map.values()));
}
else {
return TypeDescriptor.valueOf(type);
}
}
public static TypeDescriptor typeDescriptor(Collection<?> collection) {
return findCommonElement(collection).toTypeDescriptor();
}
// internal helpers
private static CommonElement findCommonElement(Collection<?> values) {
Class<?> commonType = null;
Object candidate = null;
for (Object value : values) {
if (value != null) {
if (candidate == null) {
commonType = value.getClass();
candidate = value;
} else {
commonType = commonType(commonType, value.getClass());
if (commonType == Object.class) {
return new CommonElement(Object.class, null);
}
}
}
}
return new CommonElement(commonType, candidate);
}
private static Class<?> commonType(Class<?> commonType, Class<?> valueClass) {
Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(commonType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
if (currentClass.isAssignableFrom(valueClass)) {
return currentClass;
}
Class<?> superClass = currentClass.getSuperclass();
if (superClass != null && superClass != Object.class) {
classQueue.addFirst(currentClass.getSuperclass());
}
for (Class<?> interfaceType : currentClass.getInterfaces()) {
addInterfaceHierarchy(interfaceType, interfaces);
}
}
for (Class<?> interfaceType : interfaces) {
if (interfaceType.isAssignableFrom(valueClass)) {
return interfaceType;
}
}
return Object.class;
}
private static void addInterfaceHierarchy(Class<?> interfaceType, Set<Class<?>> interfaces) {
interfaces.add(interfaceType);
for (Class<?> inheritedInterface : interfaceType.getInterfaces()) {
addInterfaceHierarchy(inheritedInterface, interfaces);
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2002-2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import org.springframework.core.GenericCollectionTypeResolver;
class FieldDescriptor extends AbstractDescriptor {
private final Field field;
private final int nestingLevel;
public FieldDescriptor(Field field) {
this(field.getType(), field, 1, 0);
}
@Override
public Annotation[] getAnnotations() {
return TypeDescriptor.nullSafeAnnotations(field.getAnnotations());
}
@Override
protected Class<?> getCollectionElementClass() {
return GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel);
}
@Override
protected Class<?> getMapKeyClass() {
return GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel);
}
@Override
protected Class<?> getMapValueClass() {
return GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
return new FieldDescriptor(type, this.field, this.nestingLevel + 1, typeIndex);
}
// internal
private FieldDescriptor(Class<?> type, Field field, int nestingLevel, int typeIndex) {
super(type);
this.field = field;
this.nestingLevel = nestingLevel;
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2002-2011 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert;
import java.lang.annotation.Annotation;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
class ParameterDescriptor extends AbstractDescriptor {
private final MethodParameter methodParameter;
public ParameterDescriptor(MethodParameter methodParameter) {
super(methodParameter.getParameterType());
if (methodParameter.getNestingLevel() != 1) {
throw new IllegalArgumentException("The MethodParameter argument must have its nestingLevel set to 1");
}
this.methodParameter = methodParameter;
}
@Override
public Annotation[] getAnnotations() {
if (methodParameter.getParameterIndex() == -1) {
return TypeDescriptor.nullSafeAnnotations(methodParameter.getMethodAnnotations());
}
else {
return TypeDescriptor.nullSafeAnnotations(methodParameter.getParameterAnnotations());
}
}
@Override
protected Class<?> getCollectionElementClass() {
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
}
@Override
protected Class<?> getMapKeyClass() {
return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter);
}
@Override
protected Class<?> getMapValueClass() {
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
MethodParameter methodParameter = new MethodParameter(this.methodParameter);
methodParameter.increaseNestingLevel();
methodParameter.setTypeIndexForCurrentLevel(typeIndex);
return new ParameterDescriptor(type, methodParameter);
}
// internal
private ParameterDescriptor(Class<?> type, MethodParameter methodParameter) {
super(type);
this.methodParameter = methodParameter;
}
}

View File

@ -19,22 +19,13 @@ package org.springframework.core.convert;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Context about a type to convert from or to.
@ -50,7 +41,7 @@ public class TypeDescriptor {
private static final Map<Class<?>, TypeDescriptor> typeDescriptorCache = new HashMap<Class<?>, TypeDescriptor>();
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
static {
typeDescriptorCache.put(boolean.class, new TypeDescriptor(boolean.class));
@ -177,11 +168,11 @@ public class TypeDescriptor {
return NULL;
}
if (object instanceof Collection<?>) {
return new TypeDescriptor(object.getClass(), findCommonElement((Collection<?>) object));
return new TypeDescriptor(object.getClass(), CommonElement.typeDescriptor((Collection<?>) object));
}
else if (object instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) object;
return new TypeDescriptor(map.getClass(), findCommonElement(map.keySet()), findCommonElement(map.values()));
return new TypeDescriptor(map.getClass(), CommonElement.typeDescriptor(map.keySet()), CommonElement.typeDescriptor(map.values()));
}
else {
return valueOf(object.getClass());
@ -438,13 +429,9 @@ public class TypeDescriptor {
}
}
// internal constructors
// package private
private TypeDescriptor(Class<?> type) {
this(new ClassDescriptor(type));
}
private TypeDescriptor(AbstractDescriptor descriptor) {
TypeDescriptor(AbstractDescriptor descriptor) {
this.type = descriptor.getType();
this.elementType = descriptor.getElementType();
this.mapKeyType = descriptor.getMapKeyType();
@ -452,26 +439,28 @@ public class TypeDescriptor {
this.annotations = descriptor.getAnnotations();
}
TypeDescriptor(Class<?> collectionType, TypeDescriptor elementType) {
this(collectionType, elementType, TypeDescriptor.NULL, TypeDescriptor.NULL);
}
TypeDescriptor(Class<?> mapType, TypeDescriptor keyType, TypeDescriptor valueType) {
this(mapType, TypeDescriptor.NULL, keyType, valueType);
}
static Annotation[] nullSafeAnnotations(Annotation[] annotations) {
return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY;
}
// internal constructors
private TypeDescriptor(Class<?> type) {
this(new ClassDescriptor(type));
}
private TypeDescriptor() {
this(null, TypeDescriptor.NULL, TypeDescriptor.NULL, TypeDescriptor.NULL);
}
private TypeDescriptor(Class<?> collectionType, TypeDescriptor elementType) {
this(collectionType, elementType, TypeDescriptor.NULL, TypeDescriptor.NULL);
}
private TypeDescriptor(Class<?> mapType, TypeDescriptor keyType, TypeDescriptor valueType) {
this(mapType, TypeDescriptor.NULL, keyType, valueType);
}
private TypeDescriptor(Class<?> collectionType, CommonElement commonElement) {
this(collectionType, fromCommonElement(commonElement), TypeDescriptor.NULL, TypeDescriptor.NULL);
}
private TypeDescriptor(Class<?> mapType, CommonElement commonKey, CommonElement commonValue) {
this(mapType, TypeDescriptor.NULL, fromCommonElement(commonKey), fromCommonElement(commonValue));
}
private TypeDescriptor(Class<?> type, TypeDescriptor elementType, TypeDescriptor mapKeyType, TypeDescriptor mapValueType) {
this.type = type;
this.elementType = elementType;
@ -480,86 +469,8 @@ public class TypeDescriptor {
this.annotations = EMPTY_ANNOTATION_ARRAY;
}
private static Annotation[] nullSafeAnnotations(Annotation[] annotations) {
return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY;
}
// internal helpers
// forObject-related internal helpers
private static CommonElement findCommonElement(Collection<?> values) {
Class<?> commonType = null;
Object candidate = null;
for (Object value : values) {
if (value != null) {
if (candidate == null) {
commonType = value.getClass();
candidate = value;
} else {
commonType = commonType(commonType, value.getClass());
if (commonType == Object.class) {
return null;
}
}
}
}
return new CommonElement(commonType, candidate);
}
private static Class<?> commonType(Class<?> commonType, Class<?> valueClass) {
Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(commonType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
if (currentClass.isAssignableFrom(valueClass)) {
return currentClass;
}
Class<?> superClass = currentClass.getSuperclass();
if (superClass != null && superClass != Object.class) {
classQueue.addFirst(currentClass.getSuperclass());
}
for (Class<?> interfaceType : currentClass.getInterfaces()) {
addInterfaceHierarchy(interfaceType, interfaces);
}
}
for (Class<?> interfaceType : interfaces) {
if (interfaceType.isAssignableFrom(valueClass)) {
return interfaceType;
}
}
return Object.class;
}
private static void addInterfaceHierarchy(Class<?> interfaceType, Set<Class<?>> interfaces) {
interfaces.add(interfaceType);
for (Class<?> inheritedInterface : interfaceType.getInterfaces()) {
addInterfaceHierarchy(inheritedInterface, interfaces);
}
}
private static TypeDescriptor fromCommonElement(CommonElement commonElement) {
if (commonElement == null) {
return TypeDescriptor.valueOf(Object.class);
}
if (commonElement.getValue() instanceof Collection<?>) {
Collection<?> collection = (Collection<?>) commonElement.getValue();
if (collection.size() == 0) {
return TypeDescriptor.valueOf(Object.class);
}
return new TypeDescriptor(commonElement.getType(), findCommonElement(collection));
}
else if (commonElement.getValue() instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) commonElement.getValue();
if (map.size() == 0) {
return TypeDescriptor.valueOf(Object.class);
}
return new TypeDescriptor(commonElement.getType(), findCommonElement(map.keySet()), findCommonElement(map.values()));
}
else {
return TypeDescriptor.valueOf(commonElement.getType());
}
}
private static TypeDescriptor nested(AbstractDescriptor descriptor, int nestingLevel) {
for (int i = 0; i < nestingLevel; i++) {
descriptor = descriptor.nested();
@ -567,365 +478,4 @@ public class TypeDescriptor {
return new TypeDescriptor(descriptor);
}
// inner classes
private abstract static class AbstractDescriptor {
private final Class<?> type;
public AbstractDescriptor(Class<?> type) {
this.type = type;
}
public Class<?> getType() {
return type;
}
public TypeDescriptor getElementType() {
if (isCollection()) {
Class<?> elementType = wildcard(getCollectionElementClass());
return new TypeDescriptor(nested(elementType, 0));
} else if (isArray()) {
Class<?> elementType = getType().getComponentType();
return new TypeDescriptor(nested(elementType, 0));
} else {
return TypeDescriptor.NULL;
}
}
public TypeDescriptor getMapKeyType() {
if (isMap()) {
Class<?> keyType = wildcard(getMapKeyClass());
return new TypeDescriptor(nested(keyType, 0));
} else {
return TypeDescriptor.NULL;
}
}
public TypeDescriptor getMapValueType() {
if (isMap()) {
Class<?> valueType = wildcard(getMapValueClass());
return new TypeDescriptor(nested(valueType, 1));
} else {
return TypeDescriptor.NULL;
}
}
public abstract Annotation[] getAnnotations();
public AbstractDescriptor nested() {
if (isCollection()) {
return nested(wildcard(getCollectionElementClass()), 0);
} else if (isArray()) {
return nested(getType().getComponentType(), 0);
} else if (isMap()) {
return nested(wildcard(getMapValueClass()), 1);
} else {
throw new IllegalStateException("Not a collection, array, or map: cannot resolve nested value types");
}
}
// subclassing hooks
protected abstract Class<?> getCollectionElementClass();
protected abstract Class<?> getMapKeyClass();
protected abstract Class<?> getMapValueClass();
protected abstract AbstractDescriptor nested(Class<?> type, int typeIndex);
// internal helpers
private boolean isCollection() {
return Collection.class.isAssignableFrom(getType());
}
private boolean isArray() {
return getType().isArray();
}
private boolean isMap() {
return Map.class.isAssignableFrom(getType());
}
private Class<?> wildcard(Class<?> type) {
return type != null ? type : Object.class;
}
}
private static class FieldDescriptor extends AbstractDescriptor {
private final Field field;
private final int nestingLevel;
public FieldDescriptor(Field field) {
this(field.getType(), field, 1, 0);
}
@Override
public Annotation[] getAnnotations() {
return nullSafeAnnotations(field.getAnnotations());
}
@Override
protected Class<?> getCollectionElementClass() {
return GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel);
}
@Override
protected Class<?> getMapKeyClass() {
return GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel);
}
@Override
protected Class<?> getMapValueClass() {
return GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
return new FieldDescriptor(type, this.field, this.nestingLevel + 1, typeIndex);
}
// internal
private FieldDescriptor(Class<?> type, Field field, int nestingLevel, int typeIndex) {
super(type);
this.field = field;
this.nestingLevel = nestingLevel;
}
}
private static class ParameterDescriptor extends AbstractDescriptor {
private final MethodParameter methodParameter;
public ParameterDescriptor(MethodParameter methodParameter) {
super(methodParameter.getParameterType());
if (methodParameter.getNestingLevel() != 1) {
throw new IllegalArgumentException("The MethodParameter argument must have its nestingLevel set to 1");
}
this.methodParameter = methodParameter;
}
@Override
public Annotation[] getAnnotations() {
if (methodParameter.getParameterIndex() == -1) {
return nullSafeAnnotations(methodParameter.getMethodAnnotations());
}
else {
return nullSafeAnnotations(methodParameter.getParameterAnnotations());
}
}
@Override
protected Class<?> getCollectionElementClass() {
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
}
@Override
protected Class<?> getMapKeyClass() {
return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter);
}
@Override
protected Class<?> getMapValueClass() {
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
MethodParameter methodParameter = new MethodParameter(this.methodParameter);
methodParameter.increaseNestingLevel();
methodParameter.setTypeIndexForCurrentLevel(typeIndex);
return new ParameterDescriptor(type, methodParameter);
}
// internal
private ParameterDescriptor(Class<?> type, MethodParameter methodParameter) {
super(type);
this.methodParameter = methodParameter;
}
}
private static class BeanPropertyDescriptor extends AbstractDescriptor {
private final Class<?> beanClass;
private final PropertyDescriptor property;
private final MethodParameter methodParameter;
private final Annotation[] annotations;
public BeanPropertyDescriptor(Class<?> beanClass, PropertyDescriptor property) {
super(property.getPropertyType());
this.beanClass = beanClass;
this.property = property;
this.methodParameter = resolveMethodParameter();
this.annotations = resolveAnnotations();
}
@Override
public Annotation[] getAnnotations() {
return annotations;
}
@Override
protected Class<?> getCollectionElementClass() {
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
}
@Override
protected Class<?> getMapKeyClass() {
return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter);
}
@Override
protected Class<?> getMapValueClass() {
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
MethodParameter methodParameter = new MethodParameter(this.methodParameter);
methodParameter.increaseNestingLevel();
methodParameter.setTypeIndexForCurrentLevel(typeIndex);
return new BeanPropertyDescriptor(type, beanClass, property, methodParameter, annotations);
}
// internal
private MethodParameter resolveMethodParameter() {
if (property.getReadMethod() != null) {
MethodParameter parameter = new MethodParameter(property.getReadMethod(), -1);
GenericTypeResolver.resolveParameterType(parameter, beanClass);
return parameter;
} else if (property.getWriteMethod() != null) {
MethodParameter parameter = new MethodParameter(property.getWriteMethod(), 0);
GenericTypeResolver.resolveParameterType(parameter, beanClass);
return parameter;
} else {
throw new IllegalArgumentException("Property is neither readable or writeable");
}
}
private Annotation[] resolveAnnotations() {
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
Method readMethod = this.property.getReadMethod();
if (readMethod != null) {
for (Annotation ann : readMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Method writeMethod = this.property.getWriteMethod();
if (writeMethod != null) {
for (Annotation ann : writeMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Field field = getField();
if (field != null) {
for (Annotation ann : field.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
return annMap.values().toArray(new Annotation[annMap.size()]);
}
private Field getField() {
String name = this.property.getName();
if (!StringUtils.hasLength(name)) {
return null;
}
Class<?> declaringClass = declaringClass();
Field field = ReflectionUtils.findField(declaringClass, name);
if (field == null) {
// Same lenient fallback checking as in CachedIntrospectionResults...
field = ReflectionUtils.findField(declaringClass, name.substring(0, 1).toLowerCase() + name.substring(1));
if (field == null) {
field = ReflectionUtils.findField(declaringClass, name.substring(0, 1).toUpperCase() + name.substring(1));
}
}
return field;
}
private Class<?> declaringClass() {
if (this.property.getReadMethod() != null) {
return this.property.getReadMethod().getDeclaringClass();
} else {
return this.property.getWriteMethod().getDeclaringClass();
}
}
private BeanPropertyDescriptor(Class<?> type, Class<?> beanClass, java.beans.PropertyDescriptor propertyDescriptor, MethodParameter methodParameter, Annotation[] annotations) {
super(type);
this.beanClass = beanClass;
this.property = propertyDescriptor;
this.methodParameter = methodParameter;
this.annotations = annotations;
}
}
private static class ClassDescriptor extends AbstractDescriptor {
private ClassDescriptor(Class<?> type) {
super(type);
}
@Override
public Annotation[] getAnnotations() {
return EMPTY_ANNOTATION_ARRAY;
}
@Override
protected Class<?> getCollectionElementClass() {
return Object.class;
}
@Override
protected Class<?> getMapKeyClass() {
return Object.class;
}
@Override
protected Class<?> getMapValueClass() {
return Object.class;
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
return new ClassDescriptor(type);
}
}
private static class CommonElement {
private Class<?> type;
private Object value;
public CommonElement(Class<?> type, Object value) {
this.type = type;
this.value = value;
}
public Class<?> getType() {
return type;
}
public Object getValue() {
return value;
}
}
}

View File

@ -306,6 +306,7 @@ public class TypeDescriptorTests {
TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property);
assertEquals(List.class, desc.getType());
assertEquals(Integer.class, desc.getElementType());
assertNotNull(desc.getAnnotation(MethodAnnotation1.class));
}
public static class GenericClass<T> {
@ -316,7 +317,8 @@ public class TypeDescriptorTests {
public void setProperty(T t) {
}
@MethodAnnotation1
public List<T> getListProperty() {
return null;
}