Coverage Report - org.apache.commons.betwixt.io.BeanCreateRule

Classes in this File Line Coverage Branch Coverage Complexity
BeanCreateRule
0% 
0% 
3.261

 1  0
 /*
 2  
  * Copyright 2001-2004 The Apache Software Foundation.
 3  
  * 
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */ 
 16  
 package org.apache.commons.betwixt.io;
 17  
 
 18  
 import java.util.HashMap;
 19  
 import java.util.List;
 20  
 import java.util.Map;
 21  
 
 22  
 import org.apache.commons.betwixt.AttributeDescriptor;
 23  
 import org.apache.commons.betwixt.ElementDescriptor;
 24  
 import org.apache.commons.betwixt.XMLBeanInfo;
 25  
 import org.apache.commons.betwixt.XMLIntrospector;
 26  
 import org.apache.commons.betwixt.digester.XMLIntrospectorHelper;
 27  
 import org.apache.commons.betwixt.expression.Context;
 28  
 import org.apache.commons.betwixt.expression.MethodUpdater;
 29  
 import org.apache.commons.betwixt.expression.Updater;
 30  
 import org.apache.commons.digester.Rule;
 31  
 import org.apache.commons.digester.Rules;
 32  
 import org.apache.commons.logging.Log;
 33  
 import org.apache.commons.logging.LogFactory;
 34  
 import org.xml.sax.Attributes;
 35  
 
 36  
 /** <p><code>BeanCreateRule</code> is a Digester Rule for creating beans
 37  
   * from the betwixt XML metadata.</p>
 38  
   *
 39  
   * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
 40  
   * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
 41  
   * @deprecated 0.5 this Rule does not allowed good integration with other Rules -
 42  
   * use {@link BeanRuleSet} instead.
 43  
   */
 44  0
 public class BeanCreateRule extends Rule {
 45  
 
 46  
     /** Logger */
 47  0
     private static Log log = LogFactory.getLog( BeanCreateRule.class );
 48  
     
 49  
     /** 
 50  
      * Set log to be used by <code>BeanCreateRule</code> instances 
 51  
      * @param aLog the <code>Log</code> implementation for this class to log to
 52  
      */
 53  
     public static void setLog(Log aLog) {
 54  0
         log = aLog;
 55  0
     }
 56  
     
 57  
     /** The descriptor of this element */
 58  
     private ElementDescriptor descriptor;
 59  
     /** The Context used when evaluating Updaters */
 60  0
     private Context context;
 61  
     /** Have we added our child rules to the digester? */
 62  
     private boolean addedChildren;
 63  
     /** In this begin-end loop did we actually create a new bean */
 64  
     private boolean createdBean;
 65  
     /** The type of the bean to create */
 66  
     private Class beanClass;
 67  
     /** The prefix added to digester rules */
 68  
     private String pathPrefix;
 69  
     /** Use id's to match beans? */
 70  0
     private boolean matchIDs = true;
 71  
     /** allows an attribute to be specified to overload the types of beans used */
 72  0
     private String classNameAttribute = "className";
 73  
     
 74  
     /**
 75  
      * Convenience constructor which uses <code>ID's</code> for matching.
 76  
      *
 77  
      * @param descriptor the <code>ElementDescriptor</code> describing the element mapped
 78  
      * @param beanClass the <code>Class</code> to be created
 79  
      * @param pathPrefix the digester style path
 80  
      */
 81  
     public BeanCreateRule(
 82  
                             ElementDescriptor descriptor, 
 83  
                             Class beanClass, 
 84  
                             String pathPrefix ) {
 85  0
         this( descriptor, beanClass, pathPrefix, true );
 86  0
     }
 87  
     
 88  
     /**
 89  
      * Constructor taking a class.
 90  
      *
 91  
      * @param descriptor the <code>ElementDescriptor</code> describing the element mapped
 92  
      * @param beanClass the <code>Class</code> to be created
 93  
      * @param pathPrefix the digester style path
 94  
      * @param matchIDs should <code>ID</code>/<code>IDREF</code>'s be used for matching
 95  
      */
 96  
     public BeanCreateRule(
 97  
                             ElementDescriptor descriptor, 
 98  
                             Class beanClass, 
 99  
                             String pathPrefix, 
 100  
                             boolean matchIDs ) {
 101  0
         this( 
 102  0
                 descriptor, 
 103  0
                 beanClass, 
 104  0
                 new Context(), 
 105  0
                 pathPrefix,
 106  0
                 matchIDs);
 107  0
     }
 108  
     
 109  
     /**
 110  
      * Convenience constructor which uses <code>ID's</code> for matching.
 111  
      *
 112  
      * @param descriptor the <code>ElementDescriptor</code> describing the element mapped
 113  
      * @param beanClass the <code>Class</code> to be created
 114  
      */    
 115  
     public BeanCreateRule( ElementDescriptor descriptor, Class beanClass ) {
 116  0
         this( descriptor, beanClass, true );
 117  0
     }
 118  
     
 119  
     /** 
 120  
      * Constructor uses standard qualified name.
 121  
      * 
 122  
      * @param descriptor the <code>ElementDescriptor</code> describing the element mapped
 123  
      * @param beanClass the <code>Class</code> to be created
 124  
      * @param matchIDs should <code>ID</code>/<code>IDREF</code>'s be used for matching
 125  
      */
 126  
     public BeanCreateRule( ElementDescriptor descriptor, Class beanClass, boolean matchIDs ) {
 127  0
         this( descriptor, beanClass, descriptor.getQualifiedName() + "/" , matchIDs );
 128  0
     }
 129  
   
 130  
     /**
 131  
      * Convenience constructor which uses <code>ID's</code> for match.
 132  
      *
 133  
      * @param descriptor the <code>ElementDescriptor</code> describing the element mapped
 134  
      * @param context the <code>Context</code> to be used to evaluate expressions
 135  
      * @param pathPrefix the digester path prefix
 136  
      */   
 137  
     public BeanCreateRule(
 138  
                             ElementDescriptor descriptor, 
 139  
                             Context context, 
 140  
                             String pathPrefix ) {    
 141  0
         this( descriptor, context, pathPrefix, true );
 142  0
     }
 143  
     
 144  
     /**
 145  
      * Constructor taking a context.
 146  
      *
 147  
      * @param descriptor the <code>ElementDescriptor</code> describing the element mapped
 148  
      * @param context the <code>Context</code> to be used to evaluate expressions
 149  
      * @param pathPrefix the digester path prefix
 150  
      * @param matchIDs should <code>ID</code>/<code>IDREF</code>'s be used for matching
 151  
      */
 152  
     public BeanCreateRule(
 153  
                             ElementDescriptor descriptor, 
 154  
                             Context context, 
 155  
                             String pathPrefix,
 156  
                             boolean matchIDs ) {
 157  0
         this( 
 158  0
                 descriptor, 
 159  0
                 descriptor.getSingularPropertyType(), 
 160  0
                 context, 
 161  0
                 pathPrefix,
 162  0
                 matchIDs );
 163  0
     }
 164  
     
 165  
     /**
 166  
      * Base constructor (used by other constructors).
 167  
      *
 168  
      * @param descriptor the <code>ElementDescriptor</code> describing the element mapped
 169  
      * @param beanClass the <code>Class</code> of the bean to be created
 170  
      * @param context the <code>Context</code> to be used to evaluate expressions
 171  
      * @param pathPrefix the digester path prefix
 172  
      * @param matchIDs should <code>ID</code>/<code>IDREF</code>'s be used for matching
 173  
      */
 174  0
     private BeanCreateRule(
 175  
                             ElementDescriptor descriptor, 
 176  
                             Class beanClass,
 177  
                             Context context, 
 178  
                             String pathPrefix,
 179  
                             boolean matchIDs ) {
 180  0
         this.descriptor = descriptor;        
 181  0
         this.context = context;
 182  0
         this.beanClass = beanClass;
 183  0
         this.pathPrefix = pathPrefix;
 184  0
         this.matchIDs = matchIDs;
 185  0
         if (log.isTraceEnabled()) {
 186  0
             log.trace("Created bean create rule");
 187  0
             log.trace("Descriptor=" + descriptor);
 188  0
             log.trace("Class=" + beanClass);
 189  0
             log.trace("Path prefix=" + pathPrefix);
 190  
         }
 191  0
     }
 192  
     
 193  
     
 194  
         
 195  
     // Rule interface
 196  
     //-------------------------------------------------------------------------    
 197  
     
 198  
     /**
 199  
      * Process the beginning of this element.
 200  
      *
 201  
      * @param attributes The attribute list of this element
 202  
      */
 203  
     public void begin(Attributes attributes) {
 204  0
         log.debug( "Called with descriptor: " + descriptor 
 205  0
                     + " propertyType: " + descriptor.getPropertyType() );
 206  
         
 207  0
         if (log.isTraceEnabled()) {
 208  0
             int attributesLength = attributes.getLength();
 209  0
             if (attributesLength > 0) {
 210  0
                 log.trace("Attributes:");
 211  
             }
 212  0
             for (int i=0, size=attributesLength; i<size; i++) {
 213  0
                 log.trace("Local:" + attributes.getLocalName(i));
 214  0
                 log.trace("URI:" + attributes.getURI(i));
 215  0
                 log.trace("QName:" + attributes.getQName(i));
 216  
             }
 217  
         }
 218  
         
 219  
 
 220  
         
 221  
         // XXX: if a single rule instance gets reused and nesting occurs
 222  
         // XXX: we should probably use a stack of booleans to test if we created a bean
 223  
         // XXX: or let digester take nulls, which would be easier for us ;-)
 224  0
         createdBean = false;
 225  
                 
 226  0
         Object instance = null;
 227  0
         if ( beanClass != null ) {
 228  0
             instance = createBean(attributes);
 229  0
             if ( instance != null ) {
 230  0
                 createdBean = true;
 231  
 
 232  0
                 context.setBean( instance );
 233  0
                 digester.push(instance);
 234  
                 
 235  
         
 236  
                 // if we are a reference to a type we should lookup the original
 237  
                 // as this ElementDescriptor will be 'hollow' and have no child attributes/elements.
 238  
                 // XXX: this should probably be done by the NodeDescriptors...
 239  0
                 ElementDescriptor typeDescriptor = getElementDescriptor( descriptor );
 240  
                 //ElementDescriptor typeDescriptor = descriptor;
 241  
         
 242  
                 // iterate through all attributes        
 243  0
                 AttributeDescriptor[] attributeDescriptors 
 244  0
                     = typeDescriptor.getAttributeDescriptors();
 245  0
                 if ( attributeDescriptors != null ) {
 246  0
                     for ( int i = 0, size = attributeDescriptors.length; i < size; i++ ) {
 247  0
                         AttributeDescriptor attributeDescriptor = attributeDescriptors[i];
 248  
                         
 249  
                         // The following isn't really the right way to find the attribute
 250  
                         // but it's quite robust.
 251  
                         // The idea is that you try both namespace and local name first
 252  
                         // and if this returns null try the qName.
 253  0
                         String value = attributes.getValue( 
 254  0
                             attributeDescriptor.getURI(),
 255  0
                             attributeDescriptor.getLocalName() 
 256  
                         );
 257  
                         
 258  0
                         if (value == null) {
 259  0
                             value = attributes.getValue(attributeDescriptor.getQualifiedName());
 260  
                         }
 261  
                         
 262  0
                         if (log.isTraceEnabled()) {
 263  0
                             log.trace("Attr URL:" + attributeDescriptor.getURI());
 264  0
                             log.trace("Attr LocalName:" + attributeDescriptor.getLocalName() );
 265  0
                             log.trace(value);
 266  
                         }
 267  
                         
 268  0
                         Updater updater = attributeDescriptor.getUpdater();
 269  0
                         log.trace(updater);
 270  0
                         if ( updater != null && value != null ) {
 271  0
                             updater.update( context, value );
 272  
                         }
 273  
                     }
 274  
                 }
 275  
                 
 276  0
                 addChildRules();
 277  
                 
 278  
                 // add bean for ID matching
 279  0
                 if ( matchIDs ) {
 280  
                     // XXX need to support custom ID attribute names
 281  
                     // XXX i have a feeling that the current mechanism might need to change
 282  
                     // XXX so i'm leaving this till later
 283  0
                     String id = attributes.getValue( "id" );
 284  0
                     if ( id != null ) {
 285  0
                         getBeansById().put( id, instance );
 286  
                     }
 287  
                 }
 288  
             }
 289  
         }
 290  0
     }
 291  
 
 292  
     /**
 293  
      * Process the end of this element.
 294  
      */
 295  
     public void end() {
 296  0
         if ( createdBean ) {
 297  
             
 298  
             // force any setters of the parent bean to be called for this new bean instance
 299  0
             Updater updater = descriptor.getUpdater();
 300  0
             Object instance = context.getBean();
 301  
 
 302  0
             Object top = digester.pop();
 303  0
             if (digester.getCount() == 0) {
 304  0
                 context.setBean(null);
 305  
             }else{
 306  0
                 context.setBean( digester.peek() );
 307  
             }
 308  
 
 309  0
             if ( updater != null ) {
 310  0
                 if ( log.isDebugEnabled() ) {
 311  0
                     log.debug( "Calling updater for: " + descriptor + " with: " 
 312  0
                         + instance + " on bean: " + context.getBean() );
 313  
                 }
 314  0
                 updater.update( context, instance );
 315  
             } else {
 316  0
                 if ( log.isDebugEnabled() ) {
 317  0
                     log.debug( "No updater for: " + descriptor + " with: " 
 318  0
                         + instance + " on bean: " + context.getBean() );
 319  
                 }
 320  
             }
 321  
         }
 322  0
     }
 323  
 
 324  
     /** 
 325  
      * Tidy up.
 326  
      */
 327  0
     public void finish() {}
 328  
 
 329  
 
 330  
     // Properties
 331  
     //-------------------------------------------------------------------------    
 332  
     
 333  
 
 334  
     /**
 335  
      * The name of the attribute which can be specified in the XML to override the
 336  
      * type of a bean used at a certain point in the schema.
 337  
      *
 338  
      * <p>The default value is 'className'.</p>
 339  
      * 
 340  
      * @return The name of the attribute used to overload the class name of a bean
 341  
      */
 342  
     public String getClassNameAttribute() {
 343  0
         return classNameAttribute;
 344  
     }
 345  
 
 346  
     /**
 347  
      * Sets the name of the attribute which can be specified in 
 348  
      * the XML to override the type of a bean used at a certain 
 349  
      * point in the schema.
 350  
      *
 351  
      * <p>The default value is 'className'.</p>
 352  
      * 
 353  
      * @param classNameAttribute The name of the attribute used to overload the class name of a bean
 354  
      */
 355  
     public void setClassNameAttribute(String classNameAttribute) {
 356  0
         this.classNameAttribute = classNameAttribute;
 357  0
     }
 358  
 
 359  
     // Implementation methods
 360  
     //-------------------------------------------------------------------------    
 361  
     
 362  
     /** 
 363  
      * Factory method to create new bean instances 
 364  
      *
 365  
      * @param attributes the <code>Attributes</code> used to match <code>ID/IDREF</code>
 366  
      * @return the created bean
 367  
      */
 368  
     protected Object createBean(Attributes attributes) {
 369  
         //
 370  
         // See if we've got an IDREF
 371  
         //
 372  
         // XXX This should be customizable but i'm not really convinced by the existing system
 373  
         // XXX maybe it's going to have to change so i'll use 'idref' for nows
 374  
         //
 375  0
         if ( matchIDs ) {
 376  0
             String idref = attributes.getValue( "idref" );
 377  0
             if ( idref != null ) {
 378  
                 // XXX need to check up about ordering
 379  
                 // XXX this is a very simple system that assumes that id occurs before idrefs
 380  
                 // XXX would need some thought about how to implement a fuller system
 381  0
                 log.trace( "Found IDREF" );
 382  0
                 Object bean = getBeansById().get( idref );
 383  0
                 if ( bean != null ) {
 384  0
                     if (log.isTraceEnabled()) {
 385  0
                         log.trace( "Matched bean " + bean );
 386  
                     }
 387  0
                     return bean;
 388  
                 }
 389  0
                 log.trace( "No match found" );
 390  
             }
 391  
         }
 392  
         
 393  0
         Class theClass = beanClass;
 394  
         try {
 395  
             
 396  0
             String className = attributes.getValue(classNameAttribute);
 397  0
             if (className != null) {
 398  
                 // load the class we should instantiate
 399  0
                 theClass = getDigester().getClassLoader().loadClass(className);
 400  
             }
 401  0
             if (log.isTraceEnabled()) {
 402  0
                 log.trace( "Creating instance of " + theClass );
 403  
             }
 404  0
             return theClass.newInstance();
 405  
             
 406  0
         } catch (Exception e) {
 407  0
             log.warn( "Could not create instance of type: " + theClass.getName() );
 408  0
             return null;
 409  
         }
 410  
     }    
 411  
         
 412  
     /** Adds the rules to the digester for all child elements */
 413  
     protected void addChildRules() {
 414  0
         if ( ! addedChildren ) {
 415  0
             addedChildren = true;
 416  
             
 417  0
             addChildRules( pathPrefix, descriptor );
 418  
         }
 419  0
     }
 420  
                         
 421  
     /** 
 422  
      * Add child rules for given descriptor at given prefix 
 423  
      *
 424  
      * @param prefix add child rules at this (digester) path prefix
 425  
      * @param currentDescriptor add child rules for this descriptor
 426  
      */
 427  
     protected void addChildRules(String prefix, ElementDescriptor currentDescriptor ) {         
 428  
         
 429  0
         if (log.isTraceEnabled()) {
 430  0
             log.trace("Adding child rules for " + currentDescriptor + "@" + prefix);
 431  
         }
 432  
         
 433  
         // if we are a reference to a type we should lookup the original
 434  
         // as this ElementDescriptor will be 'hollow' and have no child attributes/elements.
 435  
         // XXX: this should probably be done by the NodeDescriptors...
 436  0
         ElementDescriptor typeDescriptor = getElementDescriptor( currentDescriptor );
 437  
         //ElementDescriptor typeDescriptor = descriptor;
 438  
 
 439  
         
 440  0
         ElementDescriptor[] childDescriptors = typeDescriptor.getElementDescriptors();
 441  0
         if ( childDescriptors != null ) {
 442  0
             for ( int i = 0, size = childDescriptors.length; i < size; i++ ) {
 443  0
                 final ElementDescriptor childDescriptor = childDescriptors[i];
 444  0
                 if (log.isTraceEnabled()) {
 445  0
                     log.trace("Processing child " + childDescriptor);
 446  
                 }
 447  
                 
 448  0
                 String qualifiedName = childDescriptor.getQualifiedName();
 449  0
                 if ( qualifiedName == null ) {
 450  0
                     log.trace( "Ignoring" );
 451  0
                     continue;
 452  
                 }
 453  0
                 String path = prefix + qualifiedName;
 454  
                 // this code is for making sure that recursive elements
 455  
                 // can also be used..
 456  
                 
 457  0
                 if ( qualifiedName.equals( currentDescriptor.getQualifiedName() ) 
 458  0
                         && currentDescriptor.getPropertyName() != null ) {
 459  0
                     log.trace("Creating generic rule for recursive elements");
 460  0
                     int index = -1;
 461  0
                     if (childDescriptor.isWrapCollectionsInElement()) {
 462  0
                         index = prefix.indexOf(qualifiedName);
 463  0
                         if (index == -1) {
 464  
                             // shouldn't happen.. 
 465  0
                             log.debug( "Oops - this shouldn't happen" );
 466  0
                             continue;
 467  
                         }
 468  0
                         int removeSlash = prefix.endsWith("/")?1:0;
 469  0
                         path = "*/" + prefix.substring(index, prefix.length()-removeSlash);
 470  
                     }else{
 471  
                         // we have a element/element type of thing..
 472  0
                         ElementDescriptor[] desc = currentDescriptor.getElementDescriptors();
 473  0
                         if (desc.length == 1) {
 474  0
                             path = "*/"+desc[0].getQualifiedName();
 475  
                         }
 476  
                     }
 477  0
                     Rule rule = new BeanCreateRule( childDescriptor, context, path, matchIDs);
 478  0
                     addRule(path, rule);
 479  0
                     continue;
 480  
                 }
 481  0
                 if ( childDescriptor.getUpdater() != null ) {
 482  0
                     if (log.isTraceEnabled()) {
 483  0
                         log.trace("Element has updater "
 484  0
                          + ((MethodUpdater) childDescriptor.getUpdater()).getMethod().getName());
 485  
                     }
 486  0
                     if ( childDescriptor.isPrimitiveType() ) {
 487  0
                         addPrimitiveTypeRule(path, childDescriptor);
 488  
                         
 489  
                     } else {
 490  
                         // add the first child to the path
 491  0
                         ElementDescriptor[] grandChildren = childDescriptor.getElementDescriptors();
 492  0
                         if ( grandChildren != null && grandChildren.length > 0 ) {
 493  0
                             ElementDescriptor grandChild = grandChildren[0];
 494  0
                             String grandChildQName = grandChild.getQualifiedName();
 495  0
                             if ( grandChildQName != null && grandChildQName.length() > 0 ) {
 496  0
                                 if (childDescriptor.isWrapCollectionsInElement()) {
 497  0
                                     path += '/' + grandChildQName;
 498  
                                     
 499  
                                 } else {
 500  0
                                     path = prefix + (prefix.endsWith("/")?"":"/") + grandChildQName;
 501  
                                 }
 502  
                             }
 503  
                         }
 504  
                         
 505  
                         // maybe we are adding a primitve type to a collection/array
 506  0
                         Class beanClass = childDescriptor.getSingularPropertyType();
 507  0
                         if ( XMLIntrospectorHelper.isPrimitiveType( beanClass ) ) {
 508  0
                             addPrimitiveTypeRule(path, childDescriptor);
 509  
                             
 510  
                         } else {
 511  0
                             Rule rule = new BeanCreateRule( 
 512  0
                                                         childDescriptor, 
 513  0
                                                         context, 
 514  0
                                                         path + '/', 
 515  0
                                                         matchIDs );
 516  0
                             addRule( path, rule );
 517  
                         }
 518  
                     }
 519  
                 } else {
 520  0
                     log.trace("Element does not have updater");
 521  
                 }
 522  
 
 523  0
                 ElementDescriptor[] grandChildren = childDescriptor.getElementDescriptors();
 524  0
                 if ( grandChildren != null && grandChildren.length > 0 ) {
 525  0
                     log.trace("Adding grand children");
 526  0
                     addChildRules( path + '/', childDescriptor );
 527  
                 }
 528  
             }
 529  
         }
 530  0
     }
 531  
     
 532  
     /**
 533  
      * Get the associated bean reader.
 534  
      *
 535  
      * @return the <code>BeanReader</code digesting the xml
 536  
      */
 537  
     protected BeanReader getBeanReader() {
 538  
         // XXX this breaks the rule contact
 539  
         // XXX maybe the reader should be passed in the constructor
 540  0
         return (BeanReader) getDigester();
 541  
     }
 542  
     
 543  
     /** 
 544  
      * Allows the navigation from a reference to a property object to the descriptor defining what 
 545  
      * the property is. In other words, doing the join from a reference to a type to lookup its descriptor.
 546  
      * This could be done automatically by the NodeDescriptors. Refer to TODO.txt for more info.
 547  
      *
 548  
      * @param propertyDescriptor find descriptor for property object referenced by this descriptor
 549  
      * @return descriptor for the singular property class type referenced.
 550  
      */
 551  
     protected ElementDescriptor getElementDescriptor( ElementDescriptor propertyDescriptor ) {
 552  0
         Class beanClass = propertyDescriptor.getSingularPropertyType();
 553  0
         if ( beanClass != null ) {
 554  0
             XMLIntrospector introspector = getBeanReader().getXMLIntrospector();
 555  
             try {
 556  0
                 XMLBeanInfo xmlInfo = introspector.introspect( beanClass );
 557  0
                 return xmlInfo.getElementDescriptor();
 558  
                 
 559  0
             } catch (Exception e) {
 560  0
                 log.warn( "Could not introspect class: " + beanClass, e );
 561  
             }
 562  
         }
 563  
         // could not find a better descriptor so use the one we've got
 564  0
         return propertyDescriptor;
 565  
     }
 566  
     
 567  
     /** 
 568  
      * Adds a new Digester rule to process the text as a primitive type
 569  
      *
 570  
      * @param path digester path where this rule will be attached
 571  
      * @param childDescriptor update this <code>ElementDescriptor</code> with the body text
 572  
      */
 573  
     protected void addPrimitiveTypeRule(String path, final ElementDescriptor childDescriptor) {
 574  0
         Rule rule = new Rule() {
 575  
             public void body(String text) throws Exception {
 576  0
                 childDescriptor.getUpdater().update( context, text );
 577  0
             }        
 578  
         };
 579  0
         addRule( path, rule );
 580  0
     }
 581  
     
 582  
     /**
 583  
      * Safely add a rule with given path.
 584  
      *
 585  
      * @param path the digester path to add rule at
 586  
      * @param rule the <code>Rule</code> to add
 587  
      */
 588  
     protected void addRule(String path, Rule rule) {
 589  0
         Rules rules = digester.getRules();
 590  0
         List matches = rules.match(null, path);
 591  0
         if ( matches.isEmpty() ) {
 592  0
             if ( log.isDebugEnabled() ) {
 593  0
                 log.debug( "Adding digester rule for path: " + path + " rule: " + rule );
 594  
             }
 595  0
             digester.addRule( path, rule );
 596  
             
 597  
         } else {
 598  0
             if ( log.isDebugEnabled() ) {
 599  0
                 log.debug( "Ignoring duplicate digester rule for path: " 
 600  0
                             + path + " rule: " + rule );
 601  0
                 log.debug( "New rule (not added): " + rule );
 602  0
                 log.debug( "Existing rule:" + matches.get(0) );
 603  
             }
 604  
         }
 605  0
     }    
 606  
 
 607  
     /**
 608  
      * Get the map used to index beans (previously read in) by id.
 609  
      * This is stored in the evaluation context.
 610  
      *
 611  
      * @return map indexing beans created by id
 612  
      */
 613  
     protected Map getBeansById() {
 614  
         //
 615  
         // we need a single index for beans read in by id
 616  
         // so that we can use them for idref-matching
 617  
         // store this in the context
 618  
         //
 619  0
         Map beansById = (Map) context.getVariable( "beans-index" );
 620  0
         if ( beansById == null ) {
 621  
             // lazy creation
 622  0
             beansById = new HashMap();
 623  0
             context.setVariable( "beans-index", beansById );
 624  0
             log.trace( "Created new index-by-id map" );
 625  
         }
 626  
         
 627  0
         return beansById;
 628  
     }
 629  
     
 630  
     /**
 631  
      * Return something meaningful for logging.
 632  
      *
 633  
      * @return something useful for logging
 634  
      */
 635  
     public String toString() {
 636  0
         return "BeanCreateRule [path prefix=" + pathPrefix + " descriptor=" + descriptor + "]";
 637  
     }
 638  
     
 639  
 }