Coverage Report - org.apache.commons.beanutils.DynaBeanMapDecorator
 
Classes in this File Line Coverage Branch Coverage Complexity
DynaBeanMapDecorator
94%
65/69
78%
25/32
2.208
DynaBeanMapDecorator$MapEntry
58%
7/12
16%
2/12
2.208
 
 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  
 package org.apache.commons.beanutils;
 18  
 
 19  
 import java.util.Map;
 20  
 import java.util.List;
 21  
 import java.util.ArrayList;
 22  
 import java.util.Set;
 23  
 import java.util.HashSet;
 24  
 import java.util.Iterator;
 25  
 import java.util.Collection;
 26  
 import java.util.Collections;
 27  
 
 28  
 /**
 29  
  * <p>Decorates a {@link DynaBean} to provide <code>Map</code> behaviour.</p>
 30  
  *
 31  
  * <p>The motivation for this implementation is to provide access to {@link DynaBean}
 32  
  *    properties in technologies that are unaware of BeanUtils and {@link DynaBean}s -
 33  
  *    such as the expression languages of JSTL and JSF.</p>
 34  
  *
 35  
  * <p>This can be achieved either by wrapping the {@link DynaBean} prior to
 36  
  *    providing it to the technolody to process or by providing a <code>Map</code>
 37  
  *    accessor method on the DynaBean implementation:
 38  
  *    <pre><code>
 39  
  *         public Map getMap() {
 40  
  *             return new DynaBeanMapDecorator(this);
 41  
  *         }</code></pre>
 42  
  *   </ul>
 43  
  * </p>
 44  
  *
 45  
  * <p>This, for example, could be used in JSTL in the following way to access
 46  
  *    a DynaBean's <code>fooProperty</code>:
 47  
  *    <ul><li><code>${myDynaBean.<b>map</b>.fooProperty}</code></li></ul>
 48  
  * </p>
 49  
  *
 50  
  * <h3>Usage</h3>
 51  
  *
 52  
  * <p>To decorate a {@link DynaBean} simply instantiate this class with the
 53  
  *    target {@link DynaBean}:</p>
 54  
  *
 55  
  * <ul><li><code>Map fooMap = new DynaBeanMapDecorator(fooDynaBean);</code></li></ul>
 56  
  *
 57  
  * <p>The above example creates a <b><i>read only</i></b> <code>Map</code>.
 58  
  *    To create  a <code>Map</code> which can be modified, construct a
 59  
  *    <code>DynaBeanMapDecorator</code> with the <b><i>read only</i></b>
 60  
  *    attribute set to <code>false</code>:</p>
 61  
  *
 62  
  * <ul><li><code>Map fooMap = new DynaBeanMapDecorator(fooDynaBean, false);</code></li></ul>
 63  
  *
 64  
  * <h3>Limitations</h3>
 65  
  * <p>In this implementation the <code>entrySet()</code>, <code>keySet()</code>
 66  
  *    and <code>values()</code> methods create an <b><i>unmodifiable</i></b>
 67  
  *    <code>Set</code> and it does not support the Map's <code>clear()</code>
 68  
  *    and <code>remove()</code> operations.</p>
 69  
  *
 70  
  * @since BeanUtils 1.8.0
 71  
  * @version $Revision: 546471 $ $Date: 2007-06-12 08:57:20 -0400 (Tue, 12 Jun 2007) $
 72  
  */
 73  
 public class DynaBeanMapDecorator implements Map {
 74  
 
 75  
     private DynaBean dynaBean;
 76  
     private boolean readOnly;
 77  
     private transient Set keySet;
 78  
 
 79  
     // ------------------- Constructors ----------------------------------
 80  
 
 81  
     /**
 82  
      * Constructs a  read only Map for the specified
 83  
      * {@link DynaBean}.
 84  
      *
 85  
      * @param dynaBean The dyna bean being decorated
 86  
      * @throws IllegalArgumentException if the {@link DynaBean} is null.
 87  
      */
 88  
     public DynaBeanMapDecorator(DynaBean dynaBean) {
 89  14
         this(dynaBean, true);
 90  14
     }
 91  
 
 92  
     /**
 93  
      * Construct a Map for the specified {@link DynaBean}.
 94  
      *
 95  
      * @param dynaBean The dyna bean being decorated
 96  
      * @param readOnly <code>true</code> if the Mpa is read only
 97  
      * otherwise <code>false</code>
 98  
      * @throws IllegalArgumentException if the {@link DynaBean} is null.
 99  
      */
 100  27
     public DynaBeanMapDecorator(DynaBean dynaBean, boolean readOnly) {
 101  27
         if (dynaBean == null) {
 102  0
             throw new IllegalArgumentException("DynaBean is null");
 103  
         }
 104  27
         this.dynaBean = dynaBean;
 105  27
         this.readOnly = readOnly;
 106  27
     }
 107  
 
 108  
 
 109  
     // ------------------- public Methods --------------------------------
 110  
 
 111  
 
 112  
     /**
 113  
      * Indicate whether the Map is read only.
 114  
      *
 115  
      * @return <code>true</code> if the Map is read only,
 116  
      * otherwise <code>false</code>.
 117  
      */
 118  
     public boolean isReadOnly() {
 119  7
         return readOnly;
 120  
     }
 121  
 
 122  
     // ------------------- java.util.Map Methods -------------------------
 123  
 
 124  
     /**
 125  
      * clear() operation is not supported.
 126  
      *
 127  
      * @throws UnsupportedOperationException
 128  
      */
 129  
     public void clear() {
 130  2
         throw new UnsupportedOperationException();
 131  
     }
 132  
 
 133  
     /**
 134  
      * Indicate whether the {@link DynaBean} contains a specified
 135  
      * value for one (or more) of its properties.
 136  
      *
 137  
      * @param key The {@link DynaBean}'s property name
 138  
      * @return <code>true</code> if one of the {@link DynaBean}'s
 139  
      * properties contains a specified value.
 140  
      */
 141  
     public boolean containsKey(Object key) {
 142  2
         DynaClass dynaClass = getDynaBean().getDynaClass();
 143  2
         DynaProperty dynaProperty = dynaClass.getDynaProperty(toString(key));
 144  2
         return (dynaProperty == null ? false : true);
 145  
     }
 146  
 
 147  
     /**
 148  
      * Indicates whether the decorated {@link DynaBean} contains
 149  
      * a specified value.
 150  
      *
 151  
      * @param value The value to check for.
 152  
      * @return <code>true</code> if one of the the {@link DynaBean}'s
 153  
      * properties contains the specified value, otherwise
 154  
      * <code>false</code>.
 155  
      */
 156  
     public boolean containsValue(Object value) {
 157  2
         DynaProperty[] properties = getDynaProperties();
 158  7
         for (int i = 0; i < properties.length; i++) {
 159  6
             String key = properties[i].getName();
 160  6
             Object prop = getDynaBean().get(key);
 161  6
             if (value == null) {
 162  0
                 if (prop == null) {
 163  0
                     return true;
 164  
                 }
 165  
             } else {
 166  6
                 if (value.equals(prop)) {
 167  1
                     return true;
 168  
                 }
 169  
             }
 170  
         }
 171  1
         return false;
 172  
     }
 173  
 
 174  
     /**
 175  
      * <p>Returns the Set of the property/value mappings
 176  
      * in the decorated {@link DynaBean}.</p>
 177  
      *
 178  
      * <p>Each element in the Set is a <code>Map.Entry</code>
 179  
      * type.</p>
 180  
      *
 181  
      * @return An unmodifiable set of the DynaBean
 182  
      * property name/value pairs
 183  
      */
 184  
     public Set entrySet() {
 185  1
         DynaProperty[] properties = getDynaProperties();
 186  1
         Set set = new HashSet(properties.length);
 187  6
         for (int i = 0; i < properties.length; i++) {
 188  5
             String key = properties[i].getName();
 189  5
             Object value = getDynaBean().get(key);
 190  5
             set.add(new MapEntry(key, value));
 191  
         }
 192  1
         return Collections.unmodifiableSet(set);
 193  
     }
 194  
 
 195  
     /**
 196  
      * Return the value for the specified key from
 197  
      * the decorated {@link DynaBean}.
 198  
      *
 199  
      * @param key The {@link DynaBean}'s property name
 200  
      * @return The value for the specified property.
 201  
      */
 202  
     public Object get(Object key) {
 203  8
         return getDynaBean().get(toString(key));
 204  
     }
 205  
 
 206  
     /**
 207  
      * Indicate whether the decorated {@link DynaBean} has
 208  
      * any properties.
 209  
      *
 210  
      * @return <code>true</code> if the {@link DynaBean} has
 211  
      * no properties, otherwise <code>false</code>.
 212  
      */
 213  
     public boolean isEmpty() {
 214  2
         return (getDynaProperties().length == 0);
 215  
     }
 216  
 
 217  
     /**
 218  
      * <p>Returns the Set of the property
 219  
      * names in the decorated {@link DynaBean}.</p>
 220  
      *
 221  
      * <p><b>N.B.</b>For {@link DynaBean}s whose associated {@link DynaClass}
 222  
      * is a {@link MutableDynaClass} a new Set is created every
 223  
      * time, otherwise the Set is created only once and cached.</p>
 224  
      *
 225  
      * @return An unmodifiable set of the {@link DynaBean}s
 226  
      * property names.
 227  
      */
 228  
     public Set keySet() {
 229  1
         if (keySet != null) {
 230  0
             return keySet;
 231  
         }
 232  
 
 233  
         // Create a Set of the keys
 234  1
         DynaProperty[] properties = getDynaProperties();
 235  1
         Set set = new HashSet(properties.length);
 236  6
         for (int i = 0; i < properties.length; i++) {
 237  5
             set.add(properties[i].getName());
 238  
         }
 239  1
         set = Collections.unmodifiableSet(set);
 240  
 
 241  
         // Cache the keySet if Not a MutableDynaClass
 242  1
         DynaClass dynaClass = getDynaBean().getDynaClass();
 243  1
         if (!(dynaClass instanceof MutableDynaClass)) {
 244  1
             keySet = set;
 245  
         }
 246  
 
 247  1
         return set;
 248  
 
 249  
     }
 250  
 
 251  
     /**
 252  
      * Set the value for the specified property in
 253  
      * the decorated {@link DynaBean}.
 254  
      *
 255  
      * @param key The {@link DynaBean}'s property name
 256  
      * @param value The value for the specified property.
 257  
      * @return The previous property's value.
 258  
      * @throws UnsupportedOperationException if
 259  
      * <code>isReadOnly()</code> is true.
 260  
      */
 261  
     public Object put(Object key, Object value) {
 262  3
         if (isReadOnly()) {
 263  1
             throw new UnsupportedOperationException("Map is read only");
 264  
         }
 265  2
         String property = toString(key);
 266  2
         Object previous = getDynaBean().get(property);
 267  2
         getDynaBean().set(property, value);
 268  2
         return previous;
 269  
     }
 270  
 
 271  
     /**
 272  
      * Copy the contents of a Map to the decorated {@link DynaBean}.
 273  
      *
 274  
      * @param map The Map of values to copy.
 275  
      * @throws UnsupportedOperationException if
 276  
      * <code>isReadOnly()</code> is true.
 277  
      */
 278  
     public void putAll(Map map) {
 279  2
         if (isReadOnly()) {
 280  1
             throw new UnsupportedOperationException("Map is read only");
 281  
         }
 282  1
         Iterator keys = map.keySet().iterator();
 283  2
         while (keys.hasNext()) {
 284  1
             Object key = keys.next();
 285  1
             put(key, map.get(key));
 286  1
         }
 287  1
     }
 288  
 
 289  
     /**
 290  
      * remove() operation is not supported.
 291  
      *
 292  
      * @param key The {@link DynaBean}'s property name
 293  
      * @return the value removed
 294  
      * @throws UnsupportedOperationException
 295  
      */
 296  
     public Object remove(Object key) {
 297  2
         throw new UnsupportedOperationException();
 298  
     }
 299  
 
 300  
     /**
 301  
      * Returns the number properties in the decorated
 302  
      * {@link DynaBean}.
 303  
      * @return The number of properties.
 304  
      */
 305  
     public int size() {
 306  2
         return getDynaProperties().length;
 307  
     }
 308  
 
 309  
     /**
 310  
      * Returns the set of property values in the
 311  
      * decorated {@link DynaBean}.
 312  
      *
 313  
      * @return Unmodifiable collection of values.
 314  
      */
 315  
     public Collection values() {
 316  1
         DynaProperty[] properties = getDynaProperties();
 317  1
         List values = new ArrayList(properties.length);
 318  6
         for (int i = 0; i < properties.length; i++) {
 319  5
             String key = properties[i].getName();
 320  5
             Object value = getDynaBean().get(key);
 321  5
             values.add(value);
 322  
         }
 323  1
         return Collections.unmodifiableList(values);
 324  
     }
 325  
 
 326  
     // ------------------- protected Methods -----------------------------
 327  
 
 328  
     /**
 329  
      * Provide access to the underlying {@link DynaBean}
 330  
      * this Map decorates.
 331  
      *
 332  
      * @return the decorated {@link DynaBean}.
 333  
      */
 334  
     public DynaBean getDynaBean() {
 335  40
         return dynaBean;
 336  
     }
 337  
 
 338  
     // ------------------- private Methods -------------------------------
 339  
 
 340  
     /**
 341  
      * Convenience method to retrieve the {@link DynaProperty}s
 342  
      * for this {@link DynaClass}.
 343  
      *
 344  
      * @return The an array of the {@link DynaProperty}s.
 345  
      */
 346  
     private DynaProperty[] getDynaProperties() {
 347  9
         return getDynaBean().getDynaClass().getDynaProperties();
 348  
     }
 349  
 
 350  
     /**
 351  
      * Convenience method to convert an Object
 352  
      * to a String.
 353  
      *
 354  
      * @param obj The Object to convert
 355  
      * @return String representation of the object
 356  
      */
 357  
     private String toString(Object obj) {
 358  12
         return (obj == null ? null : obj.toString());
 359  
     }
 360  
 
 361  
     /**
 362  
      * Map.Entry implementation.
 363  
      */
 364  
     private static class MapEntry implements Map.Entry {
 365  
         private Object key;
 366  
         private Object value;
 367  5
         MapEntry(Object key, Object value) {
 368  5
             this.key = key;
 369  5
             this.value = value;
 370  5
         }
 371  
         public boolean equals(Object o) {
 372  0
             if (!(o instanceof Map.Entry)) {
 373  0
                 return false;
 374  
             }
 375  0
             Map.Entry e = (Map.Entry)o;
 376  0
             return ((key.equals(e.getKey())) &&
 377  
                     (value == null ? e.getValue() == null
 378  
                                    : value.equals(e.getValue())));
 379  
         }
 380  
         public int hashCode() {
 381  5
             return key.hashCode() + (value == null ? 0 : value.hashCode());
 382  
         }
 383  
         public Object getKey() {
 384  5
             return key;
 385  
         }
 386  
         public Object getValue() {
 387  5
             return value;
 388  
         }
 389  
         public Object setValue(Object value) {
 390  0
             throw new UnsupportedOperationException();
 391  
         }
 392  
     }
 393  
 
 394  
 }