1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.beans.impl.reflection;
17
18 import java.beans.IntrospectionException;
19 import java.beans.PropertyDescriptor;
20 import java.lang.annotation.Annotation;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Type;
24 import java.util.Arrays;
25 import java.util.List;
26
27 import org.joda.beans.Bean;
28 import org.joda.beans.MetaBean;
29 import org.joda.beans.Property;
30 import org.joda.beans.PropertyReadWrite;
31 import org.joda.beans.impl.BasicMetaProperty;
32 import org.joda.beans.impl.BasicProperty;
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public final class ReflectiveMetaProperty<P> extends BasicMetaProperty<P> {
47
48
49 private volatile MetaBean metaBean;
50
51 private final Class<?> declaringType;
52
53 private final Class<P> propertyType;
54
55 private final Method readMethod;
56
57 private final Method writeMethod;
58
59
60
61
62
63
64
65
66
67 public static <P> ReflectiveMetaProperty<P> of(Class<? extends Bean> beanType, String propertyName) {
68 return new ReflectiveMetaProperty<P>(beanType, propertyName);
69 }
70
71
72
73
74
75
76
77 @SuppressWarnings("unchecked")
78 private ReflectiveMetaProperty(Class<? extends Bean> beanType, String propertyName) {
79 super(propertyName);
80 PropertyDescriptor descriptor;
81 try {
82 descriptor = new PropertyDescriptor(propertyName, beanType);
83 } catch (IntrospectionException ex) {
84 throw new NoSuchFieldError("Invalid property: " + propertyName + ": " + ex.getMessage());
85 }
86 Method readMethod = descriptor.getReadMethod();
87 Method writeMethod = descriptor.getWriteMethod();
88 if (readMethod == null && writeMethod == null) {
89 throw new NoSuchFieldError("Invalid property: " + propertyName + ": Both read and write methods are missing");
90 }
91 this.declaringType = (readMethod != null ? readMethod.getDeclaringClass() : writeMethod.getDeclaringClass());
92 this.propertyType = (Class<P>) descriptor.getPropertyType();
93 this.readMethod = readMethod;
94 this.writeMethod = writeMethod;
95 }
96
97
98
99
100
101 void setMetaBean(MetaBean metaBean) {
102 this.metaBean = metaBean;
103 }
104
105
106 @Override
107 public Property<P> createProperty(Bean bean) {
108 return BasicProperty.of(bean, this);
109 }
110
111 @Override
112 public MetaBean metaBean() {
113 return metaBean;
114 }
115
116 @Override
117 public Class<?> declaringType() {
118 return declaringType;
119 }
120
121 @Override
122 public Class<P> propertyType() {
123 return propertyType;
124 }
125
126 @Override
127 public Type propertyGenericType() {
128 if (readMethod != null) {
129 return readMethod.getGenericReturnType();
130 }
131 return writeMethod.getGenericParameterTypes()[0];
132 }
133
134 @Override
135 public PropertyReadWrite readWrite() {
136 return (readMethod == null ? PropertyReadWrite.WRITE_ONLY :
137 (writeMethod == null ? PropertyReadWrite.READ_ONLY : PropertyReadWrite.READ_WRITE));
138 }
139
140 @Override
141 public List<Annotation> annotations() {
142 if (readMethod != null) {
143 return Arrays.asList(readMethod.getDeclaredAnnotations());
144 }
145 return Arrays.asList(writeMethod.getDeclaredAnnotations());
146 }
147
148
149 @Override
150 @SuppressWarnings("unchecked")
151 public P get(Bean bean) {
152 if (readWrite().isReadable() == false) {
153 throw new UnsupportedOperationException("Property cannot be read: " + name());
154 }
155 try {
156 return (P) readMethod.invoke(bean, (Object[]) null);
157 } catch (IllegalArgumentException ex) {
158 throw new UnsupportedOperationException("Property cannot be read: " + name(), ex);
159 } catch (IllegalAccessException ex) {
160 throw new UnsupportedOperationException("Property cannot be read: " + name(), ex);
161 } catch (InvocationTargetException ex) {
162 if (ex.getCause() instanceof RuntimeException) {
163 throw (RuntimeException) ex.getCause();
164 }
165 throw new RuntimeException(ex);
166 }
167 }
168
169 @Override
170 public void set(Bean bean, Object value) {
171 if (readWrite().isWritable() == false) {
172 throw new UnsupportedOperationException("Property cannot be written: " + name());
173 }
174 try {
175 writeMethod.invoke(bean, value);
176 } catch (IllegalArgumentException ex) {
177 if (value == null && writeMethod.getParameterTypes()[0].isPrimitive()) {
178 throw new NullPointerException("Property cannot be written: " + name() + ": Cannot store null in primitive");
179 }
180 if (propertyType.isInstance(value) == false) {
181 throw new ClassCastException("Property cannot be written: " + name() + ": Invalid type: " + value.getClass().getName());
182 }
183 throw new UnsupportedOperationException("Property cannot be written: " + name(), ex);
184 } catch (IllegalAccessException ex) {
185 throw new UnsupportedOperationException("Property cannot be written: " + name(), ex);
186 } catch (InvocationTargetException ex) {
187 if (ex.getCause() instanceof RuntimeException) {
188 throw (RuntimeException) ex.getCause();
189 }
190 throw new RuntimeException(ex);
191 }
192 }
193
194 }