001 package org.apache.commons.betwixt.digester; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one or more 005 * contributor license agreements. See the NOTICE file distributed with 006 * this work for additional information regarding copyright ownership. 007 * The ASF licenses this file to You under the Apache License, Version 2.0 008 * (the "License"); you may not use this file except in compliance with 009 * the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 import java.beans.IntrospectionException; 021 import java.beans.Introspector; 022 import java.beans.PropertyDescriptor; 023 import java.lang.reflect.Method; 024 import java.util.Collection; 025 import java.util.Date; 026 import java.util.Enumeration; 027 import java.util.HashMap; 028 import java.util.Iterator; 029 import java.util.Map; 030 031 import org.apache.commons.betwixt.AttributeDescriptor; 032 import org.apache.commons.betwixt.ElementDescriptor; 033 import org.apache.commons.betwixt.NodeDescriptor; 034 import org.apache.commons.betwixt.XMLIntrospector; 035 import org.apache.commons.betwixt.expression.IteratorExpression; 036 import org.apache.commons.betwixt.expression.MapEntryAdder; 037 import org.apache.commons.betwixt.expression.MethodExpression; 038 import org.apache.commons.betwixt.expression.MethodUpdater; 039 import org.apache.commons.betwixt.strategy.PluralStemmer; 040 import org.apache.commons.logging.Log; 041 import org.apache.commons.logging.LogFactory; 042 043 /** 044 * <p><code>XMLIntrospectorHelper</code> a helper class for 045 * common code shared between the digestor and introspector.</p> 046 * 047 * TODO this class will be deprecated soon 048 * need to move the isLoop and isPrimitiveType but probably need to 049 * think about whether they need replacing with something different. 050 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 051 * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a> 052 * 053 * @deprecated 054 */ 055 public class XMLIntrospectorHelper { 056 057 /** Log used for logging (Doh!) */ 058 protected static Log log = LogFactory.getLog( XMLIntrospectorHelper.class ); 059 060 /** Base constructor */ 061 public XMLIntrospectorHelper() { 062 } 063 064 /** 065 * <p>Gets the current logging implementation.</p> 066 * 067 * @return current log 068 */ 069 public static Log getLog() { 070 return log; 071 } 072 073 /** 074 * <p>Sets the current logging implementation.</p> 075 * 076 * @param aLog use this <code>Log</code> 077 */ 078 public static void setLog(Log aLog) { 079 log = aLog; 080 } 081 082 083 084 /** 085 * Process a property. 086 * Go through and work out whether it's a loop property, a primitive or a standard. 087 * The class property is ignored. 088 * 089 * @param propertyDescriptor create a <code>NodeDescriptor</code> for this property 090 * @param useAttributesForPrimitives write primitives as attributes (rather than elements) 091 * @param introspector use this <code>XMLIntrospector</code> 092 * @return a correctly configured <code>NodeDescriptor</code> for the property 093 * @throws IntrospectionException when bean introspection fails 094 * @deprecated 0.5 this method has been replaced by {@link XMLIntrospector#createDescriptor} 095 */ 096 public static NodeDescriptor createDescriptor( 097 PropertyDescriptor propertyDescriptor, 098 boolean useAttributesForPrimitives, 099 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 }