View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */ 
16  package org.apache.commons.betwixt;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.apache.commons.betwixt.expression.Expression;
22  
23  /*** <p><code>ElementDescriptor</code> describes the XML elements
24    * to be created for a bean instance.</p>
25    *
26    * <p> It contains <code>AttributeDescriptor</code>'s for all it's attributes
27    * and <code>ElementDescriptor</code>'s for it's child elements.
28    *
29    * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
30    * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
31    * @version $Revision: 1.15.2.1 $
32    */
33  public class ElementDescriptor extends NodeDescriptor {
34  
35      /*** 
36       * Descriptors for attributes this element contains.
37       * <strong>Note:</strong> Constructed lazily on demand from a List.
38       * {@link #getAttributeDescriptor()} should be called rather than accessing this
39       * field directly.
40       */
41      private AttributeDescriptor[] attributeDescriptors;
42      /*** 
43       * Descriptors for child elements.
44       * <strong>Note:</strong> Constructed lazily on demand from a List.
45       * {@link #getElementDescriptor()} should be called rather than accessing this
46       * field directly.
47       */
48      private ElementDescriptor[] elementDescriptors;
49      
50      /*** 
51       * Descriptors for child content.
52       * <strong>Note:</strong> Constructed lazily on demand from a List.
53       * {@link #getContentDescriptor()} should be called rather than accessing this
54       * field directly.
55       */
56      private Descriptor[] contentDescriptors;
57      
58      /*** 
59       * The List used on construction. It will be GC'd
60       * after initilization and the array is lazily constructed
61       */
62      private List attributeList;
63      
64      /*** 
65       * The List used on construction. It will be GC'd
66       * after initilization and the array is lazily constructed
67       */
68      private List elementList;
69      
70      /*** 
71       * The list used o construct array. It will be GC'd after
72       * initialization when the array is lazily constructed.
73       */
74      private List contentList;
75          
76      /*** the expression used to evaluate the new context of this node 
77       * or null if the same context is to be used */
78      private Expression contextExpression;
79  
80      /*** Whether this element refers to a primitive type (or property of a parent object) */
81      private boolean primitiveType;
82      
83      /*** 
84       * Whether this collection element can be used
85       * as a collection element. Defaults to true
86       */
87      private boolean wrapCollectionsInElement = true;
88      
89      /*** specifies a separate implementation class that should be instantiated
90        * when reading beans
91        * or null if there is no separate implementation */
92      private Class implementationClass = null;
93      
94      /***  
95       * Constructs an <code>ElementDescriptor</code> that refers to a primitive type.
96       */
97      public ElementDescriptor() {
98      }
99      
100     /***
101      * Base constructor.
102      * @param primitiveType if true, this element refers to a primitive type
103      */
104     public ElementDescriptor(boolean primitiveType) {
105         this.primitiveType = primitiveType;
106     }
107 
108     /*** 
109      * Creates a ElementDescriptor with no namespace URI or prefix.
110      *
111      * @param localName the (xml) local name of this node. 
112      * This will be used to set both qualified and local name for this name.
113      */
114     public ElementDescriptor(String localName) {
115         super( localName );
116     }
117 
118 
119     
120     /*** 
121      * Creates a <code>ElementDescriptor</code> with namespace URI and qualified name
122      * @param localName the (xml) local name of this  node
123      * @param qualifiedName the (xml) qualified name of this node
124      * @param uri the (xml) namespace prefix of this node
125      */
126     public ElementDescriptor(String localName, String qualifiedName, String uri) {
127         super(localName, qualifiedName, uri);
128     }
129 
130     /*** 
131      * Returns true if this element has child <code>ElementDescriptors</code>
132      * @return true if this element has child elements 
133      * @see #getElementDescriptors
134      */
135     public boolean hasChildren() {
136         return getElementDescriptors().length > 0;
137     }
138     
139     /*** 
140      * Returns true if this element has <code>AttributeDescriptors</code>
141      * @return true if this element has attributes
142      * @see #getAttributeDescriptors
143      */
144     public boolean hasAttributes() {
145         return getAttributeDescriptors().length > 0;
146     }
147     
148     /*** 
149      * Returns true if this element has child content.
150      * @return true if this element has either child mixed content or child elements
151      * @see #getContentDescriptors
152      * @since 0.5
153      */
154     public boolean hasContent() {
155         return getContentDescriptors().length > 0; 
156      } 
157     
158     
159     /*** 
160      * Sets whether <code>Collection</code> bean properties should wrap items in a parent element.
161      * In other words, should the mapping for bean properties which are <code>Collection</code>s 
162      * enclosed the item elements within a parent element.
163      * Normally only used when this describes a collection bean property.
164      *
165      * @param wrapCollectionsInElement true if the elements for the items in the collection 
166      * should be contained in a parent element
167      */
168     public void setWrapCollectionsInElement(boolean wrapCollectionsInElement) {
169         this.wrapCollectionsInElement = wrapCollectionsInElement;
170     }
171 
172     /***
173      * Returns true if collective bean properties should wrap the items in a parent element.
174      * In other words, should the mapping for bean properties which are <code>Collection</code>s 
175      * enclosed the item elements within a parent element.
176      * Normally only used when this describes a collection bean property.
177      *
178      * @return true if the elements for the items in the collection should be contained 
179      * in a parent element
180      */
181     public boolean isWrapCollectionsInElement() {
182         return this.wrapCollectionsInElement;
183     }
184 
185     /***
186      * Adds an attribute to the element this <code>ElementDescriptor</code> describes
187      * @param descriptor the <code>AttributeDescriptor</code> that will be added to the 
188      * attributes associated with element this <code>ElementDescriptor</code> describes
189      */
190     public void addAttributeDescriptor(AttributeDescriptor descriptor) {
191         if ( attributeList == null ) {
192             attributeList = new ArrayList();
193         }
194         getAttributeList().add( descriptor );
195         attributeDescriptors = null;
196     }
197     
198     
199     /*** 
200      * Returns the attribute descriptors for this element 
201      *
202      * @return descriptors for the attributes of the element that this 
203      * <code>ElementDescriptor</code> describes
204      */
205     public AttributeDescriptor[] getAttributeDescriptors() {
206         if ( attributeDescriptors == null ) {
207             if ( attributeList == null ) {
208                 attributeDescriptors = new AttributeDescriptor[0];
209             } else {
210                 attributeDescriptors = new AttributeDescriptor[ attributeList.size() ];
211                 attributeList.toArray( attributeDescriptors );
212                 
213                 // allow GC of List when initialized
214                 attributeList = null;
215             }
216         }
217         return attributeDescriptors;
218     }
219     
220     /*** 
221      * Sets the <code>AttributesDescriptors</code> for this element.
222      * This sets descriptors for the attributes of the element describe by the 
223      * <code>ElementDescriptor</code>.
224      *
225      * @param attributeDescriptors the <code>AttributeDescriptor</code> describe the attributes
226      * of the element described by this <code>ElementDescriptor</code>
227      */
228     public void setAttributeDescriptors(AttributeDescriptor[] attributeDescriptors) {
229         this.attributeDescriptors = attributeDescriptors;
230         this.attributeList = null;
231     }
232     
233     /***
234      * Adds a descriptor for a child element.
235      * 
236      * @param descriptor the <code>ElementDescriptor</code> describing the child element to add
237      */
238     public void addElementDescriptor(ElementDescriptor descriptor) {
239         if ( elementList == null ) {
240             elementList = new ArrayList();
241         }
242         getElementList().add( descriptor );
243         elementDescriptors = null;
244         addContentDescriptor( descriptor );
245     }
246     
247     /*** 
248      * Returns descriptors for the child elements of the element this describes.
249      * @return the <code>ElementDescriptor</code> describing the child elements
250      * of the element that this <code>ElementDescriptor</code> describes
251      */
252     public ElementDescriptor[] getElementDescriptors() {
253         if ( elementDescriptors == null ) {
254             if ( elementList == null ) {
255                 elementDescriptors = new ElementDescriptor[0];
256             } else {
257                 elementDescriptors = new ElementDescriptor[ elementList.size() ];
258                 elementList.toArray( elementDescriptors );
259                 
260                 // allow GC of List when initialized
261                 elementList = null;
262             }
263         }
264         return elementDescriptors;
265     }
266 
267     /*** 
268      * Sets the descriptors for the child element of the element this describes. 
269      * Also sets the child content descriptors for this element
270      *
271      * @param elementDescriptors the <code>ElementDescriptor</code>s of the element 
272      * that this describes
273      */
274     public void setElementDescriptors(ElementDescriptor[] elementDescriptors) {
275         this.elementDescriptors = elementDescriptors;
276         this.elementList = null;
277         setContentDescriptors( elementDescriptors );
278     }
279     
280     /***
281      * Adds a descriptor for child content.
282      * 
283      * @param descriptor the <code>Descriptor</code> describing the child content to add
284      * @since 0.5
285      */
286     public void addContentDescriptor(Descriptor descriptor) {
287         if ( contentList == null ) {
288             contentList = new ArrayList();
289         }
290         getContentList().add( descriptor );
291         contentDescriptors = null;
292     }
293     
294     /*** 
295      * Returns descriptors for the child content of the element this describes.
296      * @return the <code>Descriptor</code> describing the child elements
297      * of the element that this <code>ElementDescriptor</code> describes
298      * @since 0.5
299      */
300     public Descriptor[] getContentDescriptors() {
301         if ( contentDescriptors == null ) {
302             if ( contentList == null ) {
303                 contentDescriptors = new Descriptor[0];
304             } else {
305                 contentDescriptors = new Descriptor[ contentList.size() ];
306                 contentList.toArray( contentDescriptors );
307                 
308                 // allow GC of List when initialized
309                 contentList = null;
310             }
311         }
312         return contentDescriptors;
313     }
314     
315     /***
316      * <p>Gets the primary descriptor for body text of this element. 
317      * Betwixt collects all body text for any element together.
318      * This makes it rounds tripping difficult for beans that write more than one
319      * mixed content property.
320      * </p><p>
321      * The algorithm used in the default implementation is that the first TextDescriptor
322      * found amongst the descriptors is returned.
323      *
324      * @return the primary descriptor or null if this element has no mixed body content
325      * @since 0.5
326      */
327     public TextDescriptor getPrimaryBodyTextDescriptor() {
328         // todo: this probably isn't the most efficent algorithm
329         // but should avoid premature optimization
330         Descriptor[] descriptors = getContentDescriptors();
331         for (int i=0, size=descriptors.length; i<size; i++) {
332             if (descriptors[i] instanceof TextDescriptor) {
333                 return (TextDescriptor) descriptors[i];
334             }
335         }
336         // if we haven't found anything, return null.
337         return null;
338     }
339 
340     /*** 
341      * Sets the descriptors for the child content of the element this describes. 
342      * @param contentDescriptors the <code>Descriptor</code>s of the element 
343      * that this describes
344      * @since 0.5
345      */
346     public void setContentDescriptors(Descriptor[] contentDescriptors) {
347         this.contentDescriptors = contentDescriptors;
348         this.contentList = null;
349     }
350     
351     /*** 
352      * Returns the expression used to evaluate the new context of this element.
353      * @return the expression used to evaluate the new context of this element
354      */
355     public Expression getContextExpression() {
356         return contextExpression;
357     }
358     
359     /*** 
360      * Sets the expression used to evaluate the new context of this element 
361      * @param contextExpression the expression used to evaluate the new context of this element 
362      */
363     public void setContextExpression(Expression contextExpression) {
364         this.contextExpression = contextExpression;
365     }
366     
367     /*** 
368      * Returns true if this element refers to a primitive type property
369      * @return whether this element refers to a primitive type (or property of a parent object) 
370      */
371     public boolean isPrimitiveType() {
372         return primitiveType;
373     }
374     
375     /*** 
376      * Sets whether this element refers to a primitive type (or property of a parent object) 
377      * @param primitiveType true if this element refers to a primitive type
378      */
379     public void setPrimitiveType(boolean primitiveType) {
380         this.primitiveType = primitiveType;
381     }
382     
383     // Implementation methods
384     //-------------------------------------------------------------------------    
385         
386     /*** 
387      * Lazily creates the mutable List.
388      * This nullifies the attributeDescriptors array so that
389      * as items are added to the list the Array is ignored until it is
390      * explicitly asked for.
391      * 
392      * @return list of <code>AttributeDescriptors</code>'s describing the attributes
393      * of the element that this <code>ElementDescriptor</code> describes
394      */
395     protected List getAttributeList() {
396         if ( attributeList == null ) {
397             if ( attributeDescriptors != null ) {
398                 int size = attributeDescriptors.length;
399                 attributeList = new ArrayList( size );
400                 for ( int i = 0; i < size; i++ ) {
401                     attributeList.add( attributeDescriptors[i] );
402                 }
403                 // force lazy recreation later
404                 attributeDescriptors = null;
405             } else {
406                 attributeList = new ArrayList();
407             }            
408         }
409         return attributeList;
410     }
411     
412     /***  
413      * Lazily creates the mutable List of child elements.
414      * This nullifies the elementDescriptors array so that
415      * as items are added to the list the Array is ignored until it is
416      * explicitly asked for.
417      *
418      * @return list of <code>ElementDescriptor</code>'s describe the child elements of 
419      * the element that this <code>ElementDescriptor</code> describes
420      */
421     protected List getElementList() {
422         if ( elementList == null ) {
423             if ( elementDescriptors != null ) {
424                 int size = elementDescriptors.length;
425                 elementList = new ArrayList( size );
426                 for ( int i = 0; i < size; i++ ) {
427                     elementList.add( elementDescriptors[i] );
428                 }
429                 // force lazy recreation later
430                 elementDescriptors = null;
431             } else {
432                 elementList = new ArrayList();
433             }            
434         }
435         return elementList;
436     }
437     
438     /***  
439      * Lazily creates the mutable List of child content descriptors.
440      * This nullifies the contentDescriptors array so that
441      * as items are added to the list the Array is ignored until it is
442      * explicitly asked for.
443      *
444      * @return list of <code>Descriptor</code>'s describe the child content of 
445      * the element that this <code>Descriptor</code> describes
446      * @since 0.5
447      */
448     protected List getContentList() {
449         if ( contentList == null ) {
450             if ( contentDescriptors != null ) {
451                 int size = contentDescriptors.length;
452                 contentList = new ArrayList( size );
453                 for ( int i = 0; i < size; i++ ) {
454                     contentList.add( contentDescriptors[i] );
455                 }
456                 // force lazy recreation later
457                 contentDescriptors = null;
458             } else {
459                 contentList = new ArrayList();
460             }            
461         }
462         return contentList;
463     }
464     
465     /***
466       * Gets the class which should be used for instantiation.
467       * @return the class which should be used for instantiation of beans 
468       * mapped from this element, null if the standard class should be used
469       */
470     public Class getImplementationClass() {
471         return implementationClass;
472     }
473     
474     /***
475       * Sets the class which should be used for instantiation.
476       * @param implementationClass the class which should be used for instantiation
477       * or null to use the mapped type
478       * @since 0.5
479       */
480     public void setImplementationClass(Class implementationClass) {
481         this.implementationClass = implementationClass;
482     }
483     
484     /***
485      * Returns something useful for logging.
486      *
487      * @return a string useful for logging
488      */ 
489     public String toString() {
490         return 
491             "ElementDescriptor[qname=" + getQualifiedName() + ",pname=" + getPropertyName() 
492             + ",class=" + getPropertyType() + ",singular=" + getSingularPropertyType()
493             + ",updater=" + getUpdater() + ",wrap=" + isWrapCollectionsInElement() + "]";
494     }    
495 }