1   
2   
3   
4   
5   
6   
7   
8   package org.dom4j.io;
9   
10  import java.io.IOException;
11  import java.util.HashMap;
12  import java.util.Iterator;
13  import java.util.List;
14  import java.util.Map;
15  
16  import org.dom4j.Attribute;
17  import org.dom4j.Branch;
18  import org.dom4j.CDATA;
19  import org.dom4j.CharacterData;
20  import org.dom4j.Comment;
21  import org.dom4j.Document;
22  import org.dom4j.DocumentType;
23  import org.dom4j.Element;
24  import org.dom4j.Entity;
25  import org.dom4j.Namespace;
26  import org.dom4j.Node;
27  import org.dom4j.ProcessingInstruction;
28  import org.dom4j.Text;
29  import org.dom4j.tree.NamespaceStack;
30  
31  import org.xml.sax.Attributes;
32  import org.xml.sax.ContentHandler;
33  import org.xml.sax.DTDHandler;
34  import org.xml.sax.EntityResolver;
35  import org.xml.sax.ErrorHandler;
36  import org.xml.sax.InputSource;
37  import org.xml.sax.SAXException;
38  import org.xml.sax.SAXNotRecognizedException;
39  import org.xml.sax.SAXNotSupportedException;
40  import org.xml.sax.XMLReader;
41  import org.xml.sax.ext.LexicalHandler;
42  import org.xml.sax.helpers.AttributesImpl;
43  import org.xml.sax.helpers.LocatorImpl;
44  
45  /***
46   * <p>
47   * <code>SAXWriter</code> writes a DOM4J tree to a SAX ContentHandler.
48   * </p>
49   * 
50   * @author <a href="mailto:james.strachan@metastuff.com">James Strachan </a>
51   * @version $Revision: 1.24 $
52   */
53  public class SAXWriter implements XMLReader {
54      protected static final String[] LEXICAL_HANDLER_NAMES = {
55              "http://xml.org/sax/properties/lexical-handler",
56              "http://xml.org/sax/handlers/LexicalHandler" };
57  
58      protected static final String FEATURE_NAMESPACE_PREFIXES 
59              = "http://xml.org/sax/features/namespace-prefixes";
60  
61      protected static final String FEATURE_NAMESPACES 
62              = "http://xml.org/sax/features/namespaces";
63  
64      /*** <code>ContentHandler</code> to which SAX events are raised */
65      private ContentHandler contentHandler;
66  
67      /*** <code>DTDHandler</code> fired when a document has a DTD */
68      private DTDHandler dtdHandler;
69  
70      /*** <code>EntityResolver</code> fired when a document has a DTD */
71      private EntityResolver entityResolver;
72  
73      private ErrorHandler errorHandler;
74  
75      /*** <code>LexicalHandler</code> fired on Entity and CDATA sections */
76      private LexicalHandler lexicalHandler;
77  
78      /*** <code>AttributesImpl</code> used when generating the Attributes */
79      private AttributesImpl attributes = new AttributesImpl();
80  
81      /*** Stores the features */
82      private Map features = new HashMap();
83  
84      /*** Stores the properties */
85      private Map properties = new HashMap();
86  
87      /*** Whether namespace declarations are exported as attributes or not */
88      private boolean declareNamespaceAttributes;
89  
90      public SAXWriter() {
91          properties.put(FEATURE_NAMESPACE_PREFIXES, Boolean.FALSE);
92          properties.put(FEATURE_NAMESPACE_PREFIXES, Boolean.TRUE);
93      }
94  
95      public SAXWriter(ContentHandler contentHandler) {
96          this();
97          this.contentHandler = contentHandler;
98      }
99  
100     public SAXWriter(ContentHandler contentHandler,
101             LexicalHandler lexicalHandler) {
102         this();
103         this.contentHandler = contentHandler;
104         this.lexicalHandler = lexicalHandler;
105     }
106 
107     public SAXWriter(ContentHandler contentHandler,
108             LexicalHandler lexicalHandler, EntityResolver entityResolver) {
109         this();
110         this.contentHandler = contentHandler;
111         this.lexicalHandler = lexicalHandler;
112         this.entityResolver = entityResolver;
113     }
114 
115     /***
116      * A polymorphic method to write any Node to this SAX stream
117      * 
118      * @param node
119      *            DOCUMENT ME!
120      * 
121      * @throws SAXException
122      *             DOCUMENT ME!
123      */
124     public void write(Node node) throws SAXException {
125         int nodeType = node.getNodeType();
126 
127         switch (nodeType) {
128             case Node.ELEMENT_NODE:
129                 write((Element) node);
130 
131                 break;
132 
133             case Node.ATTRIBUTE_NODE:
134                 write((Attribute) node);
135 
136                 break;
137 
138             case Node.TEXT_NODE:
139                 write(node.getText());
140 
141                 break;
142 
143             case Node.CDATA_SECTION_NODE:
144                 write((CDATA) node);
145 
146                 break;
147 
148             case Node.ENTITY_REFERENCE_NODE:
149                 write((Entity) node);
150 
151                 break;
152 
153             case Node.PROCESSING_INSTRUCTION_NODE:
154                 write((ProcessingInstruction) node);
155 
156                 break;
157 
158             case Node.COMMENT_NODE:
159                 write((Comment) node);
160 
161                 break;
162 
163             case Node.DOCUMENT_NODE:
164                 write((Document) node);
165 
166                 break;
167 
168             case Node.DOCUMENT_TYPE_NODE:
169                 write((DocumentType) node);
170 
171                 break;
172 
173             case Node.NAMESPACE_NODE:
174 
175                 
176                 
177                 break;
178 
179             default:
180                 throw new SAXException("Invalid node type: " + node);
181         }
182     }
183 
184     /***
185      * Generates SAX events for the given Document and all its content
186      * 
187      * @param document
188      *            is the Document to parse
189      * 
190      * @throws SAXException
191      *             if there is a SAX error processing the events
192      */
193     public void write(Document document) throws SAXException {
194         if (document != null) {
195             checkForNullHandlers();
196 
197             documentLocator(document);
198             startDocument();
199             entityResolver(document);
200             dtdHandler(document);
201 
202             writeContent(document, new NamespaceStack());
203             endDocument();
204         }
205     }
206 
207     /***
208      * Generates SAX events for the given Element and all its content
209      * 
210      * @param element
211      *            is the Element to parse
212      * 
213      * @throws SAXException
214      *             if there is a SAX error processing the events
215      */
216     public void write(Element element) throws SAXException {
217         write(element, new NamespaceStack());
218     }
219 
220     /***
221      * <p>
222      * Writes the opening tag of an {@link Element}, including its {@link
223      * Attribute}s but without its content.
224      * </p>
225      * 
226      * @param element
227      *            <code>Element</code> to output.
228      * 
229      * @throws SAXException
230      *             DOCUMENT ME!
231      */
232     public void writeOpen(Element element) throws SAXException {
233         startElement(element, null);
234     }
235 
236     /***
237      * <p>
238      * Writes the closing tag of an {@link Element}
239      * </p>
240      * 
241      * @param element
242      *            <code>Element</code> to output.
243      * 
244      * @throws SAXException
245      *             DOCUMENT ME!
246      */
247     public void writeClose(Element element) throws SAXException {
248         endElement(element);
249     }
250 
251     /***
252      * Generates SAX events for the given text
253      * 
254      * @param text
255      *            is the text to send to the SAX ContentHandler
256      * 
257      * @throws SAXException
258      *             if there is a SAX error processing the events
259      */
260     public void write(String text) throws SAXException {
261         if (text != null) {
262             char[] chars = text.toCharArray();
263             contentHandler.characters(chars, 0, chars.length);
264         }
265     }
266 
267     /***
268      * Generates SAX events for the given CDATA
269      * 
270      * @param cdata
271      *            is the CDATA to parse
272      * 
273      * @throws SAXException
274      *             if there is a SAX error processing the events
275      */
276     public void write(CDATA cdata) throws SAXException {
277         String text = cdata.getText();
278 
279         if (lexicalHandler != null) {
280             lexicalHandler.startCDATA();
281             write(text);
282             lexicalHandler.endCDATA();
283         } else {
284             write(text);
285         }
286     }
287 
288     /***
289      * Generates SAX events for the given Comment
290      * 
291      * @param comment
292      *            is the Comment to parse
293      * 
294      * @throws SAXException
295      *             if there is a SAX error processing the events
296      */
297     public void write(Comment comment) throws SAXException {
298         if (lexicalHandler != null) {
299             String text = comment.getText();
300             char[] chars = text.toCharArray();
301             lexicalHandler.comment(chars, 0, chars.length);
302         }
303     }
304 
305     /***
306      * Generates SAX events for the given Entity
307      * 
308      * @param entity
309      *            is the Entity to parse
310      * 
311      * @throws SAXException
312      *             if there is a SAX error processing the events
313      */
314     public void write(Entity entity) throws SAXException {
315         String text = entity.getText();
316 
317         if (lexicalHandler != null) {
318             String name = entity.getName();
319             lexicalHandler.startEntity(name);
320             write(text);
321             lexicalHandler.endEntity(name);
322         } else {
323             write(text);
324         }
325     }
326 
327     /***
328      * Generates SAX events for the given ProcessingInstruction
329      * 
330      * @param pi
331      *            is the ProcessingInstruction to parse
332      * 
333      * @throws SAXException
334      *             if there is a SAX error processing the events
335      */
336     public void write(ProcessingInstruction pi) throws SAXException {
337         String target = pi.getTarget();
338         String text = pi.getText();
339         contentHandler.processingInstruction(target, text);
340     }
341 
342     /***
343      * Should namespace declarations be converted to "xmlns" attributes. This
344      * property defaults to <code>false</code> as per the SAX specification.
345      * This property is set via the SAX feature
346      * "http://xml.org/sax/features/namespace-prefixes"
347      * 
348      * @return DOCUMENT ME!
349      */
350     public boolean isDeclareNamespaceAttributes() {
351         return declareNamespaceAttributes;
352     }
353 
354     /***
355      * Sets whether namespace declarations should be exported as "xmlns"
356      * attributes or not. This property is set from the SAX feature
357      * "http://xml.org/sax/features/namespace-prefixes"
358      * 
359      * @param declareNamespaceAttrs
360      *            DOCUMENT ME!
361      */
362     public void setDeclareNamespaceAttributes(boolean declareNamespaceAttrs) {
363         this.declareNamespaceAttributes = declareNamespaceAttrs;
364     }
365 
366     
367     
368 
369     /***
370      * DOCUMENT ME!
371      * 
372      * @return the <code>ContentHandler</code> called when SAX events are
373      *         raised
374      */
375     public ContentHandler getContentHandler() {
376         return contentHandler;
377     }
378 
379     /***
380      * Sets the <code>ContentHandler</code> called when SAX events are raised
381      * 
382      * @param contentHandler
383      *            is the <code>ContentHandler</code> called when SAX events
384      *            are raised
385      */
386     public void setContentHandler(ContentHandler contentHandler) {
387         this.contentHandler = contentHandler;
388     }
389 
390     /***
391      * DOCUMENT ME!
392      * 
393      * @return the <code>DTDHandler</code>
394      */
395     public DTDHandler getDTDHandler() {
396         return dtdHandler;
397     }
398 
399     /***
400      * Sets the <code>DTDHandler</code>.
401      * 
402      * @param handler
403      *            DOCUMENT ME!
404      */
405     public void setDTDHandler(DTDHandler handler) {
406         this.dtdHandler = handler;
407     }
408 
409     /***
410      * DOCUMENT ME!
411      * 
412      * @return the <code>ErrorHandler</code>
413      */
414     public ErrorHandler getErrorHandler() {
415         return errorHandler;
416     }
417 
418     /***
419      * Sets the <code>ErrorHandler</code>.
420      * 
421      * @param errorHandler
422      *            DOCUMENT ME!
423      */
424     public void setErrorHandler(ErrorHandler errorHandler) {
425         this.errorHandler = errorHandler;
426     }
427 
428     /***
429      * DOCUMENT ME!
430      * 
431      * @return the <code>EntityResolver</code> used when a Document contains a
432      *         DTD
433      */
434     public EntityResolver getEntityResolver() {
435         return entityResolver;
436     }
437 
438     /***
439      * Sets the <code>EntityResolver</code>.
440      * 
441      * @param entityResolver
442      *            is the <code>EntityResolver</code>
443      */
444     public void setEntityResolver(EntityResolver entityResolver) {
445         this.entityResolver = entityResolver;
446     }
447 
448     /***
449      * DOCUMENT ME!
450      * 
451      * @return the <code>LexicalHandler</code> used when a Document contains a
452      *         DTD
453      */
454     public LexicalHandler getLexicalHandler() {
455         return lexicalHandler;
456     }
457 
458     /***
459      * Sets the <code>LexicalHandler</code>.
460      * 
461      * @param lexicalHandler
462      *            is the <code>LexicalHandler</code>
463      */
464     public void setLexicalHandler(LexicalHandler lexicalHandler) {
465         this.lexicalHandler = lexicalHandler;
466     }
467 
468     /***
469      * Sets the <code>XMLReader</code> used to write SAX events to
470      * 
471      * @param xmlReader
472      *            is the <code>XMLReader</code>
473      */
474     public void setXMLReader(XMLReader xmlReader) {
475         setContentHandler(xmlReader.getContentHandler());
476         setDTDHandler(xmlReader.getDTDHandler());
477         setEntityResolver(xmlReader.getEntityResolver());
478         setErrorHandler(xmlReader.getErrorHandler());
479     }
480 
481     /***
482      * Looks up the value of a feature.
483      * 
484      * @param name
485      *            DOCUMENT ME!
486      * 
487      * @return DOCUMENT ME!
488      * 
489      * @throws SAXNotRecognizedException
490      *             DOCUMENT ME!
491      * @throws SAXNotSupportedException
492      *             DOCUMENT ME!
493      */
494     public boolean getFeature(String name) throws SAXNotRecognizedException,
495             SAXNotSupportedException {
496         Boolean answer = (Boolean) features.get(name);
497 
498         return (answer != null) && answer.booleanValue();
499     }
500 
501     /***
502      * This implementation does actually use any features but just stores them
503      * for later retrieval
504      * 
505      * @param name
506      *            DOCUMENT ME!
507      * @param value
508      *            DOCUMENT ME!
509      * 
510      * @throws SAXNotRecognizedException
511      *             DOCUMENT ME!
512      * @throws SAXNotSupportedException
513      *             DOCUMENT ME!
514      */
515     public void setFeature(String name, boolean value)
516             throws SAXNotRecognizedException, SAXNotSupportedException {
517         if (FEATURE_NAMESPACE_PREFIXES.equals(name)) {
518             setDeclareNamespaceAttributes(value);
519         } else if (FEATURE_NAMESPACE_PREFIXES.equals(name)) {
520             if (!value) {
521                 String msg = "Namespace feature is always supported in dom4j";
522                 throw new SAXNotSupportedException(msg);
523             }
524         }
525 
526         features.put(name, (value) ? Boolean.TRUE : Boolean.FALSE);
527     }
528 
529     /***
530      * Sets the given SAX property
531      * 
532      * @param name
533      *            DOCUMENT ME!
534      * @param value
535      *            DOCUMENT ME!
536      */
537     public void setProperty(String name, Object value) {
538         for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
539             if (LEXICAL_HANDLER_NAMES[i].equals(name)) {
540                 setLexicalHandler((LexicalHandler) value);
541 
542                 return;
543             }
544         }
545 
546         properties.put(name, value);
547     }
548 
549     /***
550      * Gets the given SAX property
551      * 
552      * @param name
553      *            DOCUMENT ME!
554      * 
555      * @return DOCUMENT ME!
556      * 
557      * @throws SAXNotRecognizedException
558      *             DOCUMENT ME!
559      * @throws SAXNotSupportedException
560      *             DOCUMENT ME!
561      */
562     public Object getProperty(String name) throws SAXNotRecognizedException,
563             SAXNotSupportedException {
564         for (int i = 0; i < LEXICAL_HANDLER_NAMES.length; i++) {
565             if (LEXICAL_HANDLER_NAMES[i].equals(name)) {
566                 return getLexicalHandler();
567             }
568         }
569 
570         return properties.get(name);
571     }
572 
573     /***
574      * This method is not supported.
575      * 
576      * @param systemId
577      *            DOCUMENT ME!
578      * 
579      * @throws SAXNotSupportedException
580      *             DOCUMENT ME!
581      */
582     public void parse(String systemId) throws SAXNotSupportedException {
583         throw new SAXNotSupportedException("This XMLReader can only accept"
584                 + " <dom4j> InputSource objects");
585     }
586 
587     /***
588      * Parses an XML document. This method can only accept DocumentInputSource
589      * inputs otherwise a {@link SAXNotSupportedException}exception is thrown.
590      * 
591      * @param input
592      *            DOCUMENT ME!
593      * 
594      * @throws SAXException
595      *             DOCUMENT ME!
596      * @throws SAXNotSupportedException
597      *             if the input source is not wrapping a dom4j document
598      */
599     public void parse(InputSource input) throws SAXException {
600         if (input instanceof DocumentInputSource) {
601             DocumentInputSource documentInput = (DocumentInputSource) input;
602             Document document = documentInput.getDocument();
603             write(document);
604         } else {
605             throw new SAXNotSupportedException(
606                     "This XMLReader can only accept "
607                             + "<dom4j> InputSource objects");
608         }
609     }
610 
611     
612     
613     protected void writeContent(Branch branch, NamespaceStack namespaceStack)
614             throws SAXException {
615         for (Iterator iter = branch.nodeIterator(); iter.hasNext();) {
616             Object object = iter.next();
617 
618             if (object instanceof Element) {
619                 write((Element) object, namespaceStack);
620             } else if (object instanceof CharacterData) {
621                 if (object instanceof Text) {
622                     Text text = (Text) object;
623                     write(text.getText());
624                 } else if (object instanceof CDATA) {
625                     write((CDATA) object);
626                 } else if (object instanceof Comment) {
627                     write((Comment) object);
628                 } else {
629                     throw new SAXException("Invalid Node in DOM4J content: "
630                             + object + " of type: " + object.getClass());
631                 }
632             } else if (object instanceof String) {
633                 write((String) object);
634             } else if (object instanceof Entity) {
635                 write((Entity) object);
636             } else if (object instanceof ProcessingInstruction) {
637                 write((ProcessingInstruction) object);
638             } else if (object instanceof Namespace) {
639                 write((Namespace) object);
640             } else {
641                 throw new SAXException("Invalid Node in DOM4J content: "
642                         + object);
643             }
644         }
645     }
646 
647     /***
648      * The {@link org.xml.sax.Locator}is only really useful when parsing a
649      * textual document as its main purpose is to identify the line and column
650      * number. Since we are processing an in memory tree which will probably
651      * have its line number information removed, we'll just use -1 for the line
652      * and column numbers.
653      * 
654      * @param document
655      *            DOCUMENT ME!
656      * 
657      * @throws SAXException
658      *             DOCUMENT ME!
659      */
660     protected void documentLocator(Document document) throws SAXException {
661         LocatorImpl locator = new LocatorImpl();
662 
663         String publicID = null;
664         String systemID = null;
665         DocumentType docType = document.getDocType();
666 
667         if (docType != null) {
668             publicID = docType.getPublicID();
669             systemID = docType.getSystemID();
670         }
671 
672         if (publicID != null) {
673             locator.setPublicId(publicID);
674         }
675 
676         if (systemID != null) {
677             locator.setSystemId(systemID);
678         }
679 
680         locator.setLineNumber(-1);
681         locator.setColumnNumber(-1);
682 
683         contentHandler.setDocumentLocator(locator);
684     }
685 
686     protected void entityResolver(Document document) throws SAXException {
687         if (entityResolver != null) {
688             DocumentType docType = document.getDocType();
689 
690             if (docType != null) {
691                 String publicID = docType.getPublicID();
692                 String systemID = docType.getSystemID();
693 
694                 if ((publicID != null) || (systemID != null)) {
695                     try {
696                         entityResolver.resolveEntity(publicID, systemID);
697                     } catch (IOException e) {
698                         throw new SAXException("Could not resolve publicID: "
699                                 + publicID + " systemID: " + systemID, e);
700                     }
701                 }
702             }
703         }
704     }
705 
706     /***
707      * We do not yet support DTD or XML Schemas so this method does nothing
708      * right now.
709      * 
710      * @param document
711      *            DOCUMENT ME!
712      * 
713      * @throws SAXException
714      *             DOCUMENT ME!
715      */
716     protected void dtdHandler(Document document) throws SAXException {
717     }
718 
719     protected void startDocument() throws SAXException {
720         contentHandler.startDocument();
721     }
722 
723     protected void endDocument() throws SAXException {
724         contentHandler.endDocument();
725     }
726 
727     protected void write(Element element, NamespaceStack namespaceStack)
728             throws SAXException {
729         int stackSize = namespaceStack.size();
730         AttributesImpl namespaceAttributes = startPrefixMapping(element,
731                 namespaceStack);
732         startElement(element, namespaceAttributes);
733         writeContent(element, namespaceStack);
734         endElement(element);
735         endPrefixMapping(namespaceStack, stackSize);
736     }
737 
738     /***
739      * Fires a SAX startPrefixMapping event for all the namespaceStack which
740      * have just come into scope
741      * 
742      * @param element
743      *            DOCUMENT ME!
744      * @param namespaceStack
745      *            DOCUMENT ME!
746      * 
747      * @return DOCUMENT ME!
748      * 
749      * @throws SAXException
750      *             DOCUMENT ME!
751      */
752     protected AttributesImpl startPrefixMapping(Element element,
753             NamespaceStack namespaceStack) throws SAXException {
754         AttributesImpl namespaceAttributes = null;
755 
756         
757         Namespace elementNamespace = element.getNamespace();
758 
759         if ((elementNamespace != null)
760                 && !isIgnoreableNamespace(elementNamespace, namespaceStack)) {
761             namespaceStack.push(elementNamespace);
762             contentHandler.startPrefixMapping(elementNamespace.getPrefix(),
763                     elementNamespace.getURI());
764             namespaceAttributes = addNamespaceAttribute(namespaceAttributes,
765                     elementNamespace);
766         }
767 
768         List declaredNamespaces = element.declaredNamespaces();
769 
770         for (int i = 0, size = declaredNamespaces.size(); i < size; i++) {
771             Namespace namespace = (Namespace) declaredNamespaces.get(i);
772 
773             if (!isIgnoreableNamespace(namespace, namespaceStack)) {
774                 namespaceStack.push(namespace);
775                 contentHandler.startPrefixMapping(namespace.getPrefix(),
776                         namespace.getURI());
777                 namespaceAttributes = addNamespaceAttribute(
778                         namespaceAttributes, namespace);
779             }
780         }
781 
782         return namespaceAttributes;
783     }
784 
785     /***
786      * Fires a SAX endPrefixMapping event for all the namespaceStack which have
787      * gone out of scope
788      * 
789      * @param stack
790      *            DOCUMENT ME!
791      * @param stackSize
792      *            DOCUMENT ME!
793      * 
794      * @throws SAXException
795      *             DOCUMENT ME!
796      */
797     protected void endPrefixMapping(NamespaceStack stack, int stackSize)
798             throws SAXException {
799         while (stack.size() > stackSize) {
800             Namespace namespace = stack.pop();
801 
802             if (namespace != null) {
803                 contentHandler.endPrefixMapping(namespace.getPrefix());
804             }
805         }
806     }
807 
808     protected void startElement(Element element,
809             AttributesImpl namespaceAttributes) throws SAXException {
810         contentHandler.startElement(element.getNamespaceURI(), element
811                 .getName(), element.getQualifiedName(), createAttributes(
812                 element, namespaceAttributes));
813     }
814 
815     protected void endElement(Element element) throws SAXException {
816         contentHandler.endElement(element.getNamespaceURI(), element.getName(),
817                 element.getQualifiedName());
818     }
819 
820     protected Attributes createAttributes(Element element,
821             Attributes namespaceAttributes) throws SAXException {
822         attributes.clear();
823 
824         if (namespaceAttributes != null) {
825             attributes.setAttributes(namespaceAttributes);
826         }
827 
828         for (Iterator iter = element.attributeIterator(); iter.hasNext();) {
829             Attribute attribute = (Attribute) iter.next();
830             attributes.addAttribute(attribute.getNamespaceURI(), attribute
831                     .getName(), attribute.getQualifiedName(), "CDATA",
832                     attribute.getValue());
833         }
834 
835         return attributes;
836     }
837 
838     /***
839      * If isDelcareNamespaceAttributes() is enabled then this method will add
840      * the given namespace declaration to the supplied attributes object,
841      * creating one if it does not exist.
842      * 
843      * @param attrs
844      *            DOCUMENT ME!
845      * @param namespace
846      *            DOCUMENT ME!
847      * 
848      * @return DOCUMENT ME!
849      */
850     protected AttributesImpl addNamespaceAttribute(AttributesImpl attrs,
851             Namespace namespace) {
852         if (declareNamespaceAttributes) {
853             if (attrs == null) {
854                 attrs = new AttributesImpl();
855             }
856 
857             String prefix = namespace.getPrefix();
858             String qualifiedName = "xmlns";
859 
860             if ((prefix != null) && (prefix.length() > 0)) {
861                 qualifiedName = "xmlns:" + prefix;
862             }
863 
864             String uri = "";
865             String localName = prefix;
866             String type = "CDATA";
867             String value = namespace.getURI();
868 
869             attrs.addAttribute(uri, localName, qualifiedName, type, value);
870         }
871 
872         return attrs;
873     }
874 
875     /***
876      * DOCUMENT ME!
877      * 
878      * @param namespace
879      *            DOCUMENT ME!
880      * @param namespaceStack
881      *            DOCUMENT ME!
882      * 
883      * @return true if the given namespace is an ignorable namespace (such as
884      *         Namespace.NO_NAMESPACE or Namespace.XML_NAMESPACE) or if the
885      *         namespace has already been declared in the current scope
886      */
887     protected boolean isIgnoreableNamespace(Namespace namespace,
888             NamespaceStack namespaceStack) {
889         if (namespace.equals(Namespace.NO_NAMESPACE)
890                 || namespace.equals(Namespace.XML_NAMESPACE)) {
891             return true;
892         }
893 
894         String uri = namespace.getURI();
895 
896         if ((uri == null) || (uri.length() <= 0)) {
897             return true;
898         }
899 
900         return namespaceStack.contains(namespace);
901     }
902 
903     /***
904      * Ensures non-null content handlers?
905      */
906     protected void checkForNullHandlers() {
907     }
908 }
909 
910 
911 
912 
913 
914 
915 
916 
917 
918 
919 
920 
921 
922 
923 
924 
925 
926 
927 
928 
929 
930 
931 
932 
933 
934 
935 
936 
937 
938 
939 
940 
941 
942 
943 
944 
945