All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdom2.output.support.AbstractSAXOutputProcessor Maven / Gradle / Ivy

Go to download

A complete, Java-based solution for accessing, manipulating, and outputting XML data

The newest version!
/*--

 Copyright (C) 2000-2007 Jason Hunter & Brett McLaughlin.
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:

 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions, and the following disclaimer.

 2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions, and the disclaimer that follows
    these conditions in the documentation and/or other materials
    provided with the distribution.

 3. The name "JDOM" must not be used to endorse or promote products
    derived from this software without prior written permission.  For
    written permission, please contact .

 4. Products derived from this software may not be called "JDOM", nor
    may "JDOM" appear in their name, without prior written permission
    from the JDOM Project Management .

 In addition, we request (but do not require) that you include in the
 end-user documentation provided with the redistribution and/or in the
 software itself an acknowledgement equivalent to the following:
     "This product includes software developed by the
      JDOM Project (http://www.jdom.org/)."
 Alternatively, the acknowledgment may be graphical using the logos
 available at http://www.jdom.org/images/logos.

 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 DISCLAIMED.  IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 SUCH DAMAGE.

 This software consists of voluntary contributions made by many
 individuals on behalf of the JDOM Project and was originally
 created by Jason Hunter  and
 Brett McLaughlin .  For more information
 on the JDOM Project, please see .

 */

package org.jdom2.output.support;

import static org.jdom2.JDOMConstants.*;

import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;

import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import org.jdom2.Attribute;
import org.jdom2.AttributeType;
import org.jdom2.CDATA;
import org.jdom2.Comment;
import org.jdom2.Content;
import org.jdom2.DocType;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.EntityRef;
import org.jdom2.JDOMException;
import org.jdom2.Namespace;
import org.jdom2.ProcessingInstruction;
import org.jdom2.Text;
import org.jdom2.output.Format;
import org.jdom2.output.Format.TextMode;
import org.jdom2.output.XMLOutputter;
import org.jdom2.util.NamespaceStack;

/**
 * Outputs a JDOM document as a stream of SAX2 events.
 * 

* Most ContentHandler callbacks are supported. Neither * ignorableWhitespace() nor skippedEntity() have been * implemented. *

* At this time, it is not possible to access notations and unparsed entity * references in a DTD from JDOM. Therefore, full DTDHandler * call-backs have not been implemented yet. *

* The ErrorHandler call-backs have not been implemented, since * these are supposed to be invoked when the document is parsed and at this * point the document exists in memory and is known to have no errors. *

* The SAX2 API does not support whitespace formatting outside the root element. * As a consequence any Formatting options that would normally affect the * structures outside the root element will be ignored. * * @author Brett McLaughlin * @author Jason Hunter * @author Fred Trimble * @author Bradley S. Huffman * @author Rolf Lear */ public class AbstractSAXOutputProcessor extends AbstractOutputProcessor implements SAXOutputProcessor { private static void locate(SAXTarget out) { out.getContentHandler().setDocumentLocator(out.getLocator()); } @Override public void process(SAXTarget out, Format format, Document doc) throws JDOMException { try { locate(out); printDocument(out, new FormatStack(format), new NamespaceStack(), doc); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the Document: ", se); } } @Override public void process(SAXTarget out, Format format, DocType doctype) throws JDOMException { try { locate(out); printDocType(out, new FormatStack(format), doctype); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the DocType: ", se); } } @Override public void process(SAXTarget out, Format format, Element element) throws JDOMException { try { locate(out); printElement(out, new FormatStack(format), new NamespaceStack(), element); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the Element: ", se); } } @Override public void process(SAXTarget out, Format format, List list) throws JDOMException { try { locate(out); final FormatStack fstack = new FormatStack(format); final Walker walker = buildWalker(fstack, list, false); printContent(out, fstack, new NamespaceStack(), walker); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the List: ", se); } } @Override public void process(SAXTarget out, Format format, CDATA cdata) throws JDOMException { try { locate(out); final List list = Collections.singletonList(cdata); final FormatStack fstack = new FormatStack(format); final Walker walker = buildWalker(fstack, list, false); printContent(out, fstack, new NamespaceStack(), walker); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the CDATA: ", se); } } @Override public void process(SAXTarget out, Format format, Text text) throws JDOMException { try { locate(out); final List list = Collections.singletonList(text); final FormatStack fstack = new FormatStack(format); final Walker walker = buildWalker(fstack, list, false); printContent(out, fstack, new NamespaceStack(), walker); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the Text: ", se); } } @Override public void process(SAXTarget out, Format format, Comment comment) throws JDOMException { try { locate(out); printComment(out, new FormatStack(format), comment); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the Comment: ", se); } } @Override public void process(SAXTarget out, Format format, ProcessingInstruction pi) throws JDOMException { try { locate(out); printProcessingInstruction(out, new FormatStack(format), pi); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the ProcessingInstruction: ", se); } } @Override public void process(SAXTarget out, Format format, EntityRef entity) throws JDOMException { try { locate(out); printEntityRef(out, new FormatStack(format), entity); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the EntityRef: ", se); } } @Override public void processAsDocument(SAXTarget out, Format format, List nodes) throws JDOMException { try { if ((nodes == null) || (nodes.size() == 0)) { return; } locate(out); // contentHandler.setDocumentLocator() out.getContentHandler().startDocument(); FormatStack fstack = new FormatStack(format); // Fire DTD events .. if there is a DocType node if (out.isReportDTDEvents()) { for (Content c : nodes) { if (c instanceof DocType) { printDocType(out, fstack, (DocType)c); // fire only the first DocType's events // subsequent ones are ignored. break; } } } Walker walker = buildWalker(fstack, nodes, false); printContent(out, fstack, new NamespaceStack(), walker); // contentHandler.endDocument() out.getContentHandler().endDocument(); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the List: ", se); } } @Override public void processAsDocument(SAXTarget out, Format format, Element node) throws JDOMException { try { if (node == null) { return; } locate(out); // contentHandler.setDocumentLocator() out.getContentHandler().startDocument(); printElement(out, new FormatStack(format), new NamespaceStack(), node); // contentHandler.endDocument() out.getContentHandler().endDocument(); } catch (SAXException se) { throw new JDOMException( "Encountered a SAX exception processing the Element: ", se); } } /* ******************************************* * Support methods for output. Should all be protected. All content-type * print methods have a FormatStack. Only printContent is responsible for * outputting appropriate indenting and newlines, which are easily available * using the FormatStack.getLevelIndent() and FormatStack.getLevelEOL(). * ******************************************* */ /** * This will handle printing of a {@link Document}. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param nstack * the NamespaceStack * @param document * Document to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printDocument(final SAXTarget out, final FormatStack fstack, final NamespaceStack nstack, final Document document) throws SAXException { if (document == null) { return; } // contentHandler.startDocument() out.getContentHandler().startDocument(); // Fire DTD events if (out.isReportDTDEvents()) { printDocType(out, fstack, document.getDocType()); } // Handle root element, as well as any root level // processing instructions and comments // ignore DocType, if any. final int sz = document.getContentSize(); if (sz > 0) { for (int i = 0; i < sz; i++) { final Content c = document.getContent(i); out.getLocator().setNode(c); switch (c.getCType()) { case Comment : printComment(out, fstack, (Comment) c); break; case DocType : // cannot simply add a DocType to a SAX stream // it is added when the Stream is created. break; case Element : printElement(out, fstack, nstack, (Element)c); break; case ProcessingInstruction : printProcessingInstruction(out, fstack, (ProcessingInstruction)c); break; default : // do nothing. } } } // contentHandler.endDocument() out.getContentHandler().endDocument(); } /** * This will handle printing of a {@link DocType}. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param docType * DocType to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printDocType(final SAXTarget out, final FormatStack fstack, final DocType docType) throws SAXException { // Fire DTD-related events only if handlers have been registered. final DTDHandler dtdHandler = out.getDTDHandler(); final DeclHandler declHandler = out.getDeclHandler(); if ((docType != null) && ((dtdHandler != null) || (declHandler != null))) { // Build a dummy XML document that only references the DTD... String dtdDoc = new XMLOutputter().outputString(docType); try { // And parse it to fire DTD events. createDTDParser(out).parse( new InputSource(new StringReader(dtdDoc))); // We should never reach this point as the document is // ill-formed; it does not have any root element. } catch (SAXParseException e) { // Expected exception: There's no root element in document. } catch (IOException e) { throw new SAXException("DTD parsing error", e); } } } /** * This will handle printing of a {@link ProcessingInstruction}. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param pi * ProcessingInstruction to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printProcessingInstruction(final SAXTarget out, final FormatStack fstack, final ProcessingInstruction pi) throws SAXException { out.getContentHandler().processingInstruction(pi.getTarget(), pi.getData()); } /** * This will handle printing of a {@link Comment}. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param comment * Comment to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printComment(final SAXTarget out, final FormatStack fstack, final Comment comment) throws SAXException { if (out.getLexicalHandler() != null) { char[] c = comment.getText().toCharArray(); out.getLexicalHandler().comment(c, 0, c.length); } } /** * This will handle printing of an {@link EntityRef}. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param entity * EntotyRef to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printEntityRef(final SAXTarget out, final FormatStack fstack, final EntityRef entity) throws SAXException { out.getContentHandler().skippedEntity(entity.getName()); } /** * This will handle printing of a {@link CDATA}. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param cdata * CDATA to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printCDATA(final SAXTarget out, final FormatStack fstack, final CDATA cdata) throws SAXException { // CDATAs are treated like text, not indented/newline content. final LexicalHandler lexicalHandler = out.getLexicalHandler(); final char[] chars = cdata.getText().toCharArray(); if (lexicalHandler != null) { lexicalHandler.startCDATA(); out.getContentHandler().characters(chars, 0, chars.length); lexicalHandler.endCDATA(); } else { out.getContentHandler().characters(chars, 0, chars.length); } } /** * This will handle printing of a {@link Text}. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param text * Text to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printText(final SAXTarget out, final FormatStack fstack, final Text text) throws SAXException { final char[] chars = text.getText().toCharArray(); out.getContentHandler().characters(chars, 0, chars.length); } /** * This will handle printing of an {@link Element}. *

* This method arranges for outputting the Element infrastructure including * Namespace Declarations and Attributes. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param nstack * the NamespaceStack * @param element * Element to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printElement(final SAXTarget out, final FormatStack fstack, final NamespaceStack nstack, final Element element) throws SAXException { final ContentHandler ch = out.getContentHandler(); final Object origloc = out.getLocator().getNode(); nstack.push(element); try { // update locator out.getLocator().setNode(element); AttributesImpl atts = new AttributesImpl(); // contentHandler.startPrefixMapping() for (Namespace ns : nstack.addedForward()) { ch.startPrefixMapping(ns.getPrefix(), ns.getURI()); if (out.isDeclareNamespaces()) { // add a physical attribute if requested. String prefix = ns.getPrefix(); if (prefix.equals("")) { atts.addAttribute("", "", "xmlns", "CDATA", ns.getURI()); } else { atts.addAttribute("", "", "xmlns:" + ns.getPrefix(), "CDATA", ns.getURI()); } } } // Allocate attribute list. if (element.hasAttributes()) { for (Attribute a : element.getAttributes()) { if (!a.isSpecified() && fstack.isSpecifiedAttributesOnly()) { continue; } atts.addAttribute(a.getNamespaceURI(), a.getName(), a.getQualifiedName(), getAttributeTypeName(a.getAttributeType()), a.getValue()); } } // contentHandler.startElement() ch.startElement(element.getNamespaceURI(), element.getName(), element.getQualifiedName(), atts); final List content = element.getContent(); // OK, now we print out the meat of the Element if (!content.isEmpty()) { TextMode textmode = fstack.getTextMode(); // Check for xml:space and adjust format settings final String space = element.getAttributeValue("space", Namespace.XML_NAMESPACE); if ("default".equals(space)) { textmode = fstack.getDefaultMode(); } else if ("preserve".equals(space)) { textmode = TextMode.PRESERVE; } fstack.push(); try { fstack.setTextMode(textmode); Walker walker = buildWalker(fstack, content, false); if (walker.hasNext()) { if (!walker.isAllText() && fstack.getPadBetween() != null) { // we need to newline/indent final String indent = fstack.getPadBetween(); printText(out, fstack, new Text(indent)); } printContent(out, fstack, nstack, walker); if (!walker.isAllText() && fstack.getPadLast() != null) { // we need to newline/indent final String indent = fstack.getPadLast(); printText(out, fstack, new Text(indent)); } } } finally { fstack.pop(); } } // contentHandler.endElement() out.getContentHandler().endElement(element.getNamespaceURI(), element.getName(), element.getQualifiedName()); // contentHandler.endPrefixMapping() // de-map in reverse order to the mapping. for (Namespace ns : nstack.addedReverse()) { ch.endPrefixMapping(ns.getPrefix()); } } finally { nstack.pop(); out.getLocator().setNode(origloc); } } /** * This will handle printing of a List of {@link Content}. *

* It relies on the appropriate Walker to get the formatting right. * * @param out * SAXTarget to use. * @param fstack * the FormatStack * @param nstack * the NamespaceStack * @param walker * Waker of Content to write. * @throws SAXException * if the destination SAXTarget fails */ protected void printContent(final SAXTarget out, final FormatStack fstack, final NamespaceStack nstack, final Walker walker) throws SAXException { while (walker.hasNext()) { final Content c = walker.next(); if (c == null) { // Formatted Text or CDATA final String text = walker.text(); if (walker.isCDATA()) { printCDATA(out, fstack, new CDATA(text)); } else { printText(out, fstack, new Text(text)); } } else { switch (c.getCType()) { case CDATA: printCDATA(out, fstack, (CDATA)c); break; case Comment: printComment(out, fstack, (Comment)c); break; case DocType: // do nothing. break; case Element : printElement(out, fstack, nstack, (Element)c); break; case EntityRef: printEntityRef(out, fstack, (EntityRef)c); break; case ProcessingInstruction: printProcessingInstruction(out, fstack, (ProcessingInstruction)c); break; case Text: printText(out, fstack, (Text)c); break; } } } } /** *

* Returns the SAX 2.0 attribute type string from the type of a JDOM * Attribute. *

* * @param type * int the type of the JDOM attribute. * @return String the SAX 2.0 attribute type string. * @see org.jdom2.Attribute#getAttributeType * @see org.xml.sax.Attributes#getType */ private static String getAttributeTypeName(AttributeType type) { switch (type) { case UNDECLARED: return "CDATA"; default: return type.name(); } } /** *

* Creates a SAX XMLReader. *

* * @return XMLReader a SAX2 parser. * @throws Exception * if no parser can be created. */ protected XMLReader createParser() throws Exception { XMLReader parser = null; // Try using JAXP... // Note we need JAXP 1.1, and if JAXP 1.0 is all that's // available then the getXMLReader call fails and we skip // to the hard coded default parser try { Class factoryClass = Class .forName("javax.xml.parsers.SAXParserFactory"); // factory = SAXParserFactory.newInstance(); Method newParserInstance = factoryClass.getMethod("newInstance"); Object factory = newParserInstance.invoke(null); // jaxpParser = factory.newSAXParser(); Method newSAXParser = factoryClass.getMethod("newSAXParser"); Object jaxpParser = newSAXParser.invoke(factory); // parser = jaxpParser.getXMLReader(); Class parserClass = jaxpParser.getClass(); Method getXMLReader = parserClass.getMethod("getXMLReader"); parser = (XMLReader) getXMLReader.invoke(jaxpParser); } catch (ClassNotFoundException e) { // e.printStackTrace(); } catch (InvocationTargetException e) { // e.printStackTrace(); } catch (NoSuchMethodException e) { // e.printStackTrace(); } catch (IllegalAccessException e) { // e.printStackTrace(); } // Check to see if we got a parser yet, if not, try to use a // hard coded default if (parser == null) { parser = XMLReaderFactory .createXMLReader("org.apache.xerces.parsers.SAXParser"); } return parser; } /** *

* This will create a SAX XMLReader capable of parsing a DTD and configure * it so that the DTD parsing events are routed to the handlers registered * onto this SAXOutputter. *

* * @return XMLReader a SAX2 parser. * @throws JDOMException * if no parser can be created. */ private XMLReader createDTDParser(SAXTarget out) throws SAXException { XMLReader parser = null; // Get a parser instance try { parser = createParser(); } catch (Exception ex1) { throw new SAXException("Error in SAX parser allocation", ex1); } // Register handlers if (out.getDTDHandler() != null) { parser.setDTDHandler(out.getDTDHandler()); } if (out.getEntityResolver() != null) { parser.setEntityResolver(out.getEntityResolver()); } if (out.getLexicalHandler() != null) { try { parser.setProperty(SAX_PROPERTY_LEXICAL_HANDLER, out.getLexicalHandler()); } catch (SAXException ex1) { try { parser.setProperty(SAX_PROPERTY_LEXICAL_HANDLER_ALT, out.getLexicalHandler()); } catch (SAXException ex2) { // Forget it! } } } if (out.getDeclHandler() != null) { try { parser.setProperty(SAX_PROPERTY_DECLARATION_HANDLER, out.getDeclHandler()); } catch (SAXException ex1) { try { parser.setProperty(SAX_PROPERTY_DECLARATION_HANDLER_ALT, out.getDeclHandler()); } catch (SAXException ex2) { // Forget it! } } } // Absorb errors as much as possible, per Laurent parser.setErrorHandler(new DefaultHandler()); return parser; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy