Coverage Report - org.apache.commons.betwixt.digester.XMLIntrospectorHelper

Classes in this File Line Coverage Branch Coverage Complexity
XMLIntrospectorHelper
0% 
0% 
7.385

 1  
 package org.apache.commons.betwixt.digester;
 2  
 
 3  
 /*
 4  
  * Copyright 2001-2004 The Apache Software Foundation.
 5  
  * 
 6  
  * Licensed under the Apache License, Version 2.0 (the "License");
 7  
  * you may not use this file except in compliance with the License.
 8  
  * You may obtain a copy of the License at
 9  
  * 
 10  
  *      http://www.apache.org/licenses/LICENSE-2.0
 11  
  * 
 12  
  * Unless required by applicable law or agreed to in writing, software
 13  
  * distributed under the License is distributed on an "AS IS" BASIS,
 14  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 15  
  * See the License for the specific language governing permissions and
 16  
  * limitations under the License.
 17  
  */ 
 18  
 
 19  
 import java.beans.IntrospectionException;
 20  
 import java.beans.Introspector;
 21  
 import java.beans.PropertyDescriptor;
 22  
 import java.lang.reflect.Method;
 23  
 import java.util.Collection;
 24  
 import java.util.Date;
 25  
 import java.util.Enumeration;
 26  
 import java.util.HashMap;
 27  
 import java.util.Iterator;
 28  
 import java.util.Map;
 29  
 
 30  
 import org.apache.commons.betwixt.AttributeDescriptor;
 31  
 import org.apache.commons.betwixt.ElementDescriptor;
 32  
 import org.apache.commons.betwixt.NodeDescriptor;
 33  
 import org.apache.commons.betwixt.XMLIntrospector;
 34  
 import org.apache.commons.betwixt.expression.IteratorExpression;
 35  
 import org.apache.commons.betwixt.expression.MapEntryAdder;
 36  
 import org.apache.commons.betwixt.expression.MethodExpression;
 37  
 import org.apache.commons.betwixt.expression.MethodUpdater;
 38  
 import org.apache.commons.betwixt.strategy.PluralStemmer;
 39  
 import org.apache.commons.logging.Log;
 40  
 import org.apache.commons.logging.LogFactory;
 41  
 
 42  
 /** 
 43  
   * <p><code>XMLIntrospectorHelper</code> a helper class for 
 44  
   * common code shared between the digestor and introspector.</p>
 45  
   * 
 46  
   * TODO this class will be deprecated soon
 47  
   * need to move the isLoop and isPrimitiveType but probably need to
 48  
   * think about whether they need replacing with something different.
 49  
   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
 50  
   * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
 51  
   *
 52  
   * @deprecated
 53  
   */
 54  0
 public class XMLIntrospectorHelper {
 55  
 
 56  
     /** Log used for logging (Doh!) */
 57  0
     protected static Log log = LogFactory.getLog( XMLIntrospectorHelper.class );
 58  
     
 59  
     /** Base constructor */
 60  0
     public XMLIntrospectorHelper() {
 61  0
     }
 62  
     
 63  
     /**
 64  
      * <p>Gets the current logging implementation.</p>
 65  
      *
 66  
      * @return current log
 67  
      */ 
 68  
     public static Log getLog() {
 69  0
         return log;
 70  
     }
 71  
 
 72  
     /**
 73  
      * <p>Sets the current logging implementation.</p>
 74  
      *
 75  
      * @param aLog use this <code>Log</code>
 76  
      */ 
 77  
     public static void setLog(Log aLog) {
 78  0
         log = aLog;
 79  0
     }
 80  
     
 81  
 
 82  
 
 83  
     /** 
 84  
      * Process a property. 
 85  
      * Go through and work out whether it's a loop property, a primitive or a standard.
 86  
      * The class property is ignored.
 87  
      *
 88  
      * @param propertyDescriptor create a <code>NodeDescriptor</code> for this property
 89  
      * @param useAttributesForPrimitives write primitives as attributes (rather than elements)
 90  
      * @param introspector use this <code>XMLIntrospector</code>
 91  
      * @return a correctly configured <code>NodeDescriptor</code> for the property
 92  
      * @throws IntrospectionException when bean introspection fails
 93  
      * @deprecated 0.5 this method has been replaced by {@link XMLIntrospector#createDescriptor}
 94  
      */
 95  
     public static NodeDescriptor createDescriptor( 
 96  
         PropertyDescriptor propertyDescriptor, 
 97  
         boolean useAttributesForPrimitives,
 98  
         XMLIntrospector introspector
 99  
     ) throws IntrospectionException {
 100  0
         String name = propertyDescriptor.getName();
 101  0
         Class type = propertyDescriptor.getPropertyType();
 102  
        
 103  0
         if (log.isTraceEnabled()) {
 104  0
             log.trace("Creating descriptor for property: name="
 105  0
                 + name + " type=" + type);
 106  
         }
 107  
         
 108  0
         NodeDescriptor nodeDescriptor = null;
 109  0
         Method readMethod = propertyDescriptor.getReadMethod();
 110  0
         Method writeMethod = propertyDescriptor.getWriteMethod();
 111  
         
 112  0
         if ( readMethod == null ) {
 113  0
             if (log.isTraceEnabled()) {
 114  0
                 log.trace( "No read method for property: name="
 115  0
                     + name + " type=" + type);
 116  
             }
 117  0
             return null;
 118  
         }
 119  
         
 120  0
         if ( log.isTraceEnabled() ) {
 121  0
             log.trace( "Read method=" + readMethod.getName() );
 122  
         }
 123  
         
 124  
         // choose response from property type
 125  
         
 126  
         // XXX: ignore class property ??
 127  0
         if ( Class.class.equals( type ) && "class".equals( name ) ) {
 128  0
             log.trace( "Ignoring class property" );
 129  0
             return null;
 130  
         }
 131  0
         if ( isPrimitiveType( type ) ) {
 132  0
             if (log.isTraceEnabled()) {
 133  0
                 log.trace( "Primitive type: " + name);
 134  
             }
 135  0
             if ( useAttributesForPrimitives ) {
 136  0
                 if (log.isTraceEnabled()) {
 137  0
                     log.trace( "Adding property as attribute: " + name );
 138  
                 }
 139  0
                 nodeDescriptor = new AttributeDescriptor();
 140  
             } else {
 141  0
                 if (log.isTraceEnabled()) {
 142  0
                     log.trace( "Adding property as element: " + name );
 143  
                 }
 144  0
                 nodeDescriptor = new ElementDescriptor(true);
 145  
             }
 146  0
             nodeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
 147  
             
 148  0
             if ( writeMethod != null ) {
 149  0
                 nodeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
 150  
             }
 151  0
         } else if ( isLoopType( type ) ) {
 152  0
             if (log.isTraceEnabled()) {
 153  0
                 log.trace("Loop type: " + name);
 154  0
                 log.trace("Wrap in collections? " + introspector.isWrapCollectionsInElement());
 155  
             }
 156  0
             ElementDescriptor loopDescriptor = new ElementDescriptor();
 157  0
             loopDescriptor.setContextExpression(
 158  0
                 new IteratorExpression( new MethodExpression( readMethod ) )
 159  
             );
 160  0
             loopDescriptor.setWrapCollectionsInElement(
 161  0
                         introspector.isWrapCollectionsInElement());
 162  
             // XXX: need to support some kind of 'add' or handle arrays, Lists or indexed properties
 163  
             //loopDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
 164  0
             if ( Map.class.isAssignableFrom( type ) ) {
 165  0
                 loopDescriptor.setQualifiedName( "entry" );
 166  
                 // add elements for reading
 167  0
                 loopDescriptor.addElementDescriptor( new ElementDescriptor( "key" ) );
 168  0
                 loopDescriptor.addElementDescriptor( new ElementDescriptor( "value" ) );
 169  
             }
 170  
 
 171  0
             ElementDescriptor elementDescriptor = new ElementDescriptor();
 172  0
             elementDescriptor.setWrapCollectionsInElement(
 173  0
                         introspector.isWrapCollectionsInElement());
 174  0
             elementDescriptor.setElementDescriptors( new ElementDescriptor[] { loopDescriptor } );
 175  
             
 176  0
             nodeDescriptor = elementDescriptor;            
 177  
         } else {
 178  0
             if (log.isTraceEnabled()) {
 179  0
                 log.trace( "Standard property: " + name);
 180  
             }
 181  0
             ElementDescriptor elementDescriptor = new ElementDescriptor();
 182  0
             elementDescriptor.setContextExpression( new MethodExpression( readMethod ) );
 183  0
             if ( writeMethod != null ) {
 184  0
                 elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
 185  
             }
 186  
             
 187  0
             nodeDescriptor = elementDescriptor;          
 188  
         }
 189  
 
 190  0
         if (nodeDescriptor instanceof AttributeDescriptor) {
 191  
             // we want to use the attributemapper only when it is an attribute.. 
 192  0
             nodeDescriptor.setLocalName( 
 193  0
                 introspector.getAttributeNameMapper().mapTypeToElementName( name ) );
 194  
         } else {
 195  0
             nodeDescriptor.setLocalName( 
 196  0
                 introspector.getElementNameMapper().mapTypeToElementName( name ) );
 197  
         }        
 198  
   
 199  0
         nodeDescriptor.setPropertyName( propertyDescriptor.getName() );
 200  0
         nodeDescriptor.setPropertyType( type );        
 201  
         
 202  
         // XXX: associate more bean information with the descriptor?
 203  
         //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() );
 204  
         //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
 205  
         
 206  0
         if (log.isTraceEnabled()) {
 207  0
             log.trace("Created descriptor:");
 208  0
             log.trace(nodeDescriptor);
 209  
         }
 210  0
         return nodeDescriptor;
 211  
     }
 212  
     
 213  
     /**
 214  
      * Configure an <code>ElementDescriptor</code> from a <code>PropertyDescriptor</code>.
 215  
      * This uses default element updater (the write method of the property).
 216  
      *
 217  
      * @param elementDescriptor configure this <code>ElementDescriptor</code>
 218  
      * @param propertyDescriptor configure from this <code>PropertyDescriptor</code>
 219  
      * @deprecated 0.6 unused
 220  
      */
 221  
     public static void configureProperty( 
 222  
                                     ElementDescriptor elementDescriptor, 
 223  
                                     PropertyDescriptor propertyDescriptor ) {
 224  
                                     
 225  0
         configureProperty( elementDescriptor, propertyDescriptor, null, null);
 226  0
     }
 227  
                                     
 228  
     /**
 229  
      * Configure an <code>ElementDescriptor</code> from a <code>PropertyDescriptor</code>.
 230  
      * A custom update method may be set.
 231  
      *
 232  
      * @param elementDescriptor configure this <code>ElementDescriptor</code>
 233  
      * @param propertyDescriptor configure from this <code>PropertyDescriptor</code>
 234  
      * @param updateMethodName the name of the custom updater method to user. 
 235  
      * If null, then then 
 236  
      * @param beanClass the <code>Class</code> from which the update method should be found.
 237  
      * This may be null only when <code>updateMethodName</code> is also null.
 238  
      * @since 0.5
 239  
      * @deprecated 0.6 moved into ElementRule
 240  
      */
 241  
     public static void configureProperty( 
 242  
                                     ElementDescriptor elementDescriptor, 
 243  
                                     PropertyDescriptor propertyDescriptor,
 244  
                                     String updateMethodName,
 245  
                                     Class beanClass ) {
 246  
         
 247  0
         Class type = propertyDescriptor.getPropertyType();
 248  0
         Method readMethod = propertyDescriptor.getReadMethod();
 249  0
         Method writeMethod = propertyDescriptor.getWriteMethod();
 250  
         
 251  0
         elementDescriptor.setLocalName( propertyDescriptor.getName() );
 252  0
         elementDescriptor.setPropertyType( type );        
 253  
         
 254  
         // XXX: associate more bean information with the descriptor?
 255  
         //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() );
 256  
         //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
 257  
         
 258  0
         if ( readMethod == null ) {
 259  0
             log.trace( "No read method" );
 260  0
             return;
 261  
         }
 262  
         
 263  0
         if ( log.isTraceEnabled() ) {
 264  0
             log.trace( "Read method=" + readMethod.getName() );
 265  
         }
 266  
         
 267  
         // choose response from property type
 268  
         
 269  
         // XXX: ignore class property ??
 270  0
         if ( Class.class.equals( type ) && "class".equals( propertyDescriptor.getName() ) ) {
 271  0
             log.trace( "Ignoring class property" );
 272  0
             return;
 273  
         }
 274  0
         if ( isPrimitiveType( type ) ) {
 275  0
             elementDescriptor.setTextExpression( new MethodExpression( readMethod ) );
 276  
             
 277  0
         } else if ( isLoopType( type ) ) {
 278  0
             log.trace("Loop type ??");
 279  
             
 280  
             // don't wrap this in an extra element as its specified in the 
 281  
             // XML descriptor so no need.            
 282  0
             elementDescriptor.setContextExpression(
 283  0
                 new IteratorExpression( new MethodExpression( readMethod ) )
 284  
             );
 285  
 
 286  0
             writeMethod = null;
 287  
         } else {
 288  0
             log.trace( "Standard property" );
 289  0
             elementDescriptor.setContextExpression( new MethodExpression( readMethod ) );
 290  
         }
 291  
     
 292  
         // see if we have a custom method update name
 293  0
         if (updateMethodName == null) {
 294  
             // set standard write method
 295  0
             if ( writeMethod != null ) {
 296  0
                 elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
 297  
             }
 298  
             
 299  
         } else {
 300  
             // see if we can find and set the custom method
 301  0
             if ( log.isTraceEnabled() ) {
 302  0
                 log.trace( "Finding custom method: " );
 303  0
                 log.trace( "  on:" + beanClass );
 304  0
                 log.trace( "  name:" + updateMethodName );
 305  
             }
 306  
             
 307  0
             Method updateMethod = null;
 308  0
             Method[] methods = beanClass.getMethods();
 309  0
             for ( int i = 0, size = methods.length; i < size; i++ ) {
 310  0
                 Method method = methods[i];
 311  0
                 if ( updateMethodName.equals( method.getName() ) ) {
 312  
                     // we have a matching name
 313  
                     // check paramters are correct
 314  0
                     if (methods[i].getParameterTypes().length == 1) {
 315  
                         // we'll use first match
 316  0
                         updateMethod = methods[i];
 317  0
                         if ( log.isTraceEnabled() ) {
 318  0
                             log.trace("Matched method:" + updateMethod);
 319  
                         } 
 320  
                         // done since we're using the first match
 321  0
                         break;
 322  
                     }
 323  
                 }
 324  
             }
 325  
             
 326  0
             if (updateMethod == null) {
 327  0
                 if ( log.isInfoEnabled() ) {
 328  
                     
 329  0
                     log.info("No method with name '" + updateMethodName + "' found for update");
 330  
                 }
 331  
             } else {
 332  
     
 333  0
                 elementDescriptor.setUpdater( new MethodUpdater( updateMethod ) );
 334  0
                 elementDescriptor.setSingularPropertyType( updateMethod.getParameterTypes()[0] );
 335  0
                 if ( log.isTraceEnabled() ) {
 336  0
                     log.trace( "Set custom updater on " + elementDescriptor);
 337  
                 }
 338  
             }
 339  
         }
 340  0
     }
 341  
     
 342  
     /**
 343  
      * Configure an <code>AttributeDescriptor</code> from a <code>PropertyDescriptor</code>
 344  
      *
 345  
      * @param attributeDescriptor configure this <code>AttributeDescriptor</code>
 346  
      * @param propertyDescriptor configure from this <code>PropertyDescriptor</code>
 347  
      * @deprecated 0.6 moved into AttributeRule
 348  
      */
 349  
     public static void configureProperty( 
 350  
                                     AttributeDescriptor attributeDescriptor, 
 351  
                                     PropertyDescriptor propertyDescriptor ) {
 352  0
         Class type = propertyDescriptor.getPropertyType();
 353  0
         Method readMethod = propertyDescriptor.getReadMethod();
 354  0
         Method writeMethod = propertyDescriptor.getWriteMethod();
 355  
         
 356  0
         if ( readMethod == null ) {
 357  0
             log.trace( "No read method" );
 358  0
             return;
 359  
         }
 360  
         
 361  0
         if ( log.isTraceEnabled() ) {
 362  0
             log.trace( "Read method=" + readMethod );
 363  
         }
 364  
         
 365  
         // choose response from property type
 366  
         
 367  
         // XXX: ignore class property ??
 368  0
         if ( Class.class.equals( type ) && "class".equals( propertyDescriptor.getName() ) ) {
 369  0
             log.trace( "Ignoring class property" );
 370  0
             return;
 371  
         }
 372  0
         if ( isLoopType( type ) ) {
 373  0
             log.warn( "Using loop type for an attribute. Type = " 
 374  0
                     + type.getName() + " attribute: " + attributeDescriptor.getQualifiedName() );
 375  
         }
 376  
 
 377  0
         log.trace( "Standard property" );
 378  0
         attributeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
 379  
         
 380  0
         if ( writeMethod != null ) {
 381  0
             attributeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
 382  
         }
 383  
         
 384  0
         attributeDescriptor.setLocalName( propertyDescriptor.getName() );
 385  0
         attributeDescriptor.setPropertyType( type );        
 386  
         
 387  
         // XXX: associate more bean information with the descriptor?
 388  
         //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() );
 389  
         //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
 390  0
     }
 391  
     
 392  
 
 393  
     /** 
 394  
      * Add any addPropety(PropertyType) methods as Updaters 
 395  
      * which are often used for 1-N relationships in beans.
 396  
      * <br>
 397  
      * The tricky part here is finding which ElementDescriptor corresponds
 398  
      * to the method. e.g. a property 'items' might have an Element descriptor
 399  
      * which the method addItem() should match to. 
 400  
      * <br>
 401  
      * So the algorithm we'll use 
 402  
      * by default is to take the decapitalized name of the property being added
 403  
      * and find the first ElementDescriptor that matches the property starting with
 404  
      * the string. This should work for most use cases. 
 405  
      * e.g. addChild() would match the children property.
 406  
      *
 407  
      * @param introspector use this <code>XMLIntrospector</code> for introspection
 408  
      * @param rootDescriptor add defaults to this descriptor
 409  
      * @param beanClass the <code>Class</code> to which descriptor corresponds
 410  
      * @deprecated 0.6 use the method in XMLIntrospector instead
 411  
      */
 412  
     public static void defaultAddMethods( 
 413  
                                             XMLIntrospector introspector, 
 414  
                                             ElementDescriptor rootDescriptor, 
 415  
                                             Class beanClass ) {
 416  
         // lets iterate over all methods looking for one of the form
 417  
         // add*(PropertyType)
 418  0
         if ( beanClass != null ) {
 419  0
             Method[] methods = beanClass.getMethods();
 420  0
             for ( int i = 0, size = methods.length; i < size; i++ ) {
 421  0
                 Method method = methods[i];
 422  0
                 String name = method.getName();
 423  0
                 if ( name.startsWith( "add" ) ) {
 424  
                     // XXX: should we filter out non-void returning methods?
 425  
                     // some beans will return something as a helper
 426  0
                     Class[] types = method.getParameterTypes();
 427  0
                     if ( types != null) {
 428  0
                         if ( log.isTraceEnabled() ) {
 429  0
                             log.trace("Searching for match for " + method);
 430  
                         }
 431  
                         
 432  0
                         if ( ( types.length == 1 ) || types.length == 2 ) {
 433  0
                             String propertyName = Introspector.decapitalize( name.substring(3) );
 434  0
                             if (propertyName.length() == 0)
 435  0
                                 continue;
 436  0
                             if ( log.isTraceEnabled() ) {
 437  0
                                 log.trace( name + "->" + propertyName );
 438  
                             }
 439  
     
 440  
                             // now lets try find the ElementDescriptor which displays
 441  
                             // a property which starts with propertyName
 442  
                             // and if so, we'll set a new Updater on it if there
 443  
                             // is not one already
 444  0
                             ElementDescriptor descriptor = 
 445  0
                                 findGetCollectionDescriptor( 
 446  0
                                                             introspector, 
 447  0
                                                             rootDescriptor, 
 448  0
                                                             propertyName );
 449  
     
 450  0
                             if ( log.isDebugEnabled() ) {
 451  0
                                 log.debug( "!! " + propertyName + " -> " + descriptor );
 452  0
                                 log.debug( "!! " + name + " -> " 
 453  0
                                 + (descriptor!=null?descriptor.getPropertyName():"") );
 454  
                             }
 455  0
                             if ( descriptor != null ) {
 456  0
                                 boolean isMapDescriptor 
 457  0
                                     = Map.class.isAssignableFrom( descriptor.getPropertyType() );
 458  0
                                 if ( !isMapDescriptor && types.length == 1 ) {
 459  
                                     // this may match a standard collection or iteration
 460  0
                                     log.trace("Matching collection or iteration");
 461  
                                     
 462  0
                                     descriptor.setUpdater( new MethodUpdater( method ) );
 463  0
                                     descriptor.setSingularPropertyType( types[0] );
 464  
                                     
 465  0
                                     if ( log.isDebugEnabled() ) {
 466  0
                                         log.debug( "!! " + method);
 467  0
                                         log.debug( "!! " + types[0]);
 468  
                                     }
 469  
                                     
 470  
                                     // is there a child element with no localName
 471  0
                                     ElementDescriptor[] children 
 472  0
                                         = descriptor.getElementDescriptors();
 473  0
                                     if ( children != null && children.length > 0 ) {
 474  0
                                         ElementDescriptor child = children[0];
 475  0
                                         String localName = child.getLocalName();
 476  0
                                         if ( localName == null || localName.length() == 0 ) {
 477  0
                                             child.setLocalName( 
 478  0
                                                 introspector.getElementNameMapper()
 479  0
                                                     .mapTypeToElementName( propertyName ) );
 480  
                                         }
 481  
                                     }
 482  
 
 483  0
                                 } else if ( isMapDescriptor && types.length == 2 ) {
 484  
                                     // this may match a map
 485  0
                                     log.trace("Matching map");
 486  0
                                     ElementDescriptor[] children 
 487  0
                                         = descriptor.getElementDescriptors();
 488  
                                     // see if the descriptor's been set up properly
 489  0
                                     if ( children.length == 0 ) {
 490  
                                         
 491  0
                                         log.info(
 492  0
                                             "'entry' descriptor is missing for map. "
 493  
                                             + "Updaters cannot be set");
 494  
                                         
 495  
                                     } else {
 496  
                                         // loop through grandchildren 
 497  
                                         // adding updaters for key and value
 498  0
                                         ElementDescriptor[] grandchildren
 499  0
                                             = children[0].getElementDescriptors();
 500  0
                                         MapEntryAdder adder = new MapEntryAdder(method);
 501  
                                         for ( 
 502  0
                                             int n=0, 
 503  0
                                                 noOfGrandChildren = grandchildren.length;
 504  0
                                             n < noOfGrandChildren;
 505  0
                                             n++ ) {
 506  0
                                             if ( "key".equals( 
 507  0
                                                     grandchildren[n].getLocalName() ) ) {
 508  
                                             
 509  0
                                                 grandchildren[n].setUpdater( 
 510  0
                                                                 adder.getKeyUpdater() );
 511  0
                                                 grandchildren[n].setSingularPropertyType( 
 512  0
                                                                 types[0] );
 513  0
                                                 if ( log.isTraceEnabled() ) {
 514  0
                                                     log.trace(
 515  0
                                                         "Key descriptor: " + grandchildren[n]);
 516  
                                                 }                                               
 517  
                                                 
 518  
                                             } else if ( 
 519  0
                                                 "value".equals( 
 520  0
                                                     grandchildren[n].getLocalName() ) ) {
 521  
 
 522  0
                                                 grandchildren[n].setUpdater( 
 523  0
                                                                     adder.getValueUpdater() );
 524  0
                                                 grandchildren[n].setSingularPropertyType( 
 525  0
                                                                     types[1] );
 526  0
                                                 if ( log.isTraceEnabled() ) {
 527  0
                                                     log.trace(
 528  0
                                                         "Value descriptor: " + grandchildren[n]);
 529  
                                                 }
 530  
                                             }
 531  
                                         }
 532  
                                     }
 533  
                                 }
 534  
                             } else {
 535  0
                                 if ( log.isDebugEnabled() ) {
 536  0
                                     log.debug( 
 537  0
                                         "Could not find an ElementDescriptor with property name: " 
 538  0
                                         + propertyName + " to attach the add method: " + method 
 539  
                                     );
 540  
                                 }
 541  
                             }
 542  
                         }
 543  
                     } 
 544  
                 }
 545  
             }
 546  
         }
 547  0
     }
 548  
     
 549  
     /** 
 550  
      * Is this a loop type class?
 551  
      *
 552  
      * @param type is this <code>Class</code> a loop type?
 553  
      * @return true if the type is a loop type, or if type is null 
 554  
      * @deprecated 0.7 replaced by {@link org.apache.commons.betwixt.IntrospectionConfiguration#isLoopType(Class)}
 555  
      */
 556  
     public static boolean isLoopType(Class type) {
 557  
         // check for NPEs
 558  0
         if (type == null) {
 559  0
             log.trace("isLoopType: type is null");
 560  0
             return false;
 561  
         }
 562  0
         return type.isArray() 
 563  0
             || Map.class.isAssignableFrom( type ) 
 564  0
             || Collection.class.isAssignableFrom( type ) 
 565  0
             || Enumeration.class.isAssignableFrom( type ) 
 566  0
             || Iterator.class.isAssignableFrom( type );
 567  
     }
 568  
     
 569  
     
 570  
     /**
 571  
      * Is this a primitive type? 
 572  
      *      
 573  
      * TODO: this method will probably be removed when primitive types
 574  
      * are subsumed into the simple type concept.
 575  
      * This needs moving into XMLIntrospector so that the list of simple
 576  
      * type can be varied.
 577  
      * @param type is this <code>Class<code> a primitive type?
 578  
      * @return true for primitive types 
 579  
      * @deprecated 0.6 replaced by {@link org.apache.commons.betwixt.strategy.TypeBindingStrategy}
 580  
      */
 581  
     public static boolean isPrimitiveType(Class type) {
 582  0
         if ( type == null ) {
 583  0
             return false;
 584  
             
 585  0
         } else if ( type.isPrimitive() ) {
 586  0
             return true;
 587  
             
 588  0
         } else if ( type.equals( Object.class ) ) {
 589  0
             return false;
 590  
         }
 591  0
         return type.getName().startsWith( "java.lang." )
 592  0
             || Number.class.isAssignableFrom( type ) 
 593  0
             || String.class.isAssignableFrom( type ) 
 594  0
             || Date.class.isAssignableFrom( type ) 
 595  0
             || java.sql.Date.class.isAssignableFrom( type ) 
 596  0
             || java.sql.Time.class.isAssignableFrom( type ) 
 597  0
             || java.sql.Timestamp.class.isAssignableFrom( type ) 
 598  0
             || java.math.BigDecimal.class.isAssignableFrom( type ) 
 599  0
             || java.math.BigInteger.class.isAssignableFrom( type );
 600  
     }
 601  
     
 602  
     // Implementation methods
 603  
     //-------------------------------------------------------------------------    
 604  
     
 605  
     /** 
 606  
      * Attempts to find the element descriptor for the getter property that 
 607  
      * typically matches a collection or array. The property name is used
 608  
      * to match. e.g. if an addChild() method is detected the 
 609  
      * descriptor for the 'children' getter property should be returned.
 610  
      *
 611  
      * @param introspector use this <code>XMLIntrospector</code>
 612  
      * @param rootDescriptor the <code>ElementDescriptor</code> whose child element will be
 613  
      * searched for a match
 614  
      * @param propertyName the name of the 'adder' method to match
 615  
      * @return <code>ElementDescriptor</code> for the matching getter 
 616  
      * @deprecated 0.6 moved into XMLIntrospector
 617  
      */
 618  
     protected static ElementDescriptor findGetCollectionDescriptor( 
 619  
                                                 XMLIntrospector introspector, 
 620  
                                                 ElementDescriptor rootDescriptor, 
 621  
                                                 String propertyName ) {
 622  
         // create the Map of propertyName -> descriptor that the PluralStemmer will choose
 623  0
         Map map = new HashMap();
 624  
         //String propertyName = rootDescriptor.getPropertyName();
 625  0
         if ( log.isTraceEnabled() ) {
 626  0
             log.trace( "findPluralDescriptor( " + propertyName 
 627  0
                 + " ):root property name=" + rootDescriptor.getPropertyName() );
 628  
         }
 629  
         
 630  0
         if (rootDescriptor.getPropertyName() != null) {
 631  0
             map.put(propertyName, rootDescriptor);
 632  
         }
 633  0
         makeElementDescriptorMap( rootDescriptor, map );
 634  
         
 635  0
         PluralStemmer stemmer = introspector.getPluralStemmer();
 636  0
         ElementDescriptor elementDescriptor = stemmer.findPluralDescriptor( propertyName, map );
 637  
         
 638  0
         if ( log.isTraceEnabled() ) {
 639  0
             log.trace( 
 640  0
                 "findPluralDescriptor( " + propertyName 
 641  0
                     + " ):ElementDescriptor=" + elementDescriptor );
 642  
         }
 643  
         
 644  0
         return elementDescriptor;
 645  
     }
 646  
 
 647  
     /**
 648  
      * Creates a map where the keys are the property names and the values are the ElementDescriptors
 649  
      * 
 650  
      * @param rootDescriptor the values of the maps are the children of this 
 651  
      * <code>ElementDescriptor</code> index by their property names
 652  
      * @param map the map to which the elements will be added
 653  
      * @deprecated 0.6 moved into XMLIntrospector
 654  
      */
 655  
     protected static void makeElementDescriptorMap( ElementDescriptor rootDescriptor, Map map ) {
 656  0
         ElementDescriptor[] children = rootDescriptor.getElementDescriptors();
 657  0
         if ( children != null ) {
 658  0
             for ( int i = 0, size = children.length; i < size; i++ ) {
 659  0
                 ElementDescriptor child = children[i];                
 660  0
                 String propertyName = child.getPropertyName();                
 661  0
                 if ( propertyName != null ) {
 662  0
                     map.put( propertyName, child );
 663  
                 }
 664  0
                 makeElementDescriptorMap( child, map );
 665  
             }
 666  
         }
 667  0
     }
 668  
 
 669  
     /**
 670  
      * Traverse the tree of element descriptors and find the oldValue and swap it with the newValue.
 671  
      * This would be much easier to do if ElementDescriptor supported a parent relationship.
 672  
      *
 673  
      * @param rootDescriptor traverse child graph for this <code>ElementDescriptor</code>
 674  
      * @param oldValue replace this <code>ElementDescriptor</code>
 675  
      * @param newValue replace with this <code>ElementDescriptor</code>
 676  
      * @deprecated 0.6 now unused
 677  
      */     
 678  
     protected static void swapDescriptor( 
 679  
                                 ElementDescriptor rootDescriptor, 
 680  
                                 ElementDescriptor oldValue, 
 681  
                                 ElementDescriptor newValue ) {
 682  0
         ElementDescriptor[] children = rootDescriptor.getElementDescriptors();
 683  0
         if ( children != null ) {
 684  0
             for ( int i = 0, size = children.length; i < size; i++ ) {
 685  0
                 ElementDescriptor child = children[i];
 686  0
                 if ( child == oldValue ) {
 687  0
                     children[i] = newValue;
 688  0
                     break;
 689  
                 }
 690  0
                 swapDescriptor( child, oldValue, newValue );
 691  
             }
 692  
         }
 693  0
     }
 694  
 }