1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.joda.beans;
17
18 import java.beans.IntrospectionException;
19 import java.beans.PropertyDescriptor;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22
23 /***
24 * A property is a field on a bean that can typically be called via get/set.
25 *
26 * @author Stephen Colebourne
27 */
28 public class DescriptorMetaProperty<B, T> implements MetaProperty<B, T> {
29
30 /*** The property descriptor. */
31 private final PropertyDescriptor descriptor;
32 /*** The type of the bean. */
33 private final Class<B> beanType;
34 /*** The read method. */
35 private final Method readMethod;
36 /*** The write method. */
37 private final Method writeMethod;
38
39 /***
40 * Constructor.
41 *
42 * @param beanType the bean type
43 * @param propertyName the property name
44 */
45 public DescriptorMetaProperty(Class<B> beanType, String propertyName) {
46 super();
47 try {
48 descriptor = new PropertyDescriptor(propertyName, beanType);
49 } catch (IntrospectionException ex) {
50 throw new NoSuchFieldError("Invalid property: " + propertyName);
51 }
52 Method readMethod = descriptor.getReadMethod();
53 Method writeMethod = descriptor.getWriteMethod();
54 if (readMethod == null & writeMethod == null) {
55 throw new NoSuchFieldError("Invalid property: " + propertyName);
56 }
57 this.readMethod = readMethod;
58 this.writeMethod = writeMethod;
59 this.beanType = beanType;
60 }
61
62
63 /***
64 * Creates a property that binds this meta property to a specific bean.
65 *
66 * @param bean the bean to create the bound property for
67 * @return the bound property
68 */
69 public Property<B, T> createProperty(B bean) {
70 return new SimpleProperty<B, T>(bean, this);
71 }
72
73 /***
74 * Gets the property name.
75 * The JavaBean style methods getFoo() and setFoo() will lead
76 * to a property name of 'foo' and so on.
77 *
78 * @return the name of the property
79 */
80 public String getName() {
81 return descriptor.getName();
82 }
83
84 /***
85 * Get the type of the property represented as a Class.
86 *
87 * @return the type of the property
88 */
89 public Class<T> getType() {
90
91 return (Class<T>) descriptor.getPropertyType();
92 }
93
94 /***
95 * Get the type of the bean represented as a Class.
96 *
97 * @return the type of the bean
98 */
99 public Class<B> getBeanType() {
100 return beanType;
101 }
102
103 /***
104 * Gets whether the property is read-write, read-only or write-only.
105 *
106 * @return the property read-write type
107 */
108 public ReadWriteProperty getReadWrite() {
109 return (readMethod == null ? ReadWriteProperty.WRITE_ONLY :
110 (writeMethod == null ? ReadWriteProperty.READ_ONLY : ReadWriteProperty.READ_WRITE));
111 }
112
113
114 /***
115 * Gets the value of the bound property for the provided bean.
116 * <p>
117 * This is the equivalent to calling <code>getFoo()</code> on the bean itself.
118 * However some implementations of this interface may not require an actual get method.
119 *
120 * @param bean the bean to query, not null
121 * @return the value of the property on the bound bean
122 * @throws UnsupportedOperationException if the property is write-only
123 */
124 @SuppressWarnings("unchecked")
125 public T get(B bean) {
126 if (getReadWrite().isReadable() == false) {
127 throw new UnsupportedOperationException("Property '" + getName() + "' cannot be read");
128 }
129 try {
130 return (T) readMethod.invoke(bean, (Object[]) null);
131 } catch (IllegalArgumentException ex) {
132 throw new UnsupportedOperationException("Property '" + getName() + "' cannot be read");
133 } catch (IllegalAccessException ex) {
134 throw new UnsupportedOperationException("Property '" + getName() + "' cannot be read");
135 } catch (InvocationTargetException ex) {
136 throw new UnsupportedOperationException("Property '" + getName() + "' cannot be read");
137 }
138 }
139
140 /***
141 * Sets the value of the bound property on the provided bean.
142 * <p>
143 * This is the equivalent to calling <code>setFoo()</code> on the bean itself.
144 * However some implementations of this interface may not require an actual set method.
145 *
146 * @param bean the bean to update, not null
147 * @param value the value to set into the property on the bound bean
148 * @throws UnsupportedOperationException if the property is read-only
149 */
150 public void set(B bean, T value) {
151 if (getReadWrite().isWritable() == false) {
152 throw new UnsupportedOperationException("Property '" + getName() + "' cannot be written");
153 }
154 try {
155 writeMethod.invoke(bean, value);
156 } catch (IllegalArgumentException ex) {
157 throw new UnsupportedOperationException("Property '" + getName() + "' cannot be read");
158 } catch (IllegalAccessException ex) {
159 throw new UnsupportedOperationException("Property '" + getName() + "' cannot be read");
160 } catch (InvocationTargetException ex) {
161 throw new UnsupportedOperationException("Property '" + getName() + "' cannot be read");
162 }
163 }
164
165
166 /***
167 * Returns a debugging string.
168 *
169 * @return a debugging string
170 */
171 @Override
172 public String toString() {
173 return "MetaProperty:" + getName();
174 }
175
176 }