blob: 18e5e43aaa3678747853da3756531bee79e3c743 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id$
*/
package org.apache.xalan.transformer;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Properties;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.xalan.res.XSLMessages;
import org.apache.xalan.res.XSLTErrorResources;
import org.apache.xalan.templates.OutputProperties;
import org.apache.xml.serializer.Serializer;
import org.apache.xml.serializer.SerializerFactory;
import org.apache.xml.serializer.Method;
import org.apache.xml.utils.DOMBuilder;
import org.apache.xml.utils.XMLReaderManager;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Node;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DeclHandler;
import org.xml.sax.ext.LexicalHandler;
/**
* This class implements an identity transformer for
* {@link javax.xml.transform.sax.SAXTransformerFactory#newTransformerHandler()}
* and {@link javax.xml.transform.TransformerFactory#newTransformer()}. It
* simply feeds SAX events directly to a serializer ContentHandler, if the
* result is a stream. If the result is a DOM, it will send the events to
* {@link org.apache.xml.utils.DOMBuilder}. If the result is another
* content handler, it will simply pass the events on.
*/
public class TransformerIdentityImpl extends Transformer
implements TransformerHandler, DeclHandler
{
/**
* Constructor TransformerIdentityImpl creates an identity transform.
*
*/
public TransformerIdentityImpl(boolean isSecureProcessing)
{
m_outputFormat = new OutputProperties(Method.XML);
m_isSecureProcessing = isSecureProcessing;
}
/**
* Constructor TransformerIdentityImpl creates an identity transform.
*
*/
public TransformerIdentityImpl()
{
this(false);
}
/**
* Enables the user of the TransformerHandler to set the
* to set the Result for the transformation.
*
* @param result A Result instance, should not be null.
*
* @throws IllegalArgumentException if result is invalid for some reason.
*/
public void setResult(Result result) throws IllegalArgumentException
{
if(null == result)
throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_RESULT_NULL, null)); //"Result should not be null");
m_result = result;
}
/**
* Set the base ID (URI or system ID) from where relative
* URLs will be resolved.
* @param systemID Base URI for the source tree.
*/
public void setSystemId(String systemID)
{
m_systemID = systemID;
}
/**
* Get the base ID (URI or system ID) from where relative
* URLs will be resolved.
* @return The systemID that was set with {@link #setSystemId}.
*/
public String getSystemId()
{
return m_systemID;
}
/**
* Get the Transformer associated with this handler, which
* is needed in order to set parameters and output properties.
*
* @return non-null reference to the transformer.
*/
public Transformer getTransformer()
{
return this;
}
/**
* Reset the status of the transformer.
*/
public void reset()
{
m_flushedStartDoc = false;
m_foundFirstElement = false;
m_outputStream = null;
m_params.clear();
m_result = null;
m_resultContentHandler = null;
m_resultDeclHandler = null;
m_resultDTDHandler = null;
m_resultLexicalHandler = null;
m_serializer = null;
m_systemID = null;
m_URIResolver = null;
m_outputFormat = new OutputProperties(Method.XML);
}
/**
* Create a result ContentHandler from a Result object, based
* on the current OutputProperties.
*
* @param outputTarget Where the transform result should go,
* should not be null.
*
* @return A valid ContentHandler that will create the
* result tree when it is fed SAX events.
*
* @throws TransformerException
*/
private void createResultContentHandler(Result outputTarget)
throws TransformerException
{
if (outputTarget instanceof SAXResult)
{
SAXResult saxResult = (SAXResult) outputTarget;
m_resultContentHandler = saxResult.getHandler();
m_resultLexicalHandler = saxResult.getLexicalHandler();
if (m_resultContentHandler instanceof Serializer)
{
// Dubious but needed, I think.
m_serializer = (Serializer) m_resultContentHandler;
}
}
else if (outputTarget instanceof DOMResult)
{
DOMResult domResult = (DOMResult) outputTarget;
Node outputNode = domResult.getNode();
Node nextSibling = domResult.getNextSibling();
Document doc;
short type;
if (null != outputNode)
{
type = outputNode.getNodeType();
doc = (Node.DOCUMENT_NODE == type)
? (Document) outputNode : outputNode.getOwnerDocument();
}
else
{
try
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
if (m_isSecureProcessing)
{
try
{
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
}
catch (ParserConfigurationException pce) {}
}
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.newDocument();
}
catch (ParserConfigurationException pce)
{
throw new TransformerException(pce);
}
outputNode = doc;
type = outputNode.getNodeType();
((DOMResult) outputTarget).setNode(outputNode);
}
DOMBuilder domBuilder =
(Node.DOCUMENT_FRAGMENT_NODE == type)
? new DOMBuilder(doc, (DocumentFragment) outputNode)
: new DOMBuilder(doc, outputNode);
if (nextSibling != null)
domBuilder.setNextSibling(nextSibling);
m_resultContentHandler = domBuilder;
m_resultLexicalHandler = domBuilder;
}
else if (outputTarget instanceof StreamResult)
{
StreamResult sresult = (StreamResult) outputTarget;
try
{
Serializer serializer =
SerializerFactory.getSerializer(m_outputFormat.getProperties());
m_serializer = serializer;
if (null != sresult.getWriter())
serializer.setWriter(sresult.getWriter());
else if (null != sresult.getOutputStream())
serializer.setOutputStream(sresult.getOutputStream());
else if (null != sresult.getSystemId())
{
String fileURL = sresult.getSystemId();
if (fileURL.startsWith("file:///")) {
if (fileURL.substring(8).indexOf(":") >0) {
fileURL = fileURL.substring(8);
} else {
fileURL = fileURL.substring(7);
}
} else if (fileURL.startsWith("file:/")) {
if (fileURL.substring(6).indexOf(":") >0) {
fileURL = fileURL.substring(6);
} else {
fileURL = fileURL.substring(5);
}
}
m_outputStream = new java.io.FileOutputStream(fileURL);
serializer.setOutputStream(m_outputStream);
}
else
throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!");
m_resultContentHandler = serializer.asContentHandler();
}
catch (IOException ioe)
{
throw new TransformerException(ioe);
}
}
else
{
throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE, new Object[]{outputTarget.getClass().getName()})); //"Can't transform to a Result of type "
// + outputTarget.getClass().getName()
// + "!");
}
if (m_resultContentHandler instanceof DTDHandler)
m_resultDTDHandler = (DTDHandler) m_resultContentHandler;
if (m_resultContentHandler instanceof DeclHandler)
m_resultDeclHandler = (DeclHandler) m_resultContentHandler;
if (m_resultContentHandler instanceof LexicalHandler)
m_resultLexicalHandler = (LexicalHandler) m_resultContentHandler;
}
/**
* Process the source tree to the output result.
* @param source The input for the source tree.
*
* @param outputTarget The output target.
*
* @throws TransformerException If an unrecoverable error occurs
* during the course of the transformation.
*/
public void transform(Source source, Result outputTarget)
throws TransformerException
{
createResultContentHandler(outputTarget);
/*
* According to JAXP1.2, new SAXSource()/StreamSource()
* should create an empty input tree, with a default root node.
* new DOMSource()creates an empty document using DocumentBuilder.
* newDocument(); Use DocumentBuilder.newDocument() for all 3 situations,
* since there is no clear spec. how to create an empty tree when
* both SAXSource() and StreamSource() are used.
*/
if ((source instanceof StreamSource && source.getSystemId()==null &&
((StreamSource)source).getInputStream()==null &&
((StreamSource)source).getReader()==null)||
(source instanceof SAXSource &&
((SAXSource)source).getInputSource()==null &&
((SAXSource)source).getXMLReader()==null )||
(source instanceof DOMSource && ((DOMSource)source).getNode()==null)){
try {
DocumentBuilderFactory builderF = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderF.newDocumentBuilder();
String systemID = source.getSystemId();
source = new DOMSource(builder.newDocument());
// Copy system ID from original, empty Source to new Source
if (systemID != null) {
source.setSystemId(systemID);
}
} catch (ParserConfigurationException e){
throw new TransformerException(e.getMessage());
}
}
try
{
if (source instanceof DOMSource)
{
DOMSource dsource = (DOMSource) source;
m_systemID = dsource.getSystemId();
Node dNode = dsource.getNode();
if (null != dNode)
{
try
{
if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
this.startDocument();
try
{
if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
{
String data = dNode.getNodeValue();
char[] chars = data.toCharArray();
characters(chars, 0, chars.length);
}
else
{
org.apache.xml.serializer.TreeWalker walker;
walker = new org.apache.xml.serializer.TreeWalker(this, m_systemID);
walker.traverse(dNode);
}
}
finally
{
if(dNode.getNodeType() == Node.ATTRIBUTE_NODE)
this.endDocument();
}
}
catch (SAXException se)
{
throw new TransformerException(se);
}
return;
}
else
{
String messageStr = XSLMessages.createMessage(
XSLTErrorResources.ER_ILLEGAL_DOMSOURCE_INPUT, null);
throw new IllegalArgumentException(messageStr);
}
}
InputSource xmlSource = SAXSource.sourceToInputSource(source);
if (null == xmlSource)
{
throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_SOURCE_TYPE, new Object[]{source.getClass().getName()})); //"Can't transform a Source of type "
//+ source.getClass().getName() + "!");
}
if (null != xmlSource.getSystemId())
m_systemID = xmlSource.getSystemId();
XMLReader reader = null;
boolean managedReader = false;
try
{
if (source instanceof SAXSource) {
reader = ((SAXSource) source).getXMLReader();
}
if (null == reader) {
try {
reader = XMLReaderManager.getInstance().getXMLReader();
managedReader = true;
} catch (SAXException se) {
throw new TransformerException(se);
}
} else {
try {
reader.setFeature("http://xml.org/sax/features/namespace-prefixes",
true);
} catch (org.xml.sax.SAXException se) {
// We don't care.
}
}
// Get the input content handler, which will handle the
// parse events and create the source tree.
ContentHandler inputHandler = this;
reader.setContentHandler(inputHandler);
if (inputHandler instanceof org.xml.sax.DTDHandler)
reader.setDTDHandler((org.xml.sax.DTDHandler) inputHandler);
try
{
if (inputHandler instanceof org.xml.sax.ext.LexicalHandler)
reader.setProperty("http://xml.org/sax/properties/lexical-handler",
inputHandler);
if (inputHandler instanceof org.xml.sax.ext.DeclHandler)
reader.setProperty(
"http://xml.org/sax/properties/declaration-handler",
inputHandler);
}
catch (org.xml.sax.SAXException se){}
try
{
if (inputHandler instanceof org.xml.sax.ext.LexicalHandler)
reader.setProperty("http://xml.org/sax/handlers/LexicalHandler",
inputHandler);
if (inputHandler instanceof org.xml.sax.ext.DeclHandler)
reader.setProperty("http://xml.org/sax/handlers/DeclHandler",
inputHandler);
}
catch (org.xml.sax.SAXNotRecognizedException snre){}
reader.parse(xmlSource);
}
catch (org.apache.xml.utils.WrappedRuntimeException wre)
{
Throwable throwable = wre.getException();
while (throwable
instanceof org.apache.xml.utils.WrappedRuntimeException)
{
throwable =
((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
}
throw new TransformerException(wre.getException());
}
catch (org.xml.sax.SAXException se)
{
throw new TransformerException(se);
}
catch (IOException ioe)
{
throw new TransformerException(ioe);
} finally {
if (managedReader) {
XMLReaderManager.getInstance().releaseXMLReader(reader);
}
}
}
finally
{
if(null != m_outputStream)
{
try
{
m_outputStream.close();
}
catch(IOException ioe){}
m_outputStream = null;
}
}
}
/**
* Add a parameter for the transformation.
*
* <p>Pass a qualified name as a two-part string, the namespace URI
* enclosed in curly braces ({}), followed by the local name. If the
* name has a null URL, the String only contain the local name. An
* application can safely check for a non-null URI by testing to see if the first
* character of the name is a '{' character.</p>
* <p>For example, if a URI and local name were obtained from an element
* defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
* then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
* no prefix is used.</p>
*
* @param name The name of the parameter, which may begin with a namespace URI
* in curly braces ({}).
* @param value The value object. This can be any valid Java object. It is
* up to the processor to provide the proper object coersion or to simply
* pass the object on for use in an extension.
*/
public void setParameter(String name, Object value)
{
if (value == null) {
throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name}));
}
if (null == m_params)
{
m_params = new Hashtable();
}
m_params.put(name, value);
}
/**
* Get a parameter that was explicitly set with setParameter
* or setParameters.
*
* <p>This method does not return a default parameter value, which
* cannot be determined until the node context is evaluated during
* the transformation process.
*
*
* @param name Name of the parameter.
* @return A parameter that has been set with setParameter.
*/
public Object getParameter(String name)
{
if (null == m_params)
return null;
return m_params.get(name);
}
/**
* Clear all parameters set with setParameter.
*/
public void clearParameters()
{
if (null == m_params)
return;
m_params.clear();
}
/**
* Set an object that will be used to resolve URIs used in
* document().
*
* <p>If the resolver argument is null, the URIResolver value will
* be cleared, and the default behavior will be used.</p>
*
* @param resolver An object that implements the URIResolver interface,
* or null.
*/
public void setURIResolver(URIResolver resolver)
{
m_URIResolver = resolver;
}
/**
* Get an object that will be used to resolve URIs used in
* document(), etc.
*
* @return An object that implements the URIResolver interface,
* or null.
*/
public URIResolver getURIResolver()
{
return m_URIResolver;
}
/**
* Set the output properties for the transformation. These
* properties will override properties set in the Templates
* with xsl:output.
*
* <p>If argument to this function is null, any properties
* previously set are removed, and the value will revert to the value
* defined in the templates object.</p>
*
* <p>Pass a qualified property key name as a two-part string, the namespace URI
* enclosed in curly braces ({}), followed by the local name. If the
* name has a null URL, the String only contain the local name. An
* application can safely check for a non-null URI by testing to see if the first
* character of the name is a '{' character.</p>
* <p>For example, if a URI and local name were obtained from an element
* defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
* then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
* no prefix is used.</p>
*
* @param oformat A set of output properties that will be
* used to override any of the same properties in affect
* for the transformation.
*
* @see javax.xml.transform.OutputKeys
* @see java.util.Properties
*
* @throws IllegalArgumentException if any of the argument keys are not
* recognized and are not namespace qualified.
*/
public void setOutputProperties(Properties oformat)
throws IllegalArgumentException
{
if (null != oformat)
{
// See if an *explicit* method was set.
String method = (String) oformat.get(OutputKeys.METHOD);
if (null != method)
m_outputFormat = new OutputProperties(method);
else
m_outputFormat = new OutputProperties();
m_outputFormat.copyFrom(oformat);
}
else {
// if oformat is null JAXP says that any props previously set are removed
// and we are to revert back to those in the templates object (i.e. Stylesheet).
m_outputFormat = null;
}
}
/**
* Get a copy of the output properties for the transformation.
*
* <p>The properties returned should contain properties set by the user,
* and properties set by the stylesheet, and these properties
* are "defaulted" by default properties specified by <a href="http://www.w3.org/TR/xslt#output">section 16 of the
* XSL Transformations (XSLT) W3C Recommendation</a>. The properties that
* were specifically set by the user or the stylesheet should be in the base
* Properties list, while the XSLT default properties that were not
* specifically set should be the default Properties list. Thus,
* getOutputProperties().getProperty(String key) will obtain any
* property in that was set by {@link #setOutputProperty},
* {@link #setOutputProperties}, in the stylesheet, <em>or</em> the default
* properties, while
* getOutputProperties().get(String key) will only retrieve properties
* that were explicitly set by {@link #setOutputProperty},
* {@link #setOutputProperties}, or in the stylesheet.</p>
*
* <p>Note that mutation of the Properties object returned will not
* effect the properties that the transformation contains.</p>
*
* <p>If any of the argument keys are not recognized and are not
* namespace qualified, the property will be ignored. In other words the
* behaviour is not orthogonal with setOutputProperties.</p>
*
* @return A copy of the set of output properties in effect
* for the next transformation.
*
* @see javax.xml.transform.OutputKeys
* @see java.util.Properties
*/
public Properties getOutputProperties()
{
return (Properties) m_outputFormat.getProperties().clone();
}
/**
* Set an output property that will be in effect for the
* transformation.
*
* <p>Pass a qualified property name as a two-part string, the namespace URI
* enclosed in curly braces ({}), followed by the local name. If the
* name has a null URL, the String only contain the local name. An
* application can safely check for a non-null URI by testing to see if the first
* character of the name is a '{' character.</p>
* <p>For example, if a URI and local name were obtained from an element
* defined with &lt;xyz:foo xmlns:xyz="http://xyz.foo.com/yada/baz.html"/&gt;,
* then the qualified name would be "{http://xyz.foo.com/yada/baz.html}foo". Note that
* no prefix is used.</p>
*
* <p>The Properties object that was passed to {@link #setOutputProperties} won't
* be effected by calling this method.</p>
*
* @param name A non-null String that specifies an output
* property name, which may be namespace qualified.
* @param value The non-null string value of the output property.
*
* @throws IllegalArgumentException If the property is not supported, and is
* not qualified with a namespace.
*
* @see javax.xml.transform.OutputKeys
*/
public void setOutputProperty(String name, String value)
throws IllegalArgumentException
{
if (!OutputProperties.isLegalPropertyKey(name))
throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
//+ name);
m_outputFormat.setProperty(name, value);
}
/**
* Get an output property that is in effect for the
* transformation. The property specified may be a property
* that was set with setOutputProperty, or it may be a
* property specified in the stylesheet.
*
* @param name A non-null String that specifies an output
* property name, which may be namespace qualified.
*
* @return The string value of the output property, or null
* if no property was found.
*
* @throws IllegalArgumentException If the property is not supported.
*
* @see javax.xml.transform.OutputKeys
*/
public String getOutputProperty(String name) throws IllegalArgumentException
{
String value = null;
OutputProperties props = m_outputFormat;
value = props.getProperty(name);
if (null == value)
{
if (!OutputProperties.isLegalPropertyKey(name))
throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
// + name);
}
return value;
}
/**
* Set the error event listener in effect for the transformation.
*
* @param listener The new error listener.
* @throws IllegalArgumentException if listener is null.
*/
public void setErrorListener(ErrorListener listener)
throws IllegalArgumentException
{
if (listener == null)
throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null));
else
m_errorListener = listener;
}
/**
* Get the error event handler in effect for the transformation.
*
* @return The current error handler, which should never be null.
*/
public ErrorListener getErrorListener()
{
return m_errorListener;
}
////////////////////////////////////////////////////////////////////
// Default implementation of DTDHandler interface.
////////////////////////////////////////////////////////////////////
/**
* Receive notification of a notation declaration.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass if they wish to keep track of the notations
* declared in a document.</p>
*
* @param name The notation name.
* @param publicId The notation public identifier, or null if not
* available.
* @param systemId The notation system identifier.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.DTDHandler#notationDecl
*
* @throws SAXException
*/
public void notationDecl(String name, String publicId, String systemId)
throws SAXException
{
if (null != m_resultDTDHandler)
m_resultDTDHandler.notationDecl(name, publicId, systemId);
}
/**
* Receive notification of an unparsed entity declaration.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to keep track of the unparsed entities
* declared in a document.</p>
*
* @param name The entity name.
* @param publicId The entity public identifier, or null if not
* available.
* @param systemId The entity system identifier.
* @param notationName The name of the associated notation.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.DTDHandler#unparsedEntityDecl
*
* @throws SAXException
*/
public void unparsedEntityDecl(
String name, String publicId, String systemId, String notationName)
throws SAXException
{
if (null != m_resultDTDHandler)
m_resultDTDHandler.unparsedEntityDecl(name, publicId, systemId,
notationName);
}
////////////////////////////////////////////////////////////////////
// Default implementation of ContentHandler interface.
////////////////////////////////////////////////////////////////////
/**
* Receive a Locator object for document events.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass if they wish to store the locator for use
* with other document events.</p>
*
* @param locator A locator for all SAX document events.
* @see org.xml.sax.ContentHandler#setDocumentLocator
* @see org.xml.sax.Locator
*/
public void setDocumentLocator(Locator locator)
{
try
{
if (null == m_resultContentHandler)
createResultContentHandler(m_result);
}
catch (TransformerException te)
{
throw new org.apache.xml.utils.WrappedRuntimeException(te);
}
m_resultContentHandler.setDocumentLocator(locator);
}
/**
* Receive notification of the beginning of the document.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to take specific actions at the beginning
* of a document (such as allocating the root node of a tree or
* creating an output file).</p>
*
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#startDocument
*
* @throws SAXException
*/
public void startDocument() throws SAXException
{
try
{
if (null == m_resultContentHandler)
createResultContentHandler(m_result);
}
catch (TransformerException te)
{
throw new SAXException(te.getMessage(), te);
}
// Reset for multiple transforms with this transformer.
m_flushedStartDoc = false;
m_foundFirstElement = false;
}
boolean m_flushedStartDoc = false;
protected final void flushStartDoc()
throws SAXException
{
if(!m_flushedStartDoc)
{
if (m_resultContentHandler == null)
{
try
{
createResultContentHandler(m_result);
}
catch(TransformerException te)
{
throw new SAXException(te);
}
}
m_resultContentHandler.startDocument();
m_flushedStartDoc = true;
}
}
/**
* Receive notification of the end of the document.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to take specific actions at the end
* of a document (such as finalising a tree or closing an output
* file).</p>
*
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#endDocument
*
* @throws SAXException
*/
public void endDocument() throws SAXException
{
flushStartDoc();
m_resultContentHandler.endDocument();
}
/**
* Receive notification of the start of a Namespace mapping.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to take specific actions at the start of
* each Namespace prefix scope (such as storing the prefix mapping).</p>
*
* @param prefix The Namespace prefix being declared.
* @param uri The Namespace URI mapped to the prefix.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#startPrefixMapping
*
* @throws SAXException
*/
public void startPrefixMapping(String prefix, String uri)
throws SAXException
{
flushStartDoc();
m_resultContentHandler.startPrefixMapping(prefix, uri);
}
/**
* Receive notification of the end of a Namespace mapping.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to take specific actions at the end of
* each prefix mapping.</p>
*
* @param prefix The Namespace prefix being declared.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#endPrefixMapping
*
* @throws SAXException
*/
public void endPrefixMapping(String prefix) throws SAXException
{
flushStartDoc();
m_resultContentHandler.endPrefixMapping(prefix);
}
/**
* Receive notification of the start of an element.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to take specific actions at the start of
* each element (such as allocating a new tree node or writing
* output to a file).</p>
*
* @param uri The Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace
* processing is not being performed.
* @param localName The local name (without prefix), or the
* empty string if Namespace processing is not being
* performed.
* @param qName The qualified name (with prefix), or the
* empty string if qualified names are not available.
* @param attributes The specified or defaulted attributes.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#startElement
*
* @throws SAXException
*/
public void startElement(
String uri, String localName, String qName, Attributes attributes)
throws SAXException
{
if (!m_foundFirstElement && null != m_serializer)
{
m_foundFirstElement = true;
Serializer newSerializer;
try
{
newSerializer = SerializerSwitcher.switchSerializerIfHTML(uri,
localName, m_outputFormat.getProperties(), m_serializer);
}
catch (TransformerException te)
{
throw new SAXException(te);
}
if (newSerializer != m_serializer)
{
try
{
m_resultContentHandler = newSerializer.asContentHandler();
}
catch (IOException ioe) // why?
{
throw new SAXException(ioe);
}
if (m_resultContentHandler instanceof DTDHandler)
m_resultDTDHandler = (DTDHandler) m_resultContentHandler;
if (m_resultContentHandler instanceof LexicalHandler)
m_resultLexicalHandler = (LexicalHandler) m_resultContentHandler;
m_serializer = newSerializer;
}
}
flushStartDoc();
m_resultContentHandler.startElement(uri, localName, qName, attributes);
}
/**
* Receive notification of the end of an element.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to take specific actions at the end of
* each element (such as finalising a tree node or writing
* output to a file).</p>
*
* @param uri The Namespace URI, or the empty string if the
* element has no Namespace URI or if Namespace
* processing is not being performed.
* @param localName The local name (without prefix), or the
* empty string if Namespace processing is not being
* performed.
* @param qName The qualified name (with prefix), or the
* empty string if qualified names are not available.
*
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#endElement
*
* @throws SAXException
*/
public void endElement(String uri, String localName, String qName)
throws SAXException
{
m_resultContentHandler.endElement(uri, localName, qName);
}
/**
* Receive notification of character data inside an element.
*
* <p>By default, do nothing. Application writers may override this
* method to take specific actions for each chunk of character data
* (such as adding the data to a node or buffer, or printing it to
* a file).</p>
*
* @param ch The characters.
* @param start The start position in the character array.
* @param length The number of characters to use from the
* character array.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#characters
*
* @throws SAXException
*/
public void characters(char ch[], int start, int length) throws SAXException
{
flushStartDoc();
m_resultContentHandler.characters(ch, start, length);
}
/**
* Receive notification of ignorable whitespace in element content.
*
* <p>By default, do nothing. Application writers may override this
* method to take specific actions for each chunk of ignorable
* whitespace (such as adding data to a node or buffer, or printing
* it to a file).</p>
*
* @param ch The whitespace characters.
* @param start The start position in the character array.
* @param length The number of characters to use from the
* character array.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#ignorableWhitespace
*
* @throws SAXException
*/
public void ignorableWhitespace(char ch[], int start, int length)
throws SAXException
{
m_resultContentHandler.ignorableWhitespace(ch, start, length);
}
/**
* Receive notification of a processing instruction.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to take specific actions for each
* processing instruction, such as setting status variables or
* invoking other methods.</p>
*
* @param target The processing instruction target.
* @param data The processing instruction data, or null if
* none is supplied.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#processingInstruction
*
* @throws SAXException
*/
public void processingInstruction(String target, String data)
throws SAXException
{
flushStartDoc();
m_resultContentHandler.processingInstruction(target, data);
}
/**
* Receive notification of a skipped entity.
*
* <p>By default, do nothing. Application writers may override this
* method in a subclass to take specific actions for each
* processing instruction, such as setting status variables or
* invoking other methods.</p>
*
* @param name The name of the skipped entity.
* @throws org.xml.sax.SAXException Any SAX exception, possibly
* wrapping another exception.
* @see org.xml.sax.ContentHandler#processingInstruction
*
* @throws SAXException
*/
public void skippedEntity(String name) throws SAXException
{
flushStartDoc();
m_resultContentHandler.skippedEntity(name);
}
/**
* Report the start of DTD declarations, if any.
*
* <p>Any declarations are assumed to be in the internal subset
* unless otherwise indicated by a {@link #startEntity startEntity}
* event.</p>
*
* <p>Note that the start/endDTD events will appear within
* the start/endDocument events from ContentHandler and
* before the first startElement event.</p>
*
* @param name The document type name.
* @param publicId The declared public identifier for the
* external DTD subset, or null if none was declared.
* @param systemId The declared system identifier for the
* external DTD subset, or null if none was declared.
* @throws SAXException The application may raise an
* exception.
* @see #endDTD
* @see #startEntity
*/
public void startDTD(String name, String publicId, String systemId)
throws SAXException
{
flushStartDoc();
if (null != m_resultLexicalHandler)
m_resultLexicalHandler.startDTD(name, publicId, systemId);
}
/**
* Report the end of DTD declarations.
*
* @throws SAXException The application may raise an exception.
* @see #startDTD
*/
public void endDTD() throws SAXException
{
if (null != m_resultLexicalHandler)
m_resultLexicalHandler.endDTD();
}
/**
* Report the beginning of an entity in content.
*
* <p><strong>NOTE:</entity> entity references in attribute
* values -- and the start and end of the document entity --
* are never reported.</p>
*
* <p>The start and end of the external DTD subset are reported
* using the pseudo-name "[dtd]". All other events must be
* properly nested within start/end entity events.</p>
*
* <p>Note that skipped entities will be reported through the
* {@link org.xml.sax.ContentHandler#skippedEntity skippedEntity}
* event, which is part of the ContentHandler interface.</p>
*
* @param name The name of the entity. If it is a parameter
* entity, the name will begin with '%'.
* @throws SAXException The application may raise an exception.
* @see #endEntity
* @see org.xml.sax.ext.DeclHandler#internalEntityDecl
* @see org.xml.sax.ext.DeclHandler#externalEntityDecl
*/
public void startEntity(String name) throws SAXException
{
if (null != m_resultLexicalHandler)
m_resultLexicalHandler.startEntity(name);
}
/**
* Report the end of an entity.
*
* @param name The name of the entity that is ending.
* @throws SAXException The application may raise an exception.
* @see #startEntity
*/
public void endEntity(String name) throws SAXException
{
if (null != m_resultLexicalHandler)
m_resultLexicalHandler.endEntity(name);
}
/**
* Report the start of a CDATA section.
*
* <p>The contents of the CDATA section will be reported through
* the regular {@link org.xml.sax.ContentHandler#characters
* characters} event.</p>
*
* @throws SAXException The application may raise an exception.
* @see #endCDATA
*/
public void startCDATA() throws SAXException
{
if (null != m_resultLexicalHandler)
m_resultLexicalHandler.startCDATA();
}
/**
* Report the end of a CDATA section.
*
* @throws SAXException The application may raise an exception.
* @see #startCDATA
*/
public void endCDATA() throws SAXException
{
if (null != m_resultLexicalHandler)
m_resultLexicalHandler.endCDATA();
}
/**
* Report an XML comment anywhere in the document.
*
* <p>This callback will be used for comments inside or outside the
* document element, including comments in the external DTD
* subset (if read).</p>
*
* @param ch An array holding the characters in the comment.
* @param start The starting position in the array.
* @param length The number of characters to use from the array.
* @throws SAXException The application may raise an exception.
*/
public void comment(char ch[], int start, int length) throws SAXException
{
flushStartDoc();
if (null != m_resultLexicalHandler)
m_resultLexicalHandler.comment(ch, start, length);
}
// Implement DeclHandler
/**
* Report an element type declaration.
*
* <p>The content model will consist of the string "EMPTY", the
* string "ANY", or a parenthesised group, optionally followed
* by an occurrence indicator. The model will be normalized so
* that all whitespace is removed,and will include the enclosing
* parentheses.</p>
*
* @param name The element type name.
* @param model The content model as a normalized string.
* @exception SAXException The application may raise an exception.
*/
public void elementDecl (String name, String model)
throws SAXException
{
if (null != m_resultDeclHandler)
m_resultDeclHandler.elementDecl(name, model);
}
/**
* Report an attribute type declaration.
*
* <p>Only the effective (first) declaration for an attribute will
* be reported. The type will be one of the strings "CDATA",
* "ID", "IDREF", "IDREFS", "NMTOKEN", "NMTOKENS", "ENTITY",
* "ENTITIES", or "NOTATION", or a parenthesized token group with
* the separator "|" and all whitespace removed.</p>
*
* @param eName The name of the associated element.
* @param aName The name of the attribute.
* @param type A string representing the attribute type.
* @param valueDefault A string representing the attribute default
* ("#IMPLIED", "#REQUIRED", or "#FIXED") or null if
* none of these applies.
* @param value A string representing the attribute's default value,
* or null if there is none.
* @exception SAXException The application may raise an exception.
*/
public void attributeDecl (String eName,
String aName,
String type,
String valueDefault,
String value)
throws SAXException
{
if (null != m_resultDeclHandler)
m_resultDeclHandler.attributeDecl(eName, aName, type, valueDefault, value);
}
/**
* Report an internal entity declaration.
*
* <p>Only the effective (first) declaration for each entity
* will be reported.</p>
*
* @param name The name of the entity. If it is a parameter
* entity, the name will begin with '%'.
* @param value The replacement text of the entity.
* @exception SAXException The application may raise an exception.
* @see #externalEntityDecl
* @see org.xml.sax.DTDHandler#unparsedEntityDecl
*/
public void internalEntityDecl (String name, String value)
throws SAXException
{
if (null != m_resultDeclHandler)
m_resultDeclHandler.internalEntityDecl(name, value);
}
/**
* Report a parsed external entity declaration.
*
* <p>Only the effective (first) declaration for each entity
* will be reported.</p>
*
* @param name The name of the entity. If it is a parameter
* entity, the name will begin with '%'.
* @param publicId The declared public identifier of the entity, or
* null if none was declared.
* @param systemId The declared system identifier of the entity.
* @exception SAXException The application may raise an exception.
* @see #internalEntityDecl
* @see org.xml.sax.DTDHandler#unparsedEntityDecl
*/
public void externalEntityDecl (String name, String publicId,
String systemId)
throws SAXException
{
if (null != m_resultDeclHandler)
m_resultDeclHandler.externalEntityDecl(name, publicId, systemId);
}
/**
* This is null unless we own the stream.
*/
private java.io.FileOutputStream m_outputStream = null;
/** The content handler where result events will be sent. */
private ContentHandler m_resultContentHandler;
/** The lexical handler where result events will be sent. */
private LexicalHandler m_resultLexicalHandler;
/** The DTD handler where result events will be sent. */
private DTDHandler m_resultDTDHandler;
/** The Decl handler where result events will be sent. */
private DeclHandler m_resultDeclHandler;
/** The Serializer, which may or may not be null. */
private Serializer m_serializer;
/** The Result object. */
private Result m_result;
/**
* The system ID, which is unused, but must be returned to fullfill the
* TransformerHandler interface.
*/
private String m_systemID;
/**
* The parameters, which is unused, but must be returned to fullfill the
* Transformer interface.
*/
private Hashtable m_params;
/** The error listener for TrAX errors and warnings. */
private ErrorListener m_errorListener =
new org.apache.xml.utils.DefaultErrorHandler(false);
/**
* The URIResolver, which is unused, but must be returned to fullfill the
* TransformerHandler interface.
*/
URIResolver m_URIResolver;
/** The output properties. */
private OutputProperties m_outputFormat;
/** Flag to set if we've found the first element, so we can tell if we have
* to check to see if we should create an HTML serializer. */
boolean m_foundFirstElement;
/**
* State of the secure processing feature.
*/
private boolean m_isSecureProcessing = false;
}