Coverage Report - org.apache.commons.betwixt.BeanProperty

Classes in this File Line Coverage Branch Coverage Complexity
BeanProperty
86% 
100% 
2.692

 1  
 package org.apache.commons.betwixt;
 2  
 
 3  
 /*
 4  
  * Copyright 2001-2004 The Apache Software Foundation.
 5  
  * Licensed under the Apache License, Version 2.0 (the "License");
 6  
  * you may not use this file except in compliance with the License.
 7  
  * 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  
 import java.beans.PropertyDescriptor;
 19  
 import java.lang.reflect.Method;
 20  
 import java.util.Map;
 21  
 
 22  
 import org.apache.commons.beanutils.DynaProperty;
 23  
 import org.apache.commons.betwixt.expression.DynaBeanExpression;
 24  
 import org.apache.commons.betwixt.expression.DynaBeanUpdater;
 25  
 import org.apache.commons.betwixt.expression.Expression;
 26  
 import org.apache.commons.betwixt.expression.IteratorExpression;
 27  
 import org.apache.commons.betwixt.expression.MethodExpression;
 28  
 import org.apache.commons.betwixt.expression.MethodUpdater;
 29  
 import org.apache.commons.betwixt.expression.Updater;
 30  
 import org.apache.commons.betwixt.strategy.NameMapper;
 31  
 import org.apache.commons.betwixt.strategy.SimpleTypeMapper;
 32  
 import org.apache.commons.betwixt.strategy.TypeBindingStrategy;
 33  
 import org.apache.commons.logging.Log;
 34  
 
 35  
 /** 
 36  
   * Betwixt-centric view of a bean (or pseudo-bean) property.
 37  
   * This object decouples the way that the (possibly pseudo) property introspection
 38  
   * is performed from the results of that introspection.
 39  
   *
 40  
   * @author Robert Burrell Donkin
 41  
   * @since 0.5
 42  
   */
 43  
 public class BeanProperty {
 44  
 
 45  
     /** The bean name for the property (not null) */
 46  
     private final String propertyName;
 47  
     /** The type of this property (not null) */
 48  
     private final Class propertyType;
 49  
     /** The Expression used to read values of this property (possibly null) */
 50  
     private Expression propertyExpression;
 51  
     /** The Updater used to write values of this property (possibly null) */
 52  
     private Updater propertyUpdater;
 53  
 
 54  
     /**
 55  
      * Construct a BeanProperty.
 56  
      * @param propertyName not null
 57  
      * @param propertyType not null
 58  
      * @param propertyExpression the Expression used to read the property, 
 59  
      * null if the property is not readable
 60  
      * @param propertyUpdater the Updater used to write the property, 
 61  
      * null if the property is not writable
 62  
      */
 63  0
     public BeanProperty (
 64  
                         String propertyName, 
 65  
                         Class propertyType, 
 66  
                         Expression propertyExpression, 
 67  
                         Updater propertyUpdater) {
 68  0
         this.propertyName = propertyName;
 69  0
         this.propertyType = propertyType;
 70  0
         this.propertyExpression = propertyExpression;
 71  0
         this.propertyUpdater = propertyUpdater;        
 72  0
     }
 73  
     
 74  
     /**
 75  
      * Constructs a BeanProperty from a <code>PropertyDescriptor</code>.
 76  
      * @param descriptor not null
 77  
      */
 78  13598
     public BeanProperty(PropertyDescriptor descriptor) {
 79  13598
         this.propertyName = descriptor.getName();
 80  13598
         this.propertyType = descriptor.getPropertyType();
 81  
         
 82  13598
         Method readMethod = descriptor.getReadMethod();
 83  13598
         if ( readMethod != null ) {
 84  13507
             this.propertyExpression = new MethodExpression( readMethod );
 85  
         }
 86  
         
 87  13598
         Method writeMethod = descriptor.getWriteMethod();
 88  13598
         if ( writeMethod != null ) {
 89  10582
             this.propertyUpdater = new MethodUpdater( writeMethod ); 
 90  
         }
 91  13598
     }
 92  
     
 93  
     /**
 94  
      * Constructs a BeanProperty from a <code>DynaProperty</code>
 95  
      * @param dynaProperty not null
 96  
      */
 97  104
     public BeanProperty(DynaProperty dynaProperty) {
 98  104
         this.propertyName = dynaProperty.getName();
 99  104
         this.propertyType = dynaProperty.getType();
 100  104
         this.propertyExpression = new DynaBeanExpression( propertyName );
 101  104
         this.propertyUpdater = new DynaBeanUpdater( propertyName, propertyType );
 102  104
     }
 103  
 
 104  
     /**
 105  
       * Gets the bean name for this property.
 106  
       * Betwixt will map this to an xml name.
 107  
       * @return the bean name for this property, not null
 108  
       */
 109  
     public String getPropertyName() {
 110  13611
         return propertyName;
 111  
     }
 112  
 
 113  
     /** 
 114  
       * Gets the type of this property.
 115  
       * @return the property type, not null
 116  
       */
 117  
     public Class getPropertyType() {
 118  33046
         return propertyType;
 119  
     }
 120  
     
 121  
     /**
 122  
       * Gets the expression used to read this property.
 123  
       * @return the expression to be used to read this property 
 124  
       * or null if this property is not readable.
 125  
       */
 126  
     public Expression getPropertyExpression() {
 127  13702
         return propertyExpression;
 128  
     }
 129  
     
 130  
     /**
 131  
       * Gets the updater used to write to this properyty.
 132  
       * @return the Updater to the used to write to this property
 133  
       * or null if this property is not writable.
 134  
       */ 
 135  
     public Updater getPropertyUpdater() {
 136  13702
         return propertyUpdater;
 137  
     }
 138  
     
 139  
     /** 
 140  
      * Create a XML descriptor from a bean one. 
 141  
      * Go through and work out whether it's a loop property, a primitive or a standard.
 142  
      * The class property is ignored.
 143  
      *
 144  
      * @param configuration <code>IntrospectionConfiguration</code>, not null
 145  
      * @return a correctly configured <code>NodeDescriptor</code> for the property
 146  
      */
 147  
     public Descriptor createXMLDescriptor( IntrospectionConfiguration configuration ) {
 148  13702
         Log log = configuration.getIntrospectionLog();
 149  13702
         if (log.isTraceEnabled()) {
 150  0
             log.trace("Creating descriptor for property: name="
 151  0
                 + getPropertyName() + " type=" + getPropertyType());
 152  
         }
 153  
         
 154  13702
         NodeDescriptor descriptor = null;
 155  13702
         Expression propertyExpression = getPropertyExpression();
 156  13702
         Updater propertyUpdater = getPropertyUpdater();
 157  
         
 158  13702
         if ( propertyExpression == null ) {
 159  91
             if (log.isTraceEnabled()) {
 160  0
                 log.trace( "No read method for property: name="
 161  0
                     + getPropertyName() + " type=" + getPropertyType());
 162  
             }
 163  91
             return null;
 164  
         }
 165  
         
 166  13611
         if ( log.isTraceEnabled() ) {
 167  0
             log.trace( "Property expression=" + propertyExpression );
 168  
         }
 169  
         
 170  
         // choose response from property type
 171  
         
 172  
         //TODO this big conditional should be replaced with subclasses based
 173  
         // on the type
 174  
         
 175  
         //TODO complete simple type implementation
 176  13611
         TypeBindingStrategy.BindingType bindingType 
 177  13611
                         = configuration.getTypeBindingStrategy().bindingType( getPropertyType() ) ;
 178  13611
         if ( bindingType.equals( TypeBindingStrategy.BindingType.PRIMITIVE ) ) {
 179  10036
             descriptor =
 180  20072
                 createDescriptorForPrimitive(
 181  10036
                     configuration,
 182  10036
                     propertyExpression,
 183  10036
                     propertyUpdater);
 184  
             
 185  3575
         } else if ( configuration.isLoopType( getPropertyType() ) ) {
 186  
             
 187  2249
             if (log.isTraceEnabled()) {
 188  0
                 log.trace("Loop type: " + getPropertyName());
 189  0
                 log.trace("Wrap in collections? " + configuration.isWrapCollectionsInElement());
 190  
             }
 191  
             
 192  2249
             if ( Map.class.isAssignableFrom( getPropertyType() )) {
 193  338
                 descriptor = createDescriptorForMap( configuration, propertyExpression );
 194  
             } else {
 195  
             
 196  1911
                 descriptor 
 197  1911
                     = createDescriptorForCollective( configuration, propertyUpdater, propertyExpression );
 198  
             }
 199  
         } else {
 200  1326
             if (log.isTraceEnabled()) {
 201  0
                 log.trace( "Standard property: " + getPropertyName());
 202  
             }
 203  1326
             descriptor =
 204  2652
                 createDescriptorForStandard(
 205  1326
                     propertyExpression,
 206  1326
                     propertyUpdater,
 207  1326
                     configuration);
 208  
         }
 209  
         
 210  
         
 211  
        
 212  13611
         if (log.isTraceEnabled()) {
 213  0
             log.trace( "Created descriptor:" );
 214  0
             log.trace( descriptor );
 215  
         }
 216  13611
         return descriptor;
 217  
     }
 218  
 
 219  
     /**
 220  
      * Configures descriptor (in the standard way).
 221  
      * This sets the common properties.
 222  
      * 
 223  
      * @param propertyName the name of the property mapped to the Descriptor, not null
 224  
      * @param propertyType the type of the property mapped to the Descriptor, not null
 225  
      * @param descriptor Descriptor to map, not null
 226  
      * @param configuration IntrospectionConfiguration, not null
 227  
      */
 228  
     private void configureDescriptor(
 229  
         NodeDescriptor descriptor,
 230  
         IntrospectionConfiguration configuration) {
 231  11362
         NameMapper nameMapper = configuration.getElementNameMapper();
 232  11362
         if (descriptor instanceof AttributeDescriptor) {
 233  
             // we want to use the attributemapper only when it is an attribute.. 
 234  2886
             nameMapper = configuration.getAttributeNameMapper();
 235  
         
 236  
         }           
 237  11362
         descriptor.setLocalName( nameMapper.mapTypeToElementName( propertyName ));
 238  11362
         descriptor.setPropertyName( getPropertyName() );
 239  11362
         descriptor.setPropertyType( getPropertyType() );
 240  11362
     }
 241  
     
 242  
     /**
 243  
      * Creates an <code>ElementDescriptor</code> for a standard property
 244  
      * @param propertyExpression
 245  
      * @param propertyUpdater
 246  
      * @return
 247  
      */
 248  
     private ElementDescriptor createDescriptorForStandard(
 249  
         Expression propertyExpression,
 250  
         Updater propertyUpdater, 
 251  
         IntrospectionConfiguration configuration) {
 252  
             
 253  
         ElementDescriptor result;
 254  
 
 255  1326
         ElementDescriptor elementDescriptor = new ElementDescriptor();
 256  1326
         elementDescriptor.setContextExpression( propertyExpression );
 257  1326
         if ( propertyUpdater != null ) {
 258  1014
             elementDescriptor.setUpdater( propertyUpdater );
 259  
         }
 260  
         
 261  1326
         elementDescriptor.setHollow(true);
 262  
         
 263  1326
         result = elementDescriptor;
 264  
         
 265  1326
         configureDescriptor(result, configuration);
 266  1326
         return result;
 267  
     }
 268  
 
 269  
     /**
 270  
      * Creates an ElementDescriptor for an <code>Map</code> type property
 271  
      * @param configuration
 272  
      * @param propertyExpression
 273  
      * @return
 274  
      */
 275  
     private ElementDescriptor createDescriptorForMap(
 276  
         IntrospectionConfiguration configuration,
 277  
         Expression propertyExpression) {
 278  
             
 279  
         //TODO: need to clean the element descriptors so that the wrappers are plain
 280  
         ElementDescriptor result;
 281  
         
 282  338
         ElementDescriptor entryDescriptor = new ElementDescriptor();
 283  676
         entryDescriptor.setContextExpression(
 284  338
             new IteratorExpression( propertyExpression )
 285  
         );
 286  
 
 287  338
         entryDescriptor.setLocalName( "entry" );
 288  338
         entryDescriptor.setPropertyName( getPropertyName() );
 289  338
         entryDescriptor.setPropertyType( getPropertyType() );
 290  
         
 291  
         // add elements for reading
 292  338
         ElementDescriptor keyDescriptor = new ElementDescriptor( "key" );
 293  338
         keyDescriptor.setHollow( true );
 294  338
         entryDescriptor.addElementDescriptor( keyDescriptor );
 295  
         
 296  338
         ElementDescriptor valueDescriptor = new ElementDescriptor( "value" );
 297  338
         valueDescriptor.setHollow( true );
 298  338
         entryDescriptor.addElementDescriptor( valueDescriptor );
 299  
         
 300  
         
 301  338
         if ( configuration.isWrapCollectionsInElement() ) {
 302  234
             ElementDescriptor wrappingDescriptor = new ElementDescriptor();
 303  234
             wrappingDescriptor.setElementDescriptors( new ElementDescriptor[] { entryDescriptor } );
 304  234
             NameMapper nameMapper = configuration.getElementNameMapper();   
 305  234
             wrappingDescriptor.setLocalName( nameMapper.mapTypeToElementName( propertyName ));           
 306  234
             result = wrappingDescriptor;
 307  
                         
 308  
         } else {
 309  104
             result = entryDescriptor;
 310  
         }
 311  338
         result.setCollective(true);
 312  338
         return result;
 313  
     }
 314  
 
 315  
     /**
 316  
      * Creates an <code>ElementDescriptor</code> for a collective type property
 317  
      * @param configuration
 318  
      * @param propertyUpdater, <code>Updater</code> for the property, possibly null
 319  
      * @param propertyExpression
 320  
      * @return
 321  
      */
 322  
     private ElementDescriptor createDescriptorForCollective(
 323  
         IntrospectionConfiguration configuration,
 324  
         Updater propertyUpdater,
 325  
         Expression propertyExpression) {
 326  
             
 327  
         ElementDescriptor result;
 328  
         
 329  1911
         ElementDescriptor loopDescriptor = new ElementDescriptor();
 330  3822
         loopDescriptor.setContextExpression(
 331  1911
             new IteratorExpression( propertyExpression )
 332  
         );
 333  1911
         loopDescriptor.setPropertyName(getPropertyName());
 334  1911
         loopDescriptor.setPropertyType(getPropertyType());
 335  1911
         loopDescriptor.setHollow(true);
 336  
         // set the property updater (if it exists)
 337  
         // may be overridden later by the adder
 338  1911
         loopDescriptor.setUpdater(propertyUpdater);
 339  1911
         loopDescriptor.setCollective(true);
 340  
         
 341  1911
         if ( configuration.isWrapCollectionsInElement() ) {
 342  
             // create wrapping desctiptor
 343  1378
             ElementDescriptor wrappingDescriptor = new ElementDescriptor();
 344  1378
             wrappingDescriptor.setElementDescriptors( new ElementDescriptor[] { loopDescriptor } );
 345  2756
             wrappingDescriptor.setLocalName(
 346  1378
                 configuration.getElementNameMapper().mapTypeToElementName( propertyName ));
 347  1378
             result = wrappingDescriptor;
 348  
         
 349  
         } else {   
 350  
             // unwrapped Descriptor
 351  533
             result = loopDescriptor;
 352  
         }
 353  1911
         return result;
 354  
     }
 355  
 
 356  
     /**
 357  
      * Creates a NodeDescriptor for a primitive type node
 358  
      * @param configuration
 359  
      * @param name
 360  
      * @param log
 361  
      * @param propertyExpression
 362  
      * @param propertyUpdater
 363  
      * @return
 364  
      */
 365  
     private NodeDescriptor createDescriptorForPrimitive(
 366  
         IntrospectionConfiguration configuration,
 367  
         Expression propertyExpression,
 368  
         Updater propertyUpdater) {
 369  10036
         Log log = configuration.getIntrospectionLog();
 370  
         NodeDescriptor descriptor;
 371  10036
         if (log.isTraceEnabled()) {
 372  0
             log.trace( "Primitive type: " + getPropertyName());
 373  
         }
 374  10036
         SimpleTypeMapper.Binding binding 
 375  20072
             = configuration.getSimpleTypeMapper().bind(
 376  10036
                                                         propertyName, 
 377  10036
                                                         propertyType, 
 378  10036
                                                         configuration);
 379  10036
         if ( SimpleTypeMapper.Binding.ATTRIBUTE.equals( binding )) {
 380  2886
             if (log.isTraceEnabled()) {
 381  0
                 log.trace( "Adding property as attribute: " + getPropertyName() );
 382  
             }
 383  2886
             descriptor = new AttributeDescriptor();
 384  
         } else {
 385  7150
             if (log.isTraceEnabled()) {
 386  0
                 log.trace( "Adding property as element: " + getPropertyName() );
 387  
             }
 388  7150
             descriptor = new ElementDescriptor();
 389  
         }
 390  10036
         descriptor.setTextExpression( propertyExpression );
 391  10036
         if ( propertyUpdater != null ) {
 392  9204
             descriptor.setUpdater( propertyUpdater );
 393  
         }
 394  10036
         configureDescriptor(descriptor, configuration);
 395  10036
         return descriptor;
 396  
     }
 397  
 }