KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > betwixt > XMLIntrospector


1 package org.apache.commons.betwixt;
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.BeanDescriptor JavaDoc;
20 import java.beans.BeanInfo JavaDoc;
21 import java.beans.IntrospectionException JavaDoc;
22 import java.beans.Introspector JavaDoc;
23 import java.beans.PropertyDescriptor JavaDoc;
24 import java.lang.reflect.Method JavaDoc;
25 import java.net.URL JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.List JavaDoc;
30 import java.util.Map JavaDoc;
31
32 import org.apache.commons.beanutils.DynaBean;
33 import org.apache.commons.beanutils.DynaClass;
34 import org.apache.commons.beanutils.DynaProperty;
35 import org.apache.commons.betwixt.digester.XMLBeanInfoDigester;
36 import org.apache.commons.betwixt.digester.XMLIntrospectorHelper;
37 import org.apache.commons.betwixt.expression.EmptyExpression;
38 import org.apache.commons.betwixt.expression.IteratorExpression;
39 import org.apache.commons.betwixt.expression.MapEntryAdder;
40 import org.apache.commons.betwixt.expression.MethodUpdater;
41 import org.apache.commons.betwixt.expression.StringExpression;
42 import org.apache.commons.betwixt.registry.DefaultXMLBeanInfoRegistry;
43 import org.apache.commons.betwixt.registry.XMLBeanInfoRegistry;
44 import org.apache.commons.betwixt.strategy.ClassNormalizer;
45 import org.apache.commons.betwixt.strategy.DefaultNameMapper;
46 import org.apache.commons.betwixt.strategy.DefaultPluralStemmer;
47 import org.apache.commons.betwixt.strategy.NameMapper;
48 import org.apache.commons.betwixt.strategy.PluralStemmer;
49 import org.apache.commons.betwixt.strategy.TypeBindingStrategy;
50 import org.apache.commons.logging.Log;
51 import org.apache.commons.logging.LogFactory;
52
53 /**
54   * <p><code>XMLIntrospector</code> an introspector of beans to create a
55   * XMLBeanInfo instance.</p>
56   *
57   * <p>By default, <code>XMLBeanInfo</code> caching is switched on.
58   * This means that the first time that a request is made for a <code>XMLBeanInfo</code>
59   * for a particular class, the <code>XMLBeanInfo</code> is cached.
60   * Later requests for the same class will return the cached value.</p>
61   *
62   * <p>Note :</p>
63   * <p>This class makes use of the <code>java.bean.Introspector</code>
64   * class, which contains a BeanInfoSearchPath. To make sure betwixt can
65   * do his work correctly, this searchpath is completely ignored during
66   * processing. The original values will be restored after processing finished
67   * </p>
68   *
69   * @author <a HREF="mailto:jstrachan@apache.org">James Strachan</a>
70   * @author <a HREF="mailto:martin@mvdb.net">Martin van den Bemt</a>
71   */

72 public class XMLIntrospector {
73     /**
74      * Log used for logging (Doh!)
75      * @deprecated 0.6 use the {@link #getLog()} property instead
76      */

77     protected Log log = LogFactory.getLog( XMLIntrospector.class );
78     
79     /** Maps classes to <code>XMLBeanInfo</code>'s */
80     private XMLBeanInfoRegistry registry = new DefaultXMLBeanInfoRegistry();
81     
82     /** Digester used to parse the XML descriptor files */
83     private XMLBeanInfoDigester digester;
84
85     /** Configuration to be used for introspection*/
86     private IntrospectionConfiguration configuration;
87     
88     /** Base constructor */
89     public XMLIntrospector() {
90         this(new IntrospectionConfiguration());
91     }
92     
93     /**
94      * Construct allows a custom configuration to be set on construction.
95      * This allows <code>IntrospectionConfiguration</code> subclasses
96      * to be easily used.
97      * @param configuration IntrospectionConfiguration, not null
98      */

99     public XMLIntrospector(IntrospectionConfiguration configuration) {
100         setConfiguration(configuration);
101     }
102     
103     
104     // Properties
105
//-------------------------------------------------------------------------
106

107     /**
108      * <p>Gets the current logging implementation. </p>
109      * @return the Log implementation which this class logs to
110      */

111     public Log getLog() {
112         return getConfiguration().getIntrospectionLog();
113     }
114
115     /**
116      * <p>Sets the current logging implementation.</p>
117      * @param log the Log implementation to use for logging
118      */

119     public void setLog(Log log) {
120         getConfiguration().setIntrospectionLog(log);
121     }
122     
123     /**
124      * <p>Gets the current registry implementation.
125      * The registry is checked to see if it has an <code>XMLBeanInfo</code> for a class
126      * before introspecting.
127      * After standard introspection is complete, the instance will be passed to the registry.</p>
128      *
129      * <p>This allows finely grained control over the caching strategy.
130      * It also allows the standard introspection mechanism
131      * to be overridden on a per class basis.</p>
132      *
133      * @return the XMLBeanInfoRegistry currently used
134      */

135     public XMLBeanInfoRegistry getRegistry() {
136         return registry;
137     }
138     
139     /**
140      * <p>Sets the <code>XMLBeanInfoRegistry</code> implementation.
141      * The registry is checked to see if it has an <code>XMLBeanInfo</code> for a class
142      * before introspecting.
143      * After standard introspection is complete, the instance will be passed to the registry.</p>
144      *
145      * <p>This allows finely grained control over the caching strategy.
146      * It also allows the standard introspection mechanism
147      * to be overridden on a per class basis.</p>
148      *
149      * @param registry the XMLBeanInfoRegistry to use
150      */

151     public void setRegistry(XMLBeanInfoRegistry registry) {
152         this.registry = registry;
153     }
154     
155     /**
156      * Gets the configuration to be used for introspection.
157      * The various introspection-time strategies
158      * and configuration variables have been consolidated as properties
159      * of this bean.
160      * This allows the configuration to be more easily shared.
161      * @return IntrospectionConfiguration, not null
162      */

163     public IntrospectionConfiguration getConfiguration() {
164         return configuration;
165     }
166
167     /**
168      * Sets the configuration to be used for introspection.
169      * The various introspection-time strategies
170      * and configuration variables have been consolidated as properties
171      * of this bean.
172      * This allows the configuration to be more easily shared.
173      * @param configuration IntrospectionConfiguration, not null
174      */

175     public void setConfiguration(IntrospectionConfiguration configuration) {
176         this.configuration = configuration;
177     }
178     
179     
180     /**
181       * Gets the <code>ClassNormalizer</code> strategy.
182       * This is used to determine the Class to be introspected
183       * (the normalized Class).
184       *
185       * @return the <code>ClassNormalizer</code> used to determine the Class to be introspected
186       * for a given Object.
187       * @deprecated 0.6 use getConfiguration().getClassNormalizer
188       * @since 0.5
189       */

190     public ClassNormalizer getClassNormalizer() {
191         return getConfiguration().getClassNormalizer();
192     }
193     
194     /**
195       * Sets the <code>ClassNormalizer</code> strategy.
196       * This is used to determine the Class to be introspected
197       * (the normalized Class).
198       *
199       * @param classNormalizer the <code>ClassNormalizer</code> to be used to determine
200       * the Class to be introspected for a given Object.
201       * @deprecated 0.6 use getConfiguration().setClassNormalizer
202       * @since 0.5
203       *
204       */

205     public void setClassNormalizer(ClassNormalizer classNormalizer) {
206         getConfiguration().setClassNormalizer(classNormalizer);
207     }
208     
209     /**
210      * Is <code>XMLBeanInfo</code> caching enabled?
211      *
212      * @deprecated 0.5 replaced by XMlBeanInfoRegistry
213      * @return true if caching is enabled
214      */

215     public boolean isCachingEnabled() {
216         return true;
217     }
218
219     /**
220      * Set whether <code>XMLBeanInfo</code> caching should be enabled.
221      *
222      * @deprecated 0.5 replaced by XMlBeanInfoRegistry
223      * @param cachingEnabled ignored
224      */

225     public void setCachingEnabled(boolean cachingEnabled) {
226         //
227
}
228      
229     
230     /**
231       * Should attributes (or elements) be used for primitive types.
232       * @return true if primitive types will be mapped to attributes in the introspection
233       * @deprecated 0.6 use getConfiguration().isAttributesForPrimitives
234       */

235     public boolean isAttributesForPrimitives() {
236         return getConfiguration().isAttributesForPrimitives();
237     }
238
239     /**
240       * Set whether attributes (or elements) should be used for primitive types.
241       * @param attributesForPrimitives pass trus to map primitives to attributes,
242       * pass false to map primitives to elements
243       * @deprecated 0.6 use getConfiguration().setAttributesForPrimitives
244       */

245     public void setAttributesForPrimitives(boolean attributesForPrimitives) {
246         getConfiguration().setAttributesForPrimitives(attributesForPrimitives);
247     }
248
249     /**
250      * Should collections be wrapped in an extra element?
251      *
252      * @return whether we should we wrap collections in an extra element?
253      * @deprecated 0.6 use getConfiguration().isWrapCollectionsInElement
254      */

255     public boolean isWrapCollectionsInElement() {
256         return getConfiguration().isWrapCollectionsInElement();
257     }
258
259     /**
260      * Sets whether we should we wrap collections in an extra element.
261      *
262      * @param wrapCollectionsInElement pass true if collections should be wrapped in a
263      * parent element
264      * @deprecated 0.6 use getConfiguration().setWrapCollectionsInElement
265      */

266     public void setWrapCollectionsInElement(boolean wrapCollectionsInElement) {
267         getConfiguration().setWrapCollectionsInElement(wrapCollectionsInElement);
268     }
269
270     /**
271      * Get singular and plural matching strategy.
272      *
273      * @return the strategy used to detect matching singular and plural properties
274      * @deprecated 0.6 use getConfiguration().getPluralStemmer
275      */

276     public PluralStemmer getPluralStemmer() {
277         return getConfiguration().getPluralStemmer();
278     }
279     
280     /**
281      * Sets the strategy used to detect matching singular and plural properties
282      *
283      * @param pluralStemmer the PluralStemmer used to match singular and plural
284      * @deprecated 0.6 use getConfiguration().setPluralStemmer
285      */

286     public void setPluralStemmer(PluralStemmer pluralStemmer) {
287         getConfiguration().setPluralStemmer(pluralStemmer);
288     }
289
290     /**
291      * Gets the name mapper strategy.
292      *
293      * @return the strategy used to convert bean type names into element names
294      * @deprecated 0.5 getNameMapper is split up in
295      * {@link #getElementNameMapper()} and {@link #getAttributeNameMapper()}
296      */

297     public NameMapper getNameMapper() {
298         return getElementNameMapper();
299     }
300     
301     /**
302      * Sets the strategy used to convert bean type names into element names
303      * @param nameMapper the NameMapper strategy to be used
304      * @deprecated 0.5 setNameMapper is split up in
305      * {@link #setElementNameMapper(NameMapper)} and {@link #setAttributeNameMapper(NameMapper)}
306      */

307     public void setNameMapper(NameMapper nameMapper) {
308         setElementNameMapper(nameMapper);
309     }
310
311
312     /**
313      * Gets the name mapping strategy used to convert bean names into elements.
314      *
315      * @return the strategy used to convert bean type names into element
316      * names. If no element mapper is currently defined then a default one is created.
317      * @deprecated 0.6 use getConfiguration().getElementNameMapper
318      */

319     public NameMapper getElementNameMapper() {
320         return getConfiguration().getElementNameMapper();
321     }
322      
323     /**
324      * Sets the strategy used to convert bean type names into element names
325      * @param nameMapper the NameMapper to use for the conversion
326      * @deprecated 0.6 use getConfiguration().setElementNameMapper
327      */

328     public void setElementNameMapper(NameMapper nameMapper) {
329         getConfiguration().setElementNameMapper( nameMapper );
330     }
331     
332
333     /**
334      * Gets the name mapping strategy used to convert bean names into attributes.
335      *
336      * @return the strategy used to convert bean type names into attribute
337      * names. If no attributeNamemapper is known, it will default to the ElementNameMapper
338      * @deprecated 0.6 getConfiguration().getAttributeNameMapper
339      */

340     public NameMapper getAttributeNameMapper() {
341         return getConfiguration().getAttributeNameMapper();
342      }
343
344
345     /**
346      * Sets the strategy used to convert bean type names into attribute names
347      * @param nameMapper the NameMapper to use for the convertion
348      * @deprecated 0.6 use getConfiguration().setAttributeNameMapper
349      */

350     public void setAttributeNameMapper(NameMapper nameMapper) {
351         getConfiguration().setAttributeNameMapper( nameMapper );
352     }
353     
354     /**
355      * Should the original <code>java.reflect.Introspector</code> bean info search path be used?
356      * By default it will be false.
357      *
358      * @return boolean if the beanInfoSearchPath should be used.
359      * @deprecated 0.6 use getConfiguration().useBeanInfoSearchPath
360      */

361     public boolean useBeanInfoSearchPath() {
362         return getConfiguration().useBeanInfoSearchPath();
363     }
364
365     /**
366      * Specifies if you want to use the beanInfoSearchPath
367      * @see java.beans.Introspector for more details
368      * @param useBeanInfoSearchPath
369      * @deprecated 0.6 use getConfiguration().setUseBeanInfoSearchPath
370      */

371     public void setUseBeanInfoSearchPath(boolean useBeanInfoSearchPath) {
372         getConfiguration().setUseBeanInfoSearchPath( useBeanInfoSearchPath );
373     }
374     
375     // Methods
376
//-------------------------------------------------------------------------
377

378     /**
379      * Flush existing cached <code>XMLBeanInfo</code>'s.
380      *
381      * @deprecated 0.5 use flushable registry instead
382      */

383     public void flushCache() {}
384     
385     
386     /** Create a standard <code>XMLBeanInfo</code> by introspection
387       * The actual introspection depends only on the <code>BeanInfo</code>
388       * associated with the bean.
389       *
390       * @param bean introspect this bean
391       * @return XMLBeanInfo describing bean-xml mapping
392       * @throws IntrospectionException when the bean introspection fails
393       */

394     public XMLBeanInfo introspect(Object JavaDoc bean) throws IntrospectionException JavaDoc {
395         if (getLog().isDebugEnabled()) {
396             getLog().debug( "Introspecting..." );
397             getLog().debug(bean);
398         }
399         
400         if ( bean instanceof DynaBean ) {
401             // allow DynaBean implementations to be overridden by .betwixt files
402
XMLBeanInfo xmlBeanInfo = findByXMLDescriptor( bean.getClass() );
403             if (xmlBeanInfo != null) {
404                 return xmlBeanInfo;
405             }
406             // this is DynaBean use the DynaClass for introspection
407
return introspect( ((DynaBean) bean).getDynaClass() );
408             
409         } else {
410             // normal bean so normal introspection
411
Class JavaDoc normalClass = getClassNormalizer().getNormalizedClass( bean );
412             return introspect( normalClass );
413         }
414     }
415     
416     /**
417      * Creates XMLBeanInfo by reading the DynaProperties of a DynaBean.
418      * Customizing DynaBeans using betwixt is not supported.
419      *
420      * @param dynaClass the DynaBean to introspect
421      *
422      * @return XMLBeanInfo for the DynaClass
423      */

424     public XMLBeanInfo introspect(DynaClass dynaClass) {
425
426         // for now this method does not do much, since XMLBeanInfoRegistry cannot
427
// use a DynaClass as a key
428
// TODO: add caching for DynaClass XMLBeanInfo
429
// need to work out if this is possible
430

431         // this line allows subclasses to change creation strategy
432
XMLBeanInfo xmlInfo = createXMLBeanInfo( dynaClass );
433         
434         // populate the created info with
435
DynaClassBeanType beanClass = new DynaClassBeanType( dynaClass );
436         populate( xmlInfo, beanClass );
437         
438         return xmlInfo;
439     }
440     
441     /** Create a standard <code>XMLBeanInfo</code> by introspection.
442       * The actual introspection depends only on the <code>BeanInfo</code>
443       * associated with the bean.
444       *
445       * @param aClass introspect this class
446       * @return XMLBeanInfo describing bean-xml mapping
447       * @throws IntrospectionException when the bean introspection fails
448       */

449     public XMLBeanInfo introspect(Class JavaDoc aClass) throws IntrospectionException JavaDoc {
450         // we first reset the beaninfo searchpath.
451
String JavaDoc[] searchPath = null;
452         if ( !getConfiguration().useBeanInfoSearchPath() ) {
453             searchPath = Introspector.getBeanInfoSearchPath();
454             Introspector.setBeanInfoSearchPath(new String JavaDoc[] { });
455         }
456         
457         XMLBeanInfo xmlInfo = registry.get( aClass );
458         
459         if ( xmlInfo == null ) {
460             // lets see if we can find an XML descriptor first
461
if ( getLog().isDebugEnabled() ) {
462                 getLog().debug( "Attempting to lookup an XML descriptor for class: " + aClass );
463             }
464             
465             xmlInfo = findByXMLDescriptor( aClass );
466             if ( xmlInfo == null ) {
467                 BeanInfo JavaDoc info = Introspector.getBeanInfo( aClass );
468                 xmlInfo = introspect( info );
469             }
470             
471             if ( xmlInfo != null ) {
472                 registry.put( aClass, xmlInfo );
473             }
474         } else {
475             getLog().trace( "Used cached XMLBeanInfo." );
476         }
477         
478         if ( getLog().isTraceEnabled() ) {
479             getLog().trace( xmlInfo );
480         }
481         if ( !getConfiguration().useBeanInfoSearchPath() ) {
482             // we restore the beaninfo searchpath.
483
Introspector.setBeanInfoSearchPath( searchPath );
484         }
485         
486         return xmlInfo;
487     }
488     
489     /** Create a standard <code>XMLBeanInfo</code> by introspection.
490       * The actual introspection depends only on the <code>BeanInfo</code>
491       * associated with the bean.
492       *
493       * @param beanInfo the BeanInfo the xml-bean mapping is based on
494       * @return XMLBeanInfo describing bean-xml mapping
495       * @throws IntrospectionException when the bean introspection fails
496       */

497     public XMLBeanInfo introspect(BeanInfo JavaDoc beanInfo) throws IntrospectionException JavaDoc {
498         XMLBeanInfo xmlBeanInfo = createXMLBeanInfo( beanInfo );
499         populate( xmlBeanInfo, new JavaBeanType( beanInfo ) );
500         return xmlBeanInfo;
501     }
502     
503     /**
504      * Populates the given <code>XMLBeanInfo</code> based on the given type of bean.
505      *
506      * @param xmlBeanInfo populate this, not null
507      * @param bean the type definition for the bean, not null
508      */

509     private void populate(XMLBeanInfo xmlBeanInfo, BeanType bean) {
510         String JavaDoc name = bean.getBeanName();
511         
512         ElementDescriptor elementDescriptor = new ElementDescriptor();
513         elementDescriptor.setLocalName(
514             getElementNameMapper().mapTypeToElementName( name ) );
515         elementDescriptor.setPropertyType( bean.getElementType() );
516         
517         if (getLog().isTraceEnabled()) {
518             getLog().trace("Populating:" + bean);
519         }
520
521         // add default string value for primitive types
522
if ( bean.isPrimitiveType() ) {
523             getLog().trace("Bean is primitive");
524             elementDescriptor.setTextExpression( StringExpression.getInstance() );
525             
526         } else if ( bean.isLoopType() ) {
527             getLog().trace("Bean is loop");
528             ElementDescriptor loopDescriptor = new ElementDescriptor();
529             loopDescriptor.setContextExpression(
530                 new IteratorExpression( EmptyExpression.getInstance() )
531             );
532             if ( bean.isMapType() ) {
533                 loopDescriptor.setQualifiedName( "entry" );
534             }
535             elementDescriptor.setElementDescriptors( new ElementDescriptor[] { loopDescriptor } );
536             
537         } else {
538             getLog().trace("Bean is standard type");
539             List JavaDoc elements = new ArrayList JavaDoc();
540             List JavaDoc attributes = new ArrayList JavaDoc();
541             List JavaDoc contents = new ArrayList JavaDoc();
542
543             addProperties( bean.getProperties(), elements, attributes, contents );
544
545             int size = elements.size();
546             if ( size > 0 ) {
547                 ElementDescriptor[] descriptors = new ElementDescriptor[size];
548                 elements.toArray( descriptors );
549                 elementDescriptor.setElementDescriptors( descriptors );
550             }
551             size = attributes.size();
552             if ( size > 0 ) {
553                 AttributeDescriptor[] descriptors = new AttributeDescriptor[size];
554                 attributes.toArray( descriptors );
555                 elementDescriptor.setAttributeDescriptors( descriptors );
556             }
557             size = contents.size();
558             if ( size > 0 ) {
559                 if ( size > 0 ) {
560                     Descriptor[] descriptors = new Descriptor[size];
561                     contents.toArray( descriptors );
562                     elementDescriptor.setContentDescriptors( descriptors );
563                 }
564             }
565         }
566         
567         xmlBeanInfo.setElementDescriptor( elementDescriptor );
568         
569         // default any addProperty() methods
570
defaultAddMethods( elementDescriptor, bean.getElementType() );
571         
572         if (getLog().isTraceEnabled()) {
573             getLog().trace("Populated descriptor:");
574             getLog().trace(elementDescriptor);
575         }
576     }
577
578     
579     /**
580      * Creates XMLBeanInfo for the given DynaClass.
581      *
582      * @param dynaClass the class describing a DynaBean
583      *
584      * @return XMLBeanInfo that describes the properties of the given
585      * DynaClass
586      */

587     protected XMLBeanInfo createXMLBeanInfo(DynaClass dynaClass) {
588         // XXX is the chosen class right?
589
XMLBeanInfo beanInfo = new XMLBeanInfo(dynaClass.getClass());
590         return beanInfo;
591     }
592
593
594
595
596     /**
597      * Create a XML descriptor from a bean one.
598      * Go through and work out whether it's a loop property, a primitive or a standard.
599      * The class property is ignored.
600      *
601      * @param propertyDescriptor create a <code>NodeDescriptor</code> for this property
602      * @param useAttributesForPrimitives write primitives as attributes (rather than elements)
603      * @return a correctly configured <code>NodeDescriptor</code> for the property
604      * @throws IntrospectionException when bean introspection fails
605      * @deprecated 0.5 use {@link #createXMLDescriptor}.
606      */

607     public Descriptor createDescriptor(
608         PropertyDescriptor JavaDoc propertyDescriptor,
609         boolean useAttributesForPrimitives
610     ) throws IntrospectionException JavaDoc {
611         return createXMLDescriptor( new BeanProperty( propertyDescriptor ) );
612     }
613  
614     /**
615      * Create a XML descriptor from a bean one.
616      * Go through and work out whether it's a loop property, a primitive or a standard.
617      * The class property is ignored.
618      *
619      * @param beanProperty the BeanProperty specifying the property
620      * @return a correctly configured <code>NodeDescriptor</code> for the property
621      * @since 0.5
622      */

623     public Descriptor createXMLDescriptor( BeanProperty beanProperty ) {
624         return beanProperty.createXMLDescriptor( configuration );
625     }
626
627
628     /**
629      * Add any addPropety(PropertyType) methods as Updaters
630      * which are often used for 1-N relationships in beans.
631      * <br>
632      * The tricky part here is finding which ElementDescriptor corresponds
633      * to the method. e.g. a property 'items' might have an Element descriptor
634      * which the method addItem() should match to.
635      * <br>
636      * So the algorithm we'll use
637      * by default is to take the decapitalized name of the property being added
638      * and find the first ElementDescriptor that matches the property starting with
639      * the string. This should work for most use cases.
640      * e.g. addChild() would match the children property.
641      * <br>
642      * TODO this probably needs refactoring. It probably belongs in the bean wrapper
643      * (so that it'll work properly with dyna-beans) and so that the operations can
644      * be optimized by caching. Multiple hash maps are created and getMethods is
645      * called multiple times. This is relatively expensive and so it'd be better
646      * to push into a proper class and cache.
647      * <br>
648      * TODO this probably does work properly with DynaBeans: need to push
649      * implementation into an class and expose it on BeanType.
650      *
651      * @param introspector use this <code>XMLIntrospector</code> for introspection
652      * @param rootDescriptor add defaults to this descriptor
653      * @param beanClass the <code>Class</code> to which descriptor corresponds
654      */

655     public void defaultAddMethods(
656                                             ElementDescriptor rootDescriptor,
657                                             Class JavaDoc beanClass ) {
658                                               
659         // lets iterate over all methods looking for one of the form
660
// add*(PropertyType)
661
if ( beanClass != null ) {
662             ArrayList JavaDoc singleParameterAdders = new ArrayList JavaDoc();
663             ArrayList JavaDoc twinParameterAdders = new ArrayList JavaDoc();
664             
665             Method JavaDoc[] methods = beanClass.getMethods();
666             for ( int i = 0, size = methods.length; i < size; i++ ) {
667                 Method JavaDoc method = methods[i];
668                 String JavaDoc name = method.getName();
669                 if ( name.startsWith( "add" )) {
670                     // TODO: should we filter out non-void returning methods?
671
// some beans will return something as a helper
672
Class JavaDoc[] types = method.getParameterTypes();
673                     if ( types != null) {
674                         if ( getLog().isTraceEnabled() ) {
675                             getLog().trace("Searching for match for " + method);
676                         }
677                         
678                         switch (types.length)
679                         {
680                             case 1:
681                                 singleParameterAdders.add(method);
682                                 break;
683                             case 2:
684                                 twinParameterAdders.add(method);
685                                 break;
686                             default:
687                                 // ignore
688
break;
689                         }
690                     }
691                 }
692             }
693             
694             Map JavaDoc elementsByPropertyName = makeElementDescriptorMap( rootDescriptor );
695             
696             for (Iterator JavaDoc it=singleParameterAdders.iterator();it.hasNext();) {
697                 Method JavaDoc singleParameterAdder = (Method JavaDoc) it.next();
698                 setIteratorAdder(elementsByPropertyName, singleParameterAdder);
699             }
700             
701             for (Iterator JavaDoc it=twinParameterAdders.iterator();it.hasNext();) {
702                 Method JavaDoc twinParameterAdder = (Method JavaDoc) it.next();
703                 setMapAdder(elementsByPropertyName, twinParameterAdder);
704             }
705         }
706     }
707     
708     /**
709      * Sets the adder method where the corresponding property is an iterator
710      * @param rootDescriptor
711      * @param singleParameterAdder
712      */

713     private void setIteratorAdder(
714         Map JavaDoc elementsByPropertyName,
715         Method JavaDoc singleParameterAdderMethod) {
716         
717         String JavaDoc adderName = singleParameterAdderMethod.getName();
718         String JavaDoc propertyName = Introspector.decapitalize(adderName.substring(3));
719         ElementDescriptor matchingDescriptor = getMatchForAdder(propertyName, elementsByPropertyName);
720         if (matchingDescriptor != null) {
721             //TODO defensive code: probably should check descriptor type
722

723             Class JavaDoc singularType = singleParameterAdderMethod.getParameterTypes()[0];
724             if (getLog().isTraceEnabled()) {
725                 getLog().trace(adderName + "->" + propertyName);
726             }
727             // this may match a standard collection or iteration
728
getLog().trace("Matching collection or iteration");
729                                     
730             matchingDescriptor.setUpdater( new MethodUpdater( singleParameterAdderMethod ) );
731             matchingDescriptor.setSingularPropertyType( singularType );
732             matchingDescriptor.setHollow(!isPrimitiveType(singularType));
733             String JavaDoc localName = matchingDescriptor.getLocalName();
734             if ( localName == null || localName.length() == 0 ) {
735                 matchingDescriptor.setLocalName(
736                     getElementNameMapper()
737                         .mapTypeToElementName( propertyName ) );
738             }
739                                     
740             if ( getLog().isDebugEnabled() ) {
741                 getLog().debug( "!! " + singleParameterAdderMethod);
742                 getLog().debug( "!! " + singularType);
743             }
744         }
745     }
746     
747     /**
748      * Sets the adder where the corresponding property type is an map
749      * @param rootDescriptor
750      * @param singleParameterAdder
751      */

752     private void setMapAdder(
753         Map JavaDoc elementsByPropertyName,
754         Method JavaDoc twinParameterAdderMethod) {
755         String JavaDoc adderName = twinParameterAdderMethod.getName();
756         String JavaDoc propertyName = Introspector.decapitalize(adderName.substring(3));
757         ElementDescriptor matchingDescriptor = getMatchForAdder(propertyName, elementsByPropertyName);
758         if ( matchingDescriptor != null
759             && Map JavaDoc.class.isAssignableFrom( matchingDescriptor.getPropertyType() )) {
760             // this may match a map
761
getLog().trace("Matching map");
762             ElementDescriptor[] children
763                 = matchingDescriptor.getElementDescriptors();
764             // see if the descriptor's been set up properly
765
if ( children.length == 0 ) {
766                 getLog().info(
767                     "'entry' descriptor is missing for map. "
768                     + "Updaters cannot be set");
769                                         
770             } else {
771                 Class JavaDoc[] types = twinParameterAdderMethod.getParameterTypes();
772                 Class JavaDoc keyType = types[0];
773                 Class JavaDoc valueType = types[1];
774                 
775                 // loop through children
776
// adding updaters for key and value
777
MapEntryAdder adder = new MapEntryAdder(twinParameterAdderMethod);
778                 for (
779                     int n=0,
780                         noOfGrandChildren = children.length;
781                     n < noOfGrandChildren;
782                     n++ ) {
783                     if ( "key".equals( children[n].getLocalName() ) ) {
784                                       
785                         children[n].setUpdater( adder.getKeyUpdater() );
786                         children[n].setSingularPropertyType( keyType );
787                         if (children[n].getPropertyType() == null) {
788                             children[n].setPropertyType( valueType );
789                         }
790                         if ( isPrimitiveType(keyType) ) {
791                             children[n].setHollow(false);
792                         }
793                         if ( getLog().isTraceEnabled() ) {
794                             getLog().trace( "Key descriptor: " + children[n]);
795                         }
796                                                 
797                     } else if ( "value".equals( children[n].getLocalName() ) ) {
798
799                         children[n].setUpdater( adder.getValueUpdater() );
800                         children[n].setSingularPropertyType( valueType );
801                         if (children[n].getPropertyType() == null) {
802                             children[n].setPropertyType( valueType );
803                         }
804                         if ( isPrimitiveType( valueType) ) {
805                             children[n].setHollow(false);
806                         }
807                         if ( isLoopType( valueType )) {
808                             // need to attach a hollow descriptor
809
// don't know the element name
810
// so use null name (to match anything)
811
ElementDescriptor loopDescriptor = new ElementDescriptor();
812                             loopDescriptor.setHollow(true);
813                             loopDescriptor.setSingularPropertyType( valueType );
814                             loopDescriptor.setPropertyType( valueType );
815                             children[n].addElementDescriptor(loopDescriptor);
816                             
817                         }
818                         if ( getLog().isTraceEnabled() ) {
819                             getLog().trace( "Value descriptor: " + children[n]);
820                         }
821                     }
822                 }
823             }
824         }
825     }
826         
827     /**
828      * Gets an ElementDescriptor for the property matching the adder
829      * @param adderName
830      * @param rootDescriptor
831      * @return
832      */

833     private ElementDescriptor getMatchForAdder(
834                                                 String JavaDoc propertyName,
835                                                 Map JavaDoc elementsByPropertyName) {
836         ElementDescriptor matchingDescriptor = null;
837         if (propertyName.length() > 0) {
838             if ( getLog().isTraceEnabled() ) {
839                 getLog().trace( "findPluralDescriptor( " + propertyName
840                     + " ):root property name=" + propertyName );
841             }
842         
843             PluralStemmer stemmer = getPluralStemmer();
844             matchingDescriptor = stemmer.findPluralDescriptor( propertyName, elementsByPropertyName );
845         
846             if ( getLog().isTraceEnabled() ) {
847                 getLog().trace(
848                     "findPluralDescriptor( " + propertyName
849                         + " ):ElementDescriptor=" + matchingDescriptor );
850             }
851         }
852         return matchingDescriptor;
853     }
854     
855     // Implementation methods
856
//-------------------------------------------------------------------------
857

858
859     /**
860      * Creates a map where the keys are the property names and the values are the ElementDescriptors
861      */

862     private Map JavaDoc makeElementDescriptorMap( ElementDescriptor rootDescriptor ) {
863         Map JavaDoc result = new HashMap JavaDoc();
864         String JavaDoc rootPropertyName = rootDescriptor.getPropertyName();
865         if (rootPropertyName != null) {
866             result.put(rootPropertyName, rootDescriptor);
867         }
868         makeElementDescriptorMap( rootDescriptor, result );
869         return result;
870     }
871     
872     /**
873      * Creates a map where the keys are the property names and the values are the ElementDescriptors
874      *
875      * @param rootDescriptor the values of the maps are the children of this
876      * <code>ElementDescriptor</code> index by their property names
877      * @param map the map to which the elements will be added
878      */

879     private void makeElementDescriptorMap( ElementDescriptor rootDescriptor, Map JavaDoc map ) {
880         ElementDescriptor[] children = rootDescriptor.getElementDescriptors();
881         if ( children != null ) {
882             for ( int i = 0, size = children.length; i < size; i++ ) {
883                 ElementDescriptor child = children[i];
884                 String JavaDoc propertyName = child.getPropertyName();
885                 if ( propertyName != null ) {
886                     map.put( propertyName, child );
887                 }
888                 makeElementDescriptorMap( child, map );
889             }
890         }
891     }
892     
893     /**
894      * A Factory method to lazily create a new strategy
895      * to detect matching singular and plural properties.
896      *
897      * @return new defualt PluralStemmer implementation
898      * @deprecated 0.6 this method has been moved into IntrospectionConfiguration.
899      * Those who need to vary this should subclass that class instead
900      */

901     protected PluralStemmer createPluralStemmer() {
902         return new DefaultPluralStemmer();
903     }
904     
905     /**
906      * A Factory method to lazily create a strategy
907      * used to convert bean type names into element names.
908      *
909      * @return new default NameMapper implementation
910      * @deprecated 0.6 this method has been moved into IntrospectionConfiguration.
911      * Those who need to vary this should subclass that class instead
912      */

913     protected NameMapper createNameMapper() {
914         return new DefaultNameMapper();
915     }
916     
917     /**
918      * Attempt to lookup the XML descriptor for the given class using the
919      * classname + ".betwixt" using the same ClassLoader used to load the class
920      * or return null if it could not be loaded
921      *
922      * @param aClass digester .betwixt file for this class
923      * @return XMLBeanInfo digested from the .betwixt file if one can be found.
924      * Otherwise null.
925      */

926     protected synchronized XMLBeanInfo findByXMLDescriptor( Class JavaDoc aClass ) {
927         // trim the package name
928
String JavaDoc name = aClass.getName();
929         int idx = name.lastIndexOf( '.' );
930         if ( idx >= 0 ) {
931             name = name.substring( idx + 1 );
932         }
933         name += ".betwixt";
934         
935         URL JavaDoc url = aClass.getResource( name );
936         if ( url != null ) {
937             try {
938                 String JavaDoc urlText = url.toString();
939                 if ( getLog().isDebugEnabled( )) {
940                     getLog().debug( "Parsing Betwixt XML descriptor: " + urlText );
941                 }
942                 // synchronized method so this digester is only used by
943
// one thread at once
944
if ( digester == null ) {
945                     digester = new XMLBeanInfoDigester();
946                     digester.setXMLIntrospector( this );
947                 }
948                 digester.setBeanClass( aClass );
949                 return (XMLBeanInfo) digester.parse( urlText );
950             } catch (Exception JavaDoc e) {
951                 getLog().warn( "Caught exception trying to parse: " + name, e );
952             }
953         }
954         
955         if ( getLog().isTraceEnabled() ) {
956             getLog().trace( "Could not find betwixt file " + name );
957         }
958         return null;
959     }
960             
961     /**
962      * Loop through properties and process each one
963      *
964      * @param beanInfo the BeanInfo whose properties will be processed
965      * @param elements ElementDescriptor list to which elements will be added
966      * @param attributes AttributeDescriptor list to which attributes will be added
967      * @param contents Descriptor list to which mixed content will be added
968      * @throws IntrospectionException if the bean introspection fails
969      * @deprecated 0.5 use {@link #addProperties(BeanProperty[], List, List,List)}
970      */

971     protected void addProperties(
972                                     BeanInfo JavaDoc beanInfo,
973                                     List JavaDoc elements,
974                                     List JavaDoc attributes,
975                                     List JavaDoc contents)
976                                         throws
977                                             IntrospectionException JavaDoc {
978         PropertyDescriptor JavaDoc[] descriptors = beanInfo.getPropertyDescriptors();
979         if ( descriptors != null ) {
980             for ( int i = 0, size = descriptors.length; i < size; i++ ) {
981                 addProperty(beanInfo, descriptors[i], elements, attributes, contents);
982             }
983         }
984         if (getLog().isTraceEnabled()) {
985             getLog().trace(elements);
986             getLog().trace(attributes);
987             getLog().trace(contents);
988         }
989     }
990     /**
991      * Loop through properties and process each one
992      *
993      * @param beanProperties the properties to be processed
994      * @param elements ElementDescriptor list to which elements will be added
995      * @param attributes AttributeDescriptor list to which attributes will be added
996      * @param contents Descriptor list to which mixed content will be added
997      * @since 0.5
998      */

999     protected void addProperties(
1000                                    BeanProperty[] beanProperties,
1001                                    List JavaDoc elements,
1002                                    List JavaDoc attributes,
1003                                    List JavaDoc contents) {
1004        if ( beanProperties != null ) {
1005            if (getLog().isTraceEnabled()) {
1006                getLog().trace(beanProperties.length + " properties to be added");
1007            }
1008            for ( int i = 0, size = beanProperties.length; i < size; i++ ) {
1009                addProperty(beanProperties[i], elements, attributes, contents);
1010            }
1011        }
1012        if (getLog().isTraceEnabled()) {
1013            getLog().trace("After properties have been added (elements, attributes, contents):");
1014            getLog().trace(elements);
1015            getLog().trace(attributes);
1016            getLog().trace(contents);
1017        }
1018    }
1019
1020    
1021    /**
1022     * Process a property.
1023     * Go through and work out whether it's a loop property, a primitive or a standard.
1024     * The class property is ignored.
1025     *
1026     * @param beanInfo the BeanInfo whose property is being processed
1027     * @param propertyDescriptor the PropertyDescriptor to process
1028     * @param elements ElementDescriptor list to which elements will be added
1029     * @param attributes AttributeDescriptor list to which attributes will be added
1030     * @param contents Descriptor list to which mixed content will be added
1031     * @throws IntrospectionException if the bean introspection fails
1032     * @deprecated 0.5 BeanInfo is no longer required.
1033     * Use {@link #addProperty(PropertyDescriptor, List, List, List)} instead.
1034     */

1035    protected void addProperty(
1036                                BeanInfo JavaDoc beanInfo,
1037                                PropertyDescriptor JavaDoc propertyDescriptor,
1038                                List JavaDoc elements,
1039                                List JavaDoc attributes,
1040                                List JavaDoc contents)
1041                                    throws
1042                                        IntrospectionException JavaDoc {
1043       addProperty( propertyDescriptor, elements, attributes, contents);
1044    }
1045    
1046    /**
1047     * Process a property.
1048     * Go through and work out whether it's a loop property, a primitive or a standard.
1049     * The class property is ignored.
1050     *
1051     * @param propertyDescriptor the PropertyDescriptor to process
1052     * @param elements ElementDescriptor list to which elements will be added
1053     * @param attributes AttributeDescriptor list to which attributes will be added
1054     * @param contents Descriptor list to which mixed content will be added
1055     * @throws IntrospectionException if the bean introspection fails
1056     * @deprecated 0.5 use {@link #addProperty(BeanProperty, List, List, List)} instead
1057     */

1058    protected void addProperty(
1059                                PropertyDescriptor JavaDoc propertyDescriptor,
1060                                List JavaDoc elements,
1061                                List JavaDoc attributes,
1062                                List JavaDoc contents)
1063                                    throws
1064                                        IntrospectionException JavaDoc {
1065        addProperty(new BeanProperty( propertyDescriptor ), elements, attributes, contents);
1066    }
1067    
1068    /**
1069     * Process a property.
1070     * Go through and work out whether it's a loop property, a primitive or a standard.
1071     * The class property is ignored.
1072     *
1073     * @param beanProperty the bean property to process
1074     * @param elements ElementDescriptor list to which elements will be added
1075     * @param attributes AttributeDescriptor list to which attributes will be added
1076     * @param contents Descriptor list to which mixed content will be added
1077     * @since 0.5
1078     */

1079    protected void addProperty(
1080                                BeanProperty beanProperty,
1081                                List JavaDoc elements,
1082                                List JavaDoc attributes,
1083                                List JavaDoc contents) {
1084        Descriptor nodeDescriptor = createXMLDescriptor(beanProperty);
1085        if (nodeDescriptor == null) {
1086           return;
1087        }
1088        if (nodeDescriptor instanceof ElementDescriptor) {
1089           elements.add(nodeDescriptor);
1090        } else if (nodeDescriptor instanceof AttributeDescriptor) {
1091           attributes.add(nodeDescriptor);
1092        } else {
1093           contents.add(nodeDescriptor);
1094        }
1095    }
1096    
1097    /**
1098     * Loop through properties and process each one
1099     *
1100     * @param beanInfo the BeanInfo whose properties will be processed
1101     * @param elements ElementDescriptor list to which elements will be added
1102     * @param attributes AttributeDescriptor list to which attributes will be added
1103     * @throws IntrospectionException if the bean introspection fails
1104     * @deprecated 0.5 this method does not support mixed content.
1105     * Use {@link #addProperties(BeanInfo, List, List, List)} instead.
1106     */

1107    protected void addProperties(
1108                                    BeanInfo JavaDoc beanInfo,
1109                                    List JavaDoc elements,
1110                                    List JavaDoc attributes)
1111                                        throws
1112                                            IntrospectionException JavaDoc {
1113        PropertyDescriptor JavaDoc[] descriptors = beanInfo.getPropertyDescriptors();
1114        if ( descriptors != null ) {
1115            for ( int i = 0, size = descriptors.length; i < size; i++ ) {
1116                addProperty(beanInfo, descriptors[i], elements, attributes);
1117            }
1118        }
1119        if (getLog().isTraceEnabled()) {
1120            getLog().trace(elements);
1121            getLog().trace(attributes);
1122        }
1123    }
1124    
1125    /**
1126     * Process a property.
1127     * Go through and work out whether it's a loop property, a primitive or a standard.
1128     * The class property is ignored.
1129     *
1130     * @param beanInfo the BeanInfo whose property is being processed
1131     * @param propertyDescriptor the PropertyDescriptor to process
1132     * @param elements ElementDescriptor list to which elements will be added
1133     * @param attributes AttributeDescriptor list to which attributes will be added
1134     * @throws IntrospectionException if the bean introspection fails
1135     * @deprecated 0.5 this method does not support mixed content.
1136     * Use {@link #addProperty(BeanInfo, PropertyDescriptor, List, List, List)} instead.
1137     */

1138    protected void addProperty(
1139                                BeanInfo JavaDoc beanInfo,
1140                                PropertyDescriptor JavaDoc propertyDescriptor,
1141                                List JavaDoc elements,
1142                                List JavaDoc attributes)
1143                                    throws
1144                                        IntrospectionException JavaDoc {
1145        NodeDescriptor nodeDescriptor = XMLIntrospectorHelper
1146            .createDescriptor(propertyDescriptor,
1147                                 isAttributesForPrimitives(),
1148                                 this);
1149        if (nodeDescriptor == null) {
1150           return;
1151        }
1152        if (nodeDescriptor instanceof ElementDescriptor) {
1153           elements.add(nodeDescriptor);
1154        } else {
1155           attributes.add(nodeDescriptor);
1156        }
1157    }
1158
1159    
1160    /**
1161     * Factory method to create XMLBeanInfo instances
1162     *
1163     * @param beanInfo the BeanInfo from which the XMLBeanInfo will be created
1164     * @return XMLBeanInfo describing the bean-xml mapping
1165     */

1166    protected XMLBeanInfo createXMLBeanInfo( BeanInfo JavaDoc beanInfo ) {
1167        XMLBeanInfo xmlBeanInfo = new XMLBeanInfo( beanInfo.getBeanDescriptor().getBeanClass() );
1168        return xmlBeanInfo;
1169    }
1170
1171    /**
1172     * Is this class a loop?
1173     *
1174     * @param type the Class to test
1175     * @return true if the type is a loop type
1176     */

1177    public boolean isLoopType(Class JavaDoc type) {
1178        return XMLIntrospectorHelper.isLoopType(type);
1179    }
1180    
1181    
1182    /**
1183     * Is this class a primitive?
1184     * TODO: this method will probably be deprecated when primitive types
1185     * are subsumed into the simple type concept
1186     * @param type the Class to test
1187     * @return true for primitive types
1188     */

1189    public boolean isPrimitiveType(Class JavaDoc type) {
1190        TypeBindingStrategy.BindingType bindingType
1191            = configuration.getTypeBindingStrategy().bindingType( type ) ;
1192        boolean result = (bindingType.equals(TypeBindingStrategy.BindingType.PRIMITIVE));
1193        return result;
1194    }
1195
1196    
1197    /** Some type of pseudo-bean */
1198    private abstract class BeanType {
1199        /**
1200         * Gets the name for this bean type
1201         * @return the bean type name, not null
1202         */

1203        public abstract String JavaDoc getBeanName();
1204        
1205        /**
1206         * Gets the type to be used by the associated element
1207         * @return a Class that is the type not null
1208         */

1209        public abstract Class JavaDoc getElementType();
1210
1211        /**
1212         * Is this type a primitive?
1213         * @return true if this type should be treated by betwixt as a primitive
1214         */

1215        public abstract boolean isPrimitiveType();
1216        
1217        /**
1218         * is this type a map?
1219         * @return true this should be treated as a map.
1220         */

1221        public abstract boolean isMapType();
1222        
1223        /**
1224         * Is this type a loop?
1225         * @return true if this should be treated as a loop
1226         */

1227        public abstract boolean isLoopType();
1228        
1229        /**
1230         * Gets the properties associated with this bean.
1231         * @return the BeanProperty's, not null
1232         */

1233        public abstract BeanProperty[] getProperties();
1234        
1235        /**
1236         * Create string representation
1237         * @return something useful for logging
1238         */

1239        public String JavaDoc toString() {
1240            return "Bean[name=" + getBeanName() + ", type=" + getElementType();
1241        }
1242    }
1243    
1244    /** Supports standard Java Beans */
1245    private class JavaBeanType extends BeanType {
1246        /** Introspected bean */
1247        private BeanInfo JavaDoc beanInfo;
1248        /** Bean class */
1249        private Class JavaDoc beanClass;
1250        /** Bean name */
1251        private String JavaDoc name;
1252        /** Bean properties */
1253        private BeanProperty[] properties;
1254        
1255        /**
1256         * Constructs a BeanType for a standard Java Bean
1257         * @param beanInfo the BeanInfo describing the standard Java Bean, not null
1258         */

1259        public JavaBeanType(BeanInfo JavaDoc beanInfo) {
1260            this.beanInfo = beanInfo;
1261            BeanDescriptor JavaDoc beanDescriptor = beanInfo.getBeanDescriptor();
1262            beanClass = beanDescriptor.getBeanClass();
1263            name = beanDescriptor.getName();
1264            // Array's contain a bad character
1265
if (beanClass.isArray()) {
1266                // called all array's Array
1267
name = "Array";
1268            }
1269            
1270        }
1271        
1272        /** @see BeanType #getElementType */
1273        public Class JavaDoc getElementType() {
1274            return beanClass;
1275        }
1276        
1277        /** @see BeanType#getBeanName */
1278        public String JavaDoc getBeanName() {
1279            return name;
1280        }
1281        
1282        /** @see BeanType#isPrimitiveType */
1283        public boolean isPrimitiveType() {
1284            return XMLIntrospector.this.isPrimitiveType( beanClass );
1285        }
1286        
1287        /** @see BeanType#isLoopType */
1288        public boolean isLoopType() {
1289            return XMLIntrospectorHelper.isLoopType( beanClass );
1290        }
1291        
1292        /** @see BeanType#isMapType */
1293        public boolean isMapType() {
1294            return Map JavaDoc.class.isAssignableFrom( beanClass );
1295        }
1296        
1297        /** @see BeanType#getProperties */
1298        public BeanProperty[] getProperties() {
1299            // lazy creation
1300
if ( properties == null ) {
1301                ArrayList JavaDoc propertyDescriptors = new ArrayList JavaDoc();
1302                // add base bean info
1303
PropertyDescriptor JavaDoc[] descriptors = beanInfo.getPropertyDescriptors();
1304                if ( descriptors != null ) {
1305                    for (int i=0, size=descriptors.length; i<size; i++) {
1306                        propertyDescriptors.add( descriptors[i] );
1307                    }
1308                }
1309                
1310                // add properties from additional bean infos
1311
BeanInfo JavaDoc[] additionals = beanInfo.getAdditionalBeanInfo();
1312                if ( additionals != null ) {
1313                    for ( int i=0, outerSize=additionals.length; i<outerSize; i++ ) {
1314                        BeanInfo JavaDoc additionalInfo = additionals[i];
1315                        descriptors = beanInfo.getPropertyDescriptors();
1316                        if ( descriptors != null ) {
1317                            for (int j=0, innerSize=descriptors.length; j<innerSize; j++) {
1318                                propertyDescriptors.add( descriptors[j] );
1319                            }
1320                        }
1321                    }
1322                }
1323                // what happens when size is zero?
1324
properties = new BeanProperty[ propertyDescriptors.size() ];
1325                int count = 0;
1326                for ( Iterator JavaDoc it = propertyDescriptors.iterator(); it.hasNext(); count++) {
1327                    PropertyDescriptor JavaDoc propertyDescriptor = (PropertyDescriptor JavaDoc) it.next();
1328                    properties[count] = new BeanProperty( propertyDescriptor );
1329                }
1330            }
1331            return properties;
1332        }
1333    }
1334    
1335    /** Implementation for DynaClasses */
1336    private class DynaClassBeanType extends BeanType {
1337        /** BeanType for this DynaClass */
1338        private DynaClass dynaClass;
1339        /** Properties extracted in constuctor */
1340        private BeanProperty[] properties;
1341        
1342        /**
1343         * Constructs a BeanType for a DynaClass
1344         * @param dynaClass not null
1345         */

1346        public DynaClassBeanType(DynaClass dynaClass) {
1347            this.dynaClass = dynaClass;
1348            DynaProperty[] dynaProperties = dynaClass.getDynaProperties();
1349            properties = new BeanProperty[dynaProperties.length];
1350            for (int i=0, size=dynaProperties.length; i<size; i++) {
1351                properties[i] = new BeanProperty(dynaProperties[i]);
1352            }
1353        }
1354        
1355        /** @see BeanType#getBeanName */
1356        public String JavaDoc getBeanName() {
1357            return dynaClass.getName();
1358        }
1359        /** @see BeanType#getElementType */
1360        public Class JavaDoc getElementType() {
1361            return DynaClass.class;
1362        }
1363        /** @see BeanType#isPrimitiveType */
1364        public boolean isPrimitiveType() {
1365            return false;
1366        }
1367        /** @see BeanType#isMapType */
1368        public boolean isMapType() {
1369            return false;
1370        }
1371        /** @see BeanType#isLoopType */
1372        public boolean isLoopType() {
1373            return false;
1374        }
1375        /** @see BeanType#getProperties */
1376        public BeanProperty[] getProperties() {
1377            return properties;
1378        }
1379    }
1380}
1381
Popular Tags