1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.beanutils;
18
19
20 import java.beans.PropertyDescriptor;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.Map;
24
25
26 /**
27 * <p>Implementation of <code>DynaClass</code> for DynaBeans that wrap
28 * standard JavaBean instances.</p>
29 *
30 * <p>
31 * It is suggested that this class should not usually need to be used directly
32 * to create new <code>WrapDynaBean</code> instances.
33 * It's usually better to call the <code>WrapDynaBean</code> constructor directly.
34 * For example:</p>
35 * <code><pre>
36 * Object javaBean = ...;
37 * DynaBean wrapper = new WrapDynaBean(javaBean);
38 * </pre></code>
39 * <p>
40 *
41 * @author Craig McClanahan
42 * @version $Revision: 1.8 $ $Date: 2004/02/28 13:18:34 $
43 */
44
45 public class WrapDynaClass implements DynaClass {
46
47
48
49
50
51 /**
52 * Construct a new WrapDynaClass for the specified JavaBean class. This
53 * constructor is private; WrapDynaClass instances will be created as
54 * needed via calls to the <code>createDynaClass(Class)</code> method.
55 *
56 * @param beanClass JavaBean class to be introspected around
57 */
58 private WrapDynaClass(Class beanClass) {
59
60 this.beanClass = beanClass;
61 introspect();
62
63 }
64
65
66
67
68
69 /**
70 * The JavaBean <code>Class</code> which is represented by this
71 * <code>WrapDynaClass</code>.
72 */
73 protected Class beanClass = null;
74
75
76 /**
77 * The set of PropertyDescriptors for this bean class.
78 */
79 protected PropertyDescriptor descriptors[] = null;
80
81
82 /**
83 * The set of PropertyDescriptors for this bean class, keyed by the
84 * property name. Individual descriptor instances will be the same
85 * instances as those in the <code>descriptors</code> list.
86 */
87 protected HashMap descriptorsMap = new HashMap();
88
89
90 /**
91 * The set of dynamic properties that are part of this DynaClass.
92 */
93 protected DynaProperty properties[] = null;
94
95
96 /**
97 * The set of dynamic properties that are part of this DynaClass,
98 * keyed by the property name. Individual descriptor instances will
99 * be the same instances as those in the <code>properties</code> list.
100 */
101 protected HashMap propertiesMap = new HashMap();
102
103
104
105
106
107 /**
108 * The set of <code>WrapDynaClass</code> instances that have ever been
109 * created, keyed by the underlying bean Class.
110 */
111 protected static HashMap dynaClasses = new HashMap();
112
113
114
115
116
117 /**
118 * Return the name of this DynaClass (analogous to the
119 * <code>getName()</code> method of <code>java.lang.Class</code), which
120 * allows the same <code>DynaClass</code> implementation class to support
121 * different dynamic classes, with different sets of properties.
122 */
123 public String getName() {
124
125 return (this.beanClass.getName());
126
127 }
128
129
130 /**
131 * Return a property descriptor for the specified property, if it exists;
132 * otherwise, return <code>null</code>.
133 *
134 * @param name Name of the dynamic property for which a descriptor
135 * is requested
136 *
137 * @exception IllegalArgumentException if no property name is specified
138 */
139 public DynaProperty getDynaProperty(String name) {
140
141 if (name == null) {
142 throw new IllegalArgumentException
143 ("No property name specified");
144 }
145 return ((DynaProperty) propertiesMap.get(name));
146
147 }
148
149
150 /**
151 * <p>Return an array of <code>ProperyDescriptors</code> for the properties
152 * currently defined in this DynaClass. If no properties are defined, a
153 * zero-length array will be returned.</p>
154 *
155 * <p><strong>FIXME</strong> - Should we really be implementing
156 * <code>getBeanInfo()</code> instead, which returns property descriptors
157 * and a bunch of other stuff?</p>
158 */
159 public DynaProperty[] getDynaProperties() {
160
161 return (properties);
162
163 }
164
165
166 /**
167 * <p>Instantiates a new standard JavaBean instance associated with
168 * this DynaClass and return it wrapped in a new WrapDynaBean
169 * instance. <strong>NOTE</strong> the JavaBean should have a
170 * no argument constructor.</p>
171 *
172 * <strong>NOTE</strong> - Most common use cases should not need to use
173 * this method. It is usually better to create new
174 * <code>WrapDynaBean</code> instances by calling its constructor.
175 * For example:</p>
176 * <code><pre>
177 * Object javaBean = ...;
178 * DynaBean wrapper = new WrapDynaBean(javaBean);
179 * </pre></code>
180 * <p>
181 * (This method is needed for some kinds of <code>DynaBean</code> framework.)
182 * </p>
183 *
184 * @exception IllegalAccessException if the Class or the appropriate
185 * constructor is not accessible
186 * @exception InstantiationException if this Class represents an abstract
187 * class, an array class, a primitive type, or void; or if instantiation
188 * fails for some other reason
189 */
190 public DynaBean newInstance()
191 throws IllegalAccessException, InstantiationException {
192
193 return new WrapDynaBean(beanClass.newInstance());
194
195 }
196
197
198
199
200
201 /**
202 * Return the PropertyDescriptor for the specified property name, if any;
203 * otherwise return <code>null</code>.
204 *
205 * @param name Name of the property to be retrieved
206 */
207 public PropertyDescriptor getPropertyDescriptor(String name) {
208
209 return ((PropertyDescriptor) descriptorsMap.get(name));
210
211 }
212
213
214
215
216
217 /**
218 * Clear our cache of WrapDynaClass instances.
219 */
220 public static void clear() {
221
222 synchronized (dynaClasses) {
223 dynaClasses.clear();
224 }
225
226 }
227
228
229 /**
230 * Create (if necessary) and return a new <code>WrapDynaClass</code>
231 * instance for the specified bean class.
232 *
233 * @param beanClass Bean class for which a WrapDynaClass is requested
234 */
235 public static WrapDynaClass createDynaClass(Class beanClass) {
236
237 synchronized (dynaClasses) {
238 WrapDynaClass dynaClass =
239 (WrapDynaClass) dynaClasses.get(beanClass);
240 if (dynaClass == null) {
241 dynaClass = new WrapDynaClass(beanClass);
242 dynaClasses.put(beanClass, dynaClass);
243 }
244 return (dynaClass);
245 }
246
247 }
248
249
250
251
252
253 /**
254 * Introspect our bean class to identify the supported properties.
255 */
256 protected void introspect() {
257
258
259 PropertyDescriptor regulars[] =
260 PropertyUtils.getPropertyDescriptors(beanClass);
261 if (regulars == null) {
262 regulars = new PropertyDescriptor[0];
263 }
264 HashMap mappeds =
265 PropertyUtils.getMappedPropertyDescriptors(beanClass);
266 if (mappeds == null) {
267 mappeds = new HashMap();
268 }
269
270
271 properties = new DynaProperty[regulars.length + mappeds.size()];
272 for (int i = 0; i < regulars.length; i++) {
273 descriptorsMap.put(regulars[i].getName(),
274 regulars[i]);
275 properties[i] =
276 new DynaProperty(regulars[i].getName(),
277 regulars[i].getPropertyType());
278 propertiesMap.put(properties[i].getName(),
279 properties[i]);
280 }
281 int j = regulars.length;
282 Iterator names = mappeds.keySet().iterator();
283 while (names.hasNext()) {
284 String name = (String) names.next();
285 PropertyDescriptor descriptor =
286 (PropertyDescriptor) mappeds.get(name);
287 properties[j] =
288 new DynaProperty(descriptor.getName(),
289 Map.class);
290 propertiesMap.put(properties[j].getName(),
291 properties[j]);
292 j++;
293 }
294
295 }
296
297
298 }