Coverage Report - org.apache.commons.beanutils.WrapDynaClass
 
Classes in this File Line Coverage Branch Coverage Complexity
WrapDynaClass
92%
49/53
91%
11/12
1.269
WrapDynaClass$1
100%
2/2
N/A
1.269
WrapDynaClass$2
5%
1/17
N/A
1.269
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to You under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.commons.beanutils;
 19  
 
 20  
 
 21  
 import java.beans.PropertyDescriptor;
 22  
 import java.lang.ref.Reference;
 23  
 import java.lang.ref.SoftReference;
 24  
 import java.util.Collection;
 25  
 import java.util.HashMap;
 26  
 import java.util.Iterator;
 27  
 import java.util.Map;
 28  
 import java.util.Set;
 29  
 import java.util.WeakHashMap;
 30  
 
 31  
 
 32  
 /**
 33  
  * <p>Implementation of <code>DynaClass</code> for DynaBeans that wrap
 34  
  * standard JavaBean instances.</p>
 35  
  *
 36  
  * <p>
 37  
  * It is suggested that this class should not usually need to be used directly
 38  
  * to create new <code>WrapDynaBean</code> instances. 
 39  
  * It's usually better to call the <code>WrapDynaBean</code> constructor directly.
 40  
  * For example:</p>
 41  
  * <code><pre>
 42  
  *   Object javaBean = ...;
 43  
  *   DynaBean wrapper = new WrapDynaBean(javaBean);
 44  
  * </pre></code>
 45  
  * <p>
 46  
  *
 47  
  * @author Craig McClanahan
 48  
  * @version $Revision: 690380 $ $Date: 2008-08-29 16:04:38 -0400 (Fri, 29 Aug 2008) $
 49  
  */
 50  
 
 51  0
 public class WrapDynaClass implements DynaClass {
 52  
 
 53  
 
 54  
     // ----------------------------------------------------------- Constructors
 55  
 
 56  
 
 57  
     /**
 58  
      * Construct a new WrapDynaClass for the specified JavaBean class.  This
 59  
      * constructor is private; WrapDynaClass instances will be created as
 60  
      * needed via calls to the <code>createDynaClass(Class)</code> method.
 61  
      *
 62  
      * @param beanClass JavaBean class to be introspected around
 63  
      */
 64  6
     private WrapDynaClass(Class beanClass) {
 65  
 
 66  6
         this.beanClassRef = new SoftReference(beanClass);
 67  6
         this.beanClassName = beanClass.getName();
 68  6
         introspect();
 69  
 
 70  6
     }
 71  
 
 72  
 
 73  
     // ----------------------------------------------------- Instance Variables
 74  
 
 75  
     /**
 76  
      * Name of the JavaBean class represented by this WrapDynaClass.
 77  
      */
 78  6
     private String beanClassName = null;
 79  
 
 80  
     /**
 81  
      * Reference to the JavaBean class represented by this WrapDynaClass.
 82  
      */
 83  6
     private Reference beanClassRef = null;
 84  
 
 85  
     /**
 86  
      * The JavaBean <code>Class</code> which is represented by this
 87  
      * <code>WrapDynaClass</code>.
 88  
      *
 89  
      * @deprecated No longer initialized, use getBeanClass() method instead
 90  
      */
 91  6
     protected Class beanClass = null;
 92  
 
 93  
 
 94  
     /**
 95  
      * The set of PropertyDescriptors for this bean class.
 96  
      */
 97  6
     protected PropertyDescriptor[] descriptors = null;
 98  
 
 99  
 
 100  
     /**
 101  
      * The set of PropertyDescriptors for this bean class, keyed by the
 102  
      * property name.  Individual descriptor instances will be the same
 103  
      * instances as those in the <code>descriptors</code> list.
 104  
      */
 105  6
     protected HashMap descriptorsMap = new HashMap();
 106  
 
 107  
 
 108  
     /**
 109  
      * The set of dynamic properties that are part of this DynaClass.
 110  
      */
 111  6
     protected DynaProperty[] properties = null;
 112  
 
 113  
 
 114  
     /**
 115  
      * The set of dynamic properties that are part of this DynaClass,
 116  
      * keyed by the property name.  Individual descriptor instances will
 117  
      * be the same instances as those in the <code>properties</code> list.
 118  
      */
 119  6
     protected HashMap propertiesMap = new HashMap();
 120  
 
 121  
 
 122  
     // ------------------------------------------------------- Static Variables
 123  
 
 124  
 
 125  1
     private static final ContextClassLoaderLocal CLASSLOADER_CACHE = 
 126  1
         new ContextClassLoaderLocal() {
 127  
             protected Object initialValue() {
 128  1
                 return new WeakHashMap();
 129  
         }
 130  
     };
 131  
 
 132  
     /**
 133  
      * Get the wrap dyna classes cache
 134  
      */
 135  
     private static Map getDynaClassesMap() {
 136  109
         return (Map)CLASSLOADER_CACHE.get();
 137  
     }
 138  
 
 139  
     /**
 140  
      * The set of <code>WrapDynaClass</code> instances that have ever been
 141  
      * created, keyed by the underlying bean Class. The keys to this map
 142  
      * are Class objects, and the values are corresponding WrapDynaClass
 143  
      * objects.
 144  
      * <p>
 145  
      * This static variable is safe even when this code is deployed via a
 146  
      * shared classloader because it is keyed via a Class object. The same
 147  
      * class loaded via two different classloaders will result in different
 148  
      * entries in this map.
 149  
      * <p>
 150  
      * Note, however, that this HashMap can result in a memory leak. When
 151  
      * this class is in a shared classloader it will retain references to
 152  
      * classes loaded via a webapp classloader even after the webapp has been
 153  
      * undeployed. That will prevent the entire classloader and all the classes
 154  
      * it refers to and all their static members from being freed.
 155  
      *
 156  
      ************* !!!!!!!!!!!! PLEASE NOTE !!!!!!!!!!!! *************
 157  
      *
 158  
      * THE FOLLOWING IS A NASTY HACK TO SO THAT BEANUTILS REMAINS BINARY
 159  
      *              COMPATIBLE WITH PREVIOUS RELEASES.
 160  
      *
 161  
      * There are two issues here:
 162  
      * 
 163  
      * 1) Memory Issues: The static HashMap caused memory problems (See BEANUTILS-59)
 164  
      *    to resolve this it has been moved into a ContextClassLoaderLocal instance
 165  
      *    (named CLASSLOADER_CACHE above) which holds one copy per
 166  
      *    ClassLoader in a WeakHashMap.
 167  
      * 
 168  
      * 2) Binary Compatibility: As the "dynaClasses" static HashMap is "protected"
 169  
      *    removing it breaks BeanUtils binary compatibility with previous versions.
 170  
      *    To resolve this all the methods have been overriden to delegate to the
 171  
      *    Map for the ClassLoader in the ContextClassLoaderLocal.
 172  
      *
 173  
      * @deprecated The dynaClasses Map will be removed in a subsequent release
 174  
      */
 175  1
     protected static HashMap dynaClasses = new HashMap() {
 176  
         public void clear() {
 177  0
             getDynaClassesMap().clear();
 178  0
         }
 179  
         public boolean containsKey(Object key) {
 180  0
             return getDynaClassesMap().containsKey(key);
 181  
         }
 182  
         public boolean containsValue(Object value) {
 183  0
             return getDynaClassesMap().containsValue(value);
 184  
         }
 185  
         public Set entrySet() {
 186  0
             return getDynaClassesMap().entrySet();
 187  
         }
 188  
         public boolean equals(Object o) {
 189  0
             return getDynaClassesMap().equals(o);
 190  
         }
 191  
         public Object get(Object key) {
 192  0
             return getDynaClassesMap().get(key);
 193  
         }
 194  
         public int hashCode() {
 195  0
             return getDynaClassesMap().hashCode();
 196  
         }
 197  
         public boolean isEmpty() {
 198  0
             return getDynaClassesMap().isEmpty();
 199  
         }
 200  
         public Set keySet() {
 201  0
             return getDynaClassesMap().keySet();
 202  
         }
 203  
         public Object put(Object key, Object value) {
 204  0
             return getDynaClassesMap().put(key, value);
 205  
         }
 206  
         public void putAll(Map m) {
 207  0
             getDynaClassesMap().putAll(m);
 208  0
         }
 209  
         public Object remove(Object key) {
 210  0
             return getDynaClassesMap().remove(key);
 211  
         }
 212  
         public int size() {
 213  0
             return getDynaClassesMap().size();
 214  
         }
 215  
         public Collection values() {
 216  0
             return getDynaClassesMap().values();
 217  
         }
 218  
     };
 219  
 
 220  
 
 221  
     // ------------------------------------------------------ DynaClass Methods
 222  
 
 223  
     /**
 224  
      * Return the class of the underlying wrapped bean.
 225  
      *
 226  
      * @return the class of the underlying wrapped bean
 227  
      * @since 1.8.0
 228  
      */
 229  
     protected Class getBeanClass() {
 230  15
         return (Class)beanClassRef.get();
 231  
     }
 232  
 
 233  
     /**
 234  
      * Return the name of this DynaClass (analogous to the
 235  
      * <code>getName()</code> method of <code>java.lang.Class</code), which
 236  
      * allows the same <code>DynaClass</code> implementation class to support
 237  
      * different dynamic classes, with different sets of properties.
 238  
      *
 239  
      * @return the name of the DynaClass
 240  
      */
 241  
     public String getName() {
 242  
 
 243  0
         return beanClassName;
 244  
 
 245  
     }
 246  
 
 247  
 
 248  
     /**
 249  
      * Return a property descriptor for the specified property, if it exists;
 250  
      * otherwise, return <code>null</code>.
 251  
      *
 252  
      * @param name Name of the dynamic property for which a descriptor
 253  
      *  is requested
 254  
      * @return The descriptor for the specified property
 255  
      *
 256  
      * @exception IllegalArgumentException if no property name is specified
 257  
      */
 258  
     public DynaProperty getDynaProperty(String name) {
 259  
 
 260  22
         if (name == null) {
 261  1
             throw new IllegalArgumentException
 262  
                     ("No property name specified");
 263  
         }
 264  21
         return ((DynaProperty) propertiesMap.get(name));
 265  
 
 266  
     }
 267  
 
 268  
 
 269  
     /**
 270  
      * <p>Return an array of <code>ProperyDescriptors</code> for the properties
 271  
      * currently defined in this DynaClass.  If no properties are defined, a
 272  
      * zero-length array will be returned.</p>
 273  
      *
 274  
      * <p><strong>FIXME</strong> - Should we really be implementing
 275  
      * <code>getBeanInfo()</code> instead, which returns property descriptors
 276  
      * and a bunch of other stuff?</p>
 277  
      *
 278  
      * @return the set of properties for this DynaClass
 279  
      */
 280  
     public DynaProperty[] getDynaProperties() {
 281  
 
 282  3
         return (properties);
 283  
 
 284  
     }
 285  
 
 286  
 
 287  
     /**
 288  
      * <p>Instantiates a new standard JavaBean instance associated with
 289  
      * this DynaClass and return it wrapped in a new WrapDynaBean   
 290  
      * instance. <strong>NOTE</strong> the JavaBean should have a 
 291  
      * no argument constructor.</p>
 292  
      *
 293  
      * <strong>NOTE</strong> - Most common use cases should not need to use
 294  
      * this method. It is usually better to create new
 295  
      * <code>WrapDynaBean</code> instances by calling its constructor.
 296  
      * For example:</p>
 297  
      * <code><pre>
 298  
      *   Object javaBean = ...;
 299  
      *   DynaBean wrapper = new WrapDynaBean(javaBean);
 300  
      * </pre></code>
 301  
      * <p>
 302  
      * (This method is needed for some kinds of <code>DynaBean</code> framework.)
 303  
      * </p>
 304  
      *
 305  
      * @return A new <code>DynaBean</code> instance
 306  
      * @exception IllegalAccessException if the Class or the appropriate
 307  
      *  constructor is not accessible
 308  
      * @exception InstantiationException if this Class represents an abstract
 309  
      *  class, an array class, a primitive type, or void; or if instantiation
 310  
      *  fails for some other reason
 311  
      */
 312  
     public DynaBean newInstance()
 313  
             throws IllegalAccessException, InstantiationException {
 314  
 
 315  9
         return new WrapDynaBean(getBeanClass().newInstance());
 316  
 
 317  
     }
 318  
 
 319  
 
 320  
     // --------------------------------------------------------- Public Methods
 321  
 
 322  
 
 323  
     /**
 324  
      * Return the PropertyDescriptor for the specified property name, if any;
 325  
      * otherwise return <code>null</code>.
 326  
      *
 327  
      * @param name Name of the property to be retrieved
 328  
      * @return The descriptor for the specified property
 329  
      */
 330  
     public PropertyDescriptor getPropertyDescriptor(String name) {
 331  
 
 332  0
         return ((PropertyDescriptor) descriptorsMap.get(name));
 333  
 
 334  
     }
 335  
 
 336  
 
 337  
     // --------------------------------------------------------- Static Methods
 338  
 
 339  
 
 340  
     /**
 341  
      * Clear our cache of WrapDynaClass instances.
 342  
      */
 343  
     public static void clear() {
 344  
 
 345  16
         getDynaClassesMap().clear();
 346  
 
 347  16
     }
 348  
 
 349  
 
 350  
     /**
 351  
      * Create (if necessary) and return a new <code>WrapDynaClass</code>
 352  
      * instance for the specified bean class.
 353  
      *
 354  
      * @param beanClass Bean class for which a WrapDynaClass is requested
 355  
      * @return A new <i>Wrap</i> {@link DynaClass}
 356  
      */
 357  
     public static WrapDynaClass createDynaClass(Class beanClass) {
 358  
 
 359  87
             WrapDynaClass dynaClass =
 360  
                     (WrapDynaClass) getDynaClassesMap().get(beanClass);
 361  87
             if (dynaClass == null) {
 362  6
                 dynaClass = new WrapDynaClass(beanClass);
 363  6
                 getDynaClassesMap().put(beanClass, dynaClass);
 364  
             }
 365  87
             return (dynaClass);
 366  
 
 367  
     }
 368  
 
 369  
 
 370  
     // ------------------------------------------------------ Protected Methods
 371  
 
 372  
 
 373  
     /**
 374  
      * Introspect our bean class to identify the supported properties.
 375  
      */
 376  
     protected void introspect() {
 377  
 
 378  
         // Look up the property descriptors for this bean class
 379  6
         Class beanClass = getBeanClass();
 380  6
         PropertyDescriptor[] regulars =
 381  
                 PropertyUtils.getPropertyDescriptors(beanClass);
 382  6
         if (regulars == null) {
 383  0
             regulars = new PropertyDescriptor[0];
 384  
         }
 385  6
         Map mappeds =
 386  
                 PropertyUtils.getMappedPropertyDescriptors(beanClass);
 387  6
         if (mappeds == null) {
 388  3
             mappeds = new HashMap();
 389  
         }
 390  
 
 391  
         // Construct corresponding DynaProperty information
 392  6
         properties = new DynaProperty[regulars.length + mappeds.size()];
 393  83
         for (int i = 0; i < regulars.length; i++) {
 394  77
             descriptorsMap.put(regulars[i].getName(),
 395  
                     regulars[i]);
 396  77
             properties[i] =
 397  
                     new DynaProperty(regulars[i].getName(),
 398  
                             regulars[i].getPropertyType());
 399  77
             propertiesMap.put(properties[i].getName(),
 400  
                     properties[i]);
 401  
         }
 402  6
         int j = regulars.length;
 403  6
         Iterator names = mappeds.keySet().iterator();
 404  11
         while (names.hasNext()) {
 405  5
             String name = (String) names.next();
 406  5
             PropertyDescriptor descriptor =
 407  
                     (PropertyDescriptor) mappeds.get(name);
 408  5
             properties[j] =
 409  
                     new DynaProperty(descriptor.getName(),
 410  
                             Map.class);
 411  5
             propertiesMap.put(properties[j].getName(),
 412  
                     properties[j]);
 413  5
             j++;
 414  5
         }
 415  
 
 416  6
     }
 417  
 
 418  
 
 419  
 }