org.jdom2.output.SAXOutputter Maven / Gradle / Ivy
Show all versions of jdom Show documentation
/*--
Copyright (C) 2000-2012 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;
import static org.jdom2.JDOMConstants.*;
import java.util.List;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;
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.ProcessingInstruction;
import org.jdom2.Text;
import org.jdom2.output.support.AbstractSAXOutputProcessor;
import org.jdom2.output.support.SAXOutputProcessor;
import org.jdom2.output.support.SAXTarget;
/**
* Outputs a JDOM document as a stream of SAX2 events.
*
* Most ContentHandler callbacks are supported. BOTH
* ignorableWhitespace()
and skippedEntity()
have not
* been implemented. The {@link JDOMLocator}
class returned by
* {@link #getLocator}
exposes the current node being operated
* upon.
*
* At this time, it is not possible to access notations and unparsed entity
* references in a DTD from JDOM. Therefore, DTDHandler
callbacks
* have not been implemented yet.
*
* The ErrorHandler
callbacks 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.
*
*
* @author Brett McLaughlin
* @author Jason Hunter
* @author Fred Trimble
* @author Bradley S. Huffman
*/
public class SAXOutputter {
private static final class DefaultSAXOutputProcessor extends
AbstractSAXOutputProcessor {
// nothing.
}
private static final SAXOutputProcessor DEFAULT_PROCESSOR = new DefaultSAXOutputProcessor();
/** registered ContentHandler
*/
private ContentHandler contentHandler;
/** registered ErrorHandler
*/
private ErrorHandler errorHandler;
/** registered DTDHandler
*/
private DTDHandler dtdHandler;
/** registered EntityResolver
*/
private EntityResolver entityResolver;
/** registered LexicalHandler
*/
private LexicalHandler lexicalHandler;
/** registered DeclHandler
*/
private DeclHandler declHandler;
/**
* Whether to report attribute namespace declarations as xmlns attributes.
* Defaults to false
as per SAX specifications.
*
* @see SAX
* namespace specifications
*/
private boolean declareNamespaces = false;
/**
* Whether to report DTD events to DeclHandlers and LexicalHandlers.
* Defaults to true
.
*/
private boolean reportDtdEvents = true;
/**
* A SAXOutputProcessor
*/
private SAXOutputProcessor processor = DEFAULT_PROCESSOR;
/**
* The Format to use for output.
*/
private Format format = Format.getRawFormat();
/**
* This will create a SAXOutputter
without any registered
* handler. The application is then responsible for registering them using
* the setXxxHandler()
methods.
*/
public SAXOutputter() {
}
/**
* This will create a SAXOutputter
with the specified
* ContentHandler
.
*
* @param contentHandler
* contains ContentHandler
callback methods
*/
public SAXOutputter(ContentHandler contentHandler) {
this(contentHandler, null, null, null, null);
}
/**
* This will create a SAXOutputter
with the specified SAX2
* handlers. At this time, only ContentHandler
and
* EntityResolver
are supported.
*
* @param contentHandler
* contains ContentHandler
callback methods
* @param errorHandler
* contains ErrorHandler
callback methods
* @param dtdHandler
* contains DTDHandler
callback methods
* @param entityResolver
* contains EntityResolver
callback methods
*/
public SAXOutputter(ContentHandler contentHandler,
ErrorHandler errorHandler, DTDHandler dtdHandler,
EntityResolver entityResolver) {
this(contentHandler, errorHandler, dtdHandler, entityResolver, null);
}
/**
* This will create a SAXOutputter
with the specified SAX2
* handlers. At this time, only ContentHandler
and
* EntityResolver
are supported.
*
* @param contentHandler
* contains ContentHandler
callback methods
* @param errorHandler
* contains ErrorHandler
callback methods
* @param dtdHandler
* contains DTDHandler
callback methods
* @param entityResolver
* contains EntityResolver
callback methods
* @param lexicalHandler
* contains LexicalHandler
callbacks.
*/
public SAXOutputter(ContentHandler contentHandler,
ErrorHandler errorHandler, DTDHandler dtdHandler,
EntityResolver entityResolver, LexicalHandler lexicalHandler) {
this.contentHandler = contentHandler;
this.errorHandler = errorHandler;
this.dtdHandler = dtdHandler;
this.entityResolver = entityResolver;
this.lexicalHandler = lexicalHandler;
}
/**
* This will create a SAXOutputter
with the specified SAX2
* handlers. At this time, only ContentHandler
and
* EntityResolver
are supported.
*
* @param processor
* the {@link SAXOutputProcessor} to use for output.
* @param format
* the {@link Format} to use for output.
* @param contentHandler
* contains ContentHandler
callback methods
* @param errorHandler
* contains ErrorHandler
callback methods
* @param dtdHandler
* contains DTDHandler
callback methods
* @param entityResolver
* contains EntityResolver
callback methods
* @param lexicalHandler
* contains LexicalHandler
callbacks.
*/
public SAXOutputter(SAXOutputProcessor processor, Format format,
ContentHandler contentHandler, ErrorHandler errorHandler,
DTDHandler dtdHandler, EntityResolver entityResolver,
LexicalHandler lexicalHandler) {
this.processor = processor == null ? DEFAULT_PROCESSOR : processor;
this.format = format == null ? Format.getRawFormat() : format;
this.contentHandler = contentHandler;
this.errorHandler = errorHandler;
this.dtdHandler = dtdHandler;
this.entityResolver = entityResolver;
this.lexicalHandler = lexicalHandler;
}
/**
* This will set the ContentHandler
.
*
* @param contentHandler
* contains ContentHandler
callback methods.
*/
public void setContentHandler(ContentHandler contentHandler) {
this.contentHandler = contentHandler;
}
/**
* Returns the registered ContentHandler
.
*
* @return the current ContentHandler
or null
if
* none was registered.
*/
public ContentHandler getContentHandler() {
return this.contentHandler;
}
/**
* This will set the ErrorHandler
.
*
* @param errorHandler
* contains ErrorHandler
callback methods.
*/
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
/**
* Return the registered ErrorHandler
.
*
* @return the current ErrorHandler
or null
if
* none was registered.
*/
public ErrorHandler getErrorHandler() {
return this.errorHandler;
}
/**
* This will set the DTDHandler
.
*
* @param dtdHandler
* contains DTDHandler
callback methods.
*/
public void setDTDHandler(DTDHandler dtdHandler) {
this.dtdHandler = dtdHandler;
}
/**
* Return the registered DTDHandler
.
*
* @return the current DTDHandler
or null
if none
* was registered.
*/
public DTDHandler getDTDHandler() {
return this.dtdHandler;
}
/**
* This will set the EntityResolver
.
*
* @param entityResolver
* contains EntityResolver callback methods.
*/
public void setEntityResolver(EntityResolver entityResolver) {
this.entityResolver = entityResolver;
}
/**
* Return the registered EntityResolver
.
*
* @return the current EntityResolver
or null
if
* none was registered.
*/
public EntityResolver getEntityResolver() {
return this.entityResolver;
}
/**
* This will set the LexicalHandler
.
*
* @param lexicalHandler
* contains lexical callback methods.
*/
public void setLexicalHandler(LexicalHandler lexicalHandler) {
this.lexicalHandler = lexicalHandler;
}
/**
* Return the registered LexicalHandler
.
*
* @return the current LexicalHandler
or null
if
* none was registered.
*/
public LexicalHandler getLexicalHandler() {
return this.lexicalHandler;
}
/**
* This will set the DeclHandler
.
*
* @param declHandler
* contains declaration callback methods.
*/
public void setDeclHandler(DeclHandler declHandler) {
this.declHandler = declHandler;
}
/**
* Return the registered DeclHandler
.
*
* @return the current DeclHandler
or null
if none
* was registered.
*/
public DeclHandler getDeclHandler() {
return this.declHandler;
}
/**
* Returns whether attribute namespace declarations shall be reported as
* "xmlns" attributes.
*
* @return whether attribute namespace declarations shall be reported as
* "xmlns" attributes.
*/
public boolean getReportNamespaceDeclarations() {
return declareNamespaces;
}
/**
* This will define whether attribute namespace declarations shall be
* reported as "xmlns" attributes. This flag defaults to false
* and behaves as the "namespace-prefixes" SAX core feature.
*
* @param declareNamespaces
* whether attribute namespace declarations shall be reported as
* "xmlns" attributes.
*/
public void setReportNamespaceDeclarations(boolean declareNamespaces) {
this.declareNamespaces = declareNamespaces;
}
/**
* Returns whether DTD events will be reported.
*
* @return whether DTD events will be reported
*/
public boolean getReportDTDEvents() {
return reportDtdEvents;
}
/**
* This will define whether to report DTD events to SAX DeclHandlers and
* LexicalHandlers if these handlers are registered and the document to
* output includes a DocType declaration.
*
* @param reportDtdEvents
* whether to notify DTD events.
*/
public void setReportDTDEvents(boolean reportDtdEvents) {
this.reportDtdEvents = reportDtdEvents;
}
/**
* This will set the state of a SAX feature.
*
* All XMLReaders are required to support setting to true and to false.
*
*
* SAXOutputter currently supports the following SAX core features:
*
* http://xml.org/sax/features/namespaces
* - description:
true
indicates namespace
* URIs and unprefixed local names for element and attribute names will be
* available
* - access: read/write, but always
true
!
* http://xml.org/sax/features/namespace-prefixes
* - description:
true
indicates XML 1.0
* names (with prefixes) and attributes (including xmlns* attributes) will
* be available
* - access: read/write
* http://xml.org/sax/features/validation
* - description: controls whether SAXOutputter is
* reporting DTD-related events; if
true
, the DocType internal
* subset will be parsed to fire DTD events
* - access: read/write, defaults to
true
*
*
*
* @param name
* String
the feature name, which is a fully-qualified
* URI.
* @param value
* boolean
the requested state of the feature (true or
* false).
* @throws SAXNotRecognizedException
* when SAXOutputter does not recognize the feature name.
* @throws SAXNotSupportedException
* when SAXOutputter recognizes the feature name but cannot set the
* requested value.
*/
public void setFeature(String name, boolean value)
throws SAXNotRecognizedException, SAXNotSupportedException {
if (SAX_FEATURE_NAMESPACE_PREFIXES.equals(name)) {
// Namespace prefix declarations.
this.setReportNamespaceDeclarations(value);
} else {
if (SAX_FEATURE_NAMESPACES.equals(name)) {
if (value != true) {
// Namespaces feature always supported by SAXOutputter.
throw new SAXNotSupportedException(name);
}
// Else: true is OK!
} else {
if (SAX_FEATURE_VALIDATION.equals(name)) {
// Report DTD events.
this.setReportDTDEvents(value);
} else {
// Not a supported feature.
throw new SAXNotRecognizedException(name);
}
}
}
}
/**
* This will look up the value of a SAX feature.
*
* @param name
* String
the feature name, which is a fully-qualified
* URI.
* @return boolean
the current state of the feature (true or
* false).
* @throws SAXNotRecognizedException
* when SAXOutputter does not recognize the feature name.
* @throws SAXNotSupportedException
* when SAXOutputter recognizes the feature name but determine its
* value at this time.
*/
public boolean getFeature(String name) throws SAXNotRecognizedException,
SAXNotSupportedException {
if (SAX_FEATURE_NAMESPACE_PREFIXES.equals(name)) {
// Namespace prefix declarations.
return (this.declareNamespaces);
}
if (SAX_FEATURE_NAMESPACES.equals(name)) {
// Namespaces feature always supported by SAXOutputter.
return (true);
}
if (SAX_FEATURE_VALIDATION.equals(name)) {
// Report DTD events.
return (this.reportDtdEvents);
}
// Not a supported feature.
throw new SAXNotRecognizedException(name);
}
/**
* This will set the value of a SAX property. This method is also the
* standard mechanism for setting extended handlers.
*
* SAXOutputter currently supports the following SAX properties:
*
* http://xml.org/sax/properties/lexical-handler
* - data type:
*
org.xml.sax.ext.LexicalHandler
* - description: An optional extension handler for
* lexical events like comments.
* - access: read/write
* http://xml.org/sax/properties/declaration-handler
* - data type:
org.xml.sax.ext.DeclHandler
* - description: An optional extension handler for
* DTD-related events other than notations and unparsed entities.
* - access: read/write
*
*
*
* @param name
* String
the property name, which is a fully-qualified
* URI.
* @param value
* Object
the requested value for the property.
* @throws SAXNotRecognizedException
* when SAXOutputter does not recognize the property name.
* @throws SAXNotSupportedException
* when SAXOutputter recognizes the property name but cannot set the
* requested value.
*/
public void setProperty(String name, Object value)
throws SAXNotRecognizedException, SAXNotSupportedException {
if ((SAX_PROPERTY_LEXICAL_HANDLER.equals(name))
|| (SAX_PROPERTY_LEXICAL_HANDLER_ALT.equals(name))) {
this.setLexicalHandler((LexicalHandler) value);
} else {
if ((SAX_PROPERTY_DECLARATION_HANDLER.equals(name))
|| (SAX_PROPERTY_DECLARATION_HANDLER_ALT.equals(name))) {
this.setDeclHandler((DeclHandler) value);
} else {
throw new SAXNotRecognizedException(name);
}
}
}
/**
* This will look up the value of a SAX property.
*
* @param name
* String
the property name, which is a fully-qualified
* URI.
* @return Object
the current value of the property.
* @throws SAXNotRecognizedException
* when SAXOutputter does not recognize the property name.
* @throws SAXNotSupportedException
* when SAXOutputter recognizes the property name but cannot
* determine its value at this time.
*/
public Object getProperty(String name) throws SAXNotRecognizedException,
SAXNotSupportedException {
if ((SAX_PROPERTY_LEXICAL_HANDLER.equals(name))
|| (SAX_PROPERTY_LEXICAL_HANDLER_ALT.equals(name))) {
return this.getLexicalHandler();
}
if ((SAX_PROPERTY_DECLARATION_HANDLER.equals(name))
|| (SAX_PROPERTY_DECLARATION_HANDLER_ALT.equals(name))) {
return this.getDeclHandler();
}
throw new SAXNotRecognizedException(name);
}
/**
* Get the current {@link SAXOutputProcessor} being used for output.
*
* @return The current SAXOutputProcessor
*/
public SAXOutputProcessor getSAXOutputProcessor() {
return processor;
}
/**
* Set the current {@link SAXOutputProcessor} to be used for output.
*
* @param processor
* the new SAXOutputProcessor
*/
public void setSAXOutputProcessor(SAXOutputProcessor processor) {
this.processor = processor == null ? DEFAULT_PROCESSOR : processor;
}
/**
* Get the current {@link Format} being used for output
*
* @return the current Format
*/
public Format getFormat() {
return format;
}
/**
* Set the current {@link Format} to be used for output.
*
* @param format
* the new Format
*/
public void setFormat(Format format) {
this.format = format == null ? Format.getRawFormat() : format;
}
private final SAXTarget buildTarget(Document doc) {
String publicID = null;
String systemID = null;
if (doc != null) {
DocType dt = doc.getDocType();
if (dt != null) {
publicID = dt.getPublicID();
systemID = dt.getSystemID();
}
}
return new SAXTarget(contentHandler, errorHandler, dtdHandler,
entityResolver, lexicalHandler, declHandler, declareNamespaces,
reportDtdEvents, publicID, systemID);
}
/**
* This will output the JDOM Document
, firing off the SAX
* events that have been registered.
*
* @param document
* JDOM Document
to output.
* @throws JDOMException
* if any error occurred.
*/
public void output(Document document) throws JDOMException {
processor.process(buildTarget(document), format, document);
}
/**
* This will output a list of JDOM nodes as a document, firing off the SAX
* events that have been registered.
*
* Warning: This method may output ill-formed XML documents
* if the list contains top-level objects that are not legal at the document
* level (e.g. Text or CDATA nodes, multiple Element nodes, etc.). Thus, it
* should only be used to output document portions towards ContentHandlers
* capable of accepting such ill-formed documents (such as XSLT processors).
*
*
* @param nodes
* List
of JDOM nodes to output.
* @throws JDOMException
* if any error occurred.
* @see #output(org.jdom2.Document)
*/
public void output(List extends Content> nodes) throws JDOMException {
processor.processAsDocument(buildTarget(null), format, nodes);
}
/**
* This will output a single JDOM element as a document, firing off the SAX
* events that have been registered.
*
* @param node
* the Element
node to output.
* @throws JDOMException
* if any error occurred.
*/
public void output(Element node) throws JDOMException {
processor.processAsDocument(buildTarget(null), format, node);
}
/**
* This will output a list of JDOM nodes as a fragment of an XML document,
* firing off the SAX events that have been registered.
*
* Warning: This method does not call the
* {@link ContentHandler#setDocumentLocator},
* {@link ContentHandler#startDocument} and
* {@link ContentHandler#endDocument} callbacks on the
* {@link #setContentHandler ContentHandler}. The user shall invoke these
* methods directly prior/after outputting the document fragments.
*
*
* @param nodes
* List
of JDOM nodes to output.
* @throws JDOMException
* if any error occurred.
* @see #outputFragment(org.jdom2.Content)
*/
public void outputFragment(List extends Content> nodes)
throws JDOMException {
if (nodes == null) {
return;
}
processor.process(buildTarget(null), format, nodes);
}
/**
* This will output a single JDOM nodes as a fragment of an XML document,
* firing off the SAX events that have been registered.
*
* Warning: This method does not call the
* {@link ContentHandler#setDocumentLocator},
* {@link ContentHandler#startDocument} and
* {@link ContentHandler#endDocument} callbacks on the
* {@link #setContentHandler ContentHandler}. The user shall invoke these
* methods directly prior/after outputting the document fragments.
*
*
* @param node
* the Content
node to output.
* @throws JDOMException
* if any error occurred.
* @see #outputFragment(java.util.List)
*/
public void outputFragment(Content node) throws JDOMException {
if (node == null) {
return;
}
SAXTarget out = buildTarget(null);
switch (node.getCType()) {
case CDATA:
processor.process(out, format, (CDATA) node);
break;
case Comment:
processor.process(out, format, (Comment) node);
break;
case Element:
processor.process(out, format, (Element) node);
break;
case EntityRef:
processor.process(out, format, (EntityRef) node);
break;
case ProcessingInstruction:
processor.process(out, format, (ProcessingInstruction) node);
break;
case Text:
processor.process(out, format, (Text) node);
break;
default:
handleError(new JDOMException("Invalid element content: " + node));
}
}
/**
*
* Notifies the registered {@link ErrorHandler SAX error handler}
* (if any) of an input processing error. The error handler can
* choose to absorb the error and let the processing continue.
*
*
* @param exception JDOMException
containing the
* error information; will be wrapped in a
* {@link SAXParseException} when reported to
* the SAX error handler.
*
* @throws JDOMException if no error handler has been registered
* or if the error handler fired a
* {@link SAXException}.
*/
private void handleError(JDOMException exception) throws JDOMException {
if (errorHandler != null) {
try {
errorHandler.error(new SAXParseException(
exception.getMessage(), null, exception));
}
catch (SAXException se) {
if (se.getException() instanceof JDOMException) {
throw (JDOMException)(se.getException());
}
throw new JDOMException(se.getMessage(), se);
}
}
else {
throw exception;
}
}
/**
* Returns null.
*
* @return null
* @deprecated there is no way to get a meaningful document Locator outside
* of an active output process, and the contents of the locator
* are meaningless outside of an active output process anyway.
*/
@Deprecated
public JDOMLocator getLocator() {
return null;
}
}