/* * 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: TransformerImpl.java 475979 2006-11-16 23:32:48Z minchau $ */ package org.apache.xalan.transformer; import java.io.IOException; import java.io.StringWriter; import java.util.Enumeration; import java.util.Properties; import java.util.Stack; import java.util.StringTokenizer; import java.util.Vector; 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.SourceLocator; 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.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.xalan.extensions.ExtensionsTable; import org.apache.xalan.res.XSLMessages; import org.apache.xalan.res.XSLTErrorResources; import org.apache.xml.serializer.Method; import org.apache.xml.serializer.Serializer; import org.apache.xml.serializer.SerializerFactory; import org.apache.xalan.templates.AVT; import org.apache.xalan.templates.Constants; import org.apache.xalan.templates.ElemAttributeSet; import org.apache.xalan.templates.ElemForEach; import org.apache.xalan.templates.ElemSort; import org.apache.xalan.templates.ElemTemplate; import org.apache.xalan.templates.ElemTemplateElement; import org.apache.xalan.templates.ElemTextLiteral; import org.apache.xalan.templates.ElemVariable; import org.apache.xalan.templates.OutputProperties; import org.apache.xalan.templates.Stylesheet; import org.apache.xalan.templates.StylesheetComposed; import org.apache.xalan.templates.StylesheetRoot; import org.apache.xalan.templates.XUnresolvedVariable; import org.apache.xml.dtm.DTM; import org.apache.xml.dtm.DTMIterator; import org.apache.xml.dtm.DTMManager; import org.apache.xml.dtm.DTMWSFilter; import org.apache.xml.serializer.ToSAXHandler; import org.apache.xml.serializer.ToTextStream; import org.apache.xml.serializer.ToXMLSAXHandler; import org.apache.xml.serializer.SerializationHandler; import org.apache.xml.utils.BoolStack; import org.apache.xml.utils.DOMBuilder; import org.apache.xml.utils.NodeVector; import org.apache.xml.utils.ObjectPool; import org.apache.xml.utils.ObjectStack; import org.apache.xml.utils.QName; import org.apache.xml.utils.SAXSourceLocator; import org.apache.xml.utils.ThreadControllerWrapper; import org.apache.xpath.Arg; import org.apache.xpath.ExtensionsProvider; import org.apache.xpath.VariableStack; import org.apache.xpath.XPathContext; import org.apache.xpath.functions.FuncExtFunction; import org.apache.xpath.objects.XObject; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.ext.LexicalHandler; /** * This class implements the * {@link javax.xml.transform.Transformer} interface, and is the core * representation of the transformation execution.
* @xsl.usage advanced */ public class TransformerImpl extends Transformer implements Runnable, DTMWSFilter, ExtensionsProvider, org.apache.xml.serializer.SerializerTrace { // Synch object to gaurd against setting values from the TrAX interface // or reentry while the transform is going on. /** NEEDSDOC Field m_reentryGuard */ private Boolean m_reentryGuard = new Boolean(true); /** * This is null unless we own the stream. */ private java.io.FileOutputStream m_outputStream = null; /** The thread that the transformer is running on. */ private Thread m_transformThread; /** The base URL of the source tree. */ private String m_urlOfSource = null; /** The Result object at the start of the transform, if any. */ private Result m_outputTarget = null; /** * The output format object set by the user. May be null. */ private OutputProperties m_outputFormat; /** * The content handler for the source input tree. */ ContentHandler m_inputContentHandler; /** * The content handler for the result tree. */ private ContentHandler m_outputContentHandler = null; // /* // * Use member variable to store param variables as they're // * being created, use member variable so we don't // * have to create a new vector every time. // */ // private Vector m_newVars = new Vector(); /** * A pool of ResultTreeHandlers, for serialization of a subtree to text. * Please note that each of these also holds onto a Text Serializer. */ private ObjectPool m_textResultHandlerObjectPool = new ObjectPool(ToTextStream.class); /** * Related to m_textResultHandlerObjectPool, this is a pool of * StringWriters, which are passed to the Text Serializers. * (I'm not sure if this is really needed any more. -sb) */ private ObjectPool m_stringWriterObjectPool = new ObjectPool(StringWriter.class); /** * A static text format object, which can be used over and * over to create the text serializers. */ private OutputProperties m_textformat = new OutputProperties(Method.TEXT); // Commenteded out in response to problem reported by // Nicola Brownfunc:result
* instruction that has been executed for the currently active EXSLT
* func:function
*/
ObjectStack m_currentFuncResult = new ObjectStack();
/**
* The message manager, which manages error messages, warning
* messages, and other types of message events.
*/
private MsgMgr m_msgMgr;
/**
* The flag for the setting of the optimize feature;
* This flag should have the same value as the FEATURE_OPTIMIZE feature
* which is set by the TransformerFactory.setAttribut() method before a
* Transformer is created
*/
private boolean m_optimizer = true;
/**
* The flag for the setting of the incremental feature;
* This flag should have the same value as the FEATURE_INCREMENTAL feature
* which is set by the TransformerFactory.setAttribut() method before a
* Transformer is created
*/
private boolean m_incremental = false;
/**
* The flag for the setting of the source_location feature;
* This flag should have the same value as the FEATURE_SOURCE_LOCATION feature
* which is set by the TransformerFactory.setAttribut() method before a
* Transformer is created
*/
private boolean m_source_location = false;
/**
* The SAX error handler, where errors and warnings are sent.
*/
private ErrorListener m_errorHandler =
new org.apache.xml.utils.DefaultErrorHandler(false);
/**
* If the transform thread throws an exception, the exception needs to
* be stashed away so that the main thread can pass it on to the
* client.
*/
private Exception m_exceptionThrown = null;
/**
* This is needed for support of setSourceTreeDocForThread(Node doc),
* which must be called in order for the transform thread's run
* method to obtain the root of the source tree to be transformed.
*/
private int m_doc;
/** Flag to to tell if the tranformer needs to be reset. */
private boolean m_hasBeenReset = false;
/** NEEDSDOC Field m_shouldReset */
private boolean m_shouldReset = true;
/**
* A stack of current template modes.
*/
private Stack m_modes = new Stack();
//==========================================================
// SECTION: Constructor
//==========================================================
/**
* Construct a TransformerImpl.
*
* @param stylesheet The root of the stylesheet tree.
*/
public TransformerImpl(StylesheetRoot stylesheet)
// throws javax.xml.transform.TransformerException
{
m_optimizer = stylesheet.getOptimizer();
m_incremental = stylesheet.getIncremental();
m_source_location = stylesheet.getSource_location();
setStylesheet(stylesheet);
XPathContext xPath = new XPathContext(this);
xPath.setIncremental(m_incremental);
xPath.getDTMManager().setIncremental(m_incremental);
xPath.setSource_location(m_source_location);
xPath.getDTMManager().setSource_location(m_source_location);
if (stylesheet.isSecureProcessing())
xPath.setSecureProcessing(true);
setXPathContext(xPath);
getXPathContext().setNamespaceContext(stylesheet);
}
// ================ ExtensionsTable ===================
/**
* The table of ExtensionHandlers.
*/
private ExtensionsTable m_extensionsTable = null;
/**
* Get the extensions table object.
*
* @return The extensions table.
*/
public ExtensionsTable getExtensionsTable()
{
return m_extensionsTable;
}
/**
* If the stylesheet contains extensions, set the extensions table object.
*
*
* @param sroot The stylesheet.
* @throws javax.xml.transform.TransformerException
*/
void setExtensionsTable(StylesheetRoot sroot)
throws javax.xml.transform.TransformerException
{
try
{
if (sroot.getExtensions() != null)
m_extensionsTable = new ExtensionsTable(sroot);
}
catch (javax.xml.transform.TransformerException te)
{te.printStackTrace();}
}
//== Implementation of the XPath ExtensionsProvider interface.
public boolean functionAvailable(String ns, String funcName)
throws javax.xml.transform.TransformerException
{
return getExtensionsTable().functionAvailable(ns, funcName);
}
public boolean elementAvailable(String ns, String elemName)
throws javax.xml.transform.TransformerException
{
return getExtensionsTable().elementAvailable(ns, elemName);
}
public Object extFunction(String ns, String funcName,
Vector argVec, Object methodKey)
throws javax.xml.transform.TransformerException
{//System.out.println("TransImpl.extFunction() " + ns + " " + funcName +" " + getExtensionsTable());
return getExtensionsTable().extFunction(ns, funcName,
argVec, methodKey,
getXPathContext().getExpressionContext());
}
public Object extFunction(FuncExtFunction extFunction, Vector argVec)
throws javax.xml.transform.TransformerException
{
return getExtensionsTable().extFunction(extFunction, argVec,
getXPathContext().getExpressionContext());
}
//=========================
/**
* Reset the state. This needs to be called after a process() call
* is invoked, if the processor is to be used again.
*/
public void reset()
{
if (!m_hasBeenReset && m_shouldReset)
{
m_hasBeenReset = true;
if (this.m_outputStream != null)
{
try
{
m_outputStream.close();
}
catch (java.io.IOException ioe){}
}
m_outputStream = null;
// I need to look more carefully at which of these really
// needs to be reset.
m_countersTable = null;
m_xcontext.reset();
m_xcontext.getVarStack().reset();
resetUserParameters();
m_currentTemplateElements.removeAllElements();
m_currentMatchTemplates.removeAllElements();
m_currentMatchedNodes.removeAllElements();
m_serializationHandler = null;
m_outputTarget = null;
m_keyManager = new KeyManager();
m_attrSetStack = null;
m_countersTable = null;
m_currentTemplateRuleIsNull = new BoolStack();
// m_xmlSource = null; // android-removed
m_doc = DTM.NULL;
// m_isTransformDone = false; // android-removed
m_transformThread = null;
// m_inputContentHandler = null;
// For now, reset the document cache each time.
m_xcontext.getSourceTreeManager().reset();
}
// m_reportInPostExceptionFromThread = false;
}
// ========= Transformer Interface Implementation ==========
/**
* Get the thread that the transform process is on.
*
* @return The thread that the transform process is on, or null.
* @xsl.usage internal
*/
public Thread getTransformThread()
{
return m_transformThread;
}
/**
* Get the thread that the transform process is on.
*
* @param t The transform thread, may be null.
* @xsl.usage internal
*/
public void setTransformThread(Thread t)
{
m_transformThread = t;
}
/** NEEDSDOC Field m_hasTransformThreadErrorCatcher */
private boolean m_hasTransformThreadErrorCatcher = false;
/**
* Return true if the transform was initiated from the transform method,
* otherwise it was probably done from a pure parse events.
*
* NEEDSDOC ($objectName$) @return
*/
public boolean hasTransformThreadErrorCatcher()
{
return m_hasTransformThreadErrorCatcher;
}
/**
* Process the source tree to SAX parse events.
* @param source The input for the source tree.
*
* @throws TransformerException
*/
public void transform(Source source) throws TransformerException
{
transform(source, true);
}
/**
* Process the source tree to SAX parse events.
* @param source The input for the source tree.
* @param shouldRelease Flag indicating whether to release DTMManager.
*
* @throws TransformerException
*/
public void transform(Source source, boolean shouldRelease) throws TransformerException
{
try
{
// Patch for bugzilla #13863. If we don't reset the namespaceContext
// then we will get a NullPointerException if transformer is reused
// (for stylesheets that use xsl:key). Not sure if this should go
// here or in reset(). -is
if(getXPathContext().getNamespaceContext() == null){
getXPathContext().setNamespaceContext(getStylesheet());
}
String base = source.getSystemId();
// If no systemID of the source, use the base of the stylesheet.
if(null == base)
{
base = m_stylesheetRoot.getBaseIdentifier();
}
// As a last resort, use the current user dir.
if(null == base)
{
String currentDir = "";
try {
currentDir = System.getProperty("user.dir");
}
catch (SecurityException se) {}// user.dir not accessible from applet
if (currentDir.startsWith(java.io.File.separator))
base = "file://" + currentDir;
else
base = "file:///" + currentDir;
base = base + java.io.File.separatorChar
+ source.getClass().getName();
}
setBaseURLOfSource(base);
DTMManager mgr = m_xcontext.getDTMManager();
/*
* 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) {
fatalError(e);
}
}
DTM dtm = mgr.getDTM(source, false, this, true, true);
dtm.setDocumentBaseURI(base);
boolean hardDelete = true; // %REVIEW% I have to think about this. -sb
try
{
// NOTE: This will work because this is _NOT_ a shared DTM, and thus has
// only a single Document node. If it could ever be an RTF or other
// shared DTM, look at dtm.getDocumentRoot(nodeHandle).
this.transformNode(dtm.getDocument());
}
finally
{
if (shouldRelease)
mgr.release(dtm, hardDelete);
}
// Kick off the parse. When the ContentHandler gets
// the startDocument event, it will call transformNode( node ).
// reader.parse( xmlSource );
// This has to be done to catch exceptions thrown from
// the transform thread spawned by the STree handler.
Exception e = getExceptionThrown();
if (null != e)
{
if (e instanceof javax.xml.transform.TransformerException)
{
throw (javax.xml.transform.TransformerException) e;
}
else if (e instanceof org.apache.xml.utils.WrappedRuntimeException)
{
fatalError(
((org.apache.xml.utils.WrappedRuntimeException) e).getException());
}
else
{
throw new javax.xml.transform.TransformerException(e);
}
}
else if (null != m_serializationHandler)
{
m_serializationHandler.endDocument();
}
}
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();
}
fatalError(throwable);
}
// Patch attributed to David Eisenberg * The recognized standard output properties are: *
* For example: *
* tran.setOutputProperty("standalone", "yes"); **
* In the case of the cdata-section-elements property, * the value should be a whitespace separated list of * element names. The element name is the local name * of the element, if it is in no namespace, or, the URI * in braces followed immediately by the local name * if the element is in that namespace. For example: *
* tran.setOutputProperty( * "cdata-section-elements", * "elem1 {http://example.uri}elem2 elem3"); **
* The recognized Xalan extension elements are: *
* These must be in the extension namespace of * "http://xml.apache.org/xalan". This is accomplished * by putting the namespace URI in braces before the * property name, for example: *
* tran.setOutputProperty( * "{http://xml.apache.org/xalan}line-separator" , * "\n"); ** * @param name The property name. * @param value The requested value for the property. * @throws IllegalArgumentException if the property name is not legal. */ public void setOutputProperty(String name, String value) throws IllegalArgumentException { synchronized (m_reentryGuard) { // Get the output format that was set by the user, otherwise get the // output format from the stylesheet. if (null == m_outputFormat) { m_outputFormat = (OutputProperties) getStylesheet().getOutputComposed().clone(); } 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); } } /** * Set the output properties for the transformation. These * properties will override properties set in the templates * with xsl:output. * *
If argument to this function is null, any properties * previously set will be removed.
* * @param oformat A set of output properties that will be * used to override any of the same properties in effect * 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 { synchronized (m_reentryGuard) { 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 if(m_outputFormat==null) m_outputFormat = new OutputProperties(); m_outputFormat.copyFrom(oformat); // copyFrom does not set properties that have been already set, so // this must be called after, which is a bit in the reverse from // what one might think. m_outputFormat.copyFrom(m_stylesheetRoot.getOutputProperties()); } 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. These * properties will override properties set in the templates * with xsl:output. * *Note that mutation of the Properties object returned will not * effect the properties that the transformation contains.
* * @return A copy of the set of output properties in effect * for the next transformation. * * NEEDSDOC ($objectName$) @return */ public Properties getOutputProperties() { return (Properties) getOutputFormat().getProperties().clone(); } /** * 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 */ public SerializationHandler createSerializationHandler(Result outputTarget) throws TransformerException { SerializationHandler xoh = createSerializationHandler(outputTarget, getOutputFormat()); return xoh; } /** * Create a ContentHandler from a Result object and an OutputProperties. * * @param outputTarget Where the transform result should go, * should not be null. * @param format The OutputProperties object that will contain * instructions on how to serialize the output. * * @return A valid ContentHandler that will create the * result tree when it is fed SAX events. * * @throws TransformerException */ public SerializationHandler createSerializationHandler( Result outputTarget, OutputProperties format) throws TransformerException { SerializationHandler xoh; // If the Result object contains a Node, then create // a ContentHandler that will add nodes to the input node. org.w3c.dom.Node outputNode = null; if (outputTarget instanceof DOMResult) { outputNode = ((DOMResult) outputTarget).getNode(); org.w3c.dom.Node nextSibling = ((DOMResult)outputTarget).getNextSibling(); org.w3c.dom.Document doc; short type; if (null != outputNode) { type = outputNode.getNodeType(); doc = (org.w3c.dom.Node.DOCUMENT_NODE == type) ? (org.w3c.dom.Document) outputNode : outputNode.getOwnerDocument(); } else { boolean isSecureProcessing = m_stylesheetRoot.isSecureProcessing(); doc = org.apache.xml.utils.DOMHelper.createDocument(isSecureProcessing); outputNode = doc; type = outputNode.getNodeType(); ((DOMResult) outputTarget).setNode(outputNode); } DOMBuilder handler = (org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE == type) ? new DOMBuilder(doc, (org.w3c.dom.DocumentFragment) outputNode) : new DOMBuilder(doc, outputNode); if (nextSibling != null) handler.setNextSibling(nextSibling); String encoding = format.getProperty(OutputKeys.ENCODING); xoh = new ToXMLSAXHandler(handler, (LexicalHandler)handler, encoding); } else if (outputTarget instanceof SAXResult) { ContentHandler handler = ((SAXResult) outputTarget).getHandler(); if (null == handler) throw new IllegalArgumentException( "handler can not be null for a SAXResult"); LexicalHandler lexHandler; if (handler instanceof LexicalHandler) lexHandler = (LexicalHandler) handler; else lexHandler = null; String encoding = format.getProperty(OutputKeys.ENCODING); String method = format.getProperty(OutputKeys.METHOD); ToXMLSAXHandler toXMLSAXHandler = new ToXMLSAXHandler(handler, lexHandler, encoding); toXMLSAXHandler.setShouldOutputNSAttr(false); xoh = toXMLSAXHandler; String publicID = format.getProperty(OutputKeys.DOCTYPE_PUBLIC); String systemID = format.getProperty(OutputKeys.DOCTYPE_SYSTEM); if (systemID != null) xoh.setDoctypeSystem(systemID); if (publicID != null) xoh.setDoctypePublic(publicID); if (handler instanceof TransformerClient) { XalanTransformState state = new XalanTransformState(); ((TransformerClient)handler).setTransformState(state); ((ToSAXHandler)xoh).setTransformState(state); } } // Otherwise, create a ContentHandler that will serialize the // result tree to either a stream or a writer. else if (outputTarget instanceof StreamResult) { StreamResult sresult = (StreamResult) outputTarget; try { SerializationHandler serializer = (SerializationHandler) SerializerFactory.getSerializer(format.getProperties()); 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); xoh = serializer; } else throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!"); // handler = serializer.asContentHandler(); // this.setSerializer(serializer); xoh = serializer; } // catch (UnsupportedEncodingException uee) // { // throw new TransformerException(uee); // } 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() //+ "!"); } // before we forget, lets make the created handler hold a reference // to the current TransformImpl object xoh.setTransformer(this); SourceLocator srcLocator = getStylesheet(); xoh.setSourceLocator(srcLocator); return xoh; } /** * Process the source tree to the output result. * @param xmlSource The input for the source tree. * @param outputTarget The output source target. * * @throws TransformerException */ public void transform(Source xmlSource, Result outputTarget) throws TransformerException { transform(xmlSource, outputTarget, true); } /** * Process the source tree to the output result. * @param xmlSource The input for the source tree. * @param outputTarget The output source target. * @param shouldRelease Flag indicating whether to release DTMManager. * * @throws TransformerException */ public void transform(Source xmlSource, Result outputTarget, boolean shouldRelease) throws TransformerException { synchronized (m_reentryGuard) { SerializationHandler xoh = createSerializationHandler(outputTarget); this.setSerializationHandler(xoh); m_outputTarget = outputTarget; transform(xmlSource, shouldRelease); } } /** * Process the source node to the output result, if the * processor supports the "http://xml.org/trax/features/dom/input" * feature. * %REVIEW% Do we need a Node version of this? * @param node The input source node, which can be any valid DTM node. * @param outputTarget The output source target. * * @throws TransformerException */ public void transformNode(int node, Result outputTarget) throws TransformerException { SerializationHandler xoh = createSerializationHandler(outputTarget); this.setSerializationHandler(xoh); m_outputTarget = outputTarget; transformNode(node); } /** * Process the source node to the output result, if the * processor supports the "http://xml.org/trax/features/dom/input" * feature. * %REVIEW% Do we need a Node version of this? * @param node The input source node, which can be any valid DTM node. * * @throws TransformerException */ public void transformNode(int node) throws TransformerException { //dml setExtensionsTable(getStylesheet()); // Make sure we're not writing to the same output content handler. synchronized (m_serializationHandler) { m_hasBeenReset = false; XPathContext xctxt = getXPathContext(); DTM dtm = xctxt.getDTM(node); try { pushGlobalVars(node); // ========== // Give the top-level templates a chance to pass information into // the context (this is mainly for setting up tables for extensions). StylesheetRoot stylesheet = this.getStylesheet(); int n = stylesheet.getGlobalImportCount(); for (int i = 0; i < n; i++) { StylesheetComposed imported = stylesheet.getGlobalImport(i); int includedCount = imported.getIncludeCountComposed(); for (int j = -1; j < includedCount; j++) { Stylesheet included = imported.getIncludeComposed(j); included.runtimeInit(this); for (ElemTemplateElement child = included.getFirstChildElem(); child != null; child = child.getNextSiblingElem()) { child.runtimeInit(this); } } } // =========== // System.out.println("Calling applyTemplateToNode - "+Thread.currentThread().getName()); DTMIterator dtmIter = new org.apache.xpath.axes.SelfIteratorNoPredicate(); dtmIter.setRoot(node, xctxt); xctxt.pushContextNodeList(dtmIter); try { this.applyTemplateToNode(null, null, node); } finally { xctxt.popContextNodeList(); } // m_stylesheetRoot.getStartRule().execute(this); // System.out.println("Done with applyTemplateToNode - "+Thread.currentThread().getName()); if (null != m_serializationHandler) { m_serializationHandler.endDocument(); } } catch (Exception se) { // System.out.println(Thread.currentThread().getName()+" threw an exception! " // +se.getMessage()); // If an exception was thrown, we need to make sure that any waiting // handlers can terminate, which I guess is best done by sending // an endDocument. // SAXSourceLocator while(se instanceof org.apache.xml.utils.WrappedRuntimeException) { Exception e = ((org.apache.xml.utils.WrappedRuntimeException)se).getException(); if(null != e) se = e; } if (null != m_serializationHandler) { try { if(se instanceof org.xml.sax.SAXParseException) m_serializationHandler.fatalError((org.xml.sax.SAXParseException)se); else if(se instanceof TransformerException) { TransformerException te = ((TransformerException)se); SAXSourceLocator sl = new SAXSourceLocator( te.getLocator() ); m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(te.getMessage(), sl, te)); } else { m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(se.getMessage(), new SAXSourceLocator(), se)); } } catch (Exception e){} } if(se instanceof TransformerException) { m_errorHandler.fatalError((TransformerException)se); } else if(se instanceof org.xml.sax.SAXParseException) { m_errorHandler.fatalError(new TransformerException(se.getMessage(), new SAXSourceLocator((org.xml.sax.SAXParseException)se), se)); } else { m_errorHandler.fatalError(new TransformerException(se)); } } finally { this.reset(); } } } /** * Get a SAX2 ContentHandler for the input. * * @return A valid ContentHandler, which should never be null, as * long as getFeature("http://xml.org/trax/features/sax/input") * returns true. */ public ContentHandler getInputContentHandler() { return getInputContentHandler(false); } /** * Get a SAX2 ContentHandler for the input. * * @param doDocFrag true if a DocumentFragment should be created as * the root, rather than a Document. * * @return A valid ContentHandler, which should never be null, as * long as getFeature("http://xml.org/trax/features/sax/input") * returns true. */ public ContentHandler getInputContentHandler(boolean doDocFrag) { if (null == m_inputContentHandler) { // if(null == m_urlOfSource && null != m_stylesheetRoot) // m_urlOfSource = m_stylesheetRoot.getBaseIdentifier(); m_inputContentHandler = new TransformerHandlerImpl(this, doDocFrag, m_urlOfSource); } return m_inputContentHandler; } /** * Set the output properties for the transformation. These * properties will override properties set in the templates * with xsl:output. * * @param oformat A valid OutputProperties object (which will * not be mutated), or null. */ public void setOutputFormat(OutputProperties oformat) { m_outputFormat = oformat; } /** * Get the output properties used for the transformation. * * @return the output format that was set by the user, * otherwise the output format from the stylesheet. */ public OutputProperties getOutputFormat() { // Get the output format that was set by the user, otherwise get the // output format from the stylesheet. OutputProperties format = (null == m_outputFormat) ? getStylesheet().getOutputComposed() : m_outputFormat; return format; } /** * Set a parameter for the templates. * * @param name The name of the parameter. * @param namespace The namespace of the parameter. * @param value The value object. This can be any valid Java object * -- it's up to the processor to provide the proper * coersion to the object, or simply pass it on for use * in extensions. */ public void setParameter(String name, String namespace, Object value) { VariableStack varstack = getXPathContext().getVarStack(); QName qname = new QName(namespace, name); XObject xobject = XObject.create(value, getXPathContext()); StylesheetRoot sroot = m_stylesheetRoot; Vector vars = sroot.getVariablesAndParamsComposed(); int i = vars.size(); while (--i >= 0) { ElemVariable variable = (ElemVariable)vars.elementAt(i); if(variable.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE && variable.getName().equals(qname)) { varstack.setGlobalVariable(i, xobject); } } } /** NEEDSDOC Field m_userParams */ Vector m_userParams; /** * Set a parameter for the transformation. * * @param name The name of the parameter, * which may have a namespace URI. * @param value The value object. This can be any valid Java object * -- it's up to the processor to provide the proper * coersion to the object, or simply pass it on for use * in extensions. */ public void setParameter(String name, Object value) { if (value == null) { throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name})); } StringTokenizer tokenizer = new StringTokenizer(name, "{}", false); try { // The first string might be the namespace, or it might be // the local name, if the namespace is null. String s1 = tokenizer.nextToken(); String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; if (null == m_userParams) m_userParams = new Vector(); if (null == s2) { replaceOrPushUserParam(new QName(s1), XObject.create(value, getXPathContext())); setParameter(s1, null, value); } else { replaceOrPushUserParam(new QName(s1, s2), XObject.create(value, getXPathContext())); setParameter(s2, s1, value); } } catch (java.util.NoSuchElementException nsee) { // Should throw some sort of an error. } } /** * NEEDSDOC Method replaceOrPushUserParam * * * NEEDSDOC @param qname * NEEDSDOC @param xval */ private void replaceOrPushUserParam(QName qname, XObject xval) { int n = m_userParams.size(); for (int i = n - 1; i >= 0; i--) { Arg arg = (Arg) m_userParams.elementAt(i); if (arg.getQName().equals(qname)) { m_userParams.setElementAt(new Arg(qname, xval, true), i); return; } } m_userParams.addElement(new Arg(qname, xval, true)); } /** * Get a parameter that was explicitly set with setParameter * or setParameters. * * * NEEDSDOC @param name * @return A parameter that has been set with setParameter * or setParameters, * *not* all the xsl:params on the stylesheet (which require * a transformation Source to be evaluated). */ public Object getParameter(String name) { try { // VariableStack varstack = getXPathContext().getVarStack(); // The first string might be the namespace, or it might be // the local name, if the namespace is null. QName qname = QName.getQNameFromString(name); if (null == m_userParams) return null; int n = m_userParams.size(); for (int i = n - 1; i >= 0; i--) { Arg arg = (Arg) m_userParams.elementAt(i); if (arg.getQName().equals(qname)) { return arg.getVal().object(); } } return null; } catch (java.util.NoSuchElementException nsee) { // Should throw some sort of an error. return null; } } /** * Reset parameters that the user specified for the transformation. * Called during transformer.reset() after we have cleared the * variable stack. We need to make sure that user params are * reset so that the transformer object can be reused. */ private void resetUserParameters() { try { if (null == m_userParams) return; int n = m_userParams.size(); for (int i = n - 1; i >= 0; i--) { Arg arg = (Arg) m_userParams.elementAt(i); QName name = arg.getQName(); // The first string might be the namespace, or it might be // the local name, if the namespace is null. String s1 = name.getNamespace(); String s2 = name.getLocalPart(); setParameter(s2, s1, arg.getVal().object()); } } catch (java.util.NoSuchElementException nsee) { // Should throw some sort of an error. } } /** * Set a bag of parameters for the transformation. Note that * these will not be additive, they will replace the existing * set of parameters. * * NEEDSDOC @param params */ public void setParameters(Properties params) { clearParameters(); Enumeration names = params.propertyNames(); while (names.hasMoreElements()) { String name = params.getProperty((String) names.nextElement()); StringTokenizer tokenizer = new StringTokenizer(name, "{}", false); try { // The first string might be the namespace, or it might be // the local name, if the namespace is null. String s1 = tokenizer.nextToken(); String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null; if (null == s2) setParameter(s1, null, params.getProperty(name)); else setParameter(s2, s1, params.getProperty(name)); } catch (java.util.NoSuchElementException nsee) { // Should throw some sort of an error. } } } /** * Reset the parameters to a null list. */ public void clearParameters() { synchronized (m_reentryGuard) { VariableStack varstack = new VariableStack(); m_xcontext.setVarStack(varstack); m_userParams = null; } } /** * Internal -- push the global variables from the Stylesheet onto * the context's runtime variable stack. *If we encounter a variable * that is already defined in the variable stack, we ignore it. This * is because the second variable definition will be at a lower import * precedence. Presumably, global"variables at the same import precedence * with the same name will have been caught during the recompose process. *
However, if we encounter a parameter that is already defined in the * variable stack, we need to see if this is a parameter whose value was * supplied by a setParameter call. If so, we need to "receive" the one * already in the stack, ignoring this one. If it is just an earlier * xsl:param or xsl:variable definition, we ignore it using the same * reasoning as explained above for the variable. * * @param contextNode The root of the source tree, can't be null. * * @throws TransformerException */ protected void pushGlobalVars(int contextNode) throws TransformerException { XPathContext xctxt = m_xcontext; VariableStack vs = xctxt.getVarStack(); StylesheetRoot sr = getStylesheet(); Vector vars = sr.getVariablesAndParamsComposed(); int i = vars.size(); vs.link(i); while (--i >= 0) { ElemVariable v = (ElemVariable) vars.elementAt(i); // XObject xobj = v.getValue(this, contextNode); XObject xobj = new XUnresolvedVariable(v, contextNode, this, vs.getStackFrame(), 0, true); if(null == vs.elementAt(i)) vs.setGlobalVariable(i, xobj); } } /** * Set an object that will be used to resolve URIs used in * document(), etc. * @param resolver An object that implements the URIResolver interface, * or null. */ public void setURIResolver(URIResolver resolver) { synchronized (m_reentryGuard) { m_xcontext.getSourceTreeManager().setURIResolver(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_xcontext.getSourceTreeManager().getURIResolver(); } // ======== End Transformer Implementation ======== /** * Set the content event handler. * * NEEDSDOC @param handler * @throws java.lang.NullPointerException If the handler * is null. * @see org.xml.sax.XMLReader#setContentHandler */ public void setContentHandler(ContentHandler handler) { if (handler == null) { throw new NullPointerException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_CONTENT_HANDLER, null)); //"Null content handler"); } else { m_outputContentHandler = handler; if (null == m_serializationHandler) { ToXMLSAXHandler h = new ToXMLSAXHandler(); h.setContentHandler(handler); h.setTransformer(this); m_serializationHandler = h; } else m_serializationHandler.setContentHandler(handler); } } /** * Get the content event handler. * * @return The current content handler, or null if none was set. * @see org.xml.sax.XMLReader#getContentHandler */ public ContentHandler getContentHandler() { return m_outputContentHandler; } /** * Given a stylesheet element, create a result tree fragment from it's * contents. The fragment will be built within the shared RTF DTM system * used as a variable stack. * @param templateParent The template element that holds the fragment. * @return the NodeHandle for the root node of the resulting RTF. * * @throws TransformerException * @xsl.usage advanced */ public int transformToRTF(ElemTemplateElement templateParent) throws TransformerException { // Retrieve a DTM to contain the RTF. At this writing, this may be a // multi-document DTM (SAX2RTFDTM). DTM dtmFrag = m_xcontext.getRTFDTM(); return transformToRTF(templateParent,dtmFrag); } /** * Given a stylesheet element, create a result tree fragment from it's * contents. The fragment will also use the shared DTM system, but will * obtain its space from the global variable pool rather than the dynamic * variable stack. This allows late binding of XUnresolvedVariables without * the risk that their content will be discarded when the variable stack * is popped. * * @param templateParent The template element that holds the fragment. * @return the NodeHandle for the root node of the resulting RTF. * * @throws TransformerException * @xsl.usage advanced */ public int transformToGlobalRTF(ElemTemplateElement templateParent) throws TransformerException { // Retrieve a DTM to contain the RTF. At this writing, this may be a // multi-document DTM (SAX2RTFDTM). DTM dtmFrag = m_xcontext.getGlobalRTFDTM(); return transformToRTF(templateParent,dtmFrag); } /** * Given a stylesheet element, create a result tree fragment from it's * contents. * @param templateParent The template element that holds the fragment. * @param dtmFrag The DTM to write the RTF into * @return the NodeHandle for the root node of the resulting RTF. * * @throws TransformerException * @xsl.usage advanced */ private int transformToRTF(ElemTemplateElement templateParent,DTM dtmFrag) throws TransformerException { XPathContext xctxt = m_xcontext; ContentHandler rtfHandler = dtmFrag.getContentHandler(); // Obtain the ResultTreeFrag's root node. // NOTE: In SAX2RTFDTM, this value isn't available until after // the startDocument has been issued, so assignment has been moved // down a bit in the code. int resultFragment; // not yet reliably = dtmFrag.getDocument(); // Save the current result tree handler. SerializationHandler savedRTreeHandler = this.m_serializationHandler; // And make a new handler for the RTF. ToSAXHandler h = new ToXMLSAXHandler(); h.setContentHandler(rtfHandler); h.setTransformer(this); // Replace the old handler (which was already saved) m_serializationHandler = h; // use local variable for the current handler SerializationHandler rth = m_serializationHandler; try { rth.startDocument(); // startDocument is "bottlenecked" in RTH. We need it acted upon immediately, // to set the DTM's state as in-progress, so that if the xsl:variable's body causes // further RTF activity we can keep that from bashing this DTM. rth.flushPending(); try { // Do the transformation of the child elements. executeChildTemplates(templateParent, true); // Make sure everything is flushed! rth.flushPending(); // Get the document ID. May not exist until the RTH has not only // received, but flushed, the startDocument, and may be invalid // again after the document has been closed (still debating that) // ... so waiting until just before the end seems simplest/safest. resultFragment = dtmFrag.getDocument(); } finally { rth.endDocument(); } } catch (org.xml.sax.SAXException se) { throw new TransformerException(se); } finally { // Restore the previous result tree handler. this.m_serializationHandler = savedRTreeHandler; } return resultFragment; } /** * Take the contents of a template element, process it, and * convert it to a string. * * @param elem The parent element whose children will be output * as a string. * * @return The stringized result of executing the elements children. * * @throws TransformerException * @xsl.usage advanced */ public String transformToString(ElemTemplateElement elem) throws TransformerException { ElemTemplateElement firstChild = elem.getFirstChildElem(); if(null == firstChild) return ""; if(elem.hasTextLitOnly() && m_optimizer) { return ((ElemTextLiteral)firstChild).getNodeValue(); } // Save the current result tree handler. SerializationHandler savedRTreeHandler = this.m_serializationHandler; // Create a Serializer object that will handle the SAX events // and build the ResultTreeFrag nodes. StringWriter sw = (StringWriter) m_stringWriterObjectPool.getInstance(); m_serializationHandler = (ToTextStream) m_textResultHandlerObjectPool.getInstance(); if (null == m_serializationHandler) { // if we didn't get one from the pool, go make a new one Serializer serializer = org.apache.xml.serializer.SerializerFactory.getSerializer( m_textformat.getProperties()); m_serializationHandler = (SerializationHandler) serializer; } m_serializationHandler.setTransformer(this); m_serializationHandler.setWriter(sw); String result; try { /* Don't call startDocument, the SerializationHandler will * generate its own internal startDocument call anyways */ // this.m_serializationHandler.startDocument(); // Do the transformation of the child elements. executeChildTemplates(elem, true); this.m_serializationHandler.endDocument(); result = sw.toString(); } catch (org.xml.sax.SAXException se) { throw new TransformerException(se); } finally { sw.getBuffer().setLength(0); try { sw.close(); } catch (Exception ioe){} m_stringWriterObjectPool.freeInstance(sw); m_serializationHandler.reset(); m_textResultHandlerObjectPool.freeInstance(m_serializationHandler); // Restore the previous result tree handler. m_serializationHandler = savedRTreeHandler; } return result; } /** * Given an element and mode, find the corresponding * template and process the contents. * * @param xslInstruction The calling element. * @param template The template to use if xsl:for-each, current template for apply-imports, or null. * @param child The source context node. * @throws TransformerException * @return true if applied a template, false if not. * @xsl.usage advanced */ public boolean applyTemplateToNode(ElemTemplateElement xslInstruction, // xsl:apply-templates or xsl:for-each ElemTemplate template, int child) throws TransformerException { DTM dtm = m_xcontext.getDTM(child); short nodeType = dtm.getNodeType(child); boolean isDefaultTextRule = false; boolean isApplyImports = false; isApplyImports = ((xslInstruction == null) ? false : xslInstruction.getXSLToken() == Constants.ELEMNAME_APPLY_IMPORTS); if (null == template || isApplyImports) { int maxImportLevel, endImportLevel=0; if (isApplyImports) { maxImportLevel = template.getStylesheetComposed().getImportCountComposed() - 1; endImportLevel = template.getStylesheetComposed().getEndImportCountComposed(); } else { maxImportLevel = -1; } // If we're trying an xsl:apply-imports at the top level (ie there are no // imported stylesheets), we need to indicate that there is no matching template. // The above logic will calculate a maxImportLevel of -1 which indicates // that we should find any template. This is because a value of -1 for // maxImportLevel has a special meaning. But we don't want that. // We want to match -no- templates. See bugzilla bug 1170. if (isApplyImports && (maxImportLevel == -1)) { template = null; } else { // Find the XSL template that is the best match for the // element. XPathContext xctxt = m_xcontext; try { xctxt.pushNamespaceContext(xslInstruction); QName mode = this.getMode(); if (isApplyImports) template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode, maxImportLevel, endImportLevel, m_quietConflictWarnings, dtm); else template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode, m_quietConflictWarnings, dtm); } finally { xctxt.popNamespaceContext(); } } // If that didn't locate a node, fall back to a default template rule. // See http://www.w3.org/TR/xslt#built-in-rule. if (null == template) { switch (nodeType) { case DTM.DOCUMENT_FRAGMENT_NODE : case DTM.ELEMENT_NODE : template = m_stylesheetRoot.getDefaultRule(); break; case DTM.CDATA_SECTION_NODE : case DTM.TEXT_NODE : case DTM.ATTRIBUTE_NODE : template = m_stylesheetRoot.getDefaultTextRule(); isDefaultTextRule = true; break; case DTM.DOCUMENT_NODE : template = m_stylesheetRoot.getDefaultRootRule(); break; default : // No default rules for processing instructions and the like. return false; } } } // If we are processing the default text rule, then just clone // the value directly to the result tree. try { pushElemTemplateElement(template); m_xcontext.pushCurrentNode(child); pushPairCurrentMatched(template, child); // Fix copy copy29 test. if (!isApplyImports) { DTMIterator cnl = new org.apache.xpath.NodeSetDTM(child, m_xcontext.getDTMManager()); m_xcontext.pushContextNodeList(cnl); } if (isDefaultTextRule) { switch (nodeType) { case DTM.CDATA_SECTION_NODE : case DTM.TEXT_NODE : ClonerToResultTree.cloneToResultTree(child, nodeType, dtm, getResultTreeHandler(), false); break; case DTM.ATTRIBUTE_NODE : dtm.dispatchCharactersEvents(child, getResultTreeHandler(), false); break; } } else { // And execute the child templates. // 9/11/00: If template has been compiled, hand off to it // since much (most? all?) of the processing has been inlined. // (It would be nice if there was a single entry point that // worked for both... but the interpretive system works by // having the Tranformer execute the children, while the // compiled obviously has to run its own code. It's // also unclear that "execute" is really the right name for // that entry point.) m_xcontext.setSAXLocator(template); // m_xcontext.getVarStack().link(); m_xcontext.getVarStack().link(template.m_frameSize); executeChildTemplates(template, true); } } catch (org.xml.sax.SAXException se) { throw new TransformerException(se); } finally { if (!isDefaultTextRule) m_xcontext.getVarStack().unlink(); m_xcontext.popCurrentNode(); if (!isApplyImports) { m_xcontext.popContextNodeList(); } popCurrentMatched(); popElemTemplateElement(); } return true; } /** * Execute each of the children of a template element. This method * is only for extension use. * * @param elem The ElemTemplateElement that contains the children * that should execute. * NEEDSDOC @param context * @param mode The current mode. * @param handler The ContentHandler to where the result events * should be fed. * * @throws TransformerException * @xsl.usage advanced */ public void executeChildTemplates( ElemTemplateElement elem, org.w3c.dom.Node context, QName mode, ContentHandler handler) throws TransformerException { XPathContext xctxt = m_xcontext; try { if(null != mode) pushMode(mode); xctxt.pushCurrentNode(xctxt.getDTMHandleFromNode(context)); executeChildTemplates(elem, handler); } finally { xctxt.popCurrentNode(); // I'm not sure where or why this was here. It is clearly in // error though, without a corresponding pushMode(). if (null != mode) popMode(); } } /** * Execute each of the children of a template element. * * @param elem The ElemTemplateElement that contains the children * that should execute. * @param shouldAddAttrs true if xsl:attributes should be executed. * * @throws TransformerException * @xsl.usage advanced */ public void executeChildTemplates( ElemTemplateElement elem, boolean shouldAddAttrs) throws TransformerException { // Does this element have any children? ElemTemplateElement t = elem.getFirstChildElem(); if (null == t) return; if(elem.hasTextLitOnly() && m_optimizer) { char[] chars = ((ElemTextLiteral)t).getChars(); try { // Have to push stuff on for tooling... this.pushElemTemplateElement(t); m_serializationHandler.characters(chars, 0, chars.length); } catch(SAXException se) { throw new TransformerException(se); } finally { this.popElemTemplateElement(); } return; } // // Check for infinite loops if we have to. // boolean check = (m_stackGuard.m_recursionLimit > -1); // // if (check) // getStackGuard().push(elem, xctxt.getCurrentNode()); XPathContext xctxt = m_xcontext; xctxt.pushSAXLocatorNull(); int currentTemplateElementsTop = m_currentTemplateElements.size(); m_currentTemplateElements.push(null); try { // Loop through the children of the template, calling execute on // each of them. for (; t != null; t = t.getNextSiblingElem()) { if (!shouldAddAttrs && t.getXSLToken() == Constants.ELEMNAME_ATTRIBUTE) continue; xctxt.setSAXLocator(t); m_currentTemplateElements.setElementAt(t,currentTemplateElementsTop); t.execute(this); } } catch(RuntimeException re) { TransformerException te = new TransformerException(re); te.setLocator(t); throw te; } finally { m_currentTemplateElements.pop(); xctxt.popSAXLocator(); } // Check for infinite loops if we have to // if (check) // getStackGuard().pop(); } /** * Execute each of the children of a template element. * * @param elem The ElemTemplateElement that contains the children * that should execute. * @param handler The ContentHandler to where the result events * should be fed. * * @throws TransformerException * @xsl.usage advanced */ public void executeChildTemplates( ElemTemplateElement elem, ContentHandler handler) throws TransformerException { SerializationHandler xoh = this.getSerializationHandler(); // These may well not be the same! In this case when calling // the Redirect extension, it has already set the ContentHandler // in the Transformer. SerializationHandler savedHandler = xoh; try { xoh.flushPending(); // %REVIEW% Make sure current node is being pushed. LexicalHandler lex = null; if (handler instanceof LexicalHandler) { lex = (LexicalHandler) handler; } m_serializationHandler = new ToXMLSAXHandler(handler, lex, savedHandler.getEncoding()); m_serializationHandler.setTransformer(this); executeChildTemplates(elem, true); } catch (TransformerException e) { throw e; } catch (SAXException se) { throw new TransformerException(se); } finally { m_serializationHandler = savedHandler; } } /** * Get the keys for the xsl:sort elements. * Note: Should this go into ElemForEach? * * @param foreach Valid ElemForEach element, not null. * @param sourceNodeContext The current node context in the source tree, * needed to evaluate the Attribute Value Templates. * * @return A Vector of NodeSortKeys, or null. * * @throws TransformerException * @xsl.usage advanced */ public Vector processSortKeys(ElemForEach foreach, int sourceNodeContext) throws TransformerException { Vector keys = null; XPathContext xctxt = m_xcontext; int nElems = foreach.getSortElemCount(); if (nElems > 0) keys = new Vector(); // March backwards, collecting the sort keys. for (int i = 0; i < nElems; i++) { ElemSort sort = foreach.getSortElem(i); String langString = (null != sort.getLang()) ? sort.getLang().evaluate(xctxt, sourceNodeContext, foreach) : null; String dataTypeString = sort.getDataType().evaluate(xctxt, sourceNodeContext, foreach); if (dataTypeString.indexOf(":") >= 0) System.out.println( "TODO: Need to write the hooks for QNAME sort data type"); else if (!(dataTypeString.equalsIgnoreCase(Constants.ATTRVAL_DATATYPE_TEXT)) &&!(dataTypeString.equalsIgnoreCase( Constants.ATTRVAL_DATATYPE_NUMBER))) foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE, new Object[]{ Constants.ATTRNAME_DATATYPE, dataTypeString }); boolean treatAsNumbers = ((null != dataTypeString) && dataTypeString.equals( Constants.ATTRVAL_DATATYPE_NUMBER)) ? true : false; String orderString = sort.getOrder().evaluate(xctxt, sourceNodeContext, foreach); if (!(orderString.equalsIgnoreCase(Constants.ATTRVAL_ORDER_ASCENDING)) &&!(orderString.equalsIgnoreCase( Constants.ATTRVAL_ORDER_DESCENDING))) foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE, new Object[]{ Constants.ATTRNAME_ORDER, orderString }); boolean descending = ((null != orderString) && orderString.equals( Constants.ATTRVAL_ORDER_DESCENDING)) ? true : false; AVT caseOrder = sort.getCaseOrder(); boolean caseOrderUpper; if (null != caseOrder) { String caseOrderString = caseOrder.evaluate(xctxt, sourceNodeContext, foreach); if (!(caseOrderString.equalsIgnoreCase(Constants.ATTRVAL_CASEORDER_UPPER)) &&!(caseOrderString.equalsIgnoreCase( Constants.ATTRVAL_CASEORDER_LOWER))) foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE, new Object[]{ Constants.ATTRNAME_CASEORDER, caseOrderString }); caseOrderUpper = ((null != caseOrderString) && caseOrderString.equals( Constants.ATTRVAL_CASEORDER_UPPER)) ? true : false; } else { caseOrderUpper = false; } keys.addElement(new NodeSortKey(this, sort.getSelect(), treatAsNumbers, descending, langString, caseOrderUpper, foreach)); } return keys; } //========================================================== // SECTION: TransformState implementation //========================================================== /** * Get the count of how many elements are * active. * @return The number of active elements on * the currentTemplateElements stack. */ public int getCurrentTemplateElementsCount() { return m_currentTemplateElements.size(); } /** * Get the count of how many elements are * active. * @return The number of active elements on * the currentTemplateElements stack. */ public ObjectStack getCurrentTemplateElements() { return m_currentTemplateElements; } /** * Push the current template element. * * @param elem The current ElemTemplateElement (may be null, and then * set via setCurrentElement). */ public void pushElemTemplateElement(ElemTemplateElement elem) { m_currentTemplateElements.push(elem); } /** * Pop the current template element. */ public void popElemTemplateElement() { m_currentTemplateElements.pop(); } /** * Set the top of the current template elements * stack. * * @param e The current ElemTemplateElement about to * be executed. */ public void setCurrentElement(ElemTemplateElement e) { m_currentTemplateElements.setTop(e); } /** * Retrieves the current ElemTemplateElement that is * being executed. * * @return The current ElemTemplateElement that is executing, * should not normally be null. */ public ElemTemplateElement getCurrentElement() { return (m_currentTemplateElements.size() > 0) ? (ElemTemplateElement) m_currentTemplateElements.peek() : null; } /** * This method retrieves the current context node * in the source tree. * * @return The current context node (should never be null?). */ public int getCurrentNode() { return m_xcontext.getCurrentNode(); } /** * This method retrieves the xsl:template * that is in effect, which may be a matched template * or a named template. * *
Please note that the ElemTemplate returned may * be a default template, and thus may not have a template * defined in the stylesheet.
* * @return The current xsl:template, should not be null. */ public ElemTemplate getCurrentTemplate() { ElemTemplateElement elem = getCurrentElement(); while ((null != elem) && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE)) { elem = elem.getParentElem(); } return (ElemTemplate) elem; } /** * Push both the current xsl:template or xsl:for-each onto the * stack, along with the child node that was matched. * (Note: should this only be used for xsl:templates?? -sb) * * @param template xsl:template or xsl:for-each. * @param child The child that was matched. */ public void pushPairCurrentMatched(ElemTemplateElement template, int child) { m_currentMatchTemplates.push(template); m_currentMatchedNodes.push(child); } /** * Pop the elements that were pushed via pushPairCurrentMatched. */ public void popCurrentMatched() { m_currentMatchTemplates.pop(); m_currentMatchedNodes.pop(); } /** * This method retrieves the xsl:template * that was matched. Note that this may not be * the same thing as the current template (which * may be from getCurrentElement()), since a named * template may be in effect. * * @return The pushed template that was pushed via pushPairCurrentMatched. */ public ElemTemplate getMatchedTemplate() { return (ElemTemplate) m_currentMatchTemplates.peek(); } /** * Retrieves the node in the source tree that matched * the template obtained via getMatchedTemplate(). * * @return The matched node that corresponds to the * match attribute of the current xsl:template. */ public int getMatchedNode() { return m_currentMatchedNodes.peepTail(); } /** * Get the current context node list. * * @return A reset clone of the context node list. */ public DTMIterator getContextNodeList() { try { DTMIterator cnl = m_xcontext.getContextNodeList(); return (cnl == null) ? null : (DTMIterator) cnl.cloneWithReset(); } catch (CloneNotSupportedException cnse) { // should never happen. return null; } } /** * Get the TrAX Transformer object in effect. * * @return This object. */ public Transformer getTransformer() { return this; } //========================================================== // SECTION: Accessor Functions //========================================================== /** * Set the stylesheet for this processor. If this is set, then the * process calls that take only the input .xml will use * this instead of looking for a stylesheet PI. Also, * setting the stylesheet is needed if you are going * to use the processor as a SAX ContentHandler. * * @param stylesheetRoot A non-null StylesheetRoot object, * or null if you wish to clear the stylesheet reference. */ public void setStylesheet(StylesheetRoot stylesheetRoot) { m_stylesheetRoot = stylesheetRoot; } /** * Get the current stylesheet for this processor. * * @return The stylesheet that is associated with this * transformer. */ public final StylesheetRoot getStylesheet() { return m_stylesheetRoot; } /** * Get quietConflictWarnings property. If the quietConflictWarnings * property is set to true, warnings about pattern conflicts won't be * printed to the diagnostics stream. * * @return True if this transformer should not report * template match conflicts. */ public boolean getQuietConflictWarnings() { return m_quietConflictWarnings; } /** * Set the execution context for XPath. * * @param xcontext A non-null reference to the XPathContext * associated with this transformer. * @xsl.usage internal */ public void setXPathContext(XPathContext xcontext) { m_xcontext = xcontext; } /** * Get the XPath context associated with this transformer. * * @return The XPathContext reference, never null. */ public final XPathContext getXPathContext() { return m_xcontext; } /** * Get the SerializationHandler object. * * @return The current SerializationHandler, which may not * be the main result tree manager. */ public SerializationHandler getResultTreeHandler() { return m_serializationHandler; } /** * Get the SerializationHandler object. * * @return The current SerializationHandler, which may not * be the main result tree manager. */ public SerializationHandler getSerializationHandler() { return m_serializationHandler; } /** * Get the KeyManager object. * * @return A reference to the KeyManager object, which should * never be null. */ public KeyManager getKeyManager() { return m_keyManager; } /** * Check to see if this is a recursive attribute definition. * * @param attrSet A non-null ElemAttributeSet reference. * * @return true if the attribute set is recursive. */ public boolean isRecursiveAttrSet(ElemAttributeSet attrSet) { if (null == m_attrSetStack) { m_attrSetStack = new Stack(); } if (!m_attrSetStack.empty()) { int loc = m_attrSetStack.search(attrSet); if (loc > -1) { return true; } } return false; } /** * Push an executing attribute set, so we can check for * recursive attribute definitions. * * @param attrSet A non-null ElemAttributeSet reference. */ public void pushElemAttributeSet(ElemAttributeSet attrSet) { m_attrSetStack.push(attrSet); } /** * Pop the current executing attribute set. */ public void popElemAttributeSet() { m_attrSetStack.pop(); } /** * Get the table of counters, for optimized xsl:number support. * * @return The CountersTable, never null. */ public CountersTable getCountersTable() { if (null == m_countersTable) m_countersTable = new CountersTable(); return m_countersTable; } /** * Tell if the current template rule is null, i.e. if we are * directly within an apply-templates. Used for xsl:apply-imports. * * @return True if the current template rule is null. */ public boolean currentTemplateRuleIsNull() { return ((!m_currentTemplateRuleIsNull.isEmpty()) && (m_currentTemplateRuleIsNull.peek() == true)); } /** * Push true if the current template rule is null, false * otherwise. * * @param b True if the we are executing an xsl:for-each * (or xsl:call-template?). */ public void pushCurrentTemplateRuleIsNull(boolean b) { m_currentTemplateRuleIsNull.push(b); } /** * Push true if the current template rule is null, false * otherwise. */ public void popCurrentTemplateRuleIsNull() { m_currentTemplateRuleIsNull.pop(); } /** * Push a funcion result for the currently active EXSLT *func:function
.
*
* @param val the result of executing an EXSLT
* func:result
instruction for the current
* func:function
.
*/
public void pushCurrentFuncResult(Object val) {
m_currentFuncResult.push(val);
}
/**
* Pops the result of the currently active EXSLT func:function
.
*
* @return the value of the func:function
*/
public Object popCurrentFuncResult() {
return m_currentFuncResult.pop();
}
/**
* Determines whether an EXSLT func:result
instruction has been
* executed for the currently active EXSLT func:function
.
*
* @return true
if and only if a func:result
* instruction has been executed
*/
public boolean currentFuncResultSeen() {
return !m_currentFuncResult.empty()
&& m_currentFuncResult.peek() != null;
}
/**
* Return the message manager.
*
* @return The message manager, never null.
*/
public MsgMgr getMsgMgr()
{
if (null == m_msgMgr)
m_msgMgr = new MsgMgr(this);
return m_msgMgr;
}
/**
* Set the error event listener.
*
* @param listener The new error listener.
* @throws IllegalArgumentException if
*/
public void setErrorListener(ErrorListener listener)
throws IllegalArgumentException
{
synchronized (m_reentryGuard)
{
if (listener == null)
throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler");
m_errorHandler = listener;
}
}
/**
* Get the current error event handler.
*
* @return The current error handler, which should never be null.
*/
public ErrorListener getErrorListener()
{
return m_errorHandler;
}
/**
* Look up the value of a feature.
*
* The feature name is any fully-qualified URI. It is * possible for an TransformerFactory to recognize a feature name but * to be unable to return its value; this is especially true * in the case of an adapter for a SAX1 Parser, which has * no way of knowing whether the underlying parser is * validating, for example.
* *DTM
. Normally, this function
* will be called by the implementation of DTM
;
* it is not normally called directly from
* user code.
*
* @param elementHandle int Handle of the element.
* @return one of NOTSTRIP, STRIP, or INHERIT.
*/
public short getShouldStripSpace(int elementHandle, DTM dtm)
{
try
{
org.apache.xalan.templates.WhiteSpaceInfo info =
m_stylesheetRoot.getWhiteSpaceInfo(m_xcontext, elementHandle, dtm);
if (null == info)
{
return DTMWSFilter.INHERIT;
}
else
{
// System.out.println("getShouldStripSpace: "+info.getShouldStripSpace());
return info.getShouldStripSpace()
? DTMWSFilter.STRIP : DTMWSFilter.NOTSTRIP;
}
}
catch (TransformerException se)
{
return DTMWSFilter.INHERIT;
}
}
/**
* Initializer method.
*
* @param transformer non-null transformer instance
* @param realHandler Content Handler instance
*/
public void init(ToXMLSAXHandler h,Transformer transformer, ContentHandler realHandler)
{
h.setTransformer(transformer);
h.setContentHandler(realHandler);
}
public void setSerializationHandler(SerializationHandler xoh)
{
m_serializationHandler = xoh;
}
/**
* Fire off characters, cdate events.
* @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, char[], int, int)
*/
public void fireGenerateEvent(
int eventType,
char[] ch,
int start,
int length) {
}
/**
* Fire off startElement, endElement events.
* @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, Attributes)
*/
public void fireGenerateEvent(
int eventType,
String name,
Attributes atts) {
}
/**
* Fire off processingInstruction events.
*/
public void fireGenerateEvent(int eventType, String name, String data) {
}
/**
* Fire off comment and entity ref events.
* @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String)
*/
public void fireGenerateEvent(int eventType, String data) {
}
/**
* Fire off startDocument, endDocument events.
* @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int)
*/
public void fireGenerateEvent(int eventType) {
}
/**
* @see org.apache.xml.serializer.SerializerTrace#hasTraceListeners()
*/
public boolean hasTraceListeners() {
return false;
}
/**
* @return Incremental flag
*/
public boolean getIncremental() {
return m_incremental;
}
/**
* @return Optimization flag
*/
public boolean getOptimize() {
return m_optimizer;
}
/**
* @return Source location flag
*/
public boolean getSource_location() {
return m_source_location;
}
} // end TransformerImpl class