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