KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > betwixt > io > AbstractBeanWriter


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.io;
17
18 import java.beans.IntrospectionException JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Iterator JavaDoc;
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 JavaDoc;
39 import org.xml.sax.SAXException JavaDoc;
40 import org.xml.sax.helpers.AttributesImpl JavaDoc;
41
42 // FIX ME!!!
43
// Logging logic!
44

45 // FIX ME!!
46
// Error handling strategy!
47
// i'm going to add SAXExceptions everywhere since it's the easiest way to make things work quick
48
// but this is a poor strategy
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 JavaDoc idMap = new HashMap JavaDoc();
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 JavaDoc namespacesDeclared = new ArrayList JavaDoc();
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 JavaDoc, SAXException JavaDoc {
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 JavaDoc, SAXException JavaDoc {
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 JavaDoc bean) throws
147                                         IOException JavaDoc,
148                                         SAXException JavaDoc,
149                                         IntrospectionException JavaDoc {
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 JavaDoc qualifiedName,
179                 Object JavaDoc bean)
180                     throws
181                         IOException JavaDoc,
182                         SAXException JavaDoc,
183                         IntrospectionException JavaDoc {
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 JavaDoc namespaceUri,
209                 String JavaDoc localName,
210                 String JavaDoc qualifiedName,
211                 Object JavaDoc bean,
212                 Context context)
213                     throws
214                         IOException JavaDoc,
215                         SAXException JavaDoc,
216                         IntrospectionException JavaDoc {
217         
218         if ( log.isTraceEnabled() ) {
219             log.trace( "Writing bean graph (qualified name '" + qualifiedName + "'" );
220         }
221         
222         // introspect to obtain bean info
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 JavaDoc ref = null;
239                 String JavaDoc id = null;
240                 
241                 // simple type should not have IDs
242
if ( elementDescriptor.isSimple() ) {
243                     // write without an id
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 JavaDoc) idMap.get( context.getBean() );
255                     }
256                     if ( ref == null ) {
257                         // this is the first time that this bean has be written
258
AttributeDescriptor idAttribute = beanInfo.getIDAttribute();
259                         if (idAttribute == null) {
260                             // use a generated id
261
id = idGenerator.nextId();
262                             idMap.put( bean, id );
263                             
264                             if ( getBindingConfiguration().getMapIDs() ) {
265                                 // write element with id
266
writeElement(
267                                     namespaceUri,
268                                     localName,
269                                     qualifiedName,
270                                     elementDescriptor,
271                                     context ,
272                                     beanInfo.getIDAttributeName(),
273                                     id);
274                                     
275                             } else {
276                                 // write element without ID
277
writeElement(
278                                     namespaceUri,
279                                     localName,
280                                     qualifiedName,
281                                     elementDescriptor,
282                                     context );
283                             }
284                                                         
285                         } else {
286                             // use id from bean property
287
// it's up to the user to ensure uniqueness
288
// XXX should we trap nulls?
289
Object JavaDoc exp = idAttribute.getTextExpression().evaluate( context );
290                             if (exp == null) {
291                                 // we'll use a random id
292
log.debug("Using random id");
293                                 id = idGenerator.nextId();
294                                 
295                             } else {
296                                 // convert to string
297
id = exp.toString();
298                             }
299                             idMap.put( bean, id);
300                             
301                             // the ID attribute should be written automatically
302
writeElement(
303                                 namespaceUri,
304                                 localName,
305                                 qualifiedName,
306                                 elementDescriptor,
307                                 context );
308                         }
309                     } else {
310                         
311                         if ( !ignoreElement( elementDescriptor, context )) {
312                             // we've already written this bean so write an IDREF
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>&lt;element/&gt;</code> is an empty element but
402      * <code>&lt;element attr='value'/&gt;</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>&lt;element/&gt;</code> is an empty element but
417      * <code>&lt;element attr='value'/&gt;</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     // SAX-style methods
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 JavaDoc uri,
491                                 String JavaDoc localName,
492                                 String JavaDoc qName,
493                                 Attributes JavaDoc attr)
494                                     throws
495                                         IOException JavaDoc,
496                                         SAXException JavaDoc {
497         // for backwards compatbility call older methods
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 JavaDoc uri,
515                                 String JavaDoc localName,
516                                 String JavaDoc qName)
517                                     throws
518                                         IOException JavaDoc,
519                                         SAXException JavaDoc {
520         // for backwards compatibility call older interface
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 JavaDoc text)
534                                 throws IOException JavaDoc, SAXException JavaDoc {
535         // for backwards compatibility call older interface
536
bodyText(text);
537     }
538         
539     // Older SAX-style methods
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 JavaDoc uri,
556                                 String JavaDoc localName,
557                                 String JavaDoc qName,
558                                 Attributes JavaDoc attr)
559                                     throws
560                                         IOException JavaDoc,
561                                         SAXException JavaDoc {}
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 JavaDoc uri,
576                                 String JavaDoc localName,
577                                 String JavaDoc qName)
578                                     throws
579                                         IOException JavaDoc,
580                                         SAXException JavaDoc {}
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 JavaDoc text) throws IOException JavaDoc, SAXException JavaDoc {}
592     
593     // Implementation methods
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 JavaDoc namespaceUri,
610                             String JavaDoc localName,
611                             String JavaDoc qualifiedName,
612                             ElementDescriptor elementDescriptor,
613                             Context context )
614                                 throws
615                                     IOException JavaDoc,
616                                     SAXException JavaDoc,
617                                     IntrospectionException JavaDoc {
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 JavaDoc 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 JavaDoc addNamespaceDeclarations(Attributes JavaDoc attributes, String JavaDoc elementNamespaceUri) {
651         Attributes JavaDoc result = attributes;
652         AttributesImpl JavaDoc withDeclarations = null;
653         for (int i=-1, size=attributes.getLength(); i<size ; i++) {
654             String JavaDoc 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 JavaDoc(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 JavaDoc namespaceUri,
693                             String JavaDoc localName,
694                             String JavaDoc qualifiedName,
695                             ElementDescriptor elementDescriptor,
696                             Context context,
697                             String JavaDoc idAttribute,
698                             String JavaDoc idValue )
699                                 throws
700                                     IOException JavaDoc,
701                                     SAXException JavaDoc,
702                                     IntrospectionException JavaDoc {
703                    
704         if ( !ignoreElement( elementDescriptor, context ) ) {
705             writeContext.setCurrentDescriptor(elementDescriptor);
706             Attributes JavaDoc 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 wri