View Javadoc

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  public class XMLIntrospectorHelper {
55  
56      /*** Log used for logging (Doh!) */
57      protected static Log log = LogFactory.getLog( XMLIntrospectorHelper.class );
58      
59      /*** Base constructor */
60      public XMLIntrospectorHelper() {
61      }
62      
63      /***
64       * <p>Gets the current logging implementation.</p>
65       *
66       * @return current log
67       */ 
68      public static Log getLog() {
69          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          log = aLog;
79      }
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         String name = propertyDescriptor.getName();
101         Class type = propertyDescriptor.getPropertyType();
102        
103         if (log.isTraceEnabled()) {
104             log.trace("Creating descriptor for property: name="
105                 + name + " type=" + type);
106         }
107         
108         NodeDescriptor nodeDescriptor = null;
109         Method readMethod = propertyDescriptor.getReadMethod();
110         Method writeMethod = propertyDescriptor.getWriteMethod();
111         
112         if ( readMethod == null ) {
113             if (log.isTraceEnabled()) {
114                 log.trace( "No read method for property: name="
115                     + name + " type=" + type);
116             }
117             return null;
118         }
119         
120         if ( log.isTraceEnabled() ) {
121             log.trace( "Read method=" + readMethod.getName() );
122         }
123         
124         // choose response from property type
125         
126         // XXX: ignore class property ??
127         if ( Class.class.equals( type ) && "class".equals( name ) ) {
128             log.trace( "Ignoring class property" );
129             return null;
130         }
131         if ( isPrimitiveType( type ) ) {
132             if (log.isTraceEnabled()) {
133                 log.trace( "Primitive type: " + name);
134             }
135             if ( useAttributesForPrimitives ) {
136                 if (log.isTraceEnabled()) {
137                     log.trace( "Adding property as attribute: " + name );
138                 }
139                 nodeDescriptor = new AttributeDescriptor();
140             } else {
141                 if (log.isTraceEnabled()) {
142                     log.trace( "Adding property as element: " + name );
143                 }
144                 nodeDescriptor = new ElementDescriptor(true);
145             }
146             nodeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
147             
148             if ( writeMethod != null ) {
149                 nodeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
150             }
151         } else if ( isLoopType( type ) ) {
152             if (log.isTraceEnabled()) {
153                 log.trace("Loop type: " + name);
154                 log.trace("Wrap in collections? " + introspector.isWrapCollectionsInElement());
155             }
156             ElementDescriptor loopDescriptor = new ElementDescriptor();
157             loopDescriptor.setContextExpression(
158                 new IteratorExpression( new MethodExpression( readMethod ) )
159             );
160             loopDescriptor.setWrapCollectionsInElement(
161                         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             if ( Map.class.isAssignableFrom( type ) ) {
165                 loopDescriptor.setQualifiedName( "entry" );
166                 // add elements for reading
167                 loopDescriptor.addElementDescriptor( new ElementDescriptor( "key" ) );
168                 loopDescriptor.addElementDescriptor( new ElementDescriptor( "value" ) );
169             }
170 
171             ElementDescriptor elementDescriptor = new ElementDescriptor();
172             elementDescriptor.setWrapCollectionsInElement(
173                         introspector.isWrapCollectionsInElement());
174             elementDescriptor.setElementDescriptors( new ElementDescriptor[] { loopDescriptor } );
175             
176             nodeDescriptor = elementDescriptor;            
177         } else {
178             if (log.isTraceEnabled()) {
179                 log.trace( "Standard property: " + name);
180             }
181             ElementDescriptor elementDescriptor = new ElementDescriptor();
182             elementDescriptor.setContextExpression( new MethodExpression( readMethod ) );
183             if ( writeMethod != null ) {
184                 elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
185             }
186             
187             nodeDescriptor = elementDescriptor;          
188         }
189 
190         if (nodeDescriptor instanceof AttributeDescriptor) {
191             // we want to use the attributemapper only when it is an attribute.. 
192             nodeDescriptor.setLocalName( 
193                 introspector.getAttributeNameMapper().mapTypeToElementName( name ) );
194         } else {
195             nodeDescriptor.setLocalName( 
196                 introspector.getElementNameMapper().mapTypeToElementName( name ) );
197         }        
198   
199         nodeDescriptor.setPropertyName( propertyDescriptor.getName() );
200         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         if (log.isTraceEnabled()) {
207             log.trace("Created descriptor:");
208             log.trace(nodeDescriptor);
209         }
210         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         configureProperty( elementDescriptor, propertyDescriptor, null, null);
226     }
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         Class type = propertyDescriptor.getPropertyType();
248         Method readMethod = propertyDescriptor.getReadMethod();
249         Method writeMethod = propertyDescriptor.getWriteMethod();
250         
251         elementDescriptor.setLocalName( propertyDescriptor.getName() );
252         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         if ( readMethod == null ) {
259             log.trace( "No read method" );
260             return;
261         }
262         
263         if ( log.isTraceEnabled() ) {
264             log.trace( "Read method=" + readMethod.getName() );
265         }
266         
267         // choose response from property type
268         
269         // XXX: ignore class property ??
270         if ( Class.class.equals( type ) && "class".equals( propertyDescriptor.getName() ) ) {
271             log.trace( "Ignoring class property" );
272             return;
273         }
274         if ( isPrimitiveType( type ) ) {
275             elementDescriptor.setTextExpression( new MethodExpression( readMethod ) );
276             
277         } else if ( isLoopType( type ) ) {
278             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             elementDescriptor.setContextExpression(
283                 new IteratorExpression( new MethodExpression( readMethod ) )
284             );
285 
286             writeMethod = null;
287         } else {
288             log.trace( "Standard property" );
289             elementDescriptor.setContextExpression( new MethodExpression( readMethod ) );
290         }
291     
292         // see if we have a custom method update name
293         if (updateMethodName == null) {
294             // set standard write method
295             if ( writeMethod != null ) {
296                 elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
297             }
298             
299         } else {
300             // see if we can find and set the custom method
301             if ( log.isTraceEnabled() ) {
302                 log.trace( "Finding custom method: " );
303                 log.trace( "  on:" + beanClass );
304                 log.trace( "  name:" + updateMethodName );
305             }
306             
307             Method updateMethod = null;
308             Method[] methods = beanClass.getMethods();
309             for ( int i = 0, size = methods.length; i < size; i++ ) {
310                 Method method = methods[i];
311                 if ( updateMethodName.equals( method.getName() ) ) {
312                     // we have a matching name
313                     // check paramters are correct
314                     if (methods[i].getParameterTypes().length == 1) {
315                         // we'll use first match
316                         updateMethod = methods[i];
317                         if ( log.isTraceEnabled() ) {
318                             log.trace("Matched method:" + updateMethod);
319                         } 
320                         // done since we're using the first match
321                         break;
322                     }
323                 }
324             }
325             
326             if (updateMethod == null) {
327                 if ( log.isInfoEnabled() ) {
328                     
329                     log.info("No method with name '" + updateMethodName + "' found for update");
330                 }
331             } else {
332     
333                 elementDescriptor.setUpdater( new MethodUpdater( updateMethod ) );
334                 elementDescriptor.setSingularPropertyType( updateMethod.getParameterTypes()[0] );
335                 if ( log.isTraceEnabled() ) {
336                     log.trace( "Set custom updater on " + elementDescriptor);
337                 }
338             }
339         }
340     }
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         Class type = propertyDescriptor.getPropertyType();
353         Method readMethod = propertyDescriptor.getReadMethod();
354         Method writeMethod = propertyDescriptor.getWriteMethod();
355         
356         if ( readMethod == null ) {
357             log.trace( "No read method" );
358             return;
359         }
360         
361         if ( log.isTraceEnabled() ) {
362             log.trace( "Read method=" + readMethod );
363         }
364         
365         // choose response from property type
366         
367         // XXX: ignore class property ??
368         if ( Class.class.equals( type ) && "class".equals( propertyDescriptor.getName() ) ) {
369             log.trace( "Ignoring class property" );
370             return;
371         }
372         if ( isLoopType( type ) ) {
373             log.warn( "Using loop type for an attribute. Type = " 
374                     + type.getName() + " attribute: " + attributeDescriptor.getQualifiedName() );
375         }
376 
377         log.trace( "Standard property" );
378         attributeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
379         
380         if ( writeMethod != null ) {
381             attributeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
382         }
383         
384         attributeDescriptor.setLocalName( propertyDescriptor.getName() );
385         attributeDescriptor.setPropertyType( type );        
386         
387         // XXX: associate more bean information with the descriptor?
388         //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() );
389         //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
390     }
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         if ( beanClass != null ) {
419             Method[] methods = beanClass.getMethods();
420             for ( int i = 0, size = methods.length; i < size; i++ ) {
421                 Method method = methods[i];
422                 String name = method.getName();
423                 if ( name.startsWith( "add" ) ) {
424                     // XXX: should we filter out non-void returning methods?
425                     // some beans will return something as a helper
426                     Class[] types = method.getParameterTypes();
427                     if ( types != null) {
428                         if ( log.isTraceEnabled() ) {
429                             log.trace("Searching for match for " + method);
430                         }
431                         
432                         if ( ( types.length == 1 ) || types.length == 2 ) {
433                             String propertyName = Introspector.decapitalize( name.substring(3) );
434                             if (propertyName.length() == 0)
435                                 continue;
436                             if ( log.isTraceEnabled() ) {
437                                 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                             ElementDescriptor descriptor = 
445                                 findGetCollectionDescriptor( 
446                                                             introspector, 
447                                                             rootDescriptor, 
448                                                             propertyName );
449     
450                             if ( log.isDebugEnabled() ) {
451                                 log.debug( "!! " + propertyName + " -> " + descriptor );
452                                 log.debug( "!! " + name + " -> " 
453                                 + (descriptor!=null?descriptor.getPropertyName():"") );
454                             }
455                             if ( descriptor != null ) {
456                                 boolean isMapDescriptor 
457                                     = Map.class.isAssignableFrom( descriptor.getPropertyType() );
458                                 if ( !isMapDescriptor && types.length == 1 ) {
459                                     // this may match a standard collection or iteration
460                                     log.trace("Matching collection or iteration");
461                                     
462                                     descriptor.setUpdater( new MethodUpdater( method ) );
463                                     descriptor.setSingularPropertyType( types[0] );
464                                     
465                                     if ( log.isDebugEnabled() ) {
466                                         log.debug( "!! " + method);
467                                         log.debug( "!! " + types[0]);
468                                     }
469                                     
470                                     // is there a child element with no localName
471                                     ElementDescriptor[] children 
472                                         = descriptor.getElementDescriptors();
473                                     if ( children != null && children.length > 0 ) {
474                                         ElementDescriptor child = children[0];
475                                         String localName = child.getLocalName();
476                                         if ( localName == null || localName.length() == 0 ) {
477                                             child.setLocalName( 
478                                                 introspector.getElementNameMapper()
479                                                     .mapTypeToElementName( propertyName ) );
480                                         }
481                                     }
482 
483                                 } else if ( isMapDescriptor && types.length == 2 ) {
484                                     // this may match a map
485                                     log.trace("Matching map");
486                                     ElementDescriptor[] children 
487                                         = descriptor.getElementDescriptors();
488                                     // see if the descriptor's been set up properly
489                                     if ( children.length == 0 ) {
490                                         
491                                         log.info(
492                                             "'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                                         ElementDescriptor[] grandchildren
499                                             = children[0].getElementDescriptors();
500                                         MapEntryAdder adder = new MapEntryAdder(method);
501                                         for ( 
502                                             int n=0, 
503                                                 noOfGrandChildren = grandchildren.length;
504                                             n < noOfGrandChildren;
505                                             n++ ) {
506                                             if ( "key".equals( 
507                                                     grandchildren[n].getLocalName() ) ) {
508                                             
509                                                 grandchildren[n].setUpdater( 
510                                                                 adder.getKeyUpdater() );
511                                                 grandchildren[n].setSingularPropertyType( 
512                                                                 types[0] );
513                                                 if ( log.isTraceEnabled() ) {
514                                                     log.trace(
515                                                         "Key descriptor: " + grandchildren[n]);
516                                                 }                                               
517                                                 
518                                             } else if ( 
519                                                 "value".equals( 
520                                                     grandchildren[n].getLocalName() ) ) {
521 
522                                                 grandchildren[n].setUpdater( 
523                                                                     adder.getValueUpdater() );
524                                                 grandchildren[n].setSingularPropertyType( 
525                                                                     types[1] );
526                                                 if ( log.isTraceEnabled() ) {
527                                                     log.trace(
528                                                         "Value descriptor: " + grandchildren[n]);
529                                                 }
530                                             }
531                                         }
532                                     }
533                                 }
534                             } else {
535                                 if ( log.isDebugEnabled() ) {
536                                     log.debug( 
537                                         "Could not find an ElementDescriptor with property name: " 
538                                         + propertyName + " to attach the add method: " + method 
539                                     );
540                                 }
541                             }
542                         }
543                     } 
544                 }
545             }
546         }
547     }
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         if (type == null) {
559             log.trace("isLoopType: type is null");
560             return false;
561         }
562         return type.isArray() 
563             || Map.class.isAssignableFrom( type ) 
564             || Collection.class.isAssignableFrom( type ) 
565             || Enumeration.class.isAssignableFrom( type ) 
566             || 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         if ( type == null ) {
583             return false;
584             
585         } else if ( type.isPrimitive() ) {
586             return true;
587             
588         } else if ( type.equals( Object.class ) ) {
589             return false;
590         }
591         return type.getName().startsWith( "java.lang." )
592             || Number.class.isAssignableFrom( type ) 
593             || String.class.isAssignableFrom( type ) 
594             || Date.class.isAssignableFrom( type ) 
595             || java.sql.Date.class.isAssignableFrom( type ) 
596             || java.sql.Time.class.isAssignableFrom( type ) 
597             || java.sql.Timestamp.class.isAssignableFrom( type ) 
598             || java.math.BigDecimal.class.isAssignableFrom( type ) 
599             || 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         Map map = new HashMap();
624         //String propertyName = rootDescriptor.getPropertyName();
625         if ( log.isTraceEnabled() ) {
626             log.trace( "findPluralDescriptor( " + propertyName 
627                 + " ):root property name=" + rootDescriptor.getPropertyName() );
628         }
629         
630         if (rootDescriptor.getPropertyName() != null) {
631             map.put(propertyName, rootDescriptor);
632         }
633         makeElementDescriptorMap( rootDescriptor, map );
634         
635         PluralStemmer stemmer = introspector.getPluralStemmer();
636         ElementDescriptor elementDescriptor = stemmer.findPluralDescriptor( propertyName, map );
637         
638         if ( log.isTraceEnabled() ) {
639             log.trace( 
640                 "findPluralDescriptor( " + propertyName 
641                     + " ):ElementDescriptor=" + elementDescriptor );
642         }
643         
644         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         ElementDescriptor[] children = rootDescriptor.getElementDescriptors();
657         if ( children != null ) {
658             for ( int i = 0, size = children.length; i < size; i++ ) {
659                 ElementDescriptor child = children[i];                
660                 String propertyName = child.getPropertyName();                
661                 if ( propertyName != null ) {
662                     map.put( propertyName, child );
663                 }
664                 makeElementDescriptorMap( child, map );
665             }
666         }
667     }
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         ElementDescriptor[] children = rootDescriptor.getElementDescriptors();
683         if ( children != null ) {
684             for ( int i = 0, size = children.length; i < size; i++ ) {
685                 ElementDescriptor child = children[i];
686                 if ( child == oldValue ) {
687                     children[i] = newValue;
688                     break;
689                 }
690                 swapDescriptor( child, oldValue, newValue );
691             }
692         }
693     }
694 }