Coverage Report - org.apache.commons.beanutils.BasicDynaBean
 
Classes in this File Line Coverage Branch Coverage Complexity
BasicDynaBean
67%
63/93
59%
55/92
7
 
 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  
 
 19  
 package org.apache.commons.beanutils;
 20  
 
 21  
 
 22  
 import java.io.Serializable;
 23  
 import java.lang.reflect.Array;
 24  
 import java.util.HashMap;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 
 28  
 
 29  
 /**
 30  
  * <p>Minimal implementation of the <code>DynaBean</code> interface.  Can be
 31  
  * used as a convenience base class for more sophisticated implementations.</p>
 32  
  *
 33  
  * <p><strong>IMPLEMENTATION NOTE</strong> - Instances of this class that are
 34  
  * accessed from multiple threads simultaneously need to be synchronized.</p>
 35  
  *
 36  
  * <p><strong>IMPLEMENTATION NOTE</strong> - Instances of this class can be
 37  
  * successfully serialized and deserialized <strong>ONLY</strong> if all
 38  
  * property values are <code>Serializable</code>.</p>
 39  
  *
 40  
  * @author Craig McClanahan
 41  
  * @version $Revision: 926529 $ $Date: 2010-03-23 07:44:24 -0400 (Tue, 23 Mar 2010) $
 42  
  */
 43  
 
 44  
 public class BasicDynaBean implements DynaBean, Serializable {
 45  
 
 46  
 
 47  
     // ---------------------------------------------------------- Constructors
 48  
 
 49  
 
 50  
     /**
 51  
      * Construct a new <code>DynaBean</code> associated with the specified
 52  
      * <code>DynaClass</code> instance.
 53  
      *
 54  
      * @param dynaClass The DynaClass we are associated with
 55  
      */
 56  
     public BasicDynaBean(DynaClass dynaClass) {
 57  
 
 58  319
         super();
 59  319
         this.dynaClass = dynaClass;
 60  
 
 61  319
     }
 62  
 
 63  
 
 64  
     // ---------------------------------------------------- Instance Variables
 65  
 
 66  
 
 67  
     /**
 68  
      * The <code>DynaClass</code> "base class" that this DynaBean
 69  
      * is associated with.
 70  
      */
 71  319
     protected DynaClass dynaClass = null;
 72  
 
 73  
 
 74  
     /**
 75  
      * The set of property values for this DynaBean, keyed by property name.
 76  
      */
 77  319
     protected HashMap values = new HashMap();
 78  
 
 79  
     /** Map decorator for this DynaBean */
 80  
     private transient Map mapDecorator;
 81  
 
 82  
     /**
 83  
      * Return a Map representation of this DynaBean.
 84  
      * </p>
 85  
      * This, for example, could be used in JSTL in the following way to access
 86  
      * a DynaBean's <code>fooProperty</code>:
 87  
      * <ul><li><code>${myDynaBean.<b>map</b>.fooProperty}</code></li></ul>
 88  
      *
 89  
      * @return a Map representation of this DynaBean
 90  
      * @since 1.8.0
 91  
      */
 92  
     public Map getMap() {
 93  
 
 94  
         // cache the Map
 95  0
         if (mapDecorator == null) {
 96  0
             mapDecorator = new DynaBeanMapDecorator(this);
 97  
         }
 98  0
         return mapDecorator;
 99  
 
 100  
     }
 101  
 
 102  
     // ------------------------------------------------------ DynaBean Methods
 103  
 
 104  
 
 105  
     /**
 106  
      * Does the specified mapped property contain a value for the specified
 107  
      * key value?
 108  
      *
 109  
      * @param name Name of the property to check
 110  
      * @param key Name of the key to check
 111  
      * @return <code>true<code> if the mapped property contains a value for
 112  
      * the specified key, otherwise <code>false</code>
 113  
      *
 114  
      * @exception IllegalArgumentException if there is no property
 115  
      *  of the specified name
 116  
      */
 117  
     public boolean contains(String name, String key) {
 118  
 
 119  12
         Object value = values.get(name);
 120  12
         if (value == null) {
 121  0
             throw new NullPointerException
 122  
                     ("No mapped value for '" + name + "(" + key + ")'");
 123  12
         } else if (value instanceof Map) {
 124  12
             return (((Map) value).containsKey(key));
 125  
         } else {
 126  0
             throw new IllegalArgumentException
 127  
                     ("Non-mapped property for '" + name + "(" + key + ")'");
 128  
         }
 129  
 
 130  
     }
 131  
 
 132  
 
 133  
     /**
 134  
      * Return the value of a simple property with the specified name.
 135  
      *
 136  
      * @param name Name of the property whose value is to be retrieved
 137  
      * @return The property's value
 138  
      *
 139  
      * @exception IllegalArgumentException if there is no property
 140  
      *  of the specified name
 141  
      */
 142  
     public Object get(String name) {
 143  
 
 144  
         // Return any non-null value for the specified property
 145  396
         Object value = values.get(name);
 146  396
         if (value != null) {
 147  337
             return (value);
 148  
         }
 149  
 
 150  
         // Return a null value for a non-primitive property
 151  59
         Class type = getDynaProperty(name).getType();
 152  54
         if (!type.isPrimitive()) {
 153  46
             return (value);
 154  
         }
 155  
 
 156  
         // Manufacture default values for primitive properties
 157  8
         if (type == Boolean.TYPE) {
 158  4
             return (Boolean.FALSE);
 159  4
         } else if (type == Byte.TYPE) {
 160  0
             return (new Byte((byte) 0));
 161  4
         } else if (type == Character.TYPE) {
 162  0
             return (new Character((char) 0));
 163  4
         } else if (type == Double.TYPE) {
 164  0
             return (new Double(0.0));
 165  4
         } else if (type == Float.TYPE) {
 166  4
             return (new Float((float) 0.0));
 167  0
         } else if (type == Integer.TYPE) {
 168  0
             return (new Integer(0));
 169  0
         } else if (type == Long.TYPE) {
 170  0
             return (new Long(0));
 171  0
         } else if (type == Short.TYPE) {
 172  0
             return (new Short((short) 0));
 173  
         } else {
 174  0
             return (null);
 175  
         }
 176  
 
 177  
     }
 178  
 
 179  
 
 180  
     /**
 181  
      * Return the value of an indexed property with the specified name.
 182  
      *
 183  
      * @param name Name of the property whose value is to be retrieved
 184  
      * @param index Index of the value to be retrieved
 185  
      * @return The indexed property's value
 186  
      *
 187  
      * @exception IllegalArgumentException if there is no property
 188  
      *  of the specified name
 189  
      * @exception IllegalArgumentException if the specified property
 190  
      *  exists, but is not indexed
 191  
      * @exception IndexOutOfBoundsException if the specified index
 192  
      *  is outside the range of the underlying property
 193  
      * @exception NullPointerException if no array or List has been
 194  
      *  initialized for this property
 195  
      */
 196  
     public Object get(String name, int index) {
 197  
 
 198  146
         Object value = values.get(name);
 199  146
         if (value == null) {
 200  0
             throw new NullPointerException
 201  
                     ("No indexed value for '" + name + "[" + index + "]'");
 202  146
         } else if (value.getClass().isArray()) {
 203  121
             return (Array.get(value, index));
 204  25
         } else if (value instanceof List) {
 205  25
             return ((List) value).get(index);
 206  
         } else {
 207  0
             throw new IllegalArgumentException
 208  
                     ("Non-indexed property for '" + name + "[" + index + "]'");
 209  
         }
 210  
 
 211  
     }
 212  
 
 213  
 
 214  
     /**
 215  
      * Return the value of a mapped property with the specified name,
 216  
      * or <code>null</code> if there is no value for the specified key.
 217  
      *
 218  
      * @param name Name of the property whose value is to be retrieved
 219  
      * @param key Key of the value to be retrieved
 220  
      * @return The mapped property's value
 221  
      *
 222  
      * @exception IllegalArgumentException if there is no property
 223  
      *  of the specified name
 224  
      * @exception IllegalArgumentException if the specified property
 225  
      *  exists, but is not mapped
 226  
      */
 227  
     public Object get(String name, String key) {
 228  
 
 229  34
         Object value = values.get(name);
 230  34
         if (value == null) {
 231  0
             throw new NullPointerException
 232  
                     ("No mapped value for '" + name + "(" + key + ")'");
 233  34
         } else if (value instanceof Map) {
 234  34
             return (((Map) value).get(key));
 235  
         } else {
 236  0
             throw new IllegalArgumentException
 237  
                     ("Non-mapped property for '" + name + "(" + key + ")'");
 238  
         }
 239  
 
 240  
     }
 241  
 
 242  
 
 243  
     /**
 244  
      * Return the <code>DynaClass</code> instance that describes the set of
 245  
      * properties available for this DynaBean.
 246  
      *
 247  
      * @return The associated DynaClass
 248  
      */
 249  
     public DynaClass getDynaClass() {
 250  
 
 251  3902
         return (this.dynaClass);
 252  
 
 253  
     }
 254  
 
 255  
 
 256  
     /**
 257  
      * Remove any existing value for the specified key on the
 258  
      * specified mapped property.
 259  
      *
 260  
      * @param name Name of the property for which a value is to
 261  
      *  be removed
 262  
      * @param key Key of the value to be removed
 263  
      *
 264  
      * @exception IllegalArgumentException if there is no property
 265  
      *  of the specified name
 266  
      */
 267  
     public void remove(String name, String key) {
 268  
 
 269  4
         Object value = values.get(name);
 270  4
         if (value == null) {
 271  0
             throw new NullPointerException
 272  
                     ("No mapped value for '" + name + "(" + key + ")'");
 273  4
         } else if (value instanceof Map) {
 274  4
             ((Map) value).remove(key);
 275  
         } else {
 276  0
             throw new IllegalArgumentException
 277  
                     ("Non-mapped property for '" + name + "(" + key + ")'");
 278  
         }
 279  
 
 280  4
     }
 281  
 
 282  
 
 283  
     /**
 284  
      * Set the value of a simple property with the specified name.
 285  
      *
 286  
      * @param name Name of the property whose value is to be set
 287  
      * @param value Value to which this property is to be set
 288  
      *
 289  
      * @exception ConversionException if the specified value cannot be
 290  
      *  converted to the type required for this property
 291  
      * @exception IllegalArgumentException if there is no property
 292  
      *  of the specified name
 293  
      * @exception NullPointerException if an attempt is made to set a
 294  
      *  primitive property to null
 295  
      */
 296  
     public void set(String name, Object value) {
 297  
 
 298  3183
         DynaProperty descriptor = getDynaProperty(name);
 299  3183
         if (value == null) {
 300  90
             if (descriptor.getType().isPrimitive()) {
 301  0
                 throw new NullPointerException
 302  
                         ("Primitive value for '" + name + "'");
 303  
             }
 304  3093
         } else if (!isAssignable(descriptor.getType(), value.getClass())) {
 305  0
             throw new ConversionException
 306  
                     ("Cannot assign value of type '" +
 307  
                     value.getClass().getName() +
 308  
                     "' to property '" + name + "' of type '" +
 309  
                     descriptor.getType().getName() + "'");
 310  
         }
 311  3183
         values.put(name, value);
 312  
 
 313  3183
     }
 314  
 
 315  
 
 316  
     /**
 317  
      * Set the value of an indexed property with the specified name.
 318  
      *
 319  
      * @param name Name of the property whose value is to be set
 320  
      * @param index Index of the property to be set
 321  
      * @param value Value to which this property is to be set
 322  
      *
 323  
      * @exception ConversionException if the specified value cannot be
 324  
      *  converted to the type required for this property
 325  
      * @exception IllegalArgumentException if there is no property
 326  
      *  of the specified name
 327  
      * @exception IllegalArgumentException if the specified property
 328  
      *  exists, but is not indexed
 329  
      * @exception IndexOutOfBoundsException if the specified index
 330  
      *  is outside the range of the underlying property
 331  
      */
 332  
     public void set(String name, int index, Object value) {
 333  
 
 334  32
         Object prop = values.get(name);
 335  32
         if (prop == null) {
 336  0
             throw new NullPointerException
 337  
                     ("No indexed value for '" + name + "[" + index + "]'");
 338  32
         } else if (prop.getClass().isArray()) {
 339  27
             Array.set(prop, index, value);
 340  5
         } else if (prop instanceof List) {
 341  
             try {
 342  5
                 ((List) prop).set(index, value);
 343  0
             } catch (ClassCastException e) {
 344  0
                 throw new ConversionException(e.getMessage());
 345  3
             }
 346  
         } else {
 347  0
             throw new IllegalArgumentException
 348  
                     ("Non-indexed property for '" + name + "[" + index + "]'");
 349  
         }
 350  
 
 351  21
     }
 352  
 
 353  
 
 354  
     /**
 355  
      * Set the value of a mapped property with the specified name.
 356  
      *
 357  
      * @param name Name of the property whose value is to be set
 358  
      * @param key Key of the property to be set
 359  
      * @param value Value to which this property is to be set
 360  
      *
 361  
      * @exception ConversionException if the specified value cannot be
 362  
      *  converted to the type required for this property
 363  
      * @exception IllegalArgumentException if there is no property
 364  
      *  of the specified name
 365  
      * @exception IllegalArgumentException if the specified property
 366  
      *  exists, but is not mapped
 367  
      */
 368  
     public void set(String name, String key, Object value) {
 369  
 
 370  10
         Object prop = values.get(name);
 371  10
         if (prop == null) {
 372  0
             throw new NullPointerException
 373  
                     ("No mapped value for '" + name + "(" + key + ")'");
 374  10
         } else if (prop instanceof Map) {
 375  10
             ((Map) prop).put(key, value);
 376  
         } else {
 377  0
             throw new IllegalArgumentException
 378  
                     ("Non-mapped property for '" + name + "(" + key + ")'");
 379  
         }
 380  
 
 381  10
     }
 382  
 
 383  
 
 384  
     // ------------------------------------------------------ Protected Methods
 385  
 
 386  
 
 387  
     /**
 388  
      * Return the property descriptor for the specified property name.
 389  
      *
 390  
      * @param name Name of the property for which to retrieve the descriptor
 391  
      * @return The property descriptor
 392  
      *
 393  
      * @exception IllegalArgumentException if this is not a valid property
 394  
      *  name for our DynaClass
 395  
      */
 396  
     protected DynaProperty getDynaProperty(String name) {
 397  
 
 398  3242
         DynaProperty descriptor = getDynaClass().getDynaProperty(name);
 399  3240
         if (descriptor == null) {
 400  3
             throw new IllegalArgumentException
 401  
                     ("Invalid property name '" + name + "'");
 402  
         }
 403  3237
         return (descriptor);
 404  
 
 405  
     }
 406  
 
 407  
 
 408  
     /**
 409  
      * Is an object of the source class assignable to the destination class?
 410  
      *
 411  
      * @param dest Destination class
 412  
      * @param source Source class
 413  
      * @return <code>true</code> if the source class is assignable to the
 414  
      * destination class, otherwise <code>false</code>
 415  
      */
 416  
     protected boolean isAssignable(Class dest, Class source) {
 417  
 
 418  3093
         if (dest.isAssignableFrom(source) ||
 419  
                 ((dest == Boolean.TYPE) && (source == Boolean.class)) ||
 420  
                 ((dest == Byte.TYPE) && (source == Byte.class)) ||
 421  
                 ((dest == Character.TYPE) && (source == Character.class)) ||
 422  
                 ((dest == Double.TYPE) && (source == Double.class)) ||
 423  
                 ((dest == Float.TYPE) && (source == Float.class)) ||
 424  
                 ((dest == Integer.TYPE) && (source == Integer.class)) ||
 425  
                 ((dest == Long.TYPE) && (source == Long.class)) ||
 426  
                 ((dest == Short.TYPE) && (source == Short.class))) {
 427  3093
             return (true);
 428  
         } else {
 429  0
             return (false);
 430  
         }
 431  
 
 432  
     }
 433  
 
 434  
 
 435  
 }