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