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  */