View Javadoc

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