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