Browse Source
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@4456 50f2f4bb-b051-0410-bef5-90022cba6387pull/1/merge
6 changed files with 214 additions and 168 deletions
@ -0,0 +1,212 @@ |
|||||||
|
/* |
||||||
|
* 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 java.lang.reflect.Method; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.springframework.core.GenericTypeResolver; |
||||||
|
import org.springframework.core.MethodParameter; |
||||||
|
import org.springframework.util.ReflectionUtils; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* A description of a JavaBeans Property that allows us to avoid a dependency on java.beans.PropertyDescriptor. |
||||||
|
* java.beans is not available in a number of environments (e.g. Android, Java ME), so this is desirable. |
||||||
|
* Used to build a TypeDescriptor from a property location. |
||||||
|
* The built TypeDescriptor can then be used to convert from/to the property type. |
||||||
|
* @author Keith Donald |
||||||
|
* @see TypeDescriptor#TypeDescriptor(Property) |
||||||
|
* @see TypeDescriptor#nested(Property, int) |
||||||
|
*/ |
||||||
|
public final class Property { |
||||||
|
|
||||||
|
private final Class<?> objectType; |
||||||
|
|
||||||
|
private final Method readMethod; |
||||||
|
|
||||||
|
private final Method writeMethod; |
||||||
|
|
||||||
|
private final String name; |
||||||
|
|
||||||
|
private final MethodParameter methodParameter; |
||||||
|
|
||||||
|
private final Annotation[] annotations; |
||||||
|
|
||||||
|
public Property(Class<?> objectType, Method readMethod, Method writeMethod) { |
||||||
|
this.objectType = objectType; |
||||||
|
this.readMethod = readMethod; |
||||||
|
this.writeMethod = writeMethod; |
||||||
|
this.methodParameter = resolveMethodParameter(); |
||||||
|
this.name = resolveName(); |
||||||
|
this.annotations = resolveAnnotations(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The object declaring this property, either directly or in a superclass the object extends. |
||||||
|
*/ |
||||||
|
public Class<?> getObjectType() { |
||||||
|
return objectType; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The name of the property e.g. 'foo'. |
||||||
|
*/ |
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The property type e.g. java.lang.String. |
||||||
|
*/ |
||||||
|
public Class<?> getType() { |
||||||
|
return methodParameter.getParameterType(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The property getter method e.g. getFoo() |
||||||
|
*/ |
||||||
|
public Method getReadMethod() { |
||||||
|
return readMethod; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The property setter method e.g. setFoo(String). |
||||||
|
*/ |
||||||
|
public Method getWriteMethod() { |
||||||
|
return writeMethod; |
||||||
|
} |
||||||
|
|
||||||
|
// package private
|
||||||
|
|
||||||
|
MethodParameter getMethodParameter() { |
||||||
|
return methodParameter; |
||||||
|
} |
||||||
|
|
||||||
|
Annotation[] getAnnotations() { |
||||||
|
return annotations; |
||||||
|
} |
||||||
|
|
||||||
|
// internal helpers
|
||||||
|
|
||||||
|
private String resolveName() { |
||||||
|
if (readMethod != null) { |
||||||
|
int index = readMethod.getName().indexOf("get"); |
||||||
|
if (index != -1) { |
||||||
|
index += 3; |
||||||
|
} else { |
||||||
|
index = readMethod.getName().indexOf("is"); |
||||||
|
if (index == -1) { |
||||||
|
throw new IllegalArgumentException("Not a getter method"); |
||||||
|
} |
||||||
|
index += 2; |
||||||
|
} |
||||||
|
return StringUtils.uncapitalize(readMethod.getName().substring(index)); |
||||||
|
} else { |
||||||
|
int index = writeMethod.getName().indexOf("set") + 3; |
||||||
|
if (index == -1) { |
||||||
|
throw new IllegalArgumentException("Not a setter method"); |
||||||
|
} |
||||||
|
return StringUtils.uncapitalize(writeMethod.getName().substring(index)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private MethodParameter resolveMethodParameter() { |
||||||
|
MethodParameter read = resolveReadMethodParameter(); |
||||||
|
MethodParameter write = resolveWriteMethodParameter(); |
||||||
|
if (read == null && write == null) { |
||||||
|
throw new IllegalStateException("Property is neither readable or writeable"); |
||||||
|
} |
||||||
|
if (read != null && write != null && !read.getParameterType().equals(write.getParameterType())) { |
||||||
|
throw new IllegalStateException("Read and write parameter types are not the same"); |
||||||
|
} |
||||||
|
return read != null ? read : write; |
||||||
|
} |
||||||
|
|
||||||
|
private MethodParameter resolveReadMethodParameter() { |
||||||
|
if (getReadMethod() == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return resolveParameterType(new MethodParameter(getReadMethod(), -1)); |
||||||
|
} |
||||||
|
|
||||||
|
private MethodParameter resolveWriteMethodParameter() { |
||||||
|
if (getWriteMethod() == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return resolveParameterType(new MethodParameter(getWriteMethod(), 0)); |
||||||
|
} |
||||||
|
|
||||||
|
private MethodParameter resolveParameterType(MethodParameter parameter) { |
||||||
|
// needed to resolve generic property types that parameterized by sub-classes e.g. T getFoo();
|
||||||
|
GenericTypeResolver.resolveParameterType(parameter, getObjectType()); |
||||||
|
return parameter; |
||||||
|
} |
||||||
|
|
||||||
|
private Annotation[] resolveAnnotations() { |
||||||
|
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>(); |
||||||
|
Method readMethod = getReadMethod(); |
||||||
|
if (readMethod != null) { |
||||||
|
for (Annotation ann : readMethod.getAnnotations()) { |
||||||
|
annMap.put(ann.annotationType(), ann); |
||||||
|
} |
||||||
|
} |
||||||
|
Method writeMethod = 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 = 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 (getReadMethod() != null) { |
||||||
|
return getReadMethod().getDeclaringClass(); |
||||||
|
} else { |
||||||
|
return getWriteMethod().getDeclaringClass(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue