Skip to content

Commit a1b7af5

Browse files
author
Keith Donald
committed
broke out pkg private classes from TypeDescriptor to improve manageability and testability
1 parent 07f985a commit a1b7af5

File tree

8 files changed

+588
-470
lines changed

8 files changed

+588
-470
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2002-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.core.convert;
17+
18+
import java.lang.annotation.Annotation;
19+
import java.util.Collection;
20+
import java.util.Map;
21+
22+
abstract class AbstractDescriptor {
23+
24+
private final Class<?> type;
25+
26+
public AbstractDescriptor(Class<?> type) {
27+
this.type = type;
28+
}
29+
30+
public Class<?> getType() {
31+
return type;
32+
}
33+
34+
public TypeDescriptor getElementType() {
35+
if (isCollection()) {
36+
Class<?> elementType = wildcard(getCollectionElementClass());
37+
return new TypeDescriptor(nested(elementType, 0));
38+
} else if (isArray()) {
39+
Class<?> elementType = getType().getComponentType();
40+
return new TypeDescriptor(nested(elementType, 0));
41+
} else {
42+
return TypeDescriptor.NULL;
43+
}
44+
}
45+
46+
public TypeDescriptor getMapKeyType() {
47+
if (isMap()) {
48+
Class<?> keyType = wildcard(getMapKeyClass());
49+
return new TypeDescriptor(nested(keyType, 0));
50+
} else {
51+
return TypeDescriptor.NULL;
52+
}
53+
}
54+
55+
public TypeDescriptor getMapValueType() {
56+
if (isMap()) {
57+
Class<?> valueType = wildcard(getMapValueClass());
58+
return new TypeDescriptor(nested(valueType, 1));
59+
} else {
60+
return TypeDescriptor.NULL;
61+
}
62+
}
63+
64+
public abstract Annotation[] getAnnotations();
65+
66+
public AbstractDescriptor nested() {
67+
if (isCollection()) {
68+
return nested(wildcard(getCollectionElementClass()), 0);
69+
} else if (isArray()) {
70+
return nested(getType().getComponentType(), 0);
71+
} else if (isMap()) {
72+
return nested(wildcard(getMapValueClass()), 1);
73+
} else {
74+
throw new IllegalStateException("Not a collection, array, or map: cannot resolve nested value types");
75+
}
76+
}
77+
78+
// subclassing hooks
79+
80+
protected abstract Class<?> getCollectionElementClass();
81+
82+
protected abstract Class<?> getMapKeyClass();
83+
84+
protected abstract Class<?> getMapValueClass();
85+
86+
protected abstract AbstractDescriptor nested(Class<?> type, int typeIndex);
87+
88+
// internal helpers
89+
90+
private boolean isCollection() {
91+
return Collection.class.isAssignableFrom(getType());
92+
}
93+
94+
private boolean isArray() {
95+
return getType().isArray();
96+
}
97+
98+
private boolean isMap() {
99+
return Map.class.isAssignableFrom(getType());
100+
}
101+
102+
private Class<?> wildcard(Class<?> type) {
103+
return type != null ? type : Object.class;
104+
}
105+
106+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright 2002-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.core.convert;
17+
18+
import java.beans.PropertyDescriptor;
19+
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.Field;
21+
import java.lang.reflect.Method;
22+
import java.util.LinkedHashMap;
23+
import java.util.Map;
24+
25+
import org.springframework.core.GenericCollectionTypeResolver;
26+
import org.springframework.core.GenericTypeResolver;
27+
import org.springframework.core.MethodParameter;
28+
import org.springframework.util.ReflectionUtils;
29+
import org.springframework.util.StringUtils;
30+
31+
class BeanPropertyDescriptor extends AbstractDescriptor {
32+
33+
private final Class<?> beanClass;
34+
35+
private final PropertyDescriptor property;
36+
37+
private final MethodParameter methodParameter;
38+
39+
private final Annotation[] annotations;
40+
41+
public BeanPropertyDescriptor(Class<?> beanClass, PropertyDescriptor property) {
42+
super(property.getPropertyType());
43+
this.beanClass = beanClass;
44+
this.property = property;
45+
this.methodParameter = resolveMethodParameter();
46+
this.annotations = resolveAnnotations();
47+
}
48+
49+
@Override
50+
public Annotation[] getAnnotations() {
51+
return annotations;
52+
}
53+
54+
@Override
55+
protected Class<?> getCollectionElementClass() {
56+
return GenericCollectionTypeResolver.getCollectionParameterType(methodParameter);
57+
}
58+
59+
@Override
60+
protected Class<?> getMapKeyClass() {
61+
return GenericCollectionTypeResolver.getMapKeyParameterType(methodParameter);
62+
}
63+
64+
@Override
65+
protected Class<?> getMapValueClass() {
66+
return GenericCollectionTypeResolver.getMapValueParameterType(methodParameter);
67+
}
68+
69+
@Override
70+
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
71+
MethodParameter methodParameter = new MethodParameter(this.methodParameter);
72+
methodParameter.increaseNestingLevel();
73+
methodParameter.setTypeIndexForCurrentLevel(typeIndex);
74+
return new BeanPropertyDescriptor(type, beanClass, property, methodParameter, annotations);
75+
}
76+
77+
// internal
78+
79+
private MethodParameter resolveMethodParameter() {
80+
MethodParameter parameter = parameterForPropertyMethod();
81+
// needed to resolve generic property types that parameterized by sub-classes e.g. T getFoo();
82+
GenericTypeResolver.resolveParameterType(parameter, beanClass);
83+
return parameter;
84+
}
85+
86+
private MethodParameter parameterForPropertyMethod() {
87+
if (property.getReadMethod() != null) {
88+
return new MethodParameter(property.getReadMethod(), -1);
89+
} else if (property.getWriteMethod() != null) {
90+
return new MethodParameter(property.getWriteMethod(), 0);
91+
} else {
92+
throw new IllegalArgumentException("Property is neither readable or writeable");
93+
}
94+
}
95+
96+
private Annotation[] resolveAnnotations() {
97+
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
98+
Method readMethod = this.property.getReadMethod();
99+
if (readMethod != null) {
100+
for (Annotation ann : readMethod.getAnnotations()) {
101+
annMap.put(ann.annotationType(), ann);
102+
}
103+
}
104+
Method writeMethod = this.property.getWriteMethod();
105+
if (writeMethod != null) {
106+
for (Annotation ann : writeMethod.getAnnotations()) {
107+
annMap.put(ann.annotationType(), ann);
108+
}
109+
}
110+
Field field = getField();
111+
if (field != null) {
112+
for (Annotation ann : field.getAnnotations()) {
113+
annMap.put(ann.annotationType(), ann);
114+
}
115+
}
116+
return annMap.values().toArray(new Annotation[annMap.size()]);
117+
}
118+
119+
private Field getField() {
120+
String name = this.property.getName();
121+
if (!StringUtils.hasLength(name)) {
122+
return null;
123+
}
124+
Class<?> declaringClass = declaringClass();
125+
Field field = ReflectionUtils.findField(declaringClass, name);
126+
if (field == null) {
127+
// Same lenient fallback checking as in CachedIntrospectionResults...
128+
field = ReflectionUtils.findField(declaringClass, name.substring(0, 1).toLowerCase() + name.substring(1));
129+
if (field == null) {
130+
field = ReflectionUtils.findField(declaringClass, name.substring(0, 1).toUpperCase() + name.substring(1));
131+
}
132+
}
133+
return field;
134+
}
135+
136+
private Class<?> declaringClass() {
137+
if (this.property.getReadMethod() != null) {
138+
return this.property.getReadMethod().getDeclaringClass();
139+
} else {
140+
return this.property.getWriteMethod().getDeclaringClass();
141+
}
142+
}
143+
144+
private BeanPropertyDescriptor(Class<?> type, Class<?> beanClass, java.beans.PropertyDescriptor propertyDescriptor, MethodParameter methodParameter, Annotation[] annotations) {
145+
super(type);
146+
this.beanClass = beanClass;
147+
this.property = propertyDescriptor;
148+
this.methodParameter = methodParameter;
149+
this.annotations = annotations;
150+
}
151+
152+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2002-2011 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.core.convert;
17+
18+
import java.lang.annotation.Annotation;
19+
20+
class ClassDescriptor extends AbstractDescriptor {
21+
22+
ClassDescriptor(Class<?> type) {
23+
super(type);
24+
}
25+
26+
@Override
27+
public Annotation[] getAnnotations() {
28+
return TypeDescriptor.EMPTY_ANNOTATION_ARRAY;
29+
}
30+
31+
@Override
32+
protected Class<?> getCollectionElementClass() {
33+
return Object.class;
34+
}
35+
36+
@Override
37+
protected Class<?> getMapKeyClass() {
38+
return Object.class;
39+
}
40+
41+
@Override
42+
protected Class<?> getMapValueClass() {
43+
return Object.class;
44+
}
45+
46+
@Override
47+
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
48+
return new ClassDescriptor(type);
49+
}
50+
51+
}

0 commit comments

Comments
 (0)