View Javadoc

1   /*
2    * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
3    *
4    * This software is open source.
5    * See the bottom of this file for the licence.
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                 // Will be output with attributes
176                 // write((Namespace) node);
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     // XMLReader methods
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     // Implementation methods
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         // start with the namespace of the element
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  * Redistribution and use of this software and associated documentation
912  * ("Software"), with or without modification, are permitted provided that the
913  * following conditions are met:
914  * 
915  * 1. Redistributions of source code must retain copyright statements and
916  * notices. Redistributions must also contain a copy of this document.
917  * 
918  * 2. Redistributions in binary form must reproduce the above copyright notice,
919  * this list of conditions and the following disclaimer in the documentation
920  * and/or other materials provided with the distribution.
921  * 
922  * 3. The name "DOM4J" must not be used to endorse or promote products derived
923  * from this Software without prior written permission of MetaStuff, Ltd. For
924  * written permission, please contact dom4j-info@metastuff.com.
925  * 
926  * 4. Products derived from this Software may not be called "DOM4J" nor may
927  * "DOM4J" appear in their names without prior written permission of MetaStuff,
928  * Ltd. DOM4J is a registered trademark of MetaStuff, Ltd.
929  * 
930  * 5. Due credit should be given to the DOM4J Project - http://www.dom4j.org
931  * 
932  * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS ``AS IS'' AND
933  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
934  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
935  * ARE DISCLAIMED. IN NO EVENT SHALL METASTUFF, LTD. OR ITS CONTRIBUTORS BE
936  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
937  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
938  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
939  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
940  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
941  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
942  * POSSIBILITY OF SUCH DAMAGE.
943  * 
944  * Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
945  */