0001: /*
0002: * Copyright 1999-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016: /*
0017: * $Id: TransformerImpl.java,v 1.167 2005/07/15 22:21:30 mcnamara Exp $
0018: */
0019: package org.apache.xalan.transformer;
0020:
0021: import java.io.IOException;
0022: import java.io.StringWriter;
0023: import java.util.Enumeration;
0024: import java.util.Properties;
0025: import java.util.Stack;
0026: import java.util.StringTokenizer;
0027: import java.util.Vector;
0028:
0029: import javax.xml.parsers.DocumentBuilder;
0030: import javax.xml.parsers.DocumentBuilderFactory;
0031: import javax.xml.parsers.ParserConfigurationException;
0032: import javax.xml.transform.ErrorListener;
0033: import javax.xml.transform.OutputKeys;
0034: import javax.xml.transform.Result;
0035: import javax.xml.transform.Source;
0036: import javax.xml.transform.SourceLocator;
0037: import javax.xml.transform.Transformer;
0038: import javax.xml.transform.TransformerException;
0039: import javax.xml.transform.URIResolver;
0040: import javax.xml.transform.dom.DOMResult;
0041: import javax.xml.transform.dom.DOMSource;
0042: import javax.xml.transform.sax.SAXResult;
0043: import javax.xml.transform.sax.SAXSource;
0044: import javax.xml.transform.stream.StreamResult;
0045: import javax.xml.transform.stream.StreamSource;
0046:
0047: import org.apache.xalan.extensions.ExtensionsTable;
0048: import org.apache.xalan.res.XSLMessages;
0049: import org.apache.xalan.res.XSLTErrorResources;
0050: import org.apache.xml.serializer.Method;
0051: import org.apache.xml.serializer.Serializer;
0052: import org.apache.xml.serializer.SerializerFactory;
0053: import org.apache.xalan.templates.AVT;
0054: import org.apache.xalan.templates.Constants;
0055: import org.apache.xalan.templates.ElemAttributeSet;
0056: import org.apache.xalan.templates.ElemForEach;
0057: import org.apache.xalan.templates.ElemSort;
0058: import org.apache.xalan.templates.ElemTemplate;
0059: import org.apache.xalan.templates.ElemTemplateElement;
0060: import org.apache.xalan.templates.ElemTextLiteral;
0061: import org.apache.xalan.templates.ElemVariable;
0062: import org.apache.xalan.templates.OutputProperties;
0063: import org.apache.xalan.templates.Stylesheet;
0064: import org.apache.xalan.templates.StylesheetComposed;
0065: import org.apache.xalan.templates.StylesheetRoot;
0066: import org.apache.xalan.templates.XUnresolvedVariable;
0067: import org.apache.xalan.trace.GenerateEvent;
0068: import org.apache.xalan.trace.TraceManager;
0069: import org.apache.xml.dtm.DTM;
0070: import org.apache.xml.dtm.DTMIterator;
0071: import org.apache.xml.dtm.DTMManager;
0072: import org.apache.xml.dtm.DTMWSFilter;
0073: import org.apache.xml.serializer.ToHTMLSAXHandler;
0074: import org.apache.xml.serializer.ToSAXHandler;
0075: import org.apache.xml.serializer.ToTextSAXHandler;
0076: import org.apache.xml.serializer.ToTextStream;
0077: import org.apache.xml.serializer.ToXMLSAXHandler;
0078: import org.apache.xml.serializer.SerializationHandler;
0079: import org.apache.xml.utils.BoolStack;
0080: import org.apache.xml.utils.DOMBuilder;
0081: import org.apache.xml.utils.NodeVector;
0082: import org.apache.xml.utils.ObjectPool;
0083: import org.apache.xml.utils.ObjectStack;
0084: import org.apache.xml.utils.QName;
0085: import org.apache.xml.utils.SAXSourceLocator;
0086: import org.apache.xml.utils.ThreadControllerWrapper;
0087: import org.apache.xpath.Arg;
0088: import org.apache.xpath.ExtensionsProvider;
0089: import org.apache.xpath.VariableStack;
0090: import org.apache.xpath.XPathContext;
0091: import org.apache.xpath.functions.FuncExtFunction;
0092: import org.apache.xpath.objects.XObject;
0093: import org.xml.sax.Attributes;
0094: import org.xml.sax.ContentHandler;
0095: import org.xml.sax.SAXException;
0096: import org.xml.sax.SAXNotRecognizedException;
0097: import org.xml.sax.SAXNotSupportedException;
0098: import org.xml.sax.ext.DeclHandler;
0099: import org.xml.sax.ext.LexicalHandler;
0100:
0101: /**
0102: * This class implements the
0103: * {@link javax.xml.transform.Transformer} interface, and is the core
0104: * representation of the transformation execution.</p>
0105: * @xsl.usage advanced
0106: */
0107: public class TransformerImpl extends Transformer implements Runnable,
0108: DTMWSFilter, ExtensionsProvider,
0109: org.apache.xml.serializer.SerializerTrace {
0110:
0111: // Synch object to gaurd against setting values from the TrAX interface
0112: // or reentry while the transform is going on.
0113:
0114: /** NEEDSDOC Field m_reentryGuard */
0115: private Boolean m_reentryGuard = new Boolean(true);
0116:
0117: /**
0118: * This is null unless we own the stream.
0119: */
0120: private java.io.FileOutputStream m_outputStream = null;
0121:
0122: /**
0123: * True if the parser events should be on the main thread,
0124: * false if not. Experemental. Can not be set right now.
0125: */
0126: private boolean m_parserEventsOnMain = true;
0127:
0128: /** The thread that the transformer is running on. */
0129: private Thread m_transformThread;
0130:
0131: /** The base URL of the source tree. */
0132: private String m_urlOfSource = null;
0133:
0134: /** The Result object at the start of the transform, if any. */
0135: private Result m_outputTarget = null;
0136:
0137: /**
0138: * The output format object set by the user. May be null.
0139: */
0140: private OutputProperties m_outputFormat;
0141:
0142: /**
0143: * The content handler for the source input tree.
0144: */
0145: ContentHandler m_inputContentHandler;
0146:
0147: /**
0148: * The content handler for the result tree.
0149: */
0150: private ContentHandler m_outputContentHandler = null;
0151:
0152: // /*
0153: // * Use member variable to store param variables as they're
0154: // * being created, use member variable so we don't
0155: // * have to create a new vector every time.
0156: // */
0157: // private Vector m_newVars = new Vector();
0158:
0159: /** The JAXP Document Builder, mainly to create Result Tree Fragments. */
0160: DocumentBuilder m_docBuilder = null;
0161:
0162: /**
0163: * A pool of ResultTreeHandlers, for serialization of a subtree to text.
0164: * Please note that each of these also holds onto a Text Serializer.
0165: */
0166: private ObjectPool m_textResultHandlerObjectPool = new ObjectPool(
0167: ToTextStream.class);
0168:
0169: /**
0170: * Related to m_textResultHandlerObjectPool, this is a pool of
0171: * StringWriters, which are passed to the Text Serializers.
0172: * (I'm not sure if this is really needed any more. -sb)
0173: */
0174: private ObjectPool m_stringWriterObjectPool = new ObjectPool(
0175: StringWriter.class);
0176:
0177: /**
0178: * A static text format object, which can be used over and
0179: * over to create the text serializers.
0180: */
0181: private OutputProperties m_textformat = new OutputProperties(
0182: Method.TEXT);
0183:
0184: // Commenteded out in response to problem reported by
0185: // Nicola Brown <Nicola.Brown@jacobsrimell.com>
0186: // /**
0187: // * Flag to let us know if an exception should be reported inside the
0188: // * postExceptionFromThread method. This is needed if the transform is
0189: // * being generated from SAX events, and thus there is no central place
0190: // * to report the exception from. (An exception is usually picked up in
0191: // * the main thread from the transform thread in {@link #transform(Source source)}
0192: // * from {@link #getExceptionThrown()}. )
0193: // */
0194: // private boolean m_reportInPostExceptionFromThread = false;
0195:
0196: /**
0197: * A node vector used as a stack to track the current
0198: * ElemTemplateElement. Needed for the
0199: * org.apache.xalan.transformer.TransformState interface,
0200: * so a tool can discover the calling template. Note the use of an array
0201: * for this limits the recursion depth to 4K.
0202: */
0203: ObjectStack m_currentTemplateElements = new ObjectStack(
0204: XPathContext.RECURSIONLIMIT);
0205:
0206: /** The top of the currentTemplateElements stack. */
0207: //int m_currentTemplateElementsTop = 0;
0208: /**
0209: * A node vector used as a stack to track the current
0210: * ElemTemplate that was matched.
0211: * Needed for the
0212: * org.apache.xalan.transformer.TransformState interface,
0213: * so a tool can discover the matched template
0214: */
0215: Stack m_currentMatchTemplates = new Stack();
0216:
0217: /**
0218: * A node vector used as a stack to track the current
0219: * node that was matched.
0220: * Needed for the
0221: * org.apache.xalan.transformer.TransformState interface,
0222: * so a tool can discover the matched
0223: * node.
0224: */
0225: NodeVector m_currentMatchedNodes = new NodeVector();
0226:
0227: /**
0228: * The root of a linked set of stylesheets.
0229: */
0230: private StylesheetRoot m_stylesheetRoot = null;
0231:
0232: /**
0233: * If this is set to true, do not warn about pattern
0234: * match conflicts.
0235: */
0236: private boolean m_quietConflictWarnings = true;
0237:
0238: /**
0239: * The liason to the XML parser, so the XSL processor
0240: * can handle included files, and the like, and do the
0241: * initial parse of the XSL document.
0242: */
0243: private XPathContext m_xcontext;
0244:
0245: /**
0246: * Object to guard agains infinite recursion when
0247: * doing queries.
0248: */
0249: private StackGuard m_stackGuard;
0250:
0251: /**
0252: * Output handler to bottleneck SAX events.
0253: */
0254: private SerializationHandler m_serializationHandler;
0255:
0256: /** The key manager, which manages xsl:keys. */
0257: private KeyManager m_keyManager = new KeyManager();
0258:
0259: /**
0260: * Stack for the purposes of flagging infinite recursion with
0261: * attribute sets.
0262: */
0263: Stack m_attrSetStack = null;
0264:
0265: /**
0266: * The table of counters for xsl:number support.
0267: * @see ElemNumber
0268: */
0269: CountersTable m_countersTable = null;
0270:
0271: /**
0272: * Is > 0 when we're processing a for-each.
0273: */
0274: BoolStack m_currentTemplateRuleIsNull = new BoolStack();
0275:
0276: /**
0277: * Keeps track of the result delivered by any EXSLT <code>func:result</code>
0278: * instruction that has been executed for the currently active EXSLT
0279: * <code>func:function</code>
0280: */
0281: ObjectStack m_currentFuncResult = new ObjectStack();
0282:
0283: /**
0284: * The message manager, which manages error messages, warning
0285: * messages, and other types of message events.
0286: */
0287: private MsgMgr m_msgMgr;
0288:
0289: /**
0290: * The flag for the setting of the optimize feature;
0291: * This flag should have the same value as the FEATURE_OPTIMIZE feature
0292: * which is set by the TransformerFactory.setAttribut() method before a
0293: * Transformer is created
0294: */
0295: private boolean m_optimizer = true;
0296:
0297: /**
0298: * The flag for the setting of the incremental feature;
0299: * This flag should have the same value as the FEATURE_INCREMENTAL feature
0300: * which is set by the TransformerFactory.setAttribut() method before a
0301: * Transformer is created
0302: */
0303: private boolean m_incremental = false;
0304:
0305: /**
0306: * The flag for the setting of the source_location feature;
0307: * This flag should have the same value as the FEATURE_SOURCE_LOCATION feature
0308: * which is set by the TransformerFactory.setAttribut() method before a
0309: * Transformer is created
0310: */
0311: private boolean m_source_location = false;
0312:
0313: /**
0314: * This is a compile-time flag to turn off calling
0315: * of trace listeners. Set this to false for optimization purposes.
0316: */
0317: private boolean m_debug = false;
0318:
0319: /**
0320: * The SAX error handler, where errors and warnings are sent.
0321: */
0322: private ErrorListener m_errorHandler = new org.apache.xml.utils.DefaultErrorHandler(
0323: false);
0324:
0325: /**
0326: * The trace manager.
0327: */
0328: private TraceManager m_traceManager = new TraceManager(this );
0329:
0330: /**
0331: * If the transform thread throws an exception, the exception needs to
0332: * be stashed away so that the main thread can pass it on to the
0333: * client.
0334: */
0335: private Exception m_exceptionThrown = null;
0336:
0337: /**
0338: * The InputSource for the source tree, which is needed if the
0339: * parse thread is not the main thread, in order for the parse
0340: * thread's run method to get to the input source.
0341: * (Delete this if reversing threads is outlawed. -sb)
0342: */
0343: private Source m_xmlSource;
0344:
0345: /**
0346: * This is needed for support of setSourceTreeDocForThread(Node doc),
0347: * which must be called in order for the transform thread's run
0348: * method to obtain the root of the source tree to be transformed.
0349: */
0350: private int m_doc;
0351:
0352: /**
0353: * If the the transform is on the secondary thread, we
0354: * need to know when it is done, so we can return.
0355: */
0356: private boolean m_isTransformDone = false;
0357:
0358: /** Flag to to tell if the tranformer needs to be reset. */
0359: private boolean m_hasBeenReset = false;
0360:
0361: /** NEEDSDOC Field m_shouldReset */
0362: private boolean m_shouldReset = true;
0363:
0364: /**
0365: * NEEDSDOC Method setShouldReset
0366: *
0367: *
0368: * NEEDSDOC @param shouldReset
0369: */
0370: public void setShouldReset(boolean shouldReset) {
0371: m_shouldReset = shouldReset;
0372: }
0373:
0374: /**
0375: * A stack of current template modes.
0376: */
0377: private Stack m_modes = new Stack();
0378:
0379: //==========================================================
0380: // SECTION: Constructor
0381: //==========================================================
0382:
0383: /**
0384: * Construct a TransformerImpl.
0385: *
0386: * @param stylesheet The root of the stylesheet tree.
0387: */
0388: public TransformerImpl(StylesheetRoot stylesheet)
0389: // throws javax.xml.transform.TransformerException
0390: {
0391: m_optimizer = stylesheet.getOptimizer();
0392: m_incremental = stylesheet.getIncremental();
0393: m_source_location = stylesheet.getSource_location();
0394: setStylesheet(stylesheet);
0395: XPathContext xPath = new XPathContext(this );
0396: xPath.setIncremental(m_incremental);
0397: xPath.getDTMManager().setIncremental(m_incremental);
0398: xPath.setSource_location(m_source_location);
0399: xPath.getDTMManager().setSource_location(m_source_location);
0400:
0401: if (stylesheet.isSecureProcessing())
0402: xPath.setSecureProcessing(true);
0403:
0404: setXPathContext(xPath);
0405: getXPathContext().setNamespaceContext(stylesheet);
0406: m_stackGuard = new StackGuard(this );
0407: }
0408:
0409: // ================ ExtensionsTable ===================
0410:
0411: /**
0412: * The table of ExtensionHandlers.
0413: */
0414: private ExtensionsTable m_extensionsTable = null;
0415:
0416: /**
0417: * Get the extensions table object.
0418: *
0419: * @return The extensions table.
0420: */
0421: public ExtensionsTable getExtensionsTable() {
0422: return m_extensionsTable;
0423: }
0424:
0425: /**
0426: * If the stylesheet contains extensions, set the extensions table object.
0427: *
0428: *
0429: * @param sroot The stylesheet.
0430: * @throws javax.xml.transform.TransformerException
0431: */
0432: void setExtensionsTable(StylesheetRoot sroot)
0433: throws javax.xml.transform.TransformerException {
0434: try {
0435: if (sroot.getExtensions() != null)
0436: m_extensionsTable = new ExtensionsTable(sroot);
0437: } catch (javax.xml.transform.TransformerException te) {
0438: te.printStackTrace();
0439: }
0440: }
0441:
0442: //== Implementation of the XPath ExtensionsProvider interface.
0443:
0444: public boolean functionAvailable(String ns, String funcName)
0445: throws javax.xml.transform.TransformerException {
0446: return getExtensionsTable().functionAvailable(ns, funcName);
0447: }
0448:
0449: public boolean elementAvailable(String ns, String elemName)
0450: throws javax.xml.transform.TransformerException {
0451: return getExtensionsTable().elementAvailable(ns, elemName);
0452: }
0453:
0454: public Object extFunction(String ns, String funcName,
0455: Vector argVec, Object methodKey)
0456: throws javax.xml.transform.TransformerException {//System.out.println("TransImpl.extFunction() " + ns + " " + funcName +" " + getExtensionsTable());
0457: return getExtensionsTable().extFunction(ns, funcName, argVec,
0458: methodKey, getXPathContext().getExpressionContext());
0459: }
0460:
0461: public Object extFunction(FuncExtFunction extFunction, Vector argVec)
0462: throws javax.xml.transform.TransformerException {
0463: return getExtensionsTable().extFunction(extFunction, argVec,
0464: getXPathContext().getExpressionContext());
0465: }
0466:
0467: //=========================
0468:
0469: /**
0470: * Reset the state. This needs to be called after a process() call
0471: * is invoked, if the processor is to be used again.
0472: */
0473: public void reset() {
0474:
0475: if (!m_hasBeenReset && m_shouldReset) {
0476: m_hasBeenReset = true;
0477:
0478: if (this .m_outputStream != null) {
0479: try {
0480: m_outputStream.close();
0481: } catch (java.io.IOException ioe) {
0482: }
0483: }
0484:
0485: m_outputStream = null;
0486:
0487: // I need to look more carefully at which of these really
0488: // needs to be reset.
0489: m_countersTable = null;
0490:
0491: m_xcontext.reset();
0492:
0493: m_xcontext.getVarStack().reset();
0494: resetUserParameters();
0495:
0496: m_currentTemplateElements.removeAllElements();
0497: m_currentMatchTemplates.removeAllElements();
0498: m_currentMatchedNodes.removeAllElements();
0499:
0500: m_serializationHandler = null;
0501: m_outputTarget = null;
0502: m_keyManager = new KeyManager();
0503: m_attrSetStack = null;
0504: m_countersTable = null;
0505: m_currentTemplateRuleIsNull = new BoolStack();
0506: m_xmlSource = null;
0507: m_doc = DTM.NULL;
0508: m_isTransformDone = false;
0509: m_transformThread = null;
0510:
0511: // m_inputContentHandler = null;
0512: // For now, reset the document cache each time.
0513: m_xcontext.getSourceTreeManager().reset();
0514: }
0515:
0516: // m_reportInPostExceptionFromThread = false;
0517: }
0518:
0519: /**
0520: * <code>getProperty</code> returns the current setting of the
0521: * property described by the <code>property</code> argument.
0522: *
0523: * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory?
0524: *
0525: * @param property a <code>String</code> value
0526: * @return a <code>boolean</code> value
0527: */
0528: public boolean getProperty(String property) {
0529: return false;
0530: }
0531:
0532: /**
0533: * Set a runtime property for this <code>TransformerImpl</code>.
0534: *
0535: * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory?
0536: *
0537: * @param property a <code>String</code> value
0538: * @param value an <code>Object</code> value
0539: */
0540: public void setProperty(String property, Object value) {
0541: }
0542:
0543: // ========= Transformer Interface Implementation ==========
0544:
0545: /**
0546: * Get true if the parser events should be on the main thread,
0547: * false if not. Experimental. Can not be set right now.
0548: *
0549: * @return true if the parser events should be on the main thread,
0550: * false if not.
0551: * @xsl.usage experimental
0552: */
0553: public boolean isParserEventsOnMain() {
0554: return m_parserEventsOnMain;
0555: }
0556:
0557: /**
0558: * Get the thread that the transform process is on.
0559: *
0560: * @return The thread that the transform process is on, or null.
0561: * @xsl.usage internal
0562: */
0563: public Thread getTransformThread() {
0564: return m_transformThread;
0565: }
0566:
0567: /**
0568: * Get the thread that the transform process is on.
0569: *
0570: * @param t The transform thread, may be null.
0571: * @xsl.usage internal
0572: */
0573: public void setTransformThread(Thread t) {
0574: m_transformThread = t;
0575: }
0576:
0577: /** NEEDSDOC Field m_hasTransformThreadErrorCatcher */
0578: private boolean m_hasTransformThreadErrorCatcher = false;
0579:
0580: /**
0581: * Return true if the transform was initiated from the transform method,
0582: * otherwise it was probably done from a pure parse events.
0583: *
0584: * NEEDSDOC ($objectName$) @return
0585: */
0586: public boolean hasTransformThreadErrorCatcher() {
0587: return m_hasTransformThreadErrorCatcher;
0588: }
0589:
0590: /**
0591: * Process the source tree to SAX parse events.
0592: * @param source The input for the source tree.
0593: *
0594: * @throws TransformerException
0595: */
0596: public void transform(Source source) throws TransformerException {
0597: transform(source, true);
0598: }
0599:
0600: /**
0601: * Process the source tree to SAX parse events.
0602: * @param source The input for the source tree.
0603: * @param shouldRelease Flag indicating whether to release DTMManager.
0604: *
0605: * @throws TransformerException
0606: */
0607: public void transform(Source source, boolean shouldRelease)
0608: throws TransformerException {
0609:
0610: try {
0611:
0612: // Patch for bugzilla #13863. If we don't reset the namespaceContext
0613: // then we will get a NullPointerException if transformer is reused
0614: // (for stylesheets that use xsl:key). Not sure if this should go
0615: // here or in reset(). -is
0616: if (getXPathContext().getNamespaceContext() == null) {
0617: getXPathContext().setNamespaceContext(getStylesheet());
0618: }
0619: String base = source.getSystemId();
0620:
0621: // If no systemID of the source, use the base of the stylesheet.
0622: if (null == base) {
0623: base = m_stylesheetRoot.getBaseIdentifier();
0624: }
0625:
0626: // As a last resort, use the current user dir.
0627: if (null == base) {
0628: String currentDir = "";
0629: try {
0630: currentDir = System.getProperty("user.dir");
0631: } catch (SecurityException se) {
0632: }// user.dir not accessible from applet
0633:
0634: if (currentDir.startsWith(java.io.File.separator))
0635: base = "file://" + currentDir;
0636: else
0637: base = "file:///" + currentDir;
0638:
0639: base = base + java.io.File.separatorChar
0640: + source.getClass().getName();
0641: }
0642: setBaseURLOfSource(base);
0643: DTMManager mgr = m_xcontext.getDTMManager();
0644: /*
0645: * According to JAXP1.2, new SAXSource()/StreamSource()
0646: * should create an empty input tree, with a default root node.
0647: * new DOMSource()creates an empty document using DocumentBuilder.
0648: * newDocument(); Use DocumentBuilder.newDocument() for all 3 situations,
0649: * since there is no clear spec. how to create an empty tree when
0650: * both SAXSource() and StreamSource() are used.
0651: */
0652: if ((source instanceof StreamSource
0653: && source.getSystemId() == null
0654: && ((StreamSource) source).getInputStream() == null && ((StreamSource) source)
0655: .getReader() == null)
0656: || (source instanceof SAXSource
0657: && ((SAXSource) source).getInputSource() == null && ((SAXSource) source)
0658: .getXMLReader() == null)
0659: || (source instanceof DOMSource && ((DOMSource) source)
0660: .getNode() == null)) {
0661: try {
0662: DocumentBuilderFactory builderF = DocumentBuilderFactory
0663: .newInstance();
0664: DocumentBuilder builder = builderF
0665: .newDocumentBuilder();
0666: String systemID = source.getSystemId();
0667: source = new DOMSource(builder.newDocument());
0668:
0669: // Copy system ID from original, empty Source to new Source
0670: if (systemID != null) {
0671: source.setSystemId(systemID);
0672: }
0673: } catch (ParserConfigurationException e) {
0674: fatalError(e);
0675: }
0676: }
0677: DTM dtm = mgr.getDTM(source, false, this , true, true);
0678: dtm.setDocumentBaseURI(base);
0679:
0680: boolean hardDelete = true; // %REVIEW% I have to think about this. -sb
0681:
0682: try {
0683: // NOTE: This will work because this is _NOT_ a shared DTM, and thus has
0684: // only a single Document node. If it could ever be an RTF or other
0685: // shared DTM, look at dtm.getDocumentRoot(nodeHandle).
0686: this .transformNode(dtm.getDocument());
0687: } finally {
0688: if (shouldRelease)
0689: mgr.release(dtm, hardDelete);
0690: }
0691:
0692: // Kick off the parse. When the ContentHandler gets
0693: // the startDocument event, it will call transformNode( node ).
0694: // reader.parse( xmlSource );
0695: // This has to be done to catch exceptions thrown from
0696: // the transform thread spawned by the STree handler.
0697: Exception e = getExceptionThrown();
0698:
0699: if (null != e) {
0700: if (e instanceof javax.xml.transform.TransformerException) {
0701: throw (javax.xml.transform.TransformerException) e;
0702: } else if (e instanceof org.apache.xml.utils.WrappedRuntimeException) {
0703: fatalError(((org.apache.xml.utils.WrappedRuntimeException) e)
0704: .getException());
0705: } else {
0706: throw new javax.xml.transform.TransformerException(
0707: e);
0708: }
0709: } else if (null != m_serializationHandler) {
0710: m_serializationHandler.endDocument();
0711: }
0712: } catch (org.apache.xml.utils.WrappedRuntimeException wre) {
0713: Throwable throwable = wre.getException();
0714:
0715: while (throwable instanceof org.apache.xml.utils.WrappedRuntimeException) {
0716: throwable = ((org.apache.xml.utils.WrappedRuntimeException) throwable)
0717: .getException();
0718: }
0719:
0720: fatalError(throwable);
0721: }
0722:
0723: // Patch attributed to David Eisenberg <david@catcode.com>
0724: catch (org.xml.sax.SAXParseException spe) {
0725: fatalError(spe);
0726: } catch (org.xml.sax.SAXException se) {
0727: m_errorHandler.fatalError(new TransformerException(se));
0728: } finally {
0729: m_hasTransformThreadErrorCatcher = false;
0730:
0731: // This looks to be redundent to the one done in TransformNode.
0732: reset();
0733: }
0734: }
0735:
0736: private void fatalError(Throwable throwable)
0737: throws TransformerException {
0738: if (throwable instanceof org.xml.sax.SAXParseException)
0739: m_errorHandler
0740: .fatalError(new TransformerException(throwable
0741: .getMessage(), new SAXSourceLocator(
0742: (org.xml.sax.SAXParseException) throwable)));
0743: else
0744: m_errorHandler.fatalError(new TransformerException(
0745: throwable));
0746:
0747: }
0748:
0749: /**
0750: * Get the base URL of the source.
0751: *
0752: * @return The base URL of the source tree, or null.
0753: */
0754: public String getBaseURLOfSource() {
0755: return m_urlOfSource;
0756: }
0757:
0758: /**
0759: * Get the base URL of the source.
0760: *
0761: *
0762: * NEEDSDOC @param base
0763: * @return The base URL of the source tree, or null.
0764: */
0765: public void setBaseURLOfSource(String base) {
0766: m_urlOfSource = base;
0767: }
0768:
0769: /**
0770: * Get the original output target.
0771: *
0772: * @return The Result object used to kick of the transform or null.
0773: */
0774: public Result getOutputTarget() {
0775: return m_outputTarget;
0776: }
0777:
0778: /**
0779: * Set the original output target. This is useful when using a SAX transform and
0780: * supplying a ContentHandler or when the URI of the output target should
0781: * not be the same as the systemID of the original output target.
0782: *
0783: *
0784: * NEEDSDOC @param outputTarget
0785: */
0786: public void setOutputTarget(Result outputTarget) {
0787: m_outputTarget = outputTarget;
0788: }
0789:
0790: /**
0791: * Get an output property that is in effect for the
0792: * transformation. The property specified may be a property
0793: * that was set with setOutputProperty, or it may be a
0794: * property specified in the stylesheet.
0795: *
0796: * NEEDSDOC @param qnameString
0797: *
0798: * @return The string value of the output property, or null
0799: * if no property was found.
0800: *
0801: * @throws IllegalArgumentException If the property is not supported.
0802: *
0803: * @see javax.xml.transform.OutputKeys
0804: */
0805: public String getOutputProperty(String qnameString)
0806: throws IllegalArgumentException {
0807:
0808: String value = null;
0809: OutputProperties props = getOutputFormat();
0810:
0811: value = props.getProperty(qnameString);
0812:
0813: if (null == value) {
0814: if (!OutputProperties.isLegalPropertyKey(qnameString))
0815: throw new IllegalArgumentException(
0816: XSLMessages
0817: .createMessage(
0818: XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED,
0819: new Object[] { qnameString })); //"output property not recognized: "
0820: //+ qnameString);
0821: }
0822:
0823: return value;
0824: }
0825:
0826: /**
0827: * Get the value of a property, without using the default properties. This
0828: * can be used to test if a property has been explicitly set by the stylesheet
0829: * or user.
0830: *
0831: * NEEDSDOC @param qnameString
0832: *
0833: * @return The value of the property, or null if not found.
0834: *
0835: * @throws IllegalArgumentException If the property is not supported,
0836: * and is not namespaced.
0837: */
0838: public String getOutputPropertyNoDefault(String qnameString)
0839: throws IllegalArgumentException {
0840:
0841: String value = null;
0842: OutputProperties props = getOutputFormat();
0843:
0844: value = (String) props.getProperties().get(qnameString);
0845:
0846: if (null == value) {
0847: if (!OutputProperties.isLegalPropertyKey(qnameString))
0848: throw new IllegalArgumentException(
0849: XSLMessages
0850: .createMessage(
0851: XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED,
0852: new Object[] { qnameString })); //"output property not recognized: "
0853: // + qnameString);
0854: }
0855:
0856: return value;
0857: }
0858:
0859: /**
0860: * This method is used to set or override the value
0861: * of the effective xsl:output attribute values
0862: * specified in the stylesheet.
0863: * <p>
0864: * The recognized standard output properties are:
0865: * <ul>
0866: * <li>cdata-section-elements
0867: * <li>doctype-system
0868: * <li>doctype-public
0869: * <li>indent
0870: * <li>media-type
0871: * <li>method
0872: * <li>omit-xml-declaration
0873: * <li>standalone
0874: * <li>version
0875: * </ul>
0876: * <p>
0877: * For example:
0878: * <pre>
0879: * tran.setOutputProperty("standalone", "yes");
0880: * </pre>
0881: * <p>
0882: * In the case of the cdata-section-elements property,
0883: * the value should be a whitespace separated list of
0884: * element names. The element name is the local name
0885: * of the element, if it is in no namespace, or, the URI
0886: * in braces followed immediately by the local name
0887: * if the element is in that namespace. For example:
0888: * <pre>
0889: * tran.setOutputProperty(
0890: * "cdata-section-elements",
0891: * "elem1 {http://example.uri}elem2 elem3");
0892: * </pre>
0893: * <p>
0894: * The recognized Xalan extension elements are:
0895: * <ul>
0896: * <li>content-handler
0897: * <li>entities
0898: * <li>indent-amount
0899: * <li>line-separator
0900: * <li>omit-meta-tag
0901: * <li>use-url-escaping
0902: * </ul>
0903: * <p>
0904: * These must be in the extension namespace of
0905: * "http://xml.apache.org/xalan". This is accomplished
0906: * by putting the namespace URI in braces before the
0907: * property name, for example:
0908: * <pre>
0909: * tran.setOutputProperty(
0910: * "{http://xml.apache.org/xalan}line-separator" ,
0911: * "\n");
0912: * </pre>
0913: *
0914: * @param name The property name.
0915: * @param value The requested value for the property.
0916: * @throws IllegalArgumentException if the property name is not legal.
0917: */
0918: public void setOutputProperty(String name, String value)
0919: throws IllegalArgumentException {
0920:
0921: synchronized (m_reentryGuard) {
0922:
0923: // Get the output format that was set by the user, otherwise get the
0924: // output format from the stylesheet.
0925: if (null == m_outputFormat) {
0926: m_outputFormat = (OutputProperties) getStylesheet()
0927: .getOutputComposed().clone();
0928: }
0929:
0930: if (!OutputProperties.isLegalPropertyKey(name))
0931: throw new IllegalArgumentException(
0932: XSLMessages
0933: .createMessage(
0934: XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED,
0935: new Object[] { name })); //"output property not recognized: "
0936: //+ name);
0937:
0938: m_outputFormat.setProperty(name, value);
0939: }
0940: }
0941:
0942: /**
0943: * Set the output properties for the transformation. These
0944: * properties will override properties set in the templates
0945: * with xsl:output.
0946: *
0947: * <p>If argument to this function is null, any properties
0948: * previously set will be removed.</p>
0949: *
0950: * @param oformat A set of output properties that will be
0951: * used to override any of the same properties in effect
0952: * for the transformation.
0953: *
0954: * @see javax.xml.transform.OutputKeys
0955: * @see java.util.Properties
0956: *
0957: * @throws IllegalArgumentException if any of the argument keys are not
0958: * recognized and are not namespace qualified.
0959: */
0960: public void setOutputProperties(Properties oformat)
0961: throws IllegalArgumentException {
0962:
0963: synchronized (m_reentryGuard) {
0964: if (null != oformat) {
0965:
0966: // See if an *explicit* method was set.
0967: String method = (String) oformat.get(OutputKeys.METHOD);
0968:
0969: if (null != method)
0970: m_outputFormat = new OutputProperties(method);
0971: else if (m_outputFormat == null)
0972: m_outputFormat = new OutputProperties();
0973:
0974: m_outputFormat.copyFrom(oformat);
0975: // copyFrom does not set properties that have been already set, so
0976: // this must be called after, which is a bit in the reverse from
0977: // what one might think.
0978: m_outputFormat.copyFrom(m_stylesheetRoot
0979: .getOutputProperties());
0980: } else {
0981: // if oformat is null JAXP says that any props previously set are removed
0982: // and we are to revert back to those in the templates object (i.e. Stylesheet).
0983: m_outputFormat = null;
0984: }
0985: }
0986: }
0987:
0988: /**
0989: * Get a copy of the output properties for the transformation. These
0990: * properties will override properties set in the templates
0991: * with xsl:output.
0992: *
0993: * <p>Note that mutation of the Properties object returned will not
0994: * effect the properties that the transformation contains.</p>
0995: *
0996: * @return A copy of the set of output properties in effect
0997: * for the next transformation.
0998: *
0999: * NEEDSDOC ($objectName$) @return
1000: */
1001: public Properties getOutputProperties() {
1002: return (Properties) getOutputFormat().getProperties().clone();
1003: }
1004:
1005: /**
1006: * Create a result ContentHandler from a Result object, based
1007: * on the current OutputProperties.
1008: *
1009: * @param outputTarget Where the transform result should go,
1010: * should not be null.
1011: *
1012: * @return A valid ContentHandler that will create the
1013: * result tree when it is fed SAX events.
1014: *
1015: * @throws TransformerException
1016: */
1017: public SerializationHandler createSerializationHandler(
1018: Result outputTarget) throws TransformerException {
1019: SerializationHandler xoh = createSerializationHandler(
1020: outputTarget, getOutputFormat());
1021: return xoh;
1022: }
1023:
1024: /**
1025: * Create a ContentHandler from a Result object and an OutputProperties.
1026: *
1027: * @param outputTarget Where the transform result should go,
1028: * should not be null.
1029: * @param format The OutputProperties object that will contain
1030: * instructions on how to serialize the output.
1031: *
1032: * @return A valid ContentHandler that will create the
1033: * result tree when it is fed SAX events.
1034: *
1035: * @throws TransformerException
1036: */
1037: public SerializationHandler createSerializationHandler(
1038: Result outputTarget, OutputProperties format)
1039: throws TransformerException {
1040:
1041: SerializationHandler xoh;
1042:
1043: // If the Result object contains a Node, then create
1044: // a ContentHandler that will add nodes to the input node.
1045: org.w3c.dom.Node outputNode = null;
1046:
1047: if (outputTarget instanceof DOMResult) {
1048: outputNode = ((DOMResult) outputTarget).getNode();
1049: org.w3c.dom.Node nextSibling = ((DOMResult) outputTarget)
1050: .getNextSibling();
1051:
1052: org.w3c.dom.Document doc;
1053: short type;
1054:
1055: if (null != outputNode) {
1056: type = outputNode.getNodeType();
1057: doc = (org.w3c.dom.Node.DOCUMENT_NODE == type) ? (org.w3c.dom.Document) outputNode
1058: : outputNode.getOwnerDocument();
1059: } else {
1060: boolean isSecureProcessing = m_stylesheetRoot
1061: .isSecureProcessing();
1062: doc = org.apache.xml.utils.DOMHelper
1063: .createDocument(isSecureProcessing);
1064: outputNode = doc;
1065: type = outputNode.getNodeType();
1066:
1067: ((DOMResult) outputTarget).setNode(outputNode);
1068: }
1069:
1070: DOMBuilder handler = (org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE == type) ? new DOMBuilder(
1071: doc, (org.w3c.dom.DocumentFragment) outputNode)
1072: : new DOMBuilder(doc, outputNode);
1073:
1074: if (nextSibling != null)
1075: handler.setNextSibling(nextSibling);
1076:
1077: String encoding = format.getProperty(OutputKeys.ENCODING);
1078: xoh = new ToXMLSAXHandler(handler,
1079: (LexicalHandler) handler, encoding);
1080: } else if (outputTarget instanceof SAXResult) {
1081: ContentHandler handler = ((SAXResult) outputTarget)
1082: .getHandler();
1083:
1084: if (null == handler)
1085: throw new IllegalArgumentException(
1086: "handler can not be null for a SAXResult");
1087:
1088: LexicalHandler lexHandler;
1089: if (handler instanceof LexicalHandler)
1090: lexHandler = (LexicalHandler) handler;
1091: else
1092: lexHandler = null;
1093:
1094: String encoding = format.getProperty(OutputKeys.ENCODING);
1095: String method = format.getProperty(OutputKeys.METHOD);
1096: if (org.apache.xml.serializer.Method.HTML.equals(method)) {
1097: xoh = new ToHTMLSAXHandler(handler, lexHandler,
1098: encoding);
1099: } else if (org.apache.xml.serializer.Method.TEXT
1100: .equals(method)) {
1101: xoh = new ToTextSAXHandler(handler, lexHandler,
1102: encoding);
1103: } else {
1104: ToXMLSAXHandler toXMLSAXHandler = new ToXMLSAXHandler(
1105: handler, lexHandler, encoding);
1106: toXMLSAXHandler.setShouldOutputNSAttr(false);
1107: xoh = toXMLSAXHandler;
1108:
1109: }
1110:
1111: String publicID = format
1112: .getProperty(OutputKeys.DOCTYPE_PUBLIC);
1113: String systemID = format
1114: .getProperty(OutputKeys.DOCTYPE_SYSTEM);
1115: if (systemID != null)
1116: xoh.setDoctypeSystem(systemID);
1117: if (publicID != null)
1118: xoh.setDoctypePublic(publicID);
1119:
1120: if (handler instanceof TransformerClient) {
1121: XalanTransformState state = new XalanTransformState();
1122: ((TransformerClient) handler).setTransformState(state);
1123: ((ToSAXHandler) xoh).setTransformState(state);
1124: }
1125:
1126: }
1127:
1128: // Otherwise, create a ContentHandler that will serialize the
1129: // result tree to either a stream or a writer.
1130: else if (outputTarget instanceof StreamResult) {
1131: StreamResult sresult = (StreamResult) outputTarget;
1132: String method = format.getProperty(OutputKeys.METHOD);
1133:
1134: try {
1135: SerializationHandler serializer = (SerializationHandler) SerializerFactory
1136: .getSerializer(format.getProperties());
1137:
1138: if (null != sresult.getWriter())
1139: serializer.setWriter(sresult.getWriter());
1140: else if (null != sresult.getOutputStream())
1141: serializer.setOutputStream(sresult
1142: .getOutputStream());
1143: else if (null != sresult.getSystemId()) {
1144: String fileURL = sresult.getSystemId();
1145:
1146: if (fileURL.startsWith("file:///")) {
1147: if (fileURL.substring(8).indexOf(":") > 0)
1148: fileURL = fileURL.substring(8);
1149: else
1150: fileURL = fileURL.substring(7);
1151: } else if (fileURL.startsWith("file:/")) {
1152: if (fileURL.substring(6).indexOf(":") > 0)
1153: fileURL = fileURL.substring(6);
1154: else
1155: fileURL = fileURL.substring(5);
1156: }
1157:
1158: m_outputStream = new java.io.FileOutputStream(
1159: fileURL);
1160:
1161: serializer.setOutputStream(m_outputStream);
1162:
1163: xoh = serializer;
1164: } else
1165: throw new TransformerException(
1166: XSLMessages
1167: .createMessage(
1168: XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED,
1169: null)); //"No output specified!");
1170:
1171: // handler = serializer.asContentHandler();
1172:
1173: // this.setSerializer(serializer);
1174:
1175: xoh = serializer;
1176: }
1177: // catch (UnsupportedEncodingException uee)
1178: // {
1179: // throw new TransformerException(uee);
1180: // }
1181: catch (IOException ioe) {
1182: throw new TransformerException(ioe);
1183: }
1184: } else {
1185: throw new TransformerException(
1186: XSLMessages
1187: .createMessage(
1188: XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE,
1189: new Object[] { outputTarget
1190: .getClass().getName() })); //"Can't transform to a Result of type "
1191: //+ outputTarget.getClass().getName()
1192: //+ "!");
1193: }
1194:
1195: // before we forget, lets make the created handler hold a reference
1196: // to the current TransformImpl object
1197: xoh.setTransformer(this );
1198:
1199: SourceLocator srcLocator = getStylesheet();
1200: xoh.setSourceLocator(srcLocator);
1201:
1202: return xoh;
1203:
1204: }
1205:
1206: /**
1207: * Process the source tree to the output result.
1208: * @param xmlSource The input for the source tree.
1209: * @param outputTarget The output source target.
1210: *
1211: * @throws TransformerException
1212: */
1213: public void transform(Source xmlSource, Result outputTarget)
1214: throws TransformerException {
1215: transform(xmlSource, outputTarget, true);
1216: }
1217:
1218: /**
1219: * Process the source tree to the output result.
1220: * @param xmlSource The input for the source tree.
1221: * @param outputTarget The output source target.
1222: * @param shouldRelease Flag indicating whether to release DTMManager.
1223: *
1224: * @throws TransformerException
1225: */
1226: public void transform(Source xmlSource, Result outputTarget,
1227: boolean shouldRelease) throws TransformerException {
1228:
1229: synchronized (m_reentryGuard) {
1230: SerializationHandler xoh = createSerializationHandler(outputTarget);
1231: this .setSerializationHandler(xoh);
1232:
1233: m_outputTarget = outputTarget;
1234:
1235: transform(xmlSource, shouldRelease);
1236: }
1237: }
1238:
1239: /**
1240: * Process the source node to the output result, if the
1241: * processor supports the "http://xml.org/trax/features/dom/input"
1242: * feature.
1243: * %REVIEW% Do we need a Node version of this?
1244: * @param node The input source node, which can be any valid DTM node.
1245: * @param outputTarget The output source target.
1246: *
1247: * @throws TransformerException
1248: */
1249: public void transformNode(int node, Result outputTarget)
1250: throws TransformerException {
1251:
1252: SerializationHandler xoh = createSerializationHandler(outputTarget);
1253: this .setSerializationHandler(xoh);
1254:
1255: m_outputTarget = outputTarget;
1256:
1257: transformNode(node);
1258: }
1259:
1260: /**
1261: * Process the source node to the output result, if the
1262: * processor supports the "http://xml.org/trax/features/dom/input"
1263: * feature.
1264: * %REVIEW% Do we need a Node version of this?
1265: * @param node The input source node, which can be any valid DTM node.
1266: *
1267: * @throws TransformerException
1268: */
1269: public void transformNode(int node) throws TransformerException {
1270: //dml
1271: setExtensionsTable(getStylesheet());
1272: // Make sure we're not writing to the same output content handler.
1273: synchronized (m_serializationHandler) {
1274: m_hasBeenReset = false;
1275:
1276: XPathContext xctxt = getXPathContext();
1277: DTM dtm = xctxt.getDTM(node);
1278:
1279: try {
1280: pushGlobalVars(node);
1281:
1282: // ==========
1283: // Give the top-level templates a chance to pass information into
1284: // the context (this is mainly for setting up tables for extensions).
1285: StylesheetRoot stylesheet = this .getStylesheet();
1286: int n = stylesheet.getGlobalImportCount();
1287:
1288: for (int i = 0; i < n; i++) {
1289: StylesheetComposed imported = stylesheet
1290: .getGlobalImport(i);
1291: int includedCount = imported
1292: .getIncludeCountComposed();
1293:
1294: for (int j = -1; j < includedCount; j++) {
1295: Stylesheet included = imported
1296: .getIncludeComposed(j);
1297:
1298: included.runtimeInit(this );
1299:
1300: for (ElemTemplateElement child = included
1301: .getFirstChildElem(); child != null; child = child
1302: .getNextSiblingElem()) {
1303: child.runtimeInit(this );
1304: }
1305: }
1306: }
1307: // ===========
1308: // System.out.println("Calling applyTemplateToNode - "+Thread.currentThread().getName());
1309: DTMIterator dtmIter = new org.apache.xpath.axes.SelfIteratorNoPredicate();
1310: dtmIter.setRoot(node, xctxt);
1311: xctxt.pushContextNodeList(dtmIter);
1312: try {
1313: this .applyTemplateToNode(null, null, node);
1314: } finally {
1315: xctxt.popContextNodeList();
1316: }
1317: // m_stylesheetRoot.getStartRule().execute(this);
1318:
1319: // System.out.println("Done with applyTemplateToNode - "+Thread.currentThread().getName());
1320: if (null != m_serializationHandler) {
1321: m_serializationHandler.endDocument();
1322: }
1323: } catch (Exception se) {
1324:
1325: // System.out.println(Thread.currentThread().getName()+" threw an exception! "
1326: // +se.getMessage());
1327: // If an exception was thrown, we need to make sure that any waiting
1328: // handlers can terminate, which I guess is best done by sending
1329: // an endDocument.
1330:
1331: // SAXSourceLocator
1332: while (se instanceof org.apache.xml.utils.WrappedRuntimeException) {
1333: Exception e = ((org.apache.xml.utils.WrappedRuntimeException) se)
1334: .getException();
1335: if (null != e)
1336: se = e;
1337: }
1338:
1339: if (null != m_serializationHandler) {
1340: try {
1341: if (se instanceof org.xml.sax.SAXParseException)
1342: m_serializationHandler
1343: .fatalError((org.xml.sax.SAXParseException) se);
1344: else if (se instanceof TransformerException) {
1345: TransformerException te = ((TransformerException) se);
1346: SAXSourceLocator sl = new SAXSourceLocator(
1347: te.getLocator());
1348: m_serializationHandler
1349: .fatalError(new org.xml.sax.SAXParseException(
1350: te.getMessage(), sl, te));
1351: } else {
1352: m_serializationHandler
1353: .fatalError(new org.xml.sax.SAXParseException(
1354: se.getMessage(),
1355: new SAXSourceLocator(), se));
1356: }
1357: } catch (Exception e) {
1358: }
1359: }
1360:
1361: if (se instanceof TransformerException) {
1362: m_errorHandler
1363: .fatalError((TransformerException) se);
1364: } else if (se instanceof org.xml.sax.SAXParseException) {
1365: m_errorHandler
1366: .fatalError(new TransformerException(
1367: se.getMessage(),
1368: new SAXSourceLocator(
1369: (org.xml.sax.SAXParseException) se),
1370: se));
1371: } else {
1372: m_errorHandler.fatalError(new TransformerException(
1373: se));
1374: }
1375:
1376: } finally {
1377: this .reset();
1378: }
1379: }
1380: }
1381:
1382: /**
1383: * Get a SAX2 ContentHandler for the input.
1384: *
1385: * @return A valid ContentHandler, which should never be null, as
1386: * long as getFeature("http://xml.org/trax/features/sax/input")
1387: * returns true.
1388: */
1389: public ContentHandler getInputContentHandler() {
1390: return getInputContentHandler(false);
1391: }
1392:
1393: /**
1394: * Get a SAX2 ContentHandler for the input.
1395: *
1396: * @param doDocFrag true if a DocumentFragment should be created as
1397: * the root, rather than a Document.
1398: *
1399: * @return A valid ContentHandler, which should never be null, as
1400: * long as getFeature("http://xml.org/trax/features/sax/input")
1401: * returns true.
1402: */
1403: public ContentHandler getInputContentHandler(boolean doDocFrag) {
1404:
1405: if (null == m_inputContentHandler) {
1406:
1407: // if(null == m_urlOfSource && null != m_stylesheetRoot)
1408: // m_urlOfSource = m_stylesheetRoot.getBaseIdentifier();
1409: m_inputContentHandler = new TransformerHandlerImpl(this ,
1410: doDocFrag, m_urlOfSource);
1411: }
1412:
1413: return m_inputContentHandler;
1414: }
1415:
1416: /**
1417: * Get a SAX2 DeclHandler for the input.
1418: * @return A valid DeclHandler, which should never be null, as
1419: * long as getFeature("http://xml.org/trax/features/sax/input")
1420: * returns true.
1421: */
1422: public DeclHandler getInputDeclHandler() {
1423:
1424: if (m_inputContentHandler instanceof DeclHandler)
1425: return (DeclHandler) m_inputContentHandler;
1426: else
1427: return null;
1428: }
1429:
1430: /**
1431: * Get a SAX2 LexicalHandler for the input.
1432: * @return A valid LexicalHandler, which should never be null, as
1433: * long as getFeature("http://xml.org/trax/features/sax/input")
1434: * returns true.
1435: */
1436: public LexicalHandler getInputLexicalHandler() {
1437:
1438: if (m_inputContentHandler instanceof LexicalHandler)
1439: return (LexicalHandler) m_inputContentHandler;
1440: else
1441: return null;
1442: }
1443:
1444: /**
1445: * Set the output properties for the transformation. These
1446: * properties will override properties set in the templates
1447: * with xsl:output.
1448: *
1449: * @param oformat A valid OutputProperties object (which will
1450: * not be mutated), or null.
1451: */
1452: public void setOutputFormat(OutputProperties oformat) {
1453: m_outputFormat = oformat;
1454: }
1455:
1456: /**
1457: * Get the output properties used for the transformation.
1458: *
1459: * @return the output format that was set by the user,
1460: * otherwise the output format from the stylesheet.
1461: */
1462: public OutputProperties getOutputFormat() {
1463:
1464: // Get the output format that was set by the user, otherwise get the
1465: // output format from the stylesheet.
1466: OutputProperties format = (null == m_outputFormat) ? getStylesheet()
1467: .getOutputComposed()
1468: : m_outputFormat;
1469:
1470: return format;
1471: }
1472:
1473: /**
1474: * Set a parameter for the templates.
1475: *
1476: * @param name The name of the parameter.
1477: * @param namespace The namespace of the parameter.
1478: * @param value The value object. This can be any valid Java object
1479: * -- it's up to the processor to provide the proper
1480: * coersion to the object, or simply pass it on for use
1481: * in extensions.
1482: */
1483: public void setParameter(String name, String namespace, Object value) {
1484:
1485: VariableStack varstack = getXPathContext().getVarStack();
1486: QName qname = new QName(namespace, name);
1487: XObject xobject = XObject.create(value, getXPathContext());
1488:
1489: StylesheetRoot sroot = m_stylesheetRoot;
1490: Vector vars = sroot.getVariablesAndParamsComposed();
1491: int i = vars.size();
1492: while (--i >= 0) {
1493: ElemVariable variable = (ElemVariable) vars.elementAt(i);
1494: if (variable.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE
1495: && variable.getName().equals(qname)) {
1496: varstack.setGlobalVariable(i, xobject);
1497: }
1498: }
1499: }
1500:
1501: /** NEEDSDOC Field m_userParams */
1502: Vector m_userParams;
1503:
1504: /**
1505: * Set a parameter for the transformation.
1506: *
1507: * @param name The name of the parameter,
1508: * which may have a namespace URI.
1509: * @param value The value object. This can be any valid Java object
1510: * -- it's up to the processor to provide the proper
1511: * coersion to the object, or simply pass it on for use
1512: * in extensions.
1513: */
1514: public void setParameter(String name, Object value) {
1515:
1516: if (value == null) {
1517: throw new IllegalArgumentException(
1518: XSLMessages
1519: .createMessage(
1520: XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE,
1521: new Object[] { name }));
1522: }
1523:
1524: StringTokenizer tokenizer = new StringTokenizer(name, "{}",
1525: false);
1526:
1527: try {
1528:
1529: // The first string might be the namespace, or it might be
1530: // the local name, if the namespace is null.
1531: String s1 = tokenizer.nextToken();
1532: String s2 = tokenizer.hasMoreTokens() ? tokenizer
1533: .nextToken() : null;
1534:
1535: if (null == m_userParams)
1536: m_userParams = new Vector();
1537:
1538: if (null == s2) {
1539: replaceOrPushUserParam(new QName(s1), XObject.create(
1540: value, getXPathContext()));
1541: setParameter(s1, null, value);
1542: } else {
1543: replaceOrPushUserParam(new QName(s1, s2), XObject
1544: .create(value, getXPathContext()));
1545: setParameter(s2, s1, value);
1546: }
1547: } catch (java.util.NoSuchElementException nsee) {
1548:
1549: // Should throw some sort of an error.
1550: }
1551: }
1552:
1553: /**
1554: * NEEDSDOC Method replaceOrPushUserParam
1555: *
1556: *
1557: * NEEDSDOC @param qname
1558: * NEEDSDOC @param xval
1559: */
1560: private void replaceOrPushUserParam(QName qname, XObject xval) {
1561:
1562: int n = m_userParams.size();
1563:
1564: for (int i = n - 1; i >= 0; i--) {
1565: Arg arg = (Arg) m_userParams.elementAt(i);
1566:
1567: if (arg.getQName().equals(qname)) {
1568: m_userParams
1569: .setElementAt(new Arg(qname, xval, true), i);
1570:
1571: return;
1572: }
1573: }
1574:
1575: m_userParams.addElement(new Arg(qname, xval, true));
1576: }
1577:
1578: /**
1579: * Get a parameter that was explicitly set with setParameter
1580: * or setParameters.
1581: *
1582: *
1583: * NEEDSDOC @param name
1584: * @return A parameter that has been set with setParameter
1585: * or setParameters,
1586: * *not* all the xsl:params on the stylesheet (which require
1587: * a transformation Source to be evaluated).
1588: */
1589: public Object getParameter(String name) {
1590:
1591: try {
1592:
1593: // VariableStack varstack = getXPathContext().getVarStack();
1594: // The first string might be the namespace, or it might be
1595: // the local name, if the namespace is null.
1596: QName qname = QName.getQNameFromString(name);
1597:
1598: if (null == m_userParams)
1599: return null;
1600:
1601: int n = m_userParams.size();
1602:
1603: for (int i = n - 1; i >= 0; i--) {
1604: Arg arg = (Arg) m_userParams.elementAt(i);
1605:
1606: if (arg.getQName().equals(qname)) {
1607: return arg.getVal().object();
1608: }
1609: }
1610:
1611: return null;
1612: } catch (java.util.NoSuchElementException nsee) {
1613:
1614: // Should throw some sort of an error.
1615: return null;
1616: }
1617: }
1618:
1619: /**
1620: * Reset parameters that the user specified for the transformation.
1621: * Called during transformer.reset() after we have cleared the
1622: * variable stack. We need to make sure that user params are
1623: * reset so that the transformer object can be reused.
1624: */
1625: private void resetUserParameters() {
1626:
1627: try {
1628:
1629: if (null == m_userParams)
1630: return;
1631:
1632: int n = m_userParams.size();
1633: for (int i = n - 1; i >= 0; i--) {
1634: Arg arg = (Arg) m_userParams.elementAt(i);
1635: QName name = arg.getQName();
1636: // The first string might be the namespace, or it might be
1637: // the local name, if the namespace is null.
1638: String s1 = name.getNamespace();
1639: String s2 = name.getLocalPart();
1640:
1641: setParameter(s2, s1, arg.getVal().object());
1642:
1643: }
1644:
1645: } catch (java.util.NoSuchElementException nsee) {
1646: // Should throw some sort of an error.
1647:
1648: }
1649: }
1650:
1651: /**
1652: * Set a bag of parameters for the transformation. Note that
1653: * these will not be additive, they will replace the existing
1654: * set of parameters.
1655: *
1656: * NEEDSDOC @param params
1657: */
1658: public void setParameters(Properties params) {
1659:
1660: clearParameters();
1661:
1662: Enumeration names = params.propertyNames();
1663:
1664: while (names.hasMoreElements()) {
1665: String name = params.getProperty((String) names
1666: .nextElement());
1667: StringTokenizer tokenizer = new StringTokenizer(name, "{}",
1668: false);
1669:
1670: try {
1671:
1672: // The first string might be the namespace, or it might be
1673: // the local name, if the namespace is null.
1674: String s1 = tokenizer.nextToken();
1675: String s2 = tokenizer.hasMoreTokens() ? tokenizer
1676: .nextToken() : null;
1677:
1678: if (null == s2)
1679: setParameter(s1, null, params.getProperty(name));
1680: else
1681: setParameter(s2, s1, params.getProperty(name));
1682: } catch (java.util.NoSuchElementException nsee) {
1683:
1684: // Should throw some sort of an error.
1685: }
1686: }
1687: }
1688:
1689: /**
1690: * Reset the parameters to a null list.
1691: */
1692: public void clearParameters() {
1693:
1694: synchronized (m_reentryGuard) {
1695: VariableStack varstack = new VariableStack();
1696:
1697: m_xcontext.setVarStack(varstack);
1698:
1699: m_userParams = null;
1700: }
1701: }
1702:
1703: /**
1704: * Internal -- push the global variables from the Stylesheet onto
1705: * the context's runtime variable stack.
1706: * <p>If we encounter a variable
1707: * that is already defined in the variable stack, we ignore it. This
1708: * is because the second variable definition will be at a lower import
1709: * precedence. Presumably, global"variables at the same import precedence
1710: * with the same name will have been caught during the recompose process.
1711: * <p>However, if we encounter a parameter that is already defined in the
1712: * variable stack, we need to see if this is a parameter whose value was
1713: * supplied by a setParameter call. If so, we need to "receive" the one
1714: * already in the stack, ignoring this one. If it is just an earlier
1715: * xsl:param or xsl:variable definition, we ignore it using the same
1716: * reasoning as explained above for the variable.
1717: *
1718: * @param contextNode The root of the source tree, can't be null.
1719: *
1720: * @throws TransformerException
1721: */
1722: protected void pushGlobalVars(int contextNode)
1723: throws TransformerException {
1724:
1725: XPathContext xctxt = m_xcontext;
1726: VariableStack vs = xctxt.getVarStack();
1727: StylesheetRoot sr = getStylesheet();
1728: Vector vars = sr.getVariablesAndParamsComposed();
1729:
1730: int i = vars.size();
1731: vs.link(i);
1732:
1733: while (--i >= 0) {
1734: ElemVariable v = (ElemVariable) vars.elementAt(i);
1735:
1736: // XObject xobj = v.getValue(this, contextNode);
1737: XObject xobj = new XUnresolvedVariable(v, contextNode,
1738: this , vs.getStackFrame(), 0, true);
1739:
1740: if (null == vs.elementAt(i))
1741: vs.setGlobalVariable(i, xobj);
1742: }
1743:
1744: }
1745:
1746: /**
1747: * Set an object that will be used to resolve URIs used in
1748: * document(), etc.
1749: * @param resolver An object that implements the URIResolver interface,
1750: * or null.
1751: */
1752: public void setURIResolver(URIResolver resolver) {
1753:
1754: synchronized (m_reentryGuard) {
1755: m_xcontext.getSourceTreeManager().setURIResolver(resolver);
1756: }
1757: }
1758:
1759: /**
1760: * Get an object that will be used to resolve URIs used in
1761: * document(), etc.
1762: *
1763: * @return An object that implements the URIResolver interface,
1764: * or null.
1765: */
1766: public URIResolver getURIResolver() {
1767: return m_xcontext.getSourceTreeManager().getURIResolver();
1768: }
1769:
1770: // ======== End Transformer Implementation ========
1771:
1772: /**
1773: * Set the content event handler.
1774: *
1775: * NEEDSDOC @param handler
1776: * @throws java.lang.NullPointerException If the handler
1777: * is null.
1778: * @see org.xml.sax.XMLReader#setContentHandler
1779: */
1780: public void setContentHandler(ContentHandler handler) {
1781:
1782: if (handler == null) {
1783: throw new NullPointerException(XSLMessages.createMessage(
1784: XSLTErrorResources.ER_NULL_CONTENT_HANDLER, null)); //"Null content handler");
1785: } else {
1786: m_outputContentHandler = handler;
1787:
1788: if (null == m_serializationHandler) {
1789: ToXMLSAXHandler h = new ToXMLSAXHandler();
1790: h.setContentHandler(handler);
1791: h.setTransformer(this );
1792:
1793: m_serializationHandler = h;
1794: } else
1795: m_serializationHandler.setContentHandler(handler);
1796: }
1797: }
1798:
1799: /**
1800: * Get the content event handler.
1801: *
1802: * @return The current content handler, or null if none was set.
1803: * @see org.xml.sax.XMLReader#getContentHandler
1804: */
1805: public ContentHandler getContentHandler() {
1806: return m_outputContentHandler;
1807: }
1808:
1809: /**
1810: * Given a stylesheet element, create a result tree fragment from it's
1811: * contents. The fragment will be built within the shared RTF DTM system
1812: * used as a variable stack.
1813: * @param templateParent The template element that holds the fragment.
1814: * @return the NodeHandle for the root node of the resulting RTF.
1815: *
1816: * @throws TransformerException
1817: * @xsl.usage advanced
1818: */
1819: public int transformToRTF(ElemTemplateElement templateParent)
1820: throws TransformerException {
1821: // Retrieve a DTM to contain the RTF. At this writing, this may be a
1822: // multi-document DTM (SAX2RTFDTM).
1823: DTM dtmFrag = m_xcontext.getRTFDTM();
1824: return transformToRTF(templateParent, dtmFrag);
1825: }
1826:
1827: /**
1828: * Given a stylesheet element, create a result tree fragment from it's
1829: * contents. The fragment will also use the shared DTM system, but will
1830: * obtain its space from the global variable pool rather than the dynamic
1831: * variable stack. This allows late binding of XUnresolvedVariables without
1832: * the risk that their content will be discarded when the variable stack
1833: * is popped.
1834: *
1835: * @param templateParent The template element that holds the fragment.
1836: * @return the NodeHandle for the root node of the resulting RTF.
1837: *
1838: * @throws TransformerException
1839: * @xsl.usage advanced
1840: */
1841: public int transformToGlobalRTF(ElemTemplateElement templateParent)
1842: throws TransformerException {
1843: // Retrieve a DTM to contain the RTF. At this writing, this may be a
1844: // multi-document DTM (SAX2RTFDTM).
1845: DTM dtmFrag = m_xcontext.getGlobalRTFDTM();
1846: return transformToRTF(templateParent, dtmFrag);
1847: }
1848:
1849: /**
1850: * Given a stylesheet element, create a result tree fragment from it's
1851: * contents.
1852: * @param templateParent The template element that holds the fragment.
1853: * @param dtmFrag The DTM to write the RTF into
1854: * @return the NodeHandle for the root node of the resulting RTF.
1855: *
1856: * @throws TransformerException
1857: * @xsl.usage advanced
1858: */
1859: private int transformToRTF(ElemTemplateElement templateParent,
1860: DTM dtmFrag) throws TransformerException {
1861:
1862: XPathContext xctxt = m_xcontext;
1863:
1864: ContentHandler rtfHandler = dtmFrag.getContentHandler();
1865:
1866: // Obtain the ResultTreeFrag's root node.
1867: // NOTE: In SAX2RTFDTM, this value isn't available until after
1868: // the startDocument has been issued, so assignment has been moved
1869: // down a bit in the code.
1870: int resultFragment; // not yet reliably = dtmFrag.getDocument();
1871:
1872: // Save the current result tree handler.
1873: SerializationHandler savedRTreeHandler = this .m_serializationHandler;
1874:
1875: // And make a new handler for the RTF.
1876: ToSAXHandler h = new ToXMLSAXHandler();
1877: h.setContentHandler(rtfHandler);
1878: h.setTransformer(this );
1879:
1880: // Replace the old handler (which was already saved)
1881: m_serializationHandler = h;
1882:
1883: // use local variable for the current handler
1884: SerializationHandler rth = m_serializationHandler;
1885:
1886: try {
1887: rth.startDocument();
1888:
1889: // startDocument is "bottlenecked" in RTH. We need it acted upon immediately,
1890: // to set the DTM's state as in-progress, so that if the xsl:variable's body causes
1891: // further RTF activity we can keep that from bashing this DTM.
1892: rth.flushPending();
1893:
1894: try {
1895:
1896: // Do the transformation of the child elements.
1897: executeChildTemplates(templateParent, true);
1898:
1899: // Make sure everything is flushed!
1900: rth.flushPending();
1901:
1902: // Get the document ID. May not exist until the RTH has not only
1903: // received, but flushed, the startDocument, and may be invalid
1904: // again after the document has been closed (still debating that)
1905: // ... so waiting until just before the end seems simplest/safest.
1906: resultFragment = dtmFrag.getDocument();
1907: } finally {
1908: rth.endDocument();
1909: }
1910: } catch (org.xml.sax.SAXException se) {
1911: throw new TransformerException(se);
1912: } finally {
1913:
1914: // Restore the previous result tree handler.
1915: this .m_serializationHandler = savedRTreeHandler;
1916: }
1917:
1918: return resultFragment;
1919: }
1920:
1921: /**
1922: * Get the StringWriter pool, so that StringWriter
1923: * objects may be reused.
1924: *
1925: * @return The string writer pool, not null.
1926: * @xsl.usage internal
1927: */
1928: public ObjectPool getStringWriterPool() {
1929: return m_stringWriterObjectPool;
1930: }
1931:
1932: /**
1933: * Take the contents of a template element, process it, and
1934: * convert it to a string.
1935: *
1936: * @param elem The parent element whose children will be output
1937: * as a string.
1938: *
1939: * @return The stringized result of executing the elements children.
1940: *
1941: * @throws TransformerException
1942: * @xsl.usage advanced
1943: */
1944: public String transformToString(ElemTemplateElement elem)
1945: throws TransformerException {
1946: ElemTemplateElement firstChild = elem.getFirstChildElem();
1947: if (null == firstChild)
1948: return "";
1949: if (elem.hasTextLitOnly() && m_optimizer) {
1950: return ((ElemTextLiteral) firstChild).getNodeValue();
1951: }
1952:
1953: // Save the current result tree handler.
1954: SerializationHandler savedRTreeHandler = this .m_serializationHandler;
1955:
1956: // Create a Serializer object that will handle the SAX events
1957: // and build the ResultTreeFrag nodes.
1958: StringWriter sw = (StringWriter) m_stringWriterObjectPool
1959: .getInstance();
1960:
1961: m_serializationHandler = (ToTextStream) m_textResultHandlerObjectPool
1962: .getInstance();
1963:
1964: if (null == m_serializationHandler) {
1965: // if we didn't get one from the pool, go make a new one
1966:
1967: Serializer serializer = org.apache.xml.serializer.SerializerFactory
1968: .getSerializer(m_textformat.getProperties());
1969: m_serializationHandler = (SerializationHandler) serializer;
1970: }
1971:
1972: m_serializationHandler.setTransformer(this );
1973: m_serializationHandler.setWriter(sw);
1974:
1975: String result;
1976:
1977: try {
1978: /* Don't call startDocument, the SerializationHandler will
1979: * generate its own internal startDocument call anyways
1980: */
1981: // this.m_serializationHandler.startDocument();
1982: // Do the transformation of the child elements.
1983: executeChildTemplates(elem, true);
1984: this .m_serializationHandler.endDocument();
1985:
1986: result = sw.toString();
1987: } catch (org.xml.sax.SAXException se) {
1988: throw new TransformerException(se);
1989: } finally {
1990: sw.getBuffer().setLength(0);
1991:
1992: try {
1993: sw.close();
1994: } catch (Exception ioe) {
1995: }
1996:
1997: m_stringWriterObjectPool.freeInstance(sw);
1998: m_serializationHandler.reset();
1999: m_textResultHandlerObjectPool
2000: .freeInstance(m_serializationHandler);
2001:
2002: // Restore the previous result tree handler.
2003: m_serializationHandler = savedRTreeHandler;
2004: }
2005:
2006: return result;
2007: }
2008:
2009: /**
2010: * Given an element and mode, find the corresponding
2011: * template and process the contents.
2012: *
2013: * @param xslInstruction The calling element.
2014: * @param template The template to use if xsl:for-each, current template for apply-imports, or null.
2015: * @param child The source context node.
2016: * @throws TransformerException
2017: * @return true if applied a template, false if not.
2018: * @xsl.usage advanced
2019: */
2020: public boolean applyTemplateToNode(
2021: ElemTemplateElement xslInstruction, // xsl:apply-templates or xsl:for-each
2022: ElemTemplate template, int child)
2023: throws TransformerException {
2024:
2025: DTM dtm = m_xcontext.getDTM(child);
2026: short nodeType = dtm.getNodeType(child);
2027: boolean isDefaultTextRule = false;
2028: boolean isApplyImports = false;
2029:
2030: isApplyImports = ((xslInstruction == null) ? false
2031: : xslInstruction.getXSLToken() == Constants.ELEMNAME_APPLY_IMPORTS);
2032:
2033: if (null == template || isApplyImports) {
2034: int maxImportLevel, endImportLevel = 0;
2035:
2036: if (isApplyImports) {
2037: maxImportLevel = template.getStylesheetComposed()
2038: .getImportCountComposed() - 1;
2039: endImportLevel = template.getStylesheetComposed()
2040: .getEndImportCountComposed();
2041: } else {
2042: maxImportLevel = -1;
2043: }
2044:
2045: // If we're trying an xsl:apply-imports at the top level (ie there are no
2046: // imported stylesheets), we need to indicate that there is no matching template.
2047: // The above logic will calculate a maxImportLevel of -1 which indicates
2048: // that we should find any template. This is because a value of -1 for
2049: // maxImportLevel has a special meaning. But we don't want that.
2050: // We want to match -no- templates. See bugzilla bug 1170.
2051: if (isApplyImports && (maxImportLevel == -1)) {
2052: template = null;
2053: } else {
2054:
2055: // Find the XSL template that is the best match for the
2056: // element.
2057: XPathContext xctxt = m_xcontext;
2058:
2059: try {
2060: xctxt.pushNamespaceContext(xslInstruction);
2061:
2062: QName mode = this .getMode();
2063:
2064: if (isApplyImports)
2065: template = m_stylesheetRoot
2066: .getTemplateComposed(xctxt, child,
2067: mode, maxImportLevel,
2068: endImportLevel,
2069: m_quietConflictWarnings, dtm);
2070: else
2071: template = m_stylesheetRoot
2072: .getTemplateComposed(xctxt, child,
2073: mode, m_quietConflictWarnings,
2074: dtm);
2075:
2076: } finally {
2077: xctxt.popNamespaceContext();
2078: }
2079: }
2080:
2081: // If that didn't locate a node, fall back to a default template rule.
2082: // See http://www.w3.org/TR/xslt#built-in-rule.
2083: if (null == template) {
2084: switch (nodeType) {
2085: case DTM.DOCUMENT_FRAGMENT_NODE:
2086: case DTM.ELEMENT_NODE:
2087: template = m_stylesheetRoot.getDefaultRule();
2088: break;
2089: case DTM.CDATA_SECTION_NODE:
2090: case DTM.TEXT_NODE:
2091: case DTM.ATTRIBUTE_NODE:
2092: template = m_stylesheetRoot.getDefaultTextRule();
2093: isDefaultTextRule = true;
2094: break;
2095: case DTM.DOCUMENT_NODE:
2096: template = m_stylesheetRoot.getDefaultRootRule();
2097: break;
2098: default:
2099:
2100: // No default rules for processing instructions and the like.
2101: return false;
2102: }
2103: }
2104: }
2105:
2106: // If we are processing the default text rule, then just clone
2107: // the value directly to the result tree.
2108: try {
2109: pushElemTemplateElement(template);
2110: m_xcontext.pushCurrentNode(child);
2111: pushPairCurrentMatched(template, child);
2112:
2113: // Fix copy copy29 test.
2114: if (!isApplyImports) {
2115: DTMIterator cnl = new org.apache.xpath.NodeSetDTM(
2116: child, m_xcontext.getDTMManager());
2117: m_xcontext.pushContextNodeList(cnl);
2118: }
2119:
2120: if (isDefaultTextRule) {
2121: switch (nodeType) {
2122: case DTM.CDATA_SECTION_NODE:
2123: case DTM.TEXT_NODE:
2124: ClonerToResultTree.cloneToResultTree(child,
2125: nodeType, dtm, getResultTreeHandler(),
2126: false);
2127: break;
2128: case DTM.ATTRIBUTE_NODE:
2129: dtm.dispatchCharactersEvents(child,
2130: getResultTreeHandler(), false);
2131: break;
2132: }
2133: } else {
2134:
2135: // Fire a trace event for the template.
2136:
2137: if (m_debug)
2138: getTraceManager().fireTraceEvent(template);
2139: // And execute the child templates.
2140: // 9/11/00: If template has been compiled, hand off to it
2141: // since much (most? all?) of the processing has been inlined.
2142: // (It would be nice if there was a single entry point that
2143: // worked for both... but the interpretive system works by
2144: // having the Tranformer execute the children, while the
2145: // compiled obviously has to run its own code. It's
2146: // also unclear that "execute" is really the right name for
2147: // that entry point.)
2148: m_xcontext.setSAXLocator(template);
2149: // m_xcontext.getVarStack().link();
2150: m_xcontext.getVarStack().link(template.m_frameSize);
2151: executeChildTemplates(template, true);
2152:
2153: if (m_debug)
2154: getTraceManager().fireTraceEndEvent(template);
2155: }
2156: } catch (org.xml.sax.SAXException se) {
2157: throw new TransformerException(se);
2158: } finally {
2159: if (!isDefaultTextRule)
2160: m_xcontext.getVarStack().unlink();
2161: m_xcontext.popCurrentNode();
2162: if (!isApplyImports) {
2163: m_xcontext.popContextNodeList();
2164: }
2165: popCurrentMatched();
2166:
2167: popElemTemplateElement();
2168: }
2169:
2170: return true;
2171: }
2172:
2173: /**
2174: * Execute each of the children of a template element. This method
2175: * is only for extension use.
2176: *
2177: * @param elem The ElemTemplateElement that contains the children
2178: * that should execute.
2179: * NEEDSDOC @param context
2180: * @param mode The current mode.
2181: * @param handler The ContentHandler to where the result events
2182: * should be fed.
2183: *
2184: * @throws TransformerException
2185: * @xsl.usage advanced
2186: */
2187: public void executeChildTemplates(ElemTemplateElement elem,
2188: org.w3c.dom.Node context, QName mode, ContentHandler handler)
2189: throws TransformerException {
2190:
2191: XPathContext xctxt = m_xcontext;
2192:
2193: try {
2194: if (null != mode)
2195: pushMode(mode);
2196: xctxt.pushCurrentNode(xctxt.getDTMHandleFromNode(context));
2197: executeChildTemplates(elem, handler);
2198: } finally {
2199: xctxt.popCurrentNode();
2200:
2201: // I'm not sure where or why this was here. It is clearly in
2202: // error though, without a corresponding pushMode().
2203: if (null != mode)
2204: popMode();
2205: }
2206: }
2207:
2208: /**
2209: * Execute each of the children of a template element.
2210: *
2211: * @param elem The ElemTemplateElement that contains the children
2212: * that should execute.
2213: * @param shouldAddAttrs true if xsl:attributes should be executed.
2214: *
2215: * @throws TransformerException
2216: * @xsl.usage advanced
2217: */
2218: public void executeChildTemplates(ElemTemplateElement elem,
2219: boolean shouldAddAttrs) throws TransformerException {
2220:
2221: // Does this element have any children?
2222: ElemTemplateElement t = elem.getFirstChildElem();
2223:
2224: if (null == t)
2225: return;
2226:
2227: if (elem.hasTextLitOnly() && m_optimizer) {
2228: char[] chars = ((ElemTextLiteral) t).getChars();
2229: try {
2230: // Have to push stuff on for tooling...
2231: this .pushElemTemplateElement(t);
2232: m_serializationHandler.characters(chars, 0,
2233: chars.length);
2234: } catch (SAXException se) {
2235: throw new TransformerException(se);
2236: } finally {
2237: this .popElemTemplateElement();
2238: }
2239: return;
2240: }
2241:
2242: // // Check for infinite loops if we have to.
2243: // boolean check = (m_stackGuard.m_recursionLimit > -1);
2244: //
2245: // if (check)
2246: // getStackGuard().push(elem, xctxt.getCurrentNode());
2247:
2248: XPathContext xctxt = m_xcontext;
2249: xctxt.pushSAXLocatorNull();
2250: int currentTemplateElementsTop = m_currentTemplateElements
2251: .size();
2252: m_currentTemplateElements.push(null);
2253:
2254: try {
2255: // Loop through the children of the template, calling execute on
2256: // each of them.
2257: for (; t != null; t = t.getNextSiblingElem()) {
2258: if (!shouldAddAttrs
2259: && t.getXSLToken() == Constants.ELEMNAME_ATTRIBUTE)
2260: continue;
2261:
2262: xctxt.setSAXLocator(t);
2263: m_currentTemplateElements.setElementAt(t,
2264: currentTemplateElementsTop);
2265: t.execute(this );
2266: }
2267: } catch (RuntimeException re) {
2268: TransformerException te = new TransformerException(re);
2269: te.setLocator(t);
2270: throw te;
2271: } finally {
2272: m_currentTemplateElements.pop();
2273: xctxt.popSAXLocator();
2274: }
2275:
2276: // Check for infinite loops if we have to
2277: // if (check)
2278: // getStackGuard().pop();
2279: }
2280:
2281: /**
2282: * Execute each of the children of a template element.
2283: *
2284: * @param elem The ElemTemplateElement that contains the children
2285: * that should execute.
2286: * @param handler The ContentHandler to where the result events
2287: * should be fed.
2288: *
2289: * @throws TransformerException
2290: * @xsl.usage advanced
2291: */
2292: public void executeChildTemplates(ElemTemplateElement elem,
2293: ContentHandler handler) throws TransformerException {
2294:
2295: SerializationHandler xoh = this .getSerializationHandler();
2296:
2297: // These may well not be the same! In this case when calling
2298: // the Redirect extension, it has already set the ContentHandler
2299: // in the Transformer.
2300: SerializationHandler savedHandler = xoh;
2301:
2302: try {
2303: xoh.flushPending();
2304:
2305: // %REVIEW% Make sure current node is being pushed.
2306: LexicalHandler lex = null;
2307: if (handler instanceof LexicalHandler) {
2308: lex = (LexicalHandler) handler;
2309: }
2310: m_serializationHandler = new ToXMLSAXHandler(handler, lex,
2311: savedHandler.getEncoding());
2312: m_serializationHandler.setTransformer(this );
2313: executeChildTemplates(elem, true);
2314: } catch (TransformerException e) {
2315: throw e;
2316: } catch (SAXException se) {
2317: throw new TransformerException(se);
2318: } finally {
2319: m_serializationHandler = savedHandler;
2320: }
2321: }
2322:
2323: /**
2324: * Get the keys for the xsl:sort elements.
2325: * Note: Should this go into ElemForEach?
2326: *
2327: * @param foreach Valid ElemForEach element, not null.
2328: * @param sourceNodeContext The current node context in the source tree,
2329: * needed to evaluate the Attribute Value Templates.
2330: *
2331: * @return A Vector of NodeSortKeys, or null.
2332: *
2333: * @throws TransformerException
2334: * @xsl.usage advanced
2335: */
2336: public Vector processSortKeys(ElemForEach foreach,
2337: int sourceNodeContext) throws TransformerException {
2338:
2339: Vector keys = null;
2340: XPathContext xctxt = m_xcontext;
2341: int nElems = foreach.getSortElemCount();
2342:
2343: if (nElems > 0)
2344: keys = new Vector();
2345:
2346: // March backwards, collecting the sort keys.
2347: for (int i = 0; i < nElems; i++) {
2348: ElemSort sort = foreach.getSortElem(i);
2349:
2350: if (m_debug)
2351: getTraceManager().fireTraceEvent(sort);
2352:
2353: String langString = (null != sort.getLang()) ? sort
2354: .getLang().evaluate(xctxt, sourceNodeContext,
2355: foreach) : null;
2356: String dataTypeString = sort.getDataType().evaluate(xctxt,
2357: sourceNodeContext, foreach);
2358:
2359: if (dataTypeString.indexOf(":") >= 0)
2360: System.out
2361: .println("TODO: Need to write the hooks for QNAME sort data type");
2362: else if (!(dataTypeString
2363: .equalsIgnoreCase(Constants.ATTRVAL_DATATYPE_TEXT))
2364: && !(dataTypeString
2365: .equalsIgnoreCase(Constants.ATTRVAL_DATATYPE_NUMBER)))
2366: foreach.error(
2367: XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2368: new Object[] { Constants.ATTRNAME_DATATYPE,
2369: dataTypeString });
2370:
2371: boolean treatAsNumbers = ((null != dataTypeString) && dataTypeString
2372: .equals(Constants.ATTRVAL_DATATYPE_NUMBER)) ? true
2373: : false;
2374: String orderString = sort.getOrder().evaluate(xctxt,
2375: sourceNodeContext, foreach);
2376:
2377: if (!(orderString
2378: .equalsIgnoreCase(Constants.ATTRVAL_ORDER_ASCENDING))
2379: && !(orderString
2380: .equalsIgnoreCase(Constants.ATTRVAL_ORDER_DESCENDING)))
2381: foreach.error(
2382: XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2383: new Object[] { Constants.ATTRNAME_ORDER,
2384: orderString });
2385:
2386: boolean descending = ((null != orderString) && orderString
2387: .equals(Constants.ATTRVAL_ORDER_DESCENDING)) ? true
2388: : false;
2389: AVT caseOrder = sort.getCaseOrder();
2390: boolean caseOrderUpper;
2391:
2392: if (null != caseOrder) {
2393: String caseOrderString = caseOrder.evaluate(xctxt,
2394: sourceNodeContext, foreach);
2395:
2396: if (!(caseOrderString
2397: .equalsIgnoreCase(Constants.ATTRVAL_CASEORDER_UPPER))
2398: && !(caseOrderString
2399: .equalsIgnoreCase(Constants.ATTRVAL_CASEORDER_LOWER)))
2400: foreach
2401: .error(
2402: XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2403: new Object[] {
2404: Constants.ATTRNAME_CASEORDER,
2405: caseOrderString });
2406:
2407: caseOrderUpper = ((null != caseOrderString) && caseOrderString
2408: .equals(Constants.ATTRVAL_CASEORDER_UPPER)) ? true
2409: : false;
2410: } else {
2411: caseOrderUpper = false;
2412: }
2413:
2414: keys.addElement(new NodeSortKey(this , sort.getSelect(),
2415: treatAsNumbers, descending, langString,
2416: caseOrderUpper, foreach));
2417: if (m_debug)
2418: getTraceManager().fireTraceEndEvent(sort);
2419: }
2420:
2421: return keys;
2422: }
2423:
2424: //==========================================================
2425: // SECTION: TransformState implementation
2426: //==========================================================
2427:
2428: /**
2429: * Get the stack of ElemTemplateElements.
2430: *
2431: * @return A copy of stack that contains the xsl element instructions,
2432: * the earliest called in index zero, and the latest called in index size()-1.
2433: */
2434: public Vector getElementCallstack() {
2435: Vector elems = new Vector();
2436: int nStackSize = m_currentTemplateElements.size();
2437: for (int i = 0; i < nStackSize; i++) {
2438: ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements
2439: .elementAt(i);
2440: if (null != elem) {
2441: elems.addElement(elem);
2442: }
2443: }
2444: return elems;
2445: }
2446:
2447: /**
2448: * Get the count of how many elements are
2449: * active.
2450: * @return The number of active elements on
2451: * the currentTemplateElements stack.
2452: */
2453: public int getCurrentTemplateElementsCount() {
2454: return m_currentTemplateElements.size();
2455: }
2456:
2457: /**
2458: * Get the count of how many elements are
2459: * active.
2460: * @return The number of active elements on
2461: * the currentTemplateElements stack.
2462: */
2463: public ObjectStack getCurrentTemplateElements() {
2464: return m_currentTemplateElements;
2465: }
2466:
2467: /**
2468: * Push the current template element.
2469: *
2470: * @param elem The current ElemTemplateElement (may be null, and then
2471: * set via setCurrentElement).
2472: */
2473: public void pushElemTemplateElement(ElemTemplateElement elem) {
2474: m_currentTemplateElements.push(elem);
2475: }
2476:
2477: /**
2478: * Pop the current template element.
2479: */
2480: public void popElemTemplateElement() {
2481: m_currentTemplateElements.pop();
2482: }
2483:
2484: /**
2485: * Set the top of the current template elements
2486: * stack.
2487: *
2488: * @param e The current ElemTemplateElement about to
2489: * be executed.
2490: */
2491: public void setCurrentElement(ElemTemplateElement e) {
2492: m_currentTemplateElements.setTop(e);
2493: }
2494:
2495: /**
2496: * Retrieves the current ElemTemplateElement that is
2497: * being executed.
2498: *
2499: * @return The current ElemTemplateElement that is executing,
2500: * should not normally be null.
2501: */
2502: public ElemTemplateElement getCurrentElement() {
2503: return (m_currentTemplateElements.size() > 0) ? (ElemTemplateElement) m_currentTemplateElements
2504: .peek()
2505: : null;
2506: }
2507:
2508: /**
2509: * This method retrieves the current context node
2510: * in the source tree.
2511: *
2512: * @return The current context node (should never be null?).
2513: */
2514: public int getCurrentNode() {
2515: return m_xcontext.getCurrentNode();
2516: }
2517:
2518: /**
2519: * Get the call stack of xsl:template elements.
2520: *
2521: * @return A copy of stack that contains the xsl:template
2522: * (ElemTemplate) instructions, the earliest called in index
2523: * zero, and the latest called in index size()-1.
2524: */
2525: public Vector getTemplateCallstack() {
2526: Vector elems = new Vector();
2527: int nStackSize = m_currentTemplateElements.size();
2528: for (int i = 0; i < nStackSize; i++) {
2529: ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements
2530: .elementAt(i);
2531: if (null != elem
2532: && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE)) {
2533: elems.addElement(elem);
2534: }
2535: }
2536: return elems;
2537: }
2538:
2539: /**
2540: * This method retrieves the xsl:template
2541: * that is in effect, which may be a matched template
2542: * or a named template.
2543: *
2544: * <p>Please note that the ElemTemplate returned may
2545: * be a default template, and thus may not have a template
2546: * defined in the stylesheet.</p>
2547: *
2548: * @return The current xsl:template, should not be null.
2549: */
2550: public ElemTemplate getCurrentTemplate() {
2551:
2552: ElemTemplateElement elem = getCurrentElement();
2553:
2554: while ((null != elem)
2555: && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE)) {
2556: elem = elem.getParentElem();
2557: }
2558:
2559: return (ElemTemplate) elem;
2560: }
2561:
2562: /**
2563: * Push both the current xsl:template or xsl:for-each onto the
2564: * stack, along with the child node that was matched.
2565: * (Note: should this only be used for xsl:templates?? -sb)
2566: *
2567: * @param template xsl:template or xsl:for-each.
2568: * @param child The child that was matched.
2569: */
2570: public void pushPairCurrentMatched(ElemTemplateElement template,
2571: int child) {
2572: m_currentMatchTemplates.push(template);
2573: m_currentMatchedNodes.push(child);
2574: }
2575:
2576: /**
2577: * Pop the elements that were pushed via pushPairCurrentMatched.
2578: */
2579: public void popCurrentMatched() {
2580: m_currentMatchTemplates.pop();
2581: m_currentMatchedNodes.pop();
2582: }
2583:
2584: /**
2585: * This method retrieves the xsl:template
2586: * that was matched. Note that this may not be
2587: * the same thing as the current template (which
2588: * may be from getCurrentElement()), since a named
2589: * template may be in effect.
2590: *
2591: * @return The pushed template that was pushed via pushPairCurrentMatched.
2592: */
2593: public ElemTemplate getMatchedTemplate() {
2594: return (ElemTemplate) m_currentMatchTemplates.peek();
2595: }
2596:
2597: /**
2598: * Retrieves the node in the source tree that matched
2599: * the template obtained via getMatchedTemplate().
2600: *
2601: * @return The matched node that corresponds to the
2602: * match attribute of the current xsl:template.
2603: */
2604: public int getMatchedNode() {
2605: return m_currentMatchedNodes.peepTail();
2606: }
2607:
2608: /**
2609: * Get the current context node list.
2610: *
2611: * @return A reset clone of the context node list.
2612: */
2613: public DTMIterator getContextNodeList() {
2614:
2615: try {
2616: DTMIterator cnl = m_xcontext.getContextNodeList();
2617:
2618: return (cnl == null) ? null : (DTMIterator) cnl
2619: .cloneWithReset();
2620: } catch (CloneNotSupportedException cnse) {
2621:
2622: // should never happen.
2623: return null;
2624: }
2625: }
2626:
2627: /**
2628: * Get the TrAX Transformer object in effect.
2629: *
2630: * @return This object.
2631: */
2632: public Transformer getTransformer() {
2633: return this ;
2634: }
2635:
2636: //==========================================================
2637: // SECTION: Accessor Functions
2638: //==========================================================
2639:
2640: /**
2641: * Set the stylesheet for this processor. If this is set, then the
2642: * process calls that take only the input .xml will use
2643: * this instead of looking for a stylesheet PI. Also,
2644: * setting the stylesheet is needed if you are going
2645: * to use the processor as a SAX ContentHandler.
2646: *
2647: * @param stylesheetRoot A non-null StylesheetRoot object,
2648: * or null if you wish to clear the stylesheet reference.
2649: */
2650: public void setStylesheet(StylesheetRoot stylesheetRoot) {
2651: m_stylesheetRoot = stylesheetRoot;
2652: }
2653:
2654: /**
2655: * Get the current stylesheet for this processor.
2656: *
2657: * @return The stylesheet that is associated with this
2658: * transformer.
2659: */
2660: public final StylesheetRoot getStylesheet() {
2661: return m_stylesheetRoot;
2662: }
2663:
2664: /**
2665: * Get quietConflictWarnings property. If the quietConflictWarnings
2666: * property is set to true, warnings about pattern conflicts won't be
2667: * printed to the diagnostics stream.
2668: *
2669: * @return True if this transformer should not report
2670: * template match conflicts.
2671: */
2672: public boolean getQuietConflictWarnings() {
2673: return m_quietConflictWarnings;
2674: }
2675:
2676: /**
2677: * If the quietConflictWarnings property is set to
2678: * true, warnings about pattern conflicts won't be
2679: * printed to the diagnostics stream.
2680: * False by default.
2681: * (Currently setting this property will have no effect.)
2682: *
2683: * @param b true if conflict warnings should be suppressed.
2684: */
2685: public void setQuietConflictWarnings(boolean b) {
2686: m_quietConflictWarnings = b;
2687: }
2688:
2689: /**
2690: * Set the execution context for XPath.
2691: *
2692: * @param xcontext A non-null reference to the XPathContext
2693: * associated with this transformer.
2694: * @xsl.usage internal
2695: */
2696: public void setXPathContext(XPathContext xcontext) {
2697: m_xcontext = xcontext;
2698: }
2699:
2700: /**
2701: * Get the XPath context associated with this transformer.
2702: *
2703: * @return The XPathContext reference, never null.
2704: */
2705: public final XPathContext getXPathContext() {
2706: return m_xcontext;
2707: }
2708:
2709: /**
2710: * Get the object used to guard the stack from
2711: * recursion.
2712: *
2713: * @return The StackGuard object, which should never be null.
2714: * @xsl.usage internal
2715: */
2716: public StackGuard getStackGuard() {
2717: return m_stackGuard;
2718: }
2719:
2720: /**
2721: * Get the recursion limit.
2722: * Used for infinite loop check. If the value is -1, do not
2723: * check for infinite loops. Anyone who wants to enable that
2724: * check should change the value of this variable to be the
2725: * level of recursion that they want to check. Be careful setting
2726: * this variable, if the number is too low, it may report an
2727: * infinite loop situation, when there is none.
2728: * Post version 1.0.0, we'll make this a runtime feature.
2729: *
2730: * @return The limit on recursion, or -1 if no check is to be made.
2731: */
2732: public int getRecursionLimit() {
2733: return m_stackGuard.getRecursionLimit();
2734: }
2735:
2736: /**
2737: * Set the recursion limit.
2738: * Used for infinite loop check. If the value is -1, do not
2739: * check for infinite loops. Anyone who wants to enable that
2740: * check should change the value of this variable to be the
2741: * level of recursion that they want to check. Be careful setting
2742: * this variable, if the number is too low, it may report an
2743: * infinite loop situation, when there is none.
2744: * Post version 1.0.0, we'll make this a runtime feature.
2745: *
2746: * @param limit A number that represents the limit of recursion,
2747: * or -1 if no checking is to be done.
2748: */
2749: public void setRecursionLimit(int limit) {
2750: m_stackGuard.setRecursionLimit(limit);
2751: }
2752:
2753: /**
2754: * Get the SerializationHandler object.
2755: *
2756: * @return The current SerializationHandler, which may not
2757: * be the main result tree manager.
2758: */
2759: public SerializationHandler getResultTreeHandler() {
2760: return m_serializationHandler;
2761: }
2762:
2763: /**
2764: * Get the SerializationHandler object.
2765: *
2766: * @return The current SerializationHandler, which may not
2767: * be the main result tree manager.
2768: */
2769: public SerializationHandler getSerializationHandler() {
2770: return m_serializationHandler;
2771: }
2772:
2773: /**
2774: * Get the KeyManager object.
2775: *
2776: * @return A reference to the KeyManager object, which should
2777: * never be null.
2778: */
2779: public KeyManager getKeyManager() {
2780: return m_keyManager;
2781: }
2782:
2783: /**
2784: * Check to see if this is a recursive attribute definition.
2785: *
2786: * @param attrSet A non-null ElemAttributeSet reference.
2787: *
2788: * @return true if the attribute set is recursive.
2789: */
2790: public boolean isRecursiveAttrSet(ElemAttributeSet attrSet) {
2791:
2792: if (null == m_attrSetStack) {
2793: m_attrSetStack = new Stack();
2794: }
2795:
2796: if (!m_attrSetStack.empty()) {
2797: int loc = m_attrSetStack.search(attrSet);
2798:
2799: if (loc > -1) {
2800: return true;
2801: }
2802: }
2803:
2804: return false;
2805: }
2806:
2807: /**
2808: * Push an executing attribute set, so we can check for
2809: * recursive attribute definitions.
2810: *
2811: * @param attrSet A non-null ElemAttributeSet reference.
2812: */
2813: public void pushElemAttributeSet(ElemAttributeSet attrSet) {
2814: m_attrSetStack.push(attrSet);
2815: }
2816:
2817: /**
2818: * Pop the current executing attribute set.
2819: */
2820: public void popElemAttributeSet() {
2821: m_attrSetStack.pop();
2822: }
2823:
2824: /**
2825: * Get the table of counters, for optimized xsl:number support.
2826: *
2827: * @return The CountersTable, never null.
2828: */
2829: public CountersTable getCountersTable() {
2830:
2831: if (null == m_countersTable)
2832: m_countersTable = new CountersTable();
2833:
2834: return m_countersTable;
2835: }
2836:
2837: /**
2838: * Tell if the current template rule is null, i.e. if we are
2839: * directly within an apply-templates. Used for xsl:apply-imports.
2840: *
2841: * @return True if the current template rule is null.
2842: */
2843: public boolean currentTemplateRuleIsNull() {
2844: return ((!m_currentTemplateRuleIsNull.isEmpty()) && (m_currentTemplateRuleIsNull
2845: .peek() == true));
2846: }
2847:
2848: /**
2849: * Push true if the current template rule is null, false
2850: * otherwise.
2851: *
2852: * @param b True if the we are executing an xsl:for-each
2853: * (or xsl:call-template?).
2854: */
2855: public void pushCurrentTemplateRuleIsNull(boolean b) {
2856: m_currentTemplateRuleIsNull.push(b);
2857: }
2858:
2859: /**
2860: * Push true if the current template rule is null, false
2861: * otherwise.
2862: */
2863: public void popCurrentTemplateRuleIsNull() {
2864: m_currentTemplateRuleIsNull.pop();
2865: }
2866:
2867: /**
2868: * Push a funcion result for the currently active EXSLT
2869: * <code>func:function</code>.
2870: *
2871: * @param val the result of executing an EXSLT
2872: * <code>func:result</code> instruction for the current
2873: * <code>func:function</code>.
2874: */
2875: public void pushCurrentFuncResult(Object val) {
2876: m_currentFuncResult.push(val);
2877: }
2878:
2879: /**
2880: * Pops the result of the currently active EXSLT <code>func:function</code>.
2881: *
2882: * @return the value of the <code>func:function</code>
2883: */
2884: public Object popCurrentFuncResult() {
2885: return m_currentFuncResult.pop();
2886: }
2887:
2888: /**
2889: * Determines whether an EXSLT <code>func:result</code> instruction has been
2890: * executed for the currently active EXSLT <code>func:function</code>.
2891: *
2892: * @return <code>true</code> if and only if a <code>func:result</code>
2893: * instruction has been executed
2894: */
2895: public boolean currentFuncResultSeen() {
2896: return !m_currentFuncResult.empty()
2897: && m_currentFuncResult.peek() != null;
2898: }
2899:
2900: /**
2901: * Return the message manager.
2902: *
2903: * @return The message manager, never null.
2904: */
2905: public MsgMgr getMsgMgr() {
2906:
2907: if (null == m_msgMgr)
2908: m_msgMgr = new MsgMgr(this );
2909:
2910: return m_msgMgr;
2911: }
2912:
2913: /**
2914: * Set the error event listener.
2915: *
2916: * @param listener The new error listener.
2917: * @throws IllegalArgumentException if
2918: */
2919: public void setErrorListener(ErrorListener listener)
2920: throws IllegalArgumentException {
2921:
2922: synchronized (m_reentryGuard) {
2923: if (listener == null)
2924: throw new IllegalArgumentException(
2925: XSLMessages
2926: .createMessage(
2927: XSLTErrorResources.ER_NULL_ERROR_HANDLER,
2928: null)); //"Null error handler");
2929:
2930: m_errorHandler = listener;
2931: }
2932: }
2933:
2934: /**
2935: * Get the current error event handler.
2936: *
2937: * @return The current error handler, which should never be null.
2938: */
2939: public ErrorListener getErrorListener() {
2940: return m_errorHandler;
2941: }
2942:
2943: /**
2944: * Get an instance of the trace manager for this transformation.
2945: * This object can be used to set trace listeners on various
2946: * events during the transformation.
2947: *
2948: * @return A reference to the TraceManager, never null.
2949: */
2950: public TraceManager getTraceManager() {
2951: return m_traceManager;
2952: }
2953:
2954: /**
2955: * Look up the value of a feature.
2956: *
2957: * <p>The feature name is any fully-qualified URI. It is
2958: * possible for an TransformerFactory to recognize a feature name but
2959: * to be unable to return its value; this is especially true
2960: * in the case of an adapter for a SAX1 Parser, which has
2961: * no way of knowing whether the underlying parser is
2962: * validating, for example.</p>
2963: *
2964: * <h3>Open issues:</h3>
2965: * <dl>
2966: * <dt><h4>Should getFeature be changed to hasFeature?</h4></dt>
2967: * <dd>Keith Visco writes: Should getFeature be changed to hasFeature?
2968: * It returns a boolean which indicated whether the "state"
2969: * of feature is "true or false". I assume this means whether
2970: * or not a feature is supported? I know SAX is using "getFeature",
2971: * but to me "hasFeature" is cleaner.</dd>
2972: * </dl>
2973: *
2974: * @param name The feature name, which is a fully-qualified
2975: * URI.
2976: * @return The current state of the feature (true or false).
2977: * @throws org.xml.sax.SAXNotRecognizedException When the
2978: * TransformerFactory does not recognize the feature name.
2979: * @throws org.xml.sax.SAXNotSupportedException When the
2980: * TransformerFactory recognizes the feature name but
2981: * cannot determine its value at this time.
2982: *
2983: * @throws SAXNotRecognizedException
2984: * @throws SAXNotSupportedException
2985: */
2986: public boolean getFeature(String name)
2987: throws SAXNotRecognizedException, SAXNotSupportedException {
2988:
2989: if ("http://xml.org/trax/features/sax/input".equals(name))
2990: return true;
2991: else if ("http://xml.org/trax/features/dom/input".equals(name))
2992: return true;
2993:
2994: throw new SAXNotRecognizedException(name);
2995: }
2996:
2997: // %TODO% Doc
2998:
2999: /**
3000: * NEEDSDOC Method getMode
3001: *
3002: *
3003: * NEEDSDOC (getMode) @return
3004: */
3005: public QName getMode() {
3006: return m_modes.isEmpty() ? null : (QName) m_modes.peek();
3007: }
3008:
3009: // %TODO% Doc
3010:
3011: /**
3012: * NEEDSDOC Method pushMode
3013: *
3014: *
3015: * NEEDSDOC @param mode
3016: */
3017: public void pushMode(QName mode) {
3018: m_modes.push(mode);
3019: }
3020:
3021: // %TODO% Doc
3022:
3023: /**
3024: * NEEDSDOC Method popMode
3025: *
3026: */
3027: public void popMode() {
3028: m_modes.pop();
3029: }
3030:
3031: /**
3032: * Called by SourceTreeHandler to start the transformation
3033: * in a separate thread
3034: *
3035: * NEEDSDOC @param priority
3036: */
3037: public void runTransformThread(int priority) {
3038:
3039: // used in SourceTreeHandler
3040: Thread t = ThreadControllerWrapper.runThread(this , priority);
3041: this .setTransformThread(t);
3042: }
3043:
3044: /**
3045: * Called by this.transform() if isParserEventsOnMain()==false.
3046: * Similar with runTransformThread(), but no priority is set
3047: * and setTransformThread is not set.
3048: */
3049: public void runTransformThread() {
3050: ThreadControllerWrapper.runThread(this , -1);
3051: }
3052:
3053: /**
3054: * Called by CoRoutineSAXParser. Launches the CoroutineSAXParser
3055: * in a thread, and prepares it to invoke the parser from that thread
3056: * upon request.
3057: *
3058: */
3059: public static void runTransformThread(Runnable runnable) {
3060: ThreadControllerWrapper.runThread(runnable, -1);
3061: }
3062:
3063: /**
3064: * Used by SourceTreeHandler to wait until the transform
3065: * completes
3066: *
3067: * @throws SAXException
3068: */
3069: public void waitTransformThread() throws SAXException {
3070:
3071: // This is called to make sure the task is done.
3072: // It is possible that the thread has been reused -
3073: // but for a different transformation. ( what if we
3074: // recycle the transformer ? Not a problem since this is
3075: // still in use. )
3076: Thread transformThread = this .getTransformThread();
3077:
3078: if (null != transformThread) {
3079: try {
3080: ThreadControllerWrapper.waitThread(transformThread,
3081: this );
3082:
3083: if (!this .hasTransformThreadErrorCatcher()) {
3084: Exception e = this .getExceptionThrown();
3085:
3086: if (null != e) {
3087: e.printStackTrace();
3088: throw new org.xml.sax.SAXException(e);
3089: }
3090: }
3091:
3092: this .setTransformThread(null);
3093: } catch (InterruptedException ie) {
3094: }
3095: }
3096: }
3097:
3098: /**
3099: * Get the exception thrown by the secondary thread (normally
3100: * the transform thread).
3101: *
3102: * @return The thrown exception, or null if no exception was
3103: * thrown.
3104: */
3105: public Exception getExceptionThrown() {
3106: return m_exceptionThrown;
3107: }
3108:
3109: /**
3110: * Set the exception thrown by the secondary thread (normally
3111: * the transform thread).
3112: *
3113: * @param e The thrown exception, or null if no exception was
3114: * thrown.
3115: */
3116: public void setExceptionThrown(Exception e) {
3117: m_exceptionThrown = e;
3118: }
3119:
3120: /**
3121: * This is just a way to set the document for run().
3122: *
3123: * @param doc A non-null reference to the root of the
3124: * tree to be transformed.
3125: */
3126: public void setSourceTreeDocForThread(int doc) {
3127: m_doc = doc;
3128: }
3129:
3130: /**
3131: * Set the input source for the source tree, which is needed if the
3132: * parse thread is not the main thread, in order for the parse
3133: * thread's run method to get to the input source.
3134: *
3135: * @param source The input source for the source tree.
3136: */
3137: public void setXMLSource(Source source) {
3138: m_xmlSource = source;
3139: }
3140:
3141: /**
3142: * Tell if the transform method is completed.
3143: *
3144: * @return True if transformNode has completed, or
3145: * an exception was thrown.
3146: */
3147: public boolean isTransformDone() {
3148:
3149: synchronized (this ) {
3150: return m_isTransformDone;
3151: }
3152: }
3153:
3154: /**
3155: * Set if the transform method is completed.
3156: *
3157: * @param done True if transformNode has completed, or
3158: * an exception was thrown.
3159: */
3160: public void setIsTransformDone(boolean done) {
3161:
3162: synchronized (this ) {
3163: m_isTransformDone = done;
3164: }
3165: }
3166:
3167: /**
3168: * From a secondary thread, post the exception, so that
3169: * it can be picked up from the main thread.
3170: *
3171: * @param e The exception that was thrown.
3172: */
3173: void postExceptionFromThread(Exception e) {
3174:
3175: // Commented out in response to problem reported by Nicola Brown <Nicola.Brown@jacobsrimell.com>
3176: // if(m_reportInPostExceptionFromThread)
3177: // {
3178: // // Consider re-throwing the exception if this flag is set.
3179: // e.printStackTrace();
3180: // }
3181: // %REVIEW Need DTM equivelent?
3182: // if (m_inputContentHandler instanceof SourceTreeHandler)
3183: // {
3184: // SourceTreeHandler sth = (SourceTreeHandler) m_inputContentHandler;
3185: //
3186: // sth.setExceptionThrown(e);
3187: // }
3188: // ContentHandler ch = getContentHandler();
3189:
3190: // if(ch instanceof SourceTreeHandler)
3191: // {
3192: // SourceTreeHandler sth = (SourceTreeHandler) ch;
3193: // ((TransformerImpl)(sth.getTransformer())).postExceptionFromThread(e);
3194: // }
3195: m_isTransformDone = true;
3196: m_exceptionThrown = e;
3197: ; // should have already been reported via the error handler?
3198:
3199: synchronized (this ) {
3200:
3201: // See message from me on 3/27/2001 to Patrick Moore.
3202: // String msg = e.getMessage();
3203: // System.out.println(e.getMessage());
3204: // Is this really needed? -sb
3205: notifyAll();
3206:
3207: // if (null == msg)
3208: // {
3209: //
3210: // // m_throwNewError = false;
3211: // e.printStackTrace();
3212: // }
3213: // throw new org.apache.xml.utils.WrappedRuntimeException(e);
3214: }
3215: }
3216:
3217: /**
3218: * Run the transform thread.
3219: */
3220: public void run() {
3221:
3222: m_hasBeenReset = false;
3223:
3224: try {
3225:
3226: // int n = ((SourceTreeHandler)getInputContentHandler()).getDTMRoot();
3227: // transformNode(n);
3228: try {
3229: m_isTransformDone = false;
3230:
3231: // Should no longer be needed...
3232: // if(m_inputContentHandler instanceof TransformerHandlerImpl)
3233: // {
3234: // TransformerHandlerImpl thi = (TransformerHandlerImpl)m_inputContentHandler;
3235: // thi.waitForInitialEvents();
3236: // }
3237:
3238: transformNode(m_doc);
3239:
3240: } catch (Exception e) {
3241: // e.printStackTrace();
3242:
3243: // Strange that the other catch won't catch this...
3244: if (null != m_transformThread)
3245: postExceptionFromThread(e); // Assume we're on the main thread
3246: else
3247: throw new RuntimeException(e.getMessage());
3248: } finally {
3249: m_isTransformDone = true;
3250:
3251: if (m_inputContentHandler instanceof TransformerHandlerImpl) {
3252: ((TransformerHandlerImpl) m_inputContentHandler)
3253: .clearCoRoutine();
3254: }
3255:
3256: // synchronized (this)
3257: // {
3258: // notifyAll();
3259: // }
3260: }
3261: } catch (Exception e) {
3262:
3263: // e.printStackTrace();
3264: if (null != m_transformThread)
3265: postExceptionFromThread(e);
3266: else
3267: throw new RuntimeException(e.getMessage()); // Assume we're on the main thread.
3268: }
3269: }
3270:
3271: // Fragment re-execution interfaces for a tool.
3272:
3273: /**
3274: * This will get a snapshot of the current executing context
3275: *
3276: *
3277: * @return TransformSnapshot object, snapshot of executing context
3278: * @deprecated This is an internal tooling API that nobody seems to be using
3279: */
3280: public TransformSnapshot getSnapshot() {
3281: return new TransformSnapshotImpl(this );
3282: }
3283:
3284: /**
3285: * This will execute the following XSLT instructions
3286: * from the snapshot point, after the stylesheet execution
3287: * context has been reset from the snapshot point.
3288: *
3289: * @param ts The snapshot of where to start execution
3290: *
3291: * @throws TransformerException
3292: * @deprecated This is an internal tooling API that nobody seems to be using
3293: */
3294: public void executeFromSnapshot(TransformSnapshot ts)
3295: throws TransformerException {
3296:
3297: ElemTemplateElement template = getMatchedTemplate();
3298: int child = getMatchedNode();
3299:
3300: pushElemTemplateElement(template); //needed??
3301: m_xcontext.pushCurrentNode(child); //needed??
3302: this .executeChildTemplates(template, true); // getResultTreeHandler());
3303: }
3304:
3305: /**
3306: * This will reset the stylesheet execution context
3307: * from the snapshot point.
3308: *
3309: * @param ts The snapshot of where to start execution
3310: * @deprecated This is an internal tooling API that nobody seems to be using
3311: */
3312: public void resetToStylesheet(TransformSnapshot ts) {
3313: ((TransformSnapshotImpl) ts).apply(this );
3314: }
3315:
3316: /**
3317: * NEEDSDOC Method stopTransformation
3318: *
3319: */
3320: public void stopTransformation() {
3321: }
3322:
3323: /**
3324: * Test whether whitespace-only text nodes are visible in the logical
3325: * view of <code>DTM</code>. Normally, this function
3326: * will be called by the implementation of <code>DTM</code>;
3327: * it is not normally called directly from
3328: * user code.
3329: *
3330: * @param elementHandle int Handle of the element.
3331: * @return one of NOTSTRIP, STRIP, or INHERIT.
3332: */
3333: public short getShouldStripSpace(int elementHandle, DTM dtm) {
3334:
3335: try {
3336: org.apache.xalan.templates.WhiteSpaceInfo info = m_stylesheetRoot
3337: .getWhiteSpaceInfo(m_xcontext, elementHandle, dtm);
3338:
3339: if (null == info) {
3340: return DTMWSFilter.INHERIT;
3341: } else {
3342:
3343: // System.out.println("getShouldStripSpace: "+info.getShouldStripSpace());
3344: return info.getShouldStripSpace() ? DTMWSFilter.STRIP
3345: : DTMWSFilter.NOTSTRIP;
3346: }
3347: } catch (TransformerException se) {
3348: return DTMWSFilter.INHERIT;
3349: }
3350: }
3351:
3352: /**
3353: * Initializer method.
3354: *
3355: * @param transformer non-null transformer instance
3356: * @param realHandler Content Handler instance
3357: */
3358: public void init(ToXMLSAXHandler h, Transformer transformer,
3359: ContentHandler realHandler) {
3360: h.setTransformer(transformer);
3361: h.setContentHandler(realHandler);
3362: }
3363:
3364: public void setSerializationHandler(SerializationHandler xoh) {
3365: m_serializationHandler = xoh;
3366: }
3367:
3368: /**
3369: * Fire off characters, cdate events.
3370: * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, char[], int, int)
3371: */
3372: public void fireGenerateEvent(int eventType, char[] ch, int start,
3373: int length) {
3374:
3375: GenerateEvent ge = new GenerateEvent(this , eventType, ch,
3376: start, length);
3377: m_traceManager.fireGenerateEvent(ge);
3378: }
3379:
3380: /**
3381: * Fire off startElement, endElement events.
3382: * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, Attributes)
3383: */
3384: public void fireGenerateEvent(int eventType, String name,
3385: Attributes atts) {
3386:
3387: GenerateEvent ge = new GenerateEvent(this , eventType, name,
3388: atts);
3389: m_traceManager.fireGenerateEvent(ge);
3390: }
3391:
3392: /**
3393: * Fire off processingInstruction events.
3394: * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, String)
3395: */
3396: public void fireGenerateEvent(int eventType, String name,
3397: String data) {
3398: GenerateEvent ge = new GenerateEvent(this , eventType, name,
3399: data);
3400: m_traceManager.fireGenerateEvent(ge);
3401: }
3402:
3403: /**
3404: * Fire off comment and entity ref events.
3405: * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String)
3406: */
3407: public void fireGenerateEvent(int eventType, String data) {
3408: GenerateEvent ge = new GenerateEvent(this , eventType, data);
3409: m_traceManager.fireGenerateEvent(ge);
3410: }
3411:
3412: /**
3413: * Fire off startDocument, endDocument events.
3414: * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int)
3415: */
3416: public void fireGenerateEvent(int eventType) {
3417: GenerateEvent ge = new GenerateEvent(this , eventType);
3418: m_traceManager.fireGenerateEvent(ge);
3419: }
3420:
3421: /**
3422: * @see org.apache.xml.serializer.SerializerTrace#hasTraceListeners()
3423: */
3424: public boolean hasTraceListeners() {
3425: return m_traceManager.hasTraceListeners();
3426: }
3427:
3428: public boolean getDebug() {
3429: return m_debug;
3430: }
3431:
3432: public void setDebug(boolean b) {
3433: m_debug = b;
3434: }
3435:
3436: /**
3437: * @return Incremental flag
3438: */
3439: public boolean getIncremental() {
3440: return m_incremental;
3441: }
3442:
3443: /**
3444: * @return Optimization flag
3445: */
3446: public boolean getOptimize() {
3447: return m_optimizer;
3448: }
3449:
3450: /**
3451: * @return Source location flag
3452: */
3453: public boolean getSource_location() {
3454: return m_source_location;
3455: }
3456:
3457: } // end TransformerImpl class
|