1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.commons.betwixt.io;
17
18 import java.beans.IntrospectionException;
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.Iterator;
24
25 import org.apache.commons.betwixt.AttributeDescriptor;
26 import org.apache.commons.betwixt.BindingConfiguration;
27 import org.apache.commons.betwixt.Descriptor;
28 import org.apache.commons.betwixt.ElementDescriptor;
29 import org.apache.commons.betwixt.XMLBeanInfo;
30 import org.apache.commons.betwixt.XMLIntrospector;
31 import org.apache.commons.betwixt.digester.XMLIntrospectorHelper;
32 import org.apache.commons.betwixt.expression.Context;
33 import org.apache.commons.betwixt.expression.Expression;
34 import org.apache.commons.betwixt.io.id.SequentialIDGenerator;
35 import org.apache.commons.collections.ArrayStack;
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.xml.sax.Attributes;
39 import org.xml.sax.SAXException;
40 import org.xml.sax.helpers.AttributesImpl;
41
42
43
44
45
46
47
48
49
50 /***
51 * <p>Abstract superclass for bean writers.
52 * This class encapsulates the processing logic.
53 * Subclasses provide implementations for the actual expression of the xml.</p>
54 * <h5>SAX Inspired Writing API</h5>
55 * <p>
56 * This class is intended to be used by subclassing:
57 * concrete subclasses perform the actual writing by providing
58 * suitable implementations for the following methods inspired
59 * by <a href='http://www.saxproject.org'>SAX</a>:
60 * </p>
61 * <ul>
62 * <li> {@link #start} - called when processing begins</li>
63 * <li> {@link #startElement(WriteContext, String, String, String, Attributes)}
64 * - called when the start of an element
65 * should be written</li>
66 * <li> {@link #bodyText(WriteContext, String)}
67 * - called when the start of an element
68 * should be written</li>
69 * <li> {@link #endElement(WriteContext, String, String, String)}
70 * - called when the end of an element
71 * should be written</li>
72 * <li> {@link #end} - called when processing has been completed</li>
73 * </ul>
74 * <p>
75 * <strong>Note</strong> that this class contains many deprecated
76 * versions of the writing API. These will be removed soon so care
77 * should be taken to use the latest version.
78 * </p>
79 * <p>
80 * <strong>Note</strong> that this class is designed to be used
81 * in a single threaded environment. When used in multi-threaded
82 * environments, use of a common <code>XMLIntrospector</code>
83 * and pooled writer instances should be considered.
84 * </p>
85 *
86 * @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
87 */
88 public abstract class AbstractBeanWriter {
89
90 /*** Introspector used */
91 private XMLIntrospector introspector = new XMLIntrospector();
92
93 /*** Log used for logging (Doh!) */
94 private Log log = LogFactory.getLog( AbstractBeanWriter.class );
95 /*** Map containing ID attribute values for beans */
96 private HashMap idMap = new HashMap();
97 /*** Stack containing beans - used to detect cycles */
98 private ArrayStack beanStack = new ArrayStack();
99 /*** Used to generate ID attribute values*/
100 private IDGenerator idGenerator = new SequentialIDGenerator();
101 /*** Should empty elements be written out? */
102 private boolean writeEmptyElements = true;
103 /*** Dynamic binding configuration settings */
104 private BindingConfiguration bindingConfiguration = new BindingConfiguration();
105 /*** <code>WriteContext</code> implementation reused curing writing */
106 private WriteContextImpl writeContext = new WriteContextImpl();
107 /*** Collection of namespaces which have already been declared */
108 private Collection namespacesDeclared = new ArrayList();
109
110 /***
111 * Marks the start of the bean writing.
112 * By default doesn't do anything, but can be used
113 * to do extra start processing
114 * @throws IOException if an IO problem occurs during writing
115 * @throws SAXException if an SAX problem occurs during writing
116 */
117 public void start() throws IOException, SAXException {
118 }
119
120 /***
121 * Marks the start of the bean writing.
122 * By default doesn't do anything, but can be used
123 * to do extra end processing
124 * @throws IOException if an IO problem occurs during writing
125 * @throws SAXException if an SAX problem occurs during writing
126 */
127
128 public void end() throws IOException, SAXException {
129 }
130
131 /***
132 * <p> Writes the given bean to the current stream using the XML introspector.</p>
133 *
134 * <p> This writes an xml fragment representing the bean to the current stream.</p>
135 *
136 * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
137 * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
138 * setting of the </code>BindingConfiguration</code> is false.</p>
139 *
140 * @throws IOException if an IO problem occurs during writing
141 * @throws SAXException if an SAX problem occurs during writing
142 * @throws IntrospectionException if a java beans introspection problem occurs
143 *
144 * @param bean write out representation of this bean
145 */
146 public void write(Object bean) throws
147 IOException,
148 SAXException,
149 IntrospectionException {
150 if (log.isDebugEnabled()) {
151 log.debug( "Writing bean graph..." );
152 log.debug( bean );
153 }
154 start();
155 writeBean( null, null, null, bean, makeContext( bean ) );
156 end();
157 if (log.isDebugEnabled()) {
158 log.debug( "Finished writing bean graph." );
159 }
160 }
161
162 /***
163 * <p>Writes the given bean to the current stream
164 * using the given <code>qualifiedName</code>.</p>
165 *
166 * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
167 * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
168 * setting of the <code>BindingConfiguration</code> is false.</p>
169 *
170 * @param qualifiedName the string naming root element
171 * @param bean the <code>Object</code> to write out as xml
172 *
173 * @throws IOException if an IO problem occurs during writing
174 * @throws SAXException if an SAX problem occurs during writing
175 * @throws IntrospectionException if a java beans introspection problem occurs
176 */
177 public void write(
178 String qualifiedName,
179 Object bean)
180 throws
181 IOException,
182 SAXException,
183 IntrospectionException {
184 start();
185 writeBean( "", qualifiedName, qualifiedName, bean, makeContext( bean ) );
186 end();
187 }
188
189 /***
190 * <p>Writes the given bean to the current stream
191 * using the given <code>qualifiedName</code>.</p>
192 *
193 * <p>This method will throw a <code>CyclicReferenceException</code> when a cycle
194 * is encountered in the graph <strong>only</strong> if the <code>getMapIDs()</code>
195 * setting of the <code>BindingConfiguration</code> is false.</p>
196 *
197 * @param namespaceUri the namespace uri
198 * @param localName the local name
199 * @param qualifiedName the string naming root element
200 * @param bean the <code>Object</code> to write out as xml
201 * @param context not null
202 *
203 * @throws IOException if an IO problem occurs during writing
204 * @throws SAXException if an SAX problem occurs during writing
205 * @throws IntrospectionException if a java beans introspection problem occurs
206 */
207 private void writeBean (
208 String namespaceUri,
209 String localName,
210 String qualifiedName,
211 Object bean,
212 Context context)
213 throws
214 IOException,
215 SAXException,
216 IntrospectionException {
217
218 if ( log.isTraceEnabled() ) {
219 log.trace( "Writing bean graph (qualified name '" + qualifiedName + "'" );
220 }
221
222
223 XMLBeanInfo beanInfo = introspector.introspect( bean );
224 if ( beanInfo != null ) {
225 ElementDescriptor elementDescriptor = beanInfo.getElementDescriptor();
226 if ( elementDescriptor != null ) {
227 context = context.newContext( bean );
228 if ( qualifiedName == null ) {
229 qualifiedName = elementDescriptor.getQualifiedName();
230 }
231 if ( namespaceUri == null ) {
232 namespaceUri = elementDescriptor.getURI();
233 }
234 if ( localName == null ) {
235 localName = elementDescriptor.getLocalName();
236 }
237
238 String ref = null;
239 String id = null;
240
241
242 if ( elementDescriptor.isSimple() ) {
243
244 writeElement(
245 namespaceUri,
246 localName,
247 qualifiedName,
248 elementDescriptor,
249 context );
250
251 } else {
252 pushBean ( context.getBean() );
253 if ( getBindingConfiguration().getMapIDs() ) {
254 ref = (String) idMap.get( context.getBean() );
255 }
256 if ( ref == null ) {
257
258 AttributeDescriptor idAttribute = beanInfo.getIDAttribute();
259 if (idAttribute == null) {
260
261 id = idGenerator.nextId();
262 idMap.put( bean, id );
263
264 if ( getBindingConfiguration().getMapIDs() ) {
265
266 writeElement(
267 namespaceUri,
268 localName,
269 qualifiedName,
270 elementDescriptor,
271 context ,
272 beanInfo.getIDAttributeName(),
273 id);
274
275 } else {
276
277 writeElement(
278 namespaceUri,
279 localName,
280 qualifiedName,
281 elementDescriptor,
282 context );
283 }
284
285 } else {
286
287
288
289 Object exp = idAttribute.getTextExpression().evaluate( context );
290 if (exp == null) {
291
292 log.debug("Using random id");
293 id = idGenerator.nextId();
294
295 } else {
296
297 id = exp.toString();
298 }
299 idMap.put( bean, id);
300
301
302 writeElement(
303 namespaceUri,
304 localName,
305 qualifiedName,
306 elementDescriptor,
307 context );
308 }
309 } else {
310
311 if ( !ignoreElement( elementDescriptor, context )) {
312
313 writeIDREFElement(
314 elementDescriptor,
315 namespaceUri,
316 localName,
317 qualifiedName,
318 beanInfo.getIDREFAttributeName(),
319 ref);
320 }
321 }
322 popBean();
323 }
324 }
325 }
326
327 log.trace( "Finished writing bean graph." );
328 }
329
330 /***
331 * Get <code>IDGenerator</code> implementation used to
332 * generate <code>ID</code> attribute values .
333 *
334 * @return implementation used for <code>ID</code> attribute generation
335 */
336 public IDGenerator getIdGenerator() {
337 return idGenerator;
338 }
339
340 /***
341 * Set <code>IDGenerator</code> implementation
342 * used to generate <code>ID</code> attribute values.
343 * This property can be used to customize the algorithm used for generation.
344 *
345 * @param idGenerator use this implementation for <code>ID</code> attribute generation
346 */
347 public void setIdGenerator(IDGenerator idGenerator) {
348 this.idGenerator = idGenerator;
349 }
350
351
352
353 /***
354 * Gets the dynamic configuration setting to be used for bean reading.
355 * @return the BindingConfiguration settings, not null
356 * @since 0.5
357 */
358 public BindingConfiguration getBindingConfiguration() {
359 return bindingConfiguration;
360 }
361
362 /***
363 * Sets the dynamic configuration setting to be used for bean reading.
364 * @param bindingConfiguration the BindingConfiguration settings, not null
365 * @since 0.5
366 */
367 public void setBindingConfiguration(BindingConfiguration bindingConfiguration) {
368 this.bindingConfiguration = bindingConfiguration;
369 }
370
371 /***
372 * <p>Should generated <code>ID</code> attribute values be added to the elements?</p>
373 *
374 * <p>If IDs are not being written then if a cycle is encountered in the bean graph,
375 * then a {@link CyclicReferenceException} will be thrown by the write method.</p>
376 *
377 * @return true if <code>ID</code> and <code>IDREF</code> attributes are to be written
378 * @deprecated 0.5 use {@link BindingConfiguration#getMapIDs}
379 */
380 public boolean getWriteIDs() {
381 return getBindingConfiguration().getMapIDs();
382 }
383
384 /***
385 * Set whether generated <code>ID</code> attribute values should be added to the elements
386 * If this property is set to false, then <code>CyclicReferenceException</code>
387 * will be thrown whenever a cyclic occurs in the bean graph.
388 *
389 * @param writeIDs true if <code>ID</code>'s and <code>IDREF</code>'s should be written
390 * @deprecated 0.5 use {@link BindingConfiguration#setMapIDs}
391 */
392 public void setWriteIDs(boolean writeIDs) {
393 getBindingConfiguration().setMapIDs( writeIDs );
394 }
395
396 /***
397 * <p>Gets whether empty elements should be written into the output.</p>
398 *
399 * <p>An empty element is one that has no attributes, no child elements
400 * and no body text.
401 * For example, <code><element/></code> is an empty element but
402 * <code><element attr='value'/></code> is not.</p>
403 *
404 * @return true if empty elements will be written into the output
405 * @since 0.5
406 */
407 public boolean getWriteEmptyElements() {
408 return writeEmptyElements;
409 }
410
411 /***
412 * <p>Sets whether empty elements should be written into the output.</p>
413 *
414 * <p>An empty element is one that has no attributes, no child elements
415 * and no body text.
416 * For example, <code><element/></code> is an empty element but
417 * <code><element attr='value'/></code> is not.
418 *
419 * @param writeEmptyElements true if empty elements should be written into the output
420 * @since 0.5
421 */
422 public void setWriteEmptyElements(boolean writeEmptyElements) {
423 this.writeEmptyElements = writeEmptyElements;
424 }
425
426 /***
427 * <p>Gets the introspector used.</p>
428 *
429 * <p>The {@link XMLBeanInfo} used to map each bean is
430 * created by the <code>XMLIntrospector</code>.
431 * One way in which the mapping can be customized is
432 * by altering the <code>XMLIntrospector</code>. </p>
433 *
434 * @return the <code>XMLIntrospector</code> used for introspection
435 */
436 public XMLIntrospector getXMLIntrospector() {
437 return introspector;
438 }
439
440
441 /***
442 * <p>Sets the introspector to be used.</p>
443 *
444 * <p>The {@link XMLBeanInfo} used to map each bean is
445 * created by the <code>XMLIntrospector</code>.
446 * One way in which the mapping can be customized is by
447 * altering the <code>XMLIntrospector</code>. </p>
448 *
449 * @param introspector use this introspector
450 */
451 public void setXMLIntrospector(XMLIntrospector introspector) {
452 this.introspector = introspector;
453 }
454
455 /***
456 * <p>Gets the current logging implementation.</p>
457 *
458 * @return the <code>Log</code> implementation which this class logs to
459 */
460 public final Log getAbstractBeanWriterLog() {
461 return log;
462 }
463
464 /***
465 * <p> Set the current logging implementation. </p>
466 *
467 * @param log <code>Log</code> implementation to use
468 */
469 public final void setAbstractBeanWriterLog(Log log) {
470 this.log = log;
471 }
472
473
474
475
476 /***
477 * Writes the start tag for an element.
478 *
479 * @param uri the element's namespace uri
480 * @param localName the element's local name
481 * @param qName the element's qualified name
482 * @param attr the element's attributes
483 *
484 * @throws IOException if an IO problem occurs during writing
485 * @throws SAXException if an SAX problem occurs during writing
486 * @since 0.5
487 */
488 protected void startElement(
489 WriteContext context,
490 String uri,
491 String localName,
492 String qName,
493 Attributes attr)
494 throws
495 IOException,
496 SAXException {
497
498 startElement(uri, localName, qName, attr);
499 }
500
501 /***
502 * Writes the end tag for an element
503 *
504 * @param uri the element's namespace uri
505 * @param localName the element's local name
506 * @param qName the element's qualified name
507 *
508 * @throws IOException if an IO problem occurs during writing
509 * @throws SAXException if an SAX problem occurs during writing
510 * @since 0.5
511 */
512 protected void endElement(
513 WriteContext context,
514 String uri,
515 String localName,
516 String qName)
517 throws
518 IOException,
519 SAXException {
520
521 endElement(uri, localName, qName);
522 }
523
524 /***
525 * Writes body text
526 *
527 * @param text the body text to be written
528 *
529 * @throws IOException if an IO problem occurs during writing
530 * @throws SAXException if an SAX problem occurs during writing
531 * @since 0.5
532 */
533 protected void bodyText(WriteContext context, String text)
534 throws IOException, SAXException {
535
536 bodyText(text);
537 }
538
539
540
541
542 /***
543 * Writes the start tag for an element.
544 *
545 * @param uri the element's namespace uri
546 * @param localName the element's local name
547 * @param qName the element's qualified name
548 * @param attr the element's attributes
549 *
550 * @throws IOException if an IO problem occurs during writing
551 * @throws SAXException if an SAX problem occurs during writing
552 * @deprecated 0.5 use {@link #startElement(WriteContext, String, String, String, Attributes)}
553 */
554 protected void startElement(
555 String uri,
556 String localName,
557 String qName,
558 Attributes attr)
559 throws
560 IOException,
561 SAXException {}
562
563 /***
564 * Writes the end tag for an element
565 *
566 * @param uri the element's namespace uri
567 * @param localName the element's local name
568 * @param qName the element's qualified name
569 *
570 * @throws IOException if an IO problem occurs during writing
571 * @throws SAXException if an SAX problem occurs during writing
572 * @deprecated 0.5 use {@link #endElement(WriteContext, String, String, String)}
573 */
574 protected void endElement(
575 String uri,
576 String localName,
577 String qName)
578 throws
579 IOException,
580 SAXException {}
581
582 /***
583 * Writes body text
584 *
585 * @param text the body text to be written
586 *
587 * @throws IOException if an IO problem occurs during writing
588 * @throws SAXException if an SAX problem occurs during writing
589 * @deprecated 0.5 use {@link #bodyText(WriteContext, String)}
590 */
591 protected void bodyText(String text) throws IOException, SAXException {}
592
593
594
595
596 /***
597 * Writes the given element
598 *
599 * @param namespaceUri the namespace uri
600 * @param localName the local name
601 * @param qualifiedName qualified name to use for the element
602 * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
603 * @param context the <code>Context</code> to use to evaluate the bean expressions
604 * @throws IOException if an IO problem occurs during writing
605 * @throws SAXException if an SAX problem occurs during writing
606 * @throws IntrospectionException if a java beans introspection problem occurs
607 */
608 private void writeElement(
609 String namespaceUri,
610 String localName,
611 String qualifiedName,
612 ElementDescriptor elementDescriptor,
613 Context context )
614 throws
615 IOException,
616 SAXException,
617 IntrospectionException {
618 if( log.isTraceEnabled() ) {
619 log.trace( "Writing: " + qualifiedName + " element: " + elementDescriptor );
620 }
621
622 if ( !ignoreElement( elementDescriptor, context )) {
623 if ( log.isTraceEnabled() ) {
624 log.trace( "Element " + elementDescriptor + " is empty." );
625 }
626
627 Attributes attributes = addNamespaceDeclarations(
628 new ElementAttributes( elementDescriptor, context ), namespaceUri);
629 writeContext.setCurrentDescriptor(elementDescriptor);
630 startElement(
631 writeContext,
632 namespaceUri,
633 localName,
634 qualifiedName,
635 attributes);
636
637 writeElementContent( elementDescriptor, context ) ;
638 writeContext.setCurrentDescriptor(elementDescriptor);
639 endElement( writeContext, namespaceUri, localName, qualifiedName );
640
641 }
642 }
643
644 /***
645 * Adds namespace declarations (if any are needed) to the given attributes.
646 * @param attributes Attributes, not null
647 * @param elementNamespaceUri the URI for the enclosing element, possibly null
648 * @return Attributes, not null
649 */
650 private Attributes addNamespaceDeclarations(Attributes attributes, String elementNamespaceUri) {
651 Attributes result = attributes;
652 AttributesImpl withDeclarations = null;
653 for (int i=-1, size=attributes.getLength(); i<size ; i++) {
654 String uri = null;
655 if (i == -1) {
656 uri = elementNamespaceUri;
657 } else {
658 uri = attributes.getURI(i);
659 }
660 if (uri != null && !"".equals(uri) && !namespacesDeclared.contains(uri)) {
661 if (withDeclarations == null) {
662 withDeclarations = new AttributesImpl(attributes);
663 }
664 withDeclarations.addAttribute("", "", "xmlns:"
665 + getXMLIntrospector().getConfiguration().getPrefixMapper().getPrefix(uri), "NOTATION", uri);
666 namespacesDeclared.add(uri);
667 }
668 }
669
670 if (withDeclarations != null) {
671 result = withDeclarations;
672 }
673 return result;
674 }
675
676
677 /***
678 * Writes the given element adding an ID attribute
679 *
680 * @param namespaceUri the namespace uri
681 * @param localName the local name
682 * @param qualifiedName the qualified name
683 * @param elementDescriptor the ElementDescriptor describing this element
684 * @param context the context being evaliated against
685 * @param idAttribute the qualified name of the <code>ID</code> attribute
686 * @param idValue the value for the <code>ID</code> attribute
687 * @throws IOException if an IO problem occurs during writing
688 * @throws SAXException if an SAX problem occurs during writing
689 * @throws IntrospectionException if a java beans introspection problem occurs
690 */
691 private void writeElement(
692 String namespaceUri,
693 String localName,
694 String qualifiedName,
695 ElementDescriptor elementDescriptor,
696 Context context,
697 String idAttribute,
698 String idValue )
699 throws
700 IOException,
701 SAXException,
702 IntrospectionException {
703
704 if ( !ignoreElement( elementDescriptor, context ) ) {
705 writeContext.setCurrentDescriptor(elementDescriptor);
706 Attributes attributes = new IDElementAttributes(
707 elementDescriptor,
708 context,
709 idAttribute,
710 idValue );
711 startElement(
712 writeContext,
713 namespaceUri,
714 localName,
715 qualifiedName,
716 addNamespaceDeclarations(attributes, namespaceUri));
717
718 writeElementContent( elementDescriptor, context ) ;
719 writeContext.setCurrentDescriptor(elementDescriptor);
720 endElement( writeContext, namespaceUri, localName, qualifiedName );
721
722 } else if ( log.isTraceEnabled() ) {
723 log.trace( "Element " + qualifiedName + " is empty." );
724 }
725 }
726
727
728 /***
729 * Write attributes, child elements and element end
730 *
731 * @param uri the element namespace uri
732 * @param localName the local name of the element
733 * @param qualifiedName the qualified name of the element
734 * @param elementDescriptor the descriptor for this element
735 * @param context evaluate against this context
736 * @throws IOException if an IO problem occurs during writing
737 * @throws SAXException if an SAX problem occurs during writing
738 * @throws IntrospectionException if a java beans introspection problem occurs
739 */
740 private void writeRestOfElement(
741 String uri,
742 String localName,
743 String qualifiedName,
744 ElementDescriptor elementDescriptor,
745 Context context )
746 throws
747 IOException,
748 SAXException,
749 IntrospectionException {
750
751 writeElementContent( elementDescriptor, context );
752 }
753
754 /***
755 * Writes an element with a <code>IDREF</code> attribute
756 *
757 * @param uri the namespace uri
758 * @param localName the local name
759 * @param qualifiedName of the element with <code>IDREF</code> attribute
760 * @param idrefAttributeName the qualified name of the <code>IDREF</code> attribute
761 * @param idrefAttributeValue the value for the <code>IDREF</code> attribute
762 * @throws IOException if an IO problem occurs during writing
763 * @throws SAXException if an SAX problem occurs during writing
764 * @throws IntrospectionException if a java beans introspection problem occurs
765 */
766 private void writeIDREFElement(
767 ElementDescriptor elementDescriptor,
768 String uri,
769 String localName,
770 String qualifiedName,
771 String idrefAttributeName,
772 String idrefAttributeValue )
773 throws
774 IOException,
775 SAXException,
776 IntrospectionException {
777
778
779
780
781 AttributesImpl attributes = new AttributesImpl();
782
783 attributes.addAttribute(
784 "",
785 idrefAttributeName,
786 idrefAttributeName,
787 "IDREF",
788 idrefAttributeValue);
789 writeContext.setCurrentDescriptor(elementDescriptor);
790 startElement( writeContext, uri, localName, qualifiedName, addNamespaceDeclarations(attributes, uri));
791 endElement( writeContext, uri, localName, qualifiedName );
792 }
793
794 /***
795 * Writes the element content.
796 *
797 * @param elementDescriptor the <code>ElementDescriptor</code> to write as xml
798 * @param context the <code>Context</code> to use to evaluate the bean expressions
799 *
800 * @throws IOException if an IO problem occurs during writing
801 * @throws SAXException if an SAX problem occurs during writing
802 * @throws IntrospectionException if a java beans introspection problem occurs
803 */
804 private void writeElementContent(
805 ElementDescriptor elementDescriptor,
806 Context context )
807 throws
808 IOException,
809 SAXException,
810 IntrospectionException {
811 writeContext.setCurrentDescriptor( elementDescriptor );
812 Descriptor[] childDescriptors = elementDescriptor.getContentDescriptors();
813 if ( childDescriptors != null && childDescriptors.length > 0 ) {
814
815 for ( int i = 0, size = childDescriptors.length; i < size; i++ ) {
816 if (childDescriptors[i] instanceof ElementDescriptor) {
817
818 ElementDescriptor childDescriptor = (ElementDescriptor) childDescriptors[i];
819 Context childContext = context;
820 Expression childExpression = childDescriptor.getContextExpression();
821 if ( childExpression != null ) {
822 Object childBean = childExpression.evaluate( context );
823 if ( childBean != null ) {
824 String qualifiedName = childDescriptor.getQualifiedName();
825 String namespaceUri = childDescriptor.getURI();
826 String localName = childDescriptor.getLocalName();
827
828 if ( childBean instanceof Iterator ) {
829 for ( Iterator iter = (Iterator) childBean; iter.hasNext(); ) {
830 Object object = iter.next();
831 if (object == null) {
832 continue;
833 }
834 writeBean(
835 namespaceUri,
836 localName,
837 qualifiedName,
838 object,
839 context );
840 }
841 } else {
842 writeBean(
843 namespaceUri,
844 localName,
845 qualifiedName,
846 childBean,
847 context );
848 }
849 }
850 } else {
851 writeElement(
852 childDescriptor.getURI(),
853 childDescriptor.getLocalName(),
854 childDescriptor.getQualifiedName(),
855 childDescriptor,
856 childContext );
857 }
858 } else {
859
860
861 Expression expression = childDescriptors[i].getTextExpression();
862 if ( expression != null ) {
863 Object value = expression.evaluate( context );
864 String text = convertToString(
865 value,
866 childDescriptors[i],
867 context );
868 if ( text != null && text.length() > 0 ) {;
869 bodyText( writeContext, text );
870 }
871 }
872 }
873 }
874 } else {
875
876 Expression expression = elementDescriptor.getTextExpression();
877 if ( expression != null ) {
878 Object value = expression.evaluate( context );
879 String text = convertToString( value, elementDescriptor, context );
880 if ( text != null && text.length() > 0 ) {
881 bodyText( writeContext, text );
882 }
883 }
884 }
885 }
886
887 /***
888 * Pushes the bean onto the ancestry stack.
889 * If IDs are not being written, then check for cyclic references.
890 *
891 * @param bean push this bean onto the ancester stack
892 */
893 protected void pushBean( Object bean ) {
894
895 if ( !getBindingConfiguration().getMapIDs() ) {
896 Iterator it = beanStack.iterator();
897 while ( it.hasNext() ) {
898 Object next = it.next();
899
900
901 if ( bean == next ) {
902 if ( log.isDebugEnabled() ) {
903 log.debug("Element stack: ");
904 Iterator debugStack = beanStack.iterator();
905 while ( debugStack.hasNext() ) {
906 log.debug(debugStack.next());
907 }
908 }
909 log.error("Cyclic reference at bean: " + bean);
910 throw new CyclicReferenceException();
911 }
912 }
913 }
914 if (log.isTraceEnabled()) {
915 log.trace( "Pushing onto object stack: " + bean );
916 }
917 beanStack.push( bean );
918 }
919
920 /***
921 * Pops the top bean off from the ancestry stack
922 *
923 * @return the last object pushed onto the ancester stack
924 */
925 protected Object popBean() {
926 Object bean = beanStack.pop();
927 if (log.isTraceEnabled()) {
928 log.trace( "Popped from object stack: " + bean );
929 }
930 return bean;
931 }
932
933 /***
934 * Should this element (and children) be written out?
935 *
936 * @param descriptor the <code>ElementDescriptor</code> to evaluate
937 * @param context the <code>Context</code> against which the element will be evaluated
938 * @return true if this element should be written out
939 */
940 private boolean ignoreElement( ElementDescriptor descriptor, Context context ) {
941 if ( ! getWriteEmptyElements() ) {
942 return isEmptyElement( descriptor, context );
943 }
944 return false;
945 }
946
947 /***
948 * <p>Will evaluating this element against this context result in an empty element?</p>
949 *
950 * <p>An empty element is one that has no attributes, no child elements
951 * and no body text.
952 * For example, <code><element/></code> is an empty element but
953 * <code><element attr='value'/></code> is not.</p>
954 *
955 * @param descriptor the <code>ElementDescriptor</code> to evaluate
956 * @param context the <code>Context</code> against which the element will be evaluated
957 * @return true if this element is empty on evaluation
958 */
959 private boolean isEmptyElement( ElementDescriptor descriptor, Context context ) {
960 if ( log.isTraceEnabled() ) {
961 log.trace( "Is " + descriptor + " empty?" );
962 }
963
964
965 if ( descriptor.hasAttributes() ) {
966 log.trace( "Element has attributes." );
967 return false;
968 }
969
970
971 Expression expression = descriptor.getTextExpression();
972 if ( expression != null ) {
973 Object value = expression.evaluate( context );
974 String text = convertToString( value, descriptor, context );
975 if ( text != null && text.length() > 0 ) {
976 log.trace( "Element has body text which isn't empty." );
977 return false;
978 }
979 }
980
981
982 if ( XMLIntrospectorHelper.isLoopType( descriptor.getPropertyType() ) ) {
983 log.trace("Loop type so not empty.");
984 return false;
985 }
986
987
988
989 if ( descriptor.hasChildren() ) {
990 for ( int i=0, size=descriptor.getElementDescriptors().length; i<size; i++ ) {
991 if ( ! isEmptyElement( descriptor.getElementDescriptors()[i], context ) ) {
992 log.trace( "Element has child which isn't empty." );
993 return false;
994 }
995 }
996 }
997
998 log.trace( "Element is empty." );
999 return true;
1000 }
1001
1002
1003 /***
1004 * Attributes backed by attribute descriptors.
1005 * ID/IDREFs not set.
1006 */
1007 private class ElementAttributes implements Attributes {
1008 /*** Attribute descriptors backing the <code>Attributes</code> */
1009 private AttributeDescriptor[] attributes;
1010 /*** Context to be evaluated when finding values */
1011 private Context context;
1012
1013
1014
1015 /***
1016 * Construct attributes for element and context.
1017 *
1018 * @param descriptor the <code>ElementDescriptor</code> describing the element
1019 * @param context evaluate against this context
1020 */
1021 ElementAttributes( ElementDescriptor descriptor, Context context ) {
1022 attributes = descriptor.getAttributeDescriptors();
1023 this.context = context;
1024 }
1025
1026 /***
1027 * Gets the index of an attribute by qualified name.
1028 *
1029 * @param qName the qualified name of the attribute
1030 * @return the index of the attribute - or -1 if there is no matching attribute
1031 */
1032 public int getIndex( String qName ) {
1033 for ( int i=0; i<attributes.length; i++ ) {
1034 if (attributes[i].getQualifiedName() != null
1035 && attributes[i].getQualifiedName().equals( qName )) {
1036 return i;
1037 }
1038 }
1039 return -1;
1040 }
1041
1042 /***
1043 * Gets the index of an attribute by namespace name.
1044 *
1045 * @param uri the namespace uri of the attribute
1046 * @param localName the local name of the attribute
1047 * @return the index of the attribute - or -1 if there is no matching attribute
1048 */
1049 public int getIndex( String uri, String localName ) {
1050 for ( int i=0; i<attributes.length; i++ ) {
1051 if (
1052 attributes[i].getURI() != null
1053 && attributes[i].getURI().equals(uri)
1054 && attributes[i].getLocalName() != null
1055 && attributes[i].getURI().equals(localName)) {
1056 return i;
1057 }
1058 }
1059
1060 return -1;
1061 }
1062
1063 /***
1064 * Gets the number of attributes in the list.
1065 *
1066 * @return the number of attributes in this list
1067 */
1068 public int getLength() {
1069 return attributes.length;
1070 }
1071
1072 /***
1073 * Gets the local name by index.
1074 *
1075 * @param index the attribute index (zero based)
1076 * @return the attribute local name - or null if the index is out of range
1077 */
1078 public String getLocalName( int index ) {
1079 if ( indexInRange( index ) ) {
1080 return attributes[index].getLocalName();
1081 }
1082
1083 return null;
1084 }
1085
1086 /***
1087 * Gets the qualified name by index.
1088 *
1089 * @param index the attribute index (zero based)
1090 * @return the qualified name of the element - or null if the index is our of range
1091 */
1092 public String getQName( int index ) {
1093 if ( indexInRange( index ) ) {
1094 return attributes[index].getQualifiedName();
1095 }
1096
1097 return null;
1098 }
1099
1100 /***
1101 * Gets the attribute SAX type by namespace name.
1102 *
1103 * @param index the attribute index (zero based)
1104 * @return the attribute type (as a string) or null if the index is out of range
1105 */
1106 public String getType( int index ) {
1107 if ( indexInRange( index ) ) {
1108 return "CDATA";
1109 }
1110 return null;
1111 }
1112
1113 /***
1114 * Gets the attribute SAX type by qualified name.
1115 *
1116 * @param qName the qualified name of the attribute
1117 * @return the attribute type (as a string) or null if the attribute is not in the list
1118 */
1119 public String getType( String qName ) {
1120 return getType( getIndex( qName ) );
1121 }
1122
1123 /***
1124 * Gets the attribute SAX type by namespace name.
1125 *
1126 * @param uri the namespace uri of the attribute
1127 * @param localName the local name of the attribute
1128 * @return the attribute type (as a string) or null if the attribute is not in the list
1129 */
1130 public String getType( String uri, String localName ) {
1131 return getType( getIndex( uri, localName ));
1132 }
1133
1134 /***
1135 * Gets the namespace URI for attribute at the given index.
1136 *
1137 * @param index the attribute index (zero-based)
1138 * @return the namespace URI (empty string if none is available)
1139 * or null if the index is out of range
1140 */
1141 public String getURI( int index ) {
1142 if ( indexInRange( index ) ) {
1143 return attributes[index].getURI();
1144 }
1145 return null;
1146 }
1147
1148 /***
1149 * Gets the value for the attribute at given index.
1150 *
1151 * @param index the attribute index (zero based)
1152 * @return the attribute value or null if the index is out of range
1153 * @todo add value caching
1154 */
1155 public String getValue( int index ) {
1156 if ( indexInRange( index )) {
1157 Expression expression = attributes[index].getTextExpression();
1158 if ( expression != null ) {
1159 Object value = expression.evaluate( context );
1160 return convertToString( value, attributes[index], context );
1161 }
1162
1163 return "";
1164 }
1165 return null;
1166 }
1167
1168 /***
1169 * Gets the value for the attribute by qualified name.
1170 *
1171 * @param qName the qualified name
1172 * @return the attribute value or null if there are no attributes
1173 * with the given qualified name
1174 * @todo add value caching
1175 */
1176 public String getValue( String qName ) {
1177 return getValue( getIndex( qName ) );
1178 }
1179
1180 /***
1181 * Gets the value for the attribute by namespace name.
1182 *
1183 * @param uri the namespace URI of the attribute
1184 * @param localName the local name of the attribute
1185 * @return the attribute value or null if there are not attributes
1186 * with the given namespace and local name
1187 * @todo add value caching
1188 */
1189 public String getValue( String uri, String localName ) {
1190 return getValue( getIndex( uri, localName ) );
1191 }
1192
1193 /***
1194 * Is the given index within the range of the attribute list
1195 *
1196 * @param index the index whose range will be checked
1197 * @return true if the index with within the range of the attribute list
1198 */
1199 private boolean indexInRange( int index ) {
1200 return ( index >= 0 && index < attributes.length );
1201 }
1202 }
1203
1204 /***
1205 * Attributes with generate ID/IDREF attributes
1206 * //TODO: refactor the ID/REF generation so that it's fixed at introspection
1207 * and the generators are placed into the Context.
1208 * @author <a href='http://jakarta.apache.org/'>Jakarta Commons Team</a>
1209 * @version $Revision: 1.28 $
1210 */
1211 private class IDElementAttributes extends ElementAttributes {
1212 /*** ID attribute value */
1213 private String idValue;
1214 /*** ID attribute name */
1215 private String idAttributeName;
1216
1217 private boolean matchingAttribute = false;
1218 private int length;
1219 private int idIndex;
1220
1221 /***
1222 * Construct attributes for element and context.
1223 *
1224 * @param descriptor the <code>ElementDescriptor</code> describing the element
1225 * @param context evaluate against this context
1226 * @param idAttributeName the name of the id attribute
1227 * @param idValue the ID attribute value
1228 */
1229 IDElementAttributes(
1230 ElementDescriptor descriptor,
1231 Context context,
1232 String idAttributeName,
1233 String idValue) {
1234 super(descriptor, context);
1235 this.idValue = idValue;
1236 this.idAttributeName = idAttributeName;
1237
1238
1239 AttributeDescriptor[] attributeDescriptors = descriptor.getAttributeDescriptors();
1240 length = attributeDescriptors.length;
1241 for (int i=0; i<length; i++) {
1242 if (idAttributeName.equals(attributeDescriptors[i])) {
1243 matchingAttribute = true;
1244 idIndex = i;
1245 break;
1246 }
1247 }
1248 if (!matchingAttribute) {
1249 length += 1;
1250 idIndex = length-1;
1251 }
1252 }
1253
1254 public int getIndex(String uri, String localName) {
1255 if (localName.equals(idAttributeName)) {
1256 return idIndex;
1257 }
1258
1259 return super.getIndex(uri, localName);
1260 }
1261
1262 public int getIndex(String qName) {
1263 if (qName.equals(idAttributeName)) {
1264 return idIndex;
1265 }
1266
1267 return super.getIndex(qName);
1268 }
1269
1270 public int getLength() {
1271 return length;
1272 }
1273
1274 public String getLocalName(int index) {
1275 if (index == idIndex) {
1276 return idAttributeName;
1277 }
1278 return super.getLocalName(index);
1279 }
1280
1281 public String getQName(int index) {
1282 if (index == idIndex) {
1283 return idAttributeName;
1284 }
1285 return super.getQName(index);
1286 }
1287
1288 public String getType(int index) {
1289 if (index == idIndex) {
1290 return "ID";
1291 }
1292 return super.getType(index);
1293 }
1294
1295 public String getType(String uri, String localName) {
1296 return getType(getIndex(uri, localName));
1297 }
1298
1299 public String getType(String qName) {
1300 return getType(getIndex(qName));
1301 }
1302
1303 public String getURI(int index) {
1304
1305
1306
1307 if (index == idIndex) {
1308 return "";
1309 }
1310 return super.getURI(index);
1311 }
1312
1313 public String getValue(int index) {
1314 if (index == idIndex) {
1315 return idValue;
1316 }
1317 return super.getValue(index);
1318 }
1319
1320 public String getValue(String uri, String localName) {
1321 return getValue(getIndex(uri, localName));
1322 }
1323
1324 public String getValue(String qName) {
1325 return getValue(getIndex(qName));
1326 }
1327
1328 }
1329
1330
1331
1332
1333
1334
1335 /***
1336 * Get the indentation for the current element.
1337 * Used for pretty priting.
1338 *
1339 * @return the amount that the current element is indented
1340 * @deprecated 0.5 replaced by new SAX inspired API
1341 */
1342 protected int getIndentLevel() {
1343 return 0;
1344 }
1345
1346
1347
1348
1349 /***
1350 * Express an element tag start using given qualified name.
1351 *
1352 * @param qualifiedName the qualified name of the element to be expressed
1353 * @throws IOException if an IO problem occurs during writing
1354 * @throws SAXException if an SAX problem occurs during writing
1355 * @deprecated 0.5 replaced by new SAX inspired API
1356 */
1357 protected void expressElementStart(String qualifiedName)
1358 throws IOException, SAXException {
1359
1360 }
1361
1362 /***
1363 * Express an element tag start using given qualified name.
1364 *
1365 * @param uri the namespace uri
1366 * @param localName the local name for this element
1367 * @param qualifiedName the qualified name of the element to be expressed
1368 * @throws IOException if an IO problem occurs during writing
1369 * @throws SAXException if an SAX problem occurs during writing
1370 * @deprecated 0.5 replaced by new SAX inspired API
1371 */
1372 protected void expressElementStart(String uri, String localName, String qualifiedName)
1373 throws IOException, SAXException {
1374 expressElementStart( qualifiedName );
1375 }
1376
1377 /***
1378 * Express a closing tag.
1379 *
1380 * @throws IOException if an IO problem occurs during writing
1381 * @throws SAXException if an SAX problem occurs during writing
1382 * @deprecated 0.5 replaced by new SAX inspired API
1383 */
1384 protected void expressTagClose() throws IOException, SAXException {}
1385
1386 /***
1387 * Express an element end tag (with given name)
1388 *
1389 * @param qualifiedName the qualified name for the element to be closed
1390 *
1391 * @throws IOException if an IO problem occurs during writing
1392 * @throws SAXException if an SAX problem occurs during writing
1393 * @deprecated 0.5 replaced by new SAX inspired API
1394 */
1395 protected void expressElementEnd(String qualifiedName)
1396 throws IOException, SAXException {
1397
1398 }
1399
1400 /***
1401 * Express an element end tag (with given name)
1402 *
1403 * @param uri the namespace uri of the element close tag
1404 * @param localName the local name of the element close tag
1405 * @param qualifiedName the qualified name for the element to be closed
1406 *
1407 * @throws IOException if an IO problem occurs during writing
1408 * @throws SAXException if an SAX problem occurs during writing
1409 * @deprecated 0.5 replaced by new SAX inspired API
1410 */
1411 protected void expressElementEnd(
1412 String uri,
1413 String localName,
1414 String qualifiedName)
1415 throws
1416 IOException,
1417 SAXException {
1418 expressElementEnd(qualifiedName);
1419 }
1420
1421
1422 /***
1423 * Express an empty element end.
1424 *
1425 * @throws IOException if an IO problem occurs during writing
1426 * @throws SAXException if an SAX problem occurs during writing
1427 * @deprecated 0.5 replaced by new SAX inspired API
1428 */
1429 protected void expressElementEnd() throws IOException, SAXException {}
1430
1431 /***
1432 * Express body text
1433 *
1434 * @param text the string to write out as the body of the current element
1435 *
1436 * @throws IOException if an IO problem occurs during writing
1437 * @throws SAXException if an SAX problem occurs during writing
1438 * @deprecated 0.5 replaced by new SAX inspired API
1439 */
1440 protected void expressBodyText(String text) throws IOException, SAXException {}
1441
1442 /***
1443 * Express an attribute
1444 *
1445 * @param qualifiedName the qualified name of the attribute
1446 * @param value the attribute value
1447 * @throws IOException if an IO problem occurs during writing
1448 * @throws SAXException if an SAX problem occurs during writing
1449 * @deprecated 0.5 replaced by new SAX inspired API
1450 */
1451 protected void expressAttribute(
1452 String qualifiedName,
1453 String value)
1454 throws
1455 IOException,
1456 SAXException {
1457
1458 }
1459
1460 /***
1461 * Express an attribute
1462 *
1463 * @param namespaceUri the namespace uri
1464 * @param localName the local name
1465 * @param qualifiedName the qualified name of the attribute
1466 * @param value the attribute value
1467 * @throws IOException if an IO problem occurs during writing
1468 * @throws SAXException if an SAX problem occurs during writing
1469 * @deprecated 0.5 replaced by new SAX inspired API
1470 */
1471 protected void expressAttribute(
1472 String namespaceUri,
1473 String localName,
1474 String qualifiedName,
1475 String value)
1476 throws
1477 IOException,
1478 SAXException {
1479 expressAttribute(qualifiedName, value);
1480 }
1481
1482
1483 /***
1484 * Writes the given element
1485 *
1486 * @param qualifiedName qualified name to use for the element
1487 * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
1488 * @param context the <code>Context</code> to use to evaluate the bean expressions
1489 * @throws IOException if an IO problem occurs during writing
1490 * @throws SAXException if an SAX problem occurs during writing
1491 * @throws IntrospectionException if a java beans introspection problem occurs
1492 * @deprecated 0.5 replaced by new SAX inspired API
1493 */
1494 protected void write(
1495 String qualifiedName,
1496 ElementDescriptor elementDescriptor,
1497 Context context )
1498 throws
1499 IOException,
1500 SAXException,
1501 IntrospectionException {
1502 writeElement( "", qualifiedName, qualifiedName, elementDescriptor, context );
1503 }
1504
1505 /***
1506 * Writes the given element adding an ID attribute
1507 *
1508 * @param qualifiedName qualified name to use for the element
1509 * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
1510 * @param context the <code>Context</code> to use to evaluate the bean expressions
1511 * @param idAttribute the qualified name of the <code>ID</code> attribute
1512 * @param idValue the value for the <code>ID</code> attribute
1513 * @throws IOException if an IO problem occurs during writing
1514 * @throws SAXException if an SAX problem occurs during writing
1515 * @throws IntrospectionException if a java beans introspection problem occurs
1516 * @deprecated 0.5 replaced by new SAX inspired API
1517 */
1518 protected void write(
1519 String qualifiedName,
1520 ElementDescriptor elementDescriptor,
1521 Context context,
1522 String idAttribute,
1523 String idValue )
1524 throws
1525 IOException,
1526 SAXException,
1527 IntrospectionException {
1528 writeElement(
1529 "",
1530 qualifiedName,
1531 qualifiedName,
1532 elementDescriptor,
1533 context,
1534 idAttribute,
1535 idValue );
1536 }
1537
1538 /***
1539 * Write attributes, child elements and element end
1540 *
1541 * @param qualifiedName qualified name to use for the element
1542 * @param elementDescriptor the <code>ElementDescriptor</code> describing the element
1543 * @param context the <code>Context</code> to use to evaluate the bean expressions
1544 * @throws IOException if an IO problem occurs during writing
1545 * @throws SAXException if an SAX problem occurs during writing
1546 * @throws IntrospectionException if a java beans introspection problem occurs
1547 * @deprecated 0.5 replaced by new SAX inspired API
1548 */
1549 protected void writeRestOfElement(
1550 String qualifiedName,
1551 ElementDescriptor elementDescriptor,
1552 Context context )
1553 throws
1554 IOException,
1555 SAXException,
1556 IntrospectionException {
1557 writeRestOfElement( "", qualifiedName, qualifiedName, elementDescriptor, context );
1558 }
1559
1560 /***
1561 * Writes an element with a <code>IDREF</code> attribute
1562 *
1563 * @param qualifiedName of the element with <code>IDREF</code> attribute
1564 * @param idrefAttributeName the qualified name of the <code>IDREF</code> attribute
1565 * @param idrefAttributeValue the value for the <code>IDREF</code> attribute
1566 * @throws IOException if an IO problem occurs during writing
1567 * @throws SAXException if an SAX problem occurs during writing
1568 * @throws IntrospectionException if a java beans introspection problem occurs
1569 * @deprecated 0.5 replaced by new SAX inspired API
1570 */
1571 protected void writeIDREFElement(
1572 String qualifiedName,
1573 String idrefAttributeName,
1574 String idrefAttributeValue )
1575 throws
1576 IOException,
1577 SAXException,
1578 IntrospectionException {
1579
1580 AttributesImpl attributes = new AttributesImpl();
1581 attributes.addAttribute(
1582 "",
1583 idrefAttributeName,
1584 idrefAttributeName,
1585 "IDREF",
1586 idrefAttributeValue);
1587 startElement( "", qualifiedName, qualifiedName, attributes);
1588 endElement( "", qualifiedName, qualifiedName );
1589 }
1590
1591
1592 /***
1593 * Writes the element content.
1594 *
1595 * @param elementDescriptor the <code>ElementDescriptor</code> to write as xml
1596 * @param context the <code>Context</code> to use to evaluate the bean expressions
1597 * @return true if some content was written
1598 * @throws IOException if an IO problem occurs during writing
1599 * @throws SAXException if an SAX problem occurs during writing
1600 * @throws IntrospectionException if a java beans introspection problem occurs
1601 * @deprecated 0.5 replaced by new SAX inspired API
1602 */
1603 protected boolean writeContent(
1604 ElementDescriptor elementDescriptor,
1605 Context context )
1606 throws
1607 IOException,
1608 SAXException,
1609 IntrospectionException {
1610 return false;
1611 }
1612
1613
1614 /***
1615 * Writes the attribute declarations
1616 *
1617 * @param elementDescriptor the <code>ElementDescriptor</code> to be written out as xml
1618 * @param context the <code>Context</code> to use to evaluation bean expressions
1619 * @throws IOException if an IO problem occurs during writing
1620 * @throws SAXException if an SAX problem occurs during writing
1621 * @deprecated 0.5 replaced by new SAX inspired API
1622 */
1623 protected void writeAttributes(
1624 ElementDescriptor elementDescriptor,
1625 Context context )
1626 throws
1627 IOException, SAXException {
1628 if (!elementDescriptor.isWrapCollectionsInElement()) {
1629 return;
1630 }
1631
1632 AttributeDescriptor[] attributeDescriptors = elementDescriptor.getAttributeDescriptors();
1633 if ( attributeDescriptors != null ) {
1634 for ( int i = 0, size = attributeDescriptors.length; i < size; i++ ) {
1635 AttributeDescriptor attributeDescriptor = attributeDescriptors[i];
1636 writeAttribute( attributeDescriptor, context );
1637 }
1638 }
1639 }
1640
1641
1642 /***
1643 * Writes an attribute declaration
1644 *
1645 * @param attributeDescriptor the <code>AttributeDescriptor</code> to be written as xml
1646 * @param context the <code>Context</code> to use to evaluation bean expressions
1647 * @throws IOException if an IO problem occurs during writing
1648 * @throws SAXException if an SAX problem occurs during writing
1649 * @deprecated 0.5 replaced by new SAX inspired API
1650 */
1651 protected void writeAttribute(
1652 AttributeDescriptor attributeDescriptor,
1653 Context context )
1654 throws
1655 IOException, SAXException {
1656 Expression expression = attributeDescriptor.getTextExpression();
1657 if ( expression != null ) {
1658 Object value = expression.evaluate( context );
1659 if ( value != null ) {
1660 String text = value.toString();
1661 if ( text != null && text.length() > 0 ) {
1662 expressAttribute(
1663 attributeDescriptor.getURI(),
1664 attributeDescriptor.getLocalName(),
1665 attributeDescriptor.getQualifiedName(),
1666 text);
1667 }
1668 }
1669 }
1670 }
1671 /***
1672 * Writes a empty line.
1673 * This implementation does nothing but can be overridden by subclasses.
1674 *
1675 * @throws IOException if the line cannot be written
1676 * @deprecated 0.5 replaced by new SAX inspired API
1677 */
1678 protected void writePrintln() throws IOException {}
1679
1680 /***
1681 * Writes an indentation.
1682 * This implementation does nothing but can be overridden by subclasses.
1683 *
1684 * @throws IOException if the indent cannot be written
1685 * @deprecated 0.5 replaced by new BeanWriter API
1686 */
1687 protected void writeIndent() throws IOException {}
1688
1689 /***
1690 * Converts an object to a string.
1691 *
1692 * @param value the Object to represent as a String, possibly null
1693 * @param descriptor writing out this descriptor not null
1694 * @param context not null
1695 * @return String representation, not null
1696 */
1697 private String convertToString( Object value , Descriptor descriptor, Context context ) {
1698 return getBindingConfiguration()
1699 .getObjectStringConverter()
1700 .objectToString( value, descriptor.getPropertyType(), null, context );
1701 }
1702
1703 /***
1704 * Factory method for new contexts.
1705 * Ensure that they are correctly configured.
1706 * @param bean make a new Context for this bean
1707 * @return not null
1708 */
1709 private Context makeContext(Object bean) {
1710 return new Context( bean, log, bindingConfiguration );
1711 }
1712
1713
1714 /***
1715 * Basic mutable implementation of <code>WriteContext</code>.
1716 */
1717 private static class WriteContextImpl extends WriteContext {
1718
1719 private ElementDescriptor currentDescriptor;
1720
1721 /***
1722 * @see org.apache.commons.betwixt.io.WriteContext#getCurrentDescriptor()
1723 */
1724 public ElementDescriptor getCurrentDescriptor() {
1725 return currentDescriptor;
1726 }
1727
1728 /***
1729 * Sets the descriptor for the current element.
1730 * @param currentDescriptor
1731 */
1732 public void setCurrentDescriptor(ElementDescriptor currentDescriptor) {
1733 this.currentDescriptor = currentDescriptor;
1734 }
1735
1736 }
1737 }