| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| WrapDynaClass |
|
| 1.2692307692307692;1.269 | ||||
| WrapDynaClass$1 |
|
| 1.2692307692307692;1.269 | ||||
| WrapDynaClass$2 |
|
| 1.2692307692307692;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 | } |