broke out pkg private classes from TypeDescriptor to improve manageability and testability
This commit is contained in:
parent
07f985ac91
commit
a1b7af5c9c
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -19,22 +19,13 @@ package org.springframework.core.convert;
|
||||||
import java.beans.PropertyDescriptor;
|
import java.beans.PropertyDescriptor;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.LinkedHashSet;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.Map;
|
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.core.MethodParameter;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Context about a type to convert from or to.
|
* 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 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 {
|
static {
|
||||||
typeDescriptorCache.put(boolean.class, new TypeDescriptor(boolean.class));
|
typeDescriptorCache.put(boolean.class, new TypeDescriptor(boolean.class));
|
||||||
|
|
@ -177,11 +168,11 @@ public class TypeDescriptor {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (object instanceof Collection<?>) {
|
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<?, ?>) {
|
else if (object instanceof Map<?, ?>) {
|
||||||
Map<?, ?> map = (Map<?, ?>) object;
|
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 {
|
else {
|
||||||
return valueOf(object.getClass());
|
return valueOf(object.getClass());
|
||||||
|
|
@ -438,13 +429,9 @@ public class TypeDescriptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// internal constructors
|
// package private
|
||||||
|
|
||||||
private TypeDescriptor(Class<?> type) {
|
TypeDescriptor(AbstractDescriptor descriptor) {
|
||||||
this(new ClassDescriptor(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypeDescriptor(AbstractDescriptor descriptor) {
|
|
||||||
this.type = descriptor.getType();
|
this.type = descriptor.getType();
|
||||||
this.elementType = descriptor.getElementType();
|
this.elementType = descriptor.getElementType();
|
||||||
this.mapKeyType = descriptor.getMapKeyType();
|
this.mapKeyType = descriptor.getMapKeyType();
|
||||||
|
|
@ -452,26 +439,28 @@ public class TypeDescriptor {
|
||||||
this.annotations = descriptor.getAnnotations();
|
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() {
|
private TypeDescriptor() {
|
||||||
this(null, TypeDescriptor.NULL, TypeDescriptor.NULL, TypeDescriptor.NULL);
|
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) {
|
private TypeDescriptor(Class<?> type, TypeDescriptor elementType, TypeDescriptor mapKeyType, TypeDescriptor mapValueType) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.elementType = elementType;
|
this.elementType = elementType;
|
||||||
|
|
@ -480,86 +469,8 @@ public class TypeDescriptor {
|
||||||
this.annotations = EMPTY_ANNOTATION_ARRAY;
|
this.annotations = EMPTY_ANNOTATION_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Annotation[] nullSafeAnnotations(Annotation[] annotations) {
|
// internal helpers
|
||||||
return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
private static TypeDescriptor nested(AbstractDescriptor descriptor, int nestingLevel) {
|
||||||
for (int i = 0; i < nestingLevel; i++) {
|
for (int i = 0; i < nestingLevel; i++) {
|
||||||
descriptor = descriptor.nested();
|
descriptor = descriptor.nested();
|
||||||
|
|
@ -567,365 +478,4 @@ public class TypeDescriptor {
|
||||||
return new TypeDescriptor(descriptor);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -306,6 +306,7 @@ public class TypeDescriptorTests {
|
||||||
TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property);
|
TypeDescriptor desc = new TypeDescriptor(genericBean.getClass(), property);
|
||||||
assertEquals(List.class, desc.getType());
|
assertEquals(List.class, desc.getType());
|
||||||
assertEquals(Integer.class, desc.getElementType());
|
assertEquals(Integer.class, desc.getElementType());
|
||||||
|
assertNotNull(desc.getAnnotation(MethodAnnotation1.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GenericClass<T> {
|
public static class GenericClass<T> {
|
||||||
|
|
@ -316,7 +317,8 @@ public class TypeDescriptorTests {
|
||||||
|
|
||||||
public void setProperty(T t) {
|
public void setProperty(T t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MethodAnnotation1
|
||||||
public List<T> getListProperty() {
|
public List<T> getListProperty() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue