1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
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
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
329
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
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
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
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
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
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 }