001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: TransformerImpl.java 475979 2006-11-16 23:32:48Z minchau $
020     */
021    package org.apache.xalan.transformer;
022    
023    import java.io.IOException;
024    import java.io.StringWriter;
025    import java.util.Enumeration;
026    import java.util.Properties;
027    import java.util.Stack;
028    import java.util.StringTokenizer;
029    import java.util.Vector;
030    
031    import javax.xml.parsers.DocumentBuilder;
032    import javax.xml.parsers.DocumentBuilderFactory;
033    import javax.xml.parsers.ParserConfigurationException;
034    import javax.xml.transform.ErrorListener;
035    import javax.xml.transform.OutputKeys;
036    import javax.xml.transform.Result;
037    import javax.xml.transform.Source;
038    import javax.xml.transform.SourceLocator;
039    import javax.xml.transform.Transformer;
040    import javax.xml.transform.TransformerException;
041    import javax.xml.transform.URIResolver;
042    import javax.xml.transform.dom.DOMResult;
043    import javax.xml.transform.dom.DOMSource;
044    import javax.xml.transform.sax.SAXResult;
045    import javax.xml.transform.sax.SAXSource;
046    import javax.xml.transform.stream.StreamResult;
047    import javax.xml.transform.stream.StreamSource;
048    
049    import org.apache.xalan.extensions.ExtensionsTable;
050    import org.apache.xalan.res.XSLMessages;
051    import org.apache.xalan.res.XSLTErrorResources;
052    import org.apache.xml.serializer.Method;
053    import org.apache.xml.serializer.Serializer;
054    import org.apache.xml.serializer.SerializerFactory;
055    import org.apache.xalan.templates.AVT;
056    import org.apache.xalan.templates.Constants;
057    import org.apache.xalan.templates.ElemAttributeSet;
058    import org.apache.xalan.templates.ElemForEach;
059    import org.apache.xalan.templates.ElemSort;
060    import org.apache.xalan.templates.ElemTemplate;
061    import org.apache.xalan.templates.ElemTemplateElement;
062    import org.apache.xalan.templates.ElemTextLiteral;
063    import org.apache.xalan.templates.ElemVariable;
064    import org.apache.xalan.templates.OutputProperties;
065    import org.apache.xalan.templates.Stylesheet;
066    import org.apache.xalan.templates.StylesheetComposed;
067    import org.apache.xalan.templates.StylesheetRoot;
068    import org.apache.xalan.templates.XUnresolvedVariable;
069    import org.apache.xalan.trace.GenerateEvent;
070    import org.apache.xalan.trace.TraceManager;
071    import org.apache.xml.dtm.DTM;
072    import org.apache.xml.dtm.DTMIterator;
073    import org.apache.xml.dtm.DTMManager;
074    import org.apache.xml.dtm.DTMWSFilter;
075    import org.apache.xml.serializer.ToSAXHandler;
076    import org.apache.xml.serializer.ToTextStream;
077    import org.apache.xml.serializer.ToXMLSAXHandler;
078    import org.apache.xml.serializer.SerializationHandler;
079    import org.apache.xml.utils.BoolStack;
080    import org.apache.xml.utils.DOMBuilder;
081    import org.apache.xml.utils.NodeVector;
082    import org.apache.xml.utils.ObjectPool;
083    import org.apache.xml.utils.ObjectStack;
084    import org.apache.xml.utils.QName;
085    import org.apache.xml.utils.SAXSourceLocator;
086    import org.apache.xml.utils.ThreadControllerWrapper;
087    import org.apache.xpath.Arg;
088    import org.apache.xpath.ExtensionsProvider;
089    import org.apache.xpath.VariableStack;
090    import org.apache.xpath.XPathContext;
091    import org.apache.xpath.functions.FuncExtFunction;
092    import org.apache.xpath.objects.XObject;
093    import org.xml.sax.Attributes;
094    import org.xml.sax.ContentHandler;
095    import org.xml.sax.SAXException;
096    import org.xml.sax.SAXNotRecognizedException;
097    import org.xml.sax.SAXNotSupportedException;
098    import org.xml.sax.ext.DeclHandler;
099    import org.xml.sax.ext.LexicalHandler;
100    
101    /**
102     * This class implements the
103     * {@link javax.xml.transform.Transformer} interface, and is the core
104     * representation of the transformation execution.</p>
105     * @xsl.usage advanced
106     */
107    public class TransformerImpl extends Transformer
108            implements Runnable, DTMWSFilter, ExtensionsProvider, org.apache.xml.serializer.SerializerTrace
109    {
110    
111      // Synch object to gaurd against setting values from the TrAX interface 
112      // or reentry while the transform is going on.
113    
114      /** NEEDSDOC Field m_reentryGuard          */
115      private Boolean m_reentryGuard = new Boolean(true);
116    
117      /**
118       * This is null unless we own the stream.
119       */
120      private java.io.FileOutputStream m_outputStream = null;
121    
122      /**
123       * True if the parser events should be on the main thread,
124       * false if not.  Experemental.  Can not be set right now.
125       */
126      private boolean m_parserEventsOnMain = true;
127    
128      /** The thread that the transformer is running on. */
129      private Thread m_transformThread;
130    
131      /** The base URL of the source tree. */
132      private String m_urlOfSource = null;
133    
134      /** The Result object at the start of the transform, if any. */
135      private Result m_outputTarget = null;
136    
137      /**
138       * The output format object set by the user.  May be null.
139       */
140      private OutputProperties m_outputFormat;
141    
142    
143      /**
144       * The content handler for the source input tree.
145       */
146      ContentHandler m_inputContentHandler;
147    
148      /**
149       * The content handler for the result tree.
150       */
151      private ContentHandler m_outputContentHandler = null;
152    
153      //  /*
154      //   * Use member variable to store param variables as they're
155      //   * being created, use member variable so we don't
156      //   * have to create a new vector every time.
157      //   */
158      //  private Vector m_newVars = new Vector();
159    
160      /** The JAXP Document Builder, mainly to create Result Tree Fragments. */
161      DocumentBuilder m_docBuilder = null;
162    
163      /**
164       * A pool of ResultTreeHandlers, for serialization of a subtree to text.
165       *  Please note that each of these also holds onto a Text Serializer.  
166       */
167      private ObjectPool m_textResultHandlerObjectPool =
168        new ObjectPool(ToTextStream.class);
169    
170      /**
171       * Related to m_textResultHandlerObjectPool, this is a pool of
172       * StringWriters, which are passed to the Text Serializers.
173       * (I'm not sure if this is really needed any more.  -sb)      
174       */
175      private ObjectPool m_stringWriterObjectPool =
176        new ObjectPool(StringWriter.class);
177    
178      /**
179       * A static text format object, which can be used over and
180       * over to create the text serializers.    
181       */
182      private OutputProperties m_textformat = new OutputProperties(Method.TEXT);
183    
184      // Commenteded out in response to problem reported by 
185      // Nicola Brown <Nicola.Brown@jacobsrimell.com>
186      //  /**
187      //   * Flag to let us know if an exception should be reported inside the 
188      //   * postExceptionFromThread method.  This is needed if the transform is 
189      //   * being generated from SAX events, and thus there is no central place 
190      //   * to report the exception from.  (An exception is usually picked up in 
191      //   * the main thread from the transform thread in {@link #transform(Source source)} 
192      //   * from {@link #getExceptionThrown()}. )
193      //   */
194      //  private boolean m_reportInPostExceptionFromThread = false;
195    
196      /**
197       * A node vector used as a stack to track the current
198       * ElemTemplateElement.  Needed for the
199       * org.apache.xalan.transformer.TransformState interface,
200       * so a tool can discover the calling template. Note the use of an array 
201       * for this limits the recursion depth to 4K.
202       */
203      ObjectStack m_currentTemplateElements 
204          = new ObjectStack(XPathContext.RECURSIONLIMIT);
205      
206      /** The top of the currentTemplateElements stack. */
207      //int m_currentTemplateElementsTop = 0;
208    
209      /**
210       * A node vector used as a stack to track the current
211       * ElemTemplate that was matched.
212       * Needed for the
213       * org.apache.xalan.transformer.TransformState interface,
214       * so a tool can discover the matched template
215       */
216      Stack m_currentMatchTemplates = new Stack();
217    
218      /**
219       * A node vector used as a stack to track the current
220       * node that was matched.
221       * Needed for the
222       * org.apache.xalan.transformer.TransformState interface,
223       * so a tool can discover the matched
224       * node. 
225       */
226      NodeVector m_currentMatchedNodes = new NodeVector();
227    
228      /**
229       * The root of a linked set of stylesheets.
230       */
231      private StylesheetRoot m_stylesheetRoot = null;
232    
233      /**
234       * If this is set to true, do not warn about pattern
235       * match conflicts.
236       */
237      private boolean m_quietConflictWarnings = true;
238    
239      /**
240       * The liason to the XML parser, so the XSL processor
241       * can handle included files, and the like, and do the
242       * initial parse of the XSL document.
243       */
244      private XPathContext m_xcontext;
245    
246      /**
247       * Object to guard agains infinite recursion when
248       * doing queries.
249       */
250      private StackGuard m_stackGuard;
251    
252      /**
253       * Output handler to bottleneck SAX events.
254       */
255      private SerializationHandler m_serializationHandler;  
256    
257      /** The key manager, which manages xsl:keys. */
258      private KeyManager m_keyManager = new KeyManager();
259    
260      /**
261       * Stack for the purposes of flagging infinite recursion with
262       * attribute sets.
263       */
264      Stack m_attrSetStack = null;
265    
266      /**
267       * The table of counters for xsl:number support.
268       * @see ElemNumber
269       */
270      CountersTable m_countersTable = null;
271    
272      /**
273       * Is > 0 when we're processing a for-each.
274       */
275      BoolStack m_currentTemplateRuleIsNull = new BoolStack();
276    
277      /**
278       * Keeps track of the result delivered by any EXSLT <code>func:result</code>
279       * instruction that has been executed for the currently active EXSLT
280       * <code>func:function</code>
281       */
282      ObjectStack m_currentFuncResult = new ObjectStack();
283      
284      /**
285       * The message manager, which manages error messages, warning
286       * messages, and other types of message events.   
287       */
288      private MsgMgr m_msgMgr;
289    
290      /**
291       * The flag for the setting of the optimize feature;
292       * This flag should have the same value as the FEATURE_OPTIMIZE feature
293       * which is set by the TransformerFactory.setAttribut() method before a
294       * Transformer is created
295       */    
296      private boolean m_optimizer = true;
297    
298      /**
299       * The flag for the setting of the incremental feature;
300       * This flag should have the same value as the FEATURE_INCREMENTAL feature
301       * which is set by the TransformerFactory.setAttribut() method before a
302       * Transformer is created
303       */    
304      private boolean m_incremental = false;
305    
306      /**
307       * The flag for the setting of the source_location feature;
308       * This flag should have the same value as the FEATURE_SOURCE_LOCATION feature
309       * which is set by the TransformerFactory.setAttribut() method before a
310       * Transformer is created
311       */  
312      private boolean m_source_location = false;
313        
314      /**
315       * This is a compile-time flag to turn off calling
316       * of trace listeners. Set this to false for optimization purposes.
317       */
318      private boolean m_debug = false;
319    
320      /**
321       * The SAX error handler, where errors and warnings are sent.
322       */
323      private ErrorListener m_errorHandler =
324        new org.apache.xml.utils.DefaultErrorHandler(false);
325    
326      /**
327       * The trace manager.
328       */
329      private TraceManager m_traceManager = new TraceManager(this);
330    
331      /**
332       * If the transform thread throws an exception, the exception needs to
333       * be stashed away so that the main thread can pass it on to the
334       * client. 
335       */
336      private Exception m_exceptionThrown = null;
337    
338      /**
339       * The InputSource for the source tree, which is needed if the
340       * parse thread is not the main thread, in order for the parse
341       * thread's run method to get to the input source.
342       * (Delete this if reversing threads is outlawed. -sb)    
343       */
344      private Source m_xmlSource;
345    
346      /**
347       * This is needed for support of setSourceTreeDocForThread(Node doc),
348       * which must be called in order for the transform thread's run
349       * method to obtain the root of the source tree to be transformed.     
350       */
351      private int m_doc;
352    
353      /**
354       * If the the transform is on the secondary thread, we
355       * need to know when it is done, so we can return.
356       */
357      private boolean m_isTransformDone = false;
358    
359      /** Flag to to tell if the tranformer needs to be reset. */
360      private boolean m_hasBeenReset = false;
361    
362      /** NEEDSDOC Field m_shouldReset          */
363      private boolean m_shouldReset = true;
364    
365      /**
366       * NEEDSDOC Method setShouldReset 
367       *
368       *
369       * NEEDSDOC @param shouldReset
370       */
371      public void setShouldReset(boolean shouldReset)
372      {
373        m_shouldReset = shouldReset;
374      }
375    
376      /**
377       * A stack of current template modes.
378       */
379      private Stack m_modes = new Stack();
380    
381      //==========================================================
382      // SECTION: Constructor
383      //==========================================================
384    
385      /**
386       * Construct a TransformerImpl.
387       *
388       * @param stylesheet The root of the stylesheet tree.
389       */
390      public TransformerImpl(StylesheetRoot stylesheet)
391       // throws javax.xml.transform.TransformerException    
392      {
393        m_optimizer = stylesheet.getOptimizer();
394        m_incremental = stylesheet.getIncremental();
395        m_source_location = stylesheet.getSource_location();        
396        setStylesheet(stylesheet);
397        XPathContext xPath = new XPathContext(this);
398        xPath.setIncremental(m_incremental);
399        xPath.getDTMManager().setIncremental(m_incremental);
400        xPath.setSource_location(m_source_location);
401        xPath.getDTMManager().setSource_location(m_source_location);
402        
403        if (stylesheet.isSecureProcessing())
404          xPath.setSecureProcessing(true);
405        
406        setXPathContext(xPath);
407        getXPathContext().setNamespaceContext(stylesheet);
408        m_stackGuard = new StackGuard(this);    
409      }
410      
411      // ================ ExtensionsTable ===================
412    
413      /**
414       * The table of ExtensionHandlers.
415       */
416      private ExtensionsTable m_extensionsTable = null;
417    
418      /**
419       * Get the extensions table object. 
420       *
421       * @return The extensions table.
422       */
423      public ExtensionsTable getExtensionsTable()
424      {
425        return m_extensionsTable;
426      }
427    
428      /**
429       * If the stylesheet contains extensions, set the extensions table object.
430       *
431       *
432       * @param sroot The stylesheet.
433       * @throws javax.xml.transform.TransformerException
434       */
435      void setExtensionsTable(StylesheetRoot sroot)
436           throws javax.xml.transform.TransformerException
437      {
438        try
439        {
440          if (sroot.getExtensions() != null)
441            m_extensionsTable = new ExtensionsTable(sroot);
442        }
443        catch (javax.xml.transform.TransformerException te)
444        {te.printStackTrace();}
445      }
446      
447      //== Implementation of the XPath ExtensionsProvider interface.
448      
449      public boolean functionAvailable(String ns, String funcName)
450              throws javax.xml.transform.TransformerException
451      {
452        return getExtensionsTable().functionAvailable(ns, funcName);
453      }
454      
455      public boolean elementAvailable(String ns, String elemName)
456              throws javax.xml.transform.TransformerException
457      {
458        return getExtensionsTable().elementAvailable(ns, elemName);   
459      }
460       
461      public Object extFunction(String ns, String funcName, 
462                                Vector argVec, Object methodKey)
463                throws javax.xml.transform.TransformerException
464      {//System.out.println("TransImpl.extFunction() " + ns + " " + funcName +" " + getExtensionsTable());
465        return getExtensionsTable().extFunction(ns, funcName, 
466                                            argVec, methodKey,
467                                            getXPathContext().getExpressionContext());   
468      }
469    
470      public Object extFunction(FuncExtFunction extFunction, Vector argVec)
471                throws javax.xml.transform.TransformerException
472      {
473        return getExtensionsTable().extFunction(extFunction, argVec,
474                                                getXPathContext().getExpressionContext());   
475      }
476      
477      //=========================
478    
479      /**
480       * Reset the state.  This needs to be called after a process() call
481       * is invoked, if the processor is to be used again.
482       */
483      public void reset()
484      {
485    
486        if (!m_hasBeenReset && m_shouldReset)
487        {
488          m_hasBeenReset = true;
489    
490          if (this.m_outputStream != null)
491          {
492            try
493            {
494              m_outputStream.close();
495            }
496            catch (java.io.IOException ioe){}
497          }
498    
499          m_outputStream = null;
500    
501          // I need to look more carefully at which of these really
502          // needs to be reset.
503          m_countersTable = null;
504    
505          m_xcontext.reset();
506          
507          m_xcontext.getVarStack().reset();
508          resetUserParameters();
509          
510    
511          m_currentTemplateElements.removeAllElements();     
512          m_currentMatchTemplates.removeAllElements();
513          m_currentMatchedNodes.removeAllElements();
514          
515          m_serializationHandler = null;      
516          m_outputTarget = null;
517          m_keyManager = new KeyManager();
518          m_attrSetStack = null;
519          m_countersTable = null;
520          m_currentTemplateRuleIsNull = new BoolStack();
521          m_xmlSource = null;
522          m_doc = DTM.NULL;
523          m_isTransformDone = false;
524          m_transformThread = null;
525    
526          // m_inputContentHandler = null;
527          // For now, reset the document cache each time.
528          m_xcontext.getSourceTreeManager().reset();
529        }
530    
531        //    m_reportInPostExceptionFromThread = false;
532      }
533    
534      /**
535       * <code>getProperty</code> returns the current setting of the
536       * property described by the <code>property</code> argument.
537       *
538       * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory?
539       *
540       * @param property a <code>String</code> value
541       * @return a <code>boolean</code> value
542       */
543      public boolean getProperty(String property)
544      {
545        return false;
546      }
547    
548      /**
549       * Set a runtime property for this <code>TransformerImpl</code>.
550       *
551       * %REVIEW% Obsolete now that source_location is handled in the TransformerFactory?
552       *
553       * @param property a <code>String</code> value
554       * @param value an <code>Object</code> value
555       */
556      public void setProperty(String property, Object value)
557      {
558      }
559    
560      // ========= Transformer Interface Implementation ==========
561    
562      /**
563       * Get true if the parser events should be on the main thread,
564       * false if not.  Experimental.  Can not be set right now.
565       *
566       * @return true if the parser events should be on the main thread,
567       * false if not.
568       * @xsl.usage experimental
569       */
570      public boolean isParserEventsOnMain()
571      {
572        return m_parserEventsOnMain;
573      }
574    
575      /**
576       * Get the thread that the transform process is on.
577       *
578       * @return The thread that the transform process is on, or null.
579       * @xsl.usage internal
580       */
581      public Thread getTransformThread()
582      {
583        return m_transformThread;
584      }
585    
586      /**
587       * Get the thread that the transform process is on.
588       *
589       * @param t The transform thread, may be null.
590       * @xsl.usage internal
591       */
592      public void setTransformThread(Thread t)
593      {
594        m_transformThread = t;
595      }
596    
597      /** NEEDSDOC Field m_hasTransformThreadErrorCatcher          */
598      private boolean m_hasTransformThreadErrorCatcher = false;
599    
600      /**
601       * Return true if the transform was initiated from the transform method,
602       * otherwise it was probably done from a pure parse events.
603       *
604       * NEEDSDOC ($objectName$) @return
605       */
606      public boolean hasTransformThreadErrorCatcher()
607      {
608        return m_hasTransformThreadErrorCatcher;
609      }
610            
611            /**
612       * Process the source tree to SAX parse events.
613       * @param source  The input for the source tree.
614       *
615       * @throws TransformerException
616       */
617      public void transform(Source source) throws TransformerException
618      {
619                    transform(source, true); 
620            }
621    
622      /**
623       * Process the source tree to SAX parse events.
624       * @param source  The input for the source tree.
625       * @param shouldRelease  Flag indicating whether to release DTMManager.
626       *
627       * @throws TransformerException
628       */
629      public void transform(Source source, boolean shouldRelease) throws TransformerException
630      {
631    
632        try
633        {
634            
635          // Patch for bugzilla #13863.  If we don't reset the namespaceContext
636          // then we will get a NullPointerException if transformer is reused 
637          // (for stylesheets that use xsl:key).  Not sure if this should go 
638          // here or in reset(). -is  
639          if(getXPathContext().getNamespaceContext() == null){
640             getXPathContext().setNamespaceContext(getStylesheet());
641          }
642          String base = source.getSystemId();
643          
644          // If no systemID of the source, use the base of the stylesheet.
645          if(null == base)
646          {
647            base = m_stylesheetRoot.getBaseIdentifier();
648          }
649    
650          // As a last resort, use the current user dir.
651          if(null == base)
652          {
653            String currentDir = "";
654            try {
655              currentDir = System.getProperty("user.dir");
656            }
657            catch (SecurityException se) {}// user.dir not accessible from applet
658                  
659            if (currentDir.startsWith(java.io.File.separator))
660              base = "file://" + currentDir;
661            else
662              base = "file:///" + currentDir;
663            
664            base = base + java.io.File.separatorChar
665                   + source.getClass().getName();
666          }
667          setBaseURLOfSource(base);
668          DTMManager mgr = m_xcontext.getDTMManager();
669          /*
670           * According to JAXP1.2, new SAXSource()/StreamSource()
671           * should create an empty input tree, with a default root node. 
672           * new DOMSource()creates an empty document using DocumentBuilder.
673           * newDocument(); Use DocumentBuilder.newDocument() for all 3 situations,
674           * since there is no clear spec. how to create an empty tree when
675           * both SAXSource() and StreamSource() are used.
676           */
677          if ((source instanceof StreamSource && source.getSystemId()==null &&
678             ((StreamSource)source).getInputStream()==null &&
679             ((StreamSource)source).getReader()==null)||
680             (source instanceof SAXSource &&
681             ((SAXSource)source).getInputSource()==null &&
682             ((SAXSource)source).getXMLReader()==null )||
683             (source instanceof DOMSource && ((DOMSource)source).getNode()==null)){
684            try {
685              DocumentBuilderFactory builderF = 
686                       DocumentBuilderFactory.newInstance();
687              DocumentBuilder builder = builderF.newDocumentBuilder();
688              String systemID = source.getSystemId();
689              source = new DOMSource(builder.newDocument());
690    
691              // Copy system ID from original, empty Source to new Source
692              if (systemID != null) {
693                source.setSystemId(systemID);
694              }
695            } catch (ParserConfigurationException e) {
696              fatalError(e);
697            }           
698          }
699          DTM dtm = mgr.getDTM(source, false, this, true, true);
700          dtm.setDocumentBaseURI(base);
701          
702          boolean hardDelete = true;  // %REVIEW% I have to think about this. -sb
703    
704          try
705          {
706            // NOTE: This will work because this is _NOT_ a shared DTM, and thus has
707            // only a single Document node. If it could ever be an RTF or other
708            // shared DTM, look at dtm.getDocumentRoot(nodeHandle).
709            this.transformNode(dtm.getDocument());
710          }
711          finally
712          {
713            if (shouldRelease)
714              mgr.release(dtm, hardDelete);
715          }
716    
717          // Kick off the parse.  When the ContentHandler gets 
718          // the startDocument event, it will call transformNode( node ).
719          // reader.parse( xmlSource );
720          // This has to be done to catch exceptions thrown from 
721          // the transform thread spawned by the STree handler.
722          Exception e = getExceptionThrown();
723    
724          if (null != e)
725          {
726            if (e instanceof javax.xml.transform.TransformerException)
727            {
728              throw (javax.xml.transform.TransformerException) e;
729            }
730            else if (e instanceof org.apache.xml.utils.WrappedRuntimeException)
731            {
732              fatalError(
733                  ((org.apache.xml.utils.WrappedRuntimeException) e).getException());
734            }
735            else
736            {
737              throw new javax.xml.transform.TransformerException(e);
738            }
739          }
740          else if (null != m_serializationHandler)
741          {
742            m_serializationHandler.endDocument();
743          }
744        }
745        catch (org.apache.xml.utils.WrappedRuntimeException wre)
746        {
747          Throwable throwable = wre.getException();
748    
749          while (throwable
750                 instanceof org.apache.xml.utils.WrappedRuntimeException)
751          {
752            throwable =
753              ((org.apache.xml.utils.WrappedRuntimeException) throwable).getException();
754          }
755    
756          fatalError(throwable);
757        }
758    
759        // Patch attributed to David Eisenberg <david@catcode.com>
760        catch (org.xml.sax.SAXParseException spe)
761        {
762          fatalError(spe);
763        }
764        catch (org.xml.sax.SAXException se)
765        {
766          m_errorHandler.fatalError(new TransformerException(se));
767        }
768        finally
769        {
770          m_hasTransformThreadErrorCatcher = false;
771    
772          // This looks to be redundent to the one done in TransformNode.
773          reset();
774        }
775      }
776      
777      private void fatalError(Throwable throwable) throws TransformerException
778      {
779        if (throwable instanceof org.xml.sax.SAXParseException)
780          m_errorHandler.fatalError(new TransformerException(throwable.getMessage(),new SAXSourceLocator((org.xml.sax.SAXParseException)throwable)));
781        else
782          m_errorHandler.fatalError(new TransformerException(throwable));
783        
784      }
785    
786      /**
787       * Get the base URL of the source.
788       *
789       * @return The base URL of the source tree, or null.
790       */
791      public String getBaseURLOfSource()
792      {
793        return m_urlOfSource;
794      }
795    
796      /**
797       * Get the base URL of the source.
798       *
799       *
800       * NEEDSDOC @param base
801       * @return The base URL of the source tree, or null.
802       */
803      public void setBaseURLOfSource(String base)
804      {
805        m_urlOfSource = base;
806      }
807    
808      /**
809       * Get the original output target.
810       *
811       * @return The Result object used to kick of the transform or null.
812       */
813      public Result getOutputTarget()
814      {
815        return m_outputTarget;
816      }
817    
818      /**
819       * Set the original output target.  This is useful when using a SAX transform and
820       * supplying a ContentHandler or when the URI of the output target should
821       * not be the same as the systemID of the original output target.
822       *
823       *
824       * NEEDSDOC @param outputTarget
825       */
826      public void setOutputTarget(Result outputTarget)
827      {
828        m_outputTarget = outputTarget;
829      }
830    
831      /**
832       * Get an output property that is in effect for the
833       * transformation.  The property specified may be a property
834       * that was set with setOutputProperty, or it may be a
835       * property specified in the stylesheet.
836       *
837       * NEEDSDOC @param qnameString
838       *
839       * @return The string value of the output property, or null
840       * if no property was found.
841       *
842       * @throws IllegalArgumentException If the property is not supported.
843       *
844       * @see javax.xml.transform.OutputKeys
845       */
846      public String getOutputProperty(String qnameString)
847              throws IllegalArgumentException
848      {
849    
850        String value = null;
851        OutputProperties props = getOutputFormat();
852    
853        value = props.getProperty(qnameString);
854    
855        if (null == value)
856        {
857          if (!OutputProperties.isLegalPropertyKey(qnameString))
858            throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: "
859                                               //+ qnameString);
860        }
861    
862        return value;
863      }
864    
865      /**
866       * Get the value of a property, without using the default properties.  This
867       * can be used to test if a property has been explicitly set by the stylesheet
868       * or user.
869       *
870       * NEEDSDOC @param qnameString
871       *
872       * @return The value of the property, or null if not found.
873       *
874       * @throws IllegalArgumentException If the property is not supported,
875       * and is not namespaced.
876       */
877      public String getOutputPropertyNoDefault(String qnameString)
878              throws IllegalArgumentException
879      {
880    
881        String value = null;
882        OutputProperties props = getOutputFormat();
883    
884        value = (String) props.getProperties().get(qnameString);
885    
886        if (null == value)
887        {
888          if (!OutputProperties.isLegalPropertyKey(qnameString))
889            throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{qnameString})); //"output property not recognized: "
890                                              // + qnameString);
891        }
892    
893        return value;
894      }
895    
896      /**
897       * This method is used to set or override the value
898       * of the effective xsl:output attribute values
899       * specified in the stylesheet.
900       * <p>
901       * The recognized standard output properties are:
902       * <ul>
903       * <li>cdata-section-elements
904       * <li>doctype-system
905       * <li>doctype-public
906       * <li>indent
907       * <li>media-type
908       * <li>method
909       * <li>omit-xml-declaration
910       * <li>standalone
911       * <li>version
912       * </ul>
913       * <p>
914       * For example:
915       * <pre>
916       *   tran.setOutputProperty("standalone", "yes");
917       * </pre>
918       * <p>
919       * In the case of the cdata-section-elements property,
920       * the value should be a whitespace separated list of
921       * element names.  The element name is the local name
922       * of the element, if it is in no namespace, or, the URI
923       * in braces followed immediately by the local name
924       * if the element is in that namespace. For example: 
925       * <pre>
926       * tran.setOutputProperty(
927       *   "cdata-section-elements", 
928       *   "elem1 {http://example.uri}elem2 elem3");
929       * </pre>
930       * <p>
931       * The recognized Xalan extension elements are: 
932       * <ul>
933       * <li>content-handler
934       * <li>entities
935       * <li>indent-amount
936       * <li>line-separator
937       * <li>omit-meta-tag
938       * <li>use-url-escaping
939       * </ul>
940       * <p>
941       * These must be in the extension namespace of
942       * "http://xml.apache.org/xalan".  This is accomplished
943       * by putting the namespace URI in braces before the 
944       * property name, for example:
945       * <pre>
946       *   tran.setOutputProperty(
947       *     "{http://xml.apache.org/xalan}line-separator" ,
948       *     "\n");
949       * </pre> 
950       *
951       * @param name The property name.
952       * @param value The requested value for the property.
953       * @throws IllegalArgumentException if the property name is not legal.
954       */
955      public void setOutputProperty(String name, String value)
956              throws IllegalArgumentException
957      {
958    
959        synchronized (m_reentryGuard)
960        {
961    
962          // Get the output format that was set by the user, otherwise get the 
963          // output format from the stylesheet.
964          if (null == m_outputFormat)
965          {
966            m_outputFormat =
967              (OutputProperties) getStylesheet().getOutputComposed().clone();
968          }
969    
970          if (!OutputProperties.isLegalPropertyKey(name))
971            throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_OUTPUT_PROPERTY_NOT_RECOGNIZED, new Object[]{name})); //"output property not recognized: "
972                                               //+ name);
973    
974          m_outputFormat.setProperty(name, value);
975        }
976      }
977    
978      /**
979       * Set the output properties for the transformation.  These
980       * properties will override properties set in the templates
981       * with xsl:output.
982       *
983       * <p>If argument to this function is null, any properties
984       * previously set will be removed.</p>
985       *
986       * @param oformat A set of output properties that will be
987       * used to override any of the same properties in effect
988       * for the transformation.
989       *
990       * @see javax.xml.transform.OutputKeys
991       * @see java.util.Properties
992       *
993       * @throws IllegalArgumentException if any of the argument keys are not
994       * recognized and are not namespace qualified.   
995       */
996      public void setOutputProperties(Properties oformat)
997                    throws IllegalArgumentException
998      {
999    
1000        synchronized (m_reentryGuard)
1001        {
1002          if (null != oformat)
1003          {
1004    
1005            // See if an *explicit* method was set.
1006            String method = (String) oformat.get(OutputKeys.METHOD);
1007    
1008            if (null != method)
1009              m_outputFormat = new OutputProperties(method);
1010            else if(m_outputFormat==null)
1011              m_outputFormat = new OutputProperties();
1012    
1013            m_outputFormat.copyFrom(oformat);
1014            // copyFrom does not set properties that have been already set, so 
1015            // this must be called after, which is a bit in the reverse from 
1016            // what one might think.
1017            m_outputFormat.copyFrom(m_stylesheetRoot.getOutputProperties());
1018          }
1019          else {
1020            // if oformat is null JAXP says that any props previously set are removed
1021            // and we are to revert back to those in the templates object (i.e. Stylesheet).
1022            m_outputFormat = null;
1023          }
1024        }
1025      }
1026    
1027      /**
1028       * Get a copy of the output properties for the transformation.  These
1029       * properties will override properties set in the templates
1030       * with xsl:output.
1031       *
1032       * <p>Note that mutation of the Properties object returned will not
1033       * effect the properties that the transformation contains.</p>
1034       *
1035       * @return  A copy of the set of output properties in effect
1036       * for the next transformation.
1037       *
1038       * NEEDSDOC ($objectName$) @return
1039       */
1040      public Properties getOutputProperties()
1041      {
1042        return (Properties) getOutputFormat().getProperties().clone();
1043      }
1044    
1045        /**
1046         * Create a result ContentHandler from a Result object, based
1047         * on the current OutputProperties.
1048         *
1049         * @param outputTarget Where the transform result should go,
1050         * should not be null.
1051         *
1052         * @return A valid ContentHandler that will create the
1053         * result tree when it is fed SAX events.
1054         *
1055         * @throws TransformerException
1056         */
1057        public SerializationHandler createSerializationHandler(Result outputTarget)
1058                throws TransformerException
1059        {
1060           SerializationHandler xoh =
1061            createSerializationHandler(outputTarget, getOutputFormat());
1062           return xoh;
1063        }
1064    
1065        /**
1066         * Create a ContentHandler from a Result object and an OutputProperties.
1067         *
1068         * @param outputTarget Where the transform result should go,
1069         * should not be null.
1070         * @param format The OutputProperties object that will contain
1071         * instructions on how to serialize the output.
1072         *
1073         * @return A valid ContentHandler that will create the
1074         * result tree when it is fed SAX events.
1075         *
1076         * @throws TransformerException
1077         */
1078        public SerializationHandler createSerializationHandler(
1079                Result outputTarget, OutputProperties format)
1080                  throws TransformerException
1081        {
1082    
1083          SerializationHandler xoh;
1084    
1085          // If the Result object contains a Node, then create
1086          // a ContentHandler that will add nodes to the input node.
1087          org.w3c.dom.Node outputNode = null;
1088    
1089          if (outputTarget instanceof DOMResult)
1090          {
1091            outputNode = ((DOMResult) outputTarget).getNode();
1092            org.w3c.dom.Node nextSibling = ((DOMResult)outputTarget).getNextSibling();
1093    
1094            org.w3c.dom.Document doc;
1095            short type;
1096    
1097            if (null != outputNode)
1098            {
1099              type = outputNode.getNodeType();
1100              doc = (org.w3c.dom.Node.DOCUMENT_NODE == type)
1101                    ? (org.w3c.dom.Document) outputNode
1102                    : outputNode.getOwnerDocument();
1103            }
1104            else
1105            {
1106              boolean isSecureProcessing = m_stylesheetRoot.isSecureProcessing();
1107              doc = org.apache.xml.utils.DOMHelper.createDocument(isSecureProcessing);
1108              outputNode = doc;
1109              type = outputNode.getNodeType();
1110    
1111              ((DOMResult) outputTarget).setNode(outputNode);
1112            }
1113    
1114            DOMBuilder handler =
1115              (org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE == type)
1116              ? new DOMBuilder(doc, (org.w3c.dom.DocumentFragment) outputNode)
1117              : new DOMBuilder(doc, outputNode);
1118            
1119            if (nextSibling != null)
1120              handler.setNextSibling(nextSibling);
1121            
1122              String encoding = format.getProperty(OutputKeys.ENCODING);          
1123              xoh = new ToXMLSAXHandler(handler, (LexicalHandler)handler, encoding);
1124          }
1125          else if (outputTarget instanceof SAXResult)
1126          {
1127            ContentHandler handler = ((SAXResult) outputTarget).getHandler();
1128            
1129            if (null == handler)
1130               throw new IllegalArgumentException(
1131                 "handler can not be null for a SAXResult"); 
1132                 
1133            LexicalHandler lexHandler;
1134            if (handler instanceof LexicalHandler)     
1135                lexHandler = (LexicalHandler)  handler;
1136            else
1137                lexHandler = null;
1138                
1139            String encoding = format.getProperty(OutputKeys.ENCODING); 
1140            String method = format.getProperty(OutputKeys.METHOD);
1141    
1142            ToXMLSAXHandler toXMLSAXHandler = new ToXMLSAXHandler(handler, lexHandler, encoding);
1143            toXMLSAXHandler.setShouldOutputNSAttr(false);
1144            xoh = toXMLSAXHandler;   
1145    
1146    
1147            String publicID = format.getProperty(OutputKeys.DOCTYPE_PUBLIC); 
1148            String systemID = format.getProperty(OutputKeys.DOCTYPE_SYSTEM); 
1149            if (systemID != null)
1150                xoh.setDoctypeSystem(systemID);
1151            if (publicID != null)
1152                xoh.setDoctypePublic(publicID);
1153            
1154            if (handler instanceof TransformerClient) {
1155                XalanTransformState state = new XalanTransformState();
1156                ((TransformerClient)handler).setTransformState(state);
1157                ((ToSAXHandler)xoh).setTransformState(state);
1158            }
1159    
1160     
1161          }
1162    
1163          // Otherwise, create a ContentHandler that will serialize the
1164          // result tree to either a stream or a writer.
1165          else if (outputTarget instanceof StreamResult)
1166          {
1167            StreamResult sresult = (StreamResult) outputTarget;
1168    
1169            try
1170            {
1171              SerializationHandler serializer =
1172                (SerializationHandler) SerializerFactory.getSerializer(format.getProperties());
1173    
1174              if (null != sresult.getWriter())
1175                serializer.setWriter(sresult.getWriter());
1176              else if (null != sresult.getOutputStream())
1177                serializer.setOutputStream(sresult.getOutputStream());
1178              else if (null != sresult.getSystemId())
1179              {
1180                String fileURL = sresult.getSystemId();
1181    
1182                if (fileURL.startsWith("file:///"))
1183                {
1184                  if (fileURL.substring(8).indexOf(":") >0)
1185                    fileURL = fileURL.substring(8);
1186                  else
1187                    fileURL = fileURL.substring(7);
1188                }
1189                else if (fileURL.startsWith("file:/"))
1190                {
1191                    if (fileURL.substring(6).indexOf(":") >0)
1192                        fileURL = fileURL.substring(6);
1193                      else
1194                        fileURL = fileURL.substring(5);             
1195                }
1196    
1197                m_outputStream = new java.io.FileOutputStream(fileURL);
1198    
1199                serializer.setOutputStream(m_outputStream);
1200                
1201                xoh = serializer;
1202              }
1203              else
1204                throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_NO_OUTPUT_SPECIFIED, null)); //"No output specified!");
1205    
1206              // handler = serializer.asContentHandler();
1207    
1208            //  this.setSerializer(serializer);
1209    
1210              xoh = serializer;  
1211            }
1212    //        catch (UnsupportedEncodingException uee)
1213    //        {
1214    //          throw new TransformerException(uee);
1215    //        }
1216            catch (IOException ioe)
1217            {
1218              throw new TransformerException(ioe);
1219            }
1220          }
1221          else
1222          {
1223            throw new TransformerException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_TRANSFORM_TO_RESULT_TYPE, new Object[]{outputTarget.getClass().getName()})); //"Can't transform to a Result of type "
1224                                           //+ outputTarget.getClass().getName()
1225                                           //+ "!");
1226          }
1227          
1228          // before we forget, lets make the created handler hold a reference
1229          // to the current TransformImpl object
1230          xoh.setTransformer(this);
1231    
1232          SourceLocator srcLocator = getStylesheet();
1233          xoh.setSourceLocator(srcLocator);
1234          
1235          
1236          return xoh;
1237    
1238       
1239        }
1240            
1241            /**
1242       * Process the source tree to the output result.
1243       * @param xmlSource  The input for the source tree.
1244       * @param outputTarget The output source target.
1245       *
1246       * @throws TransformerException
1247       */
1248      public void transform(Source xmlSource, Result outputTarget)
1249              throws TransformerException
1250      {
1251                    transform(xmlSource, outputTarget, true);
1252            }
1253    
1254      /**
1255       * Process the source tree to the output result.
1256       * @param xmlSource  The input for the source tree.
1257       * @param outputTarget The output source target.
1258       * @param shouldRelease  Flag indicating whether to release DTMManager. 
1259       *
1260       * @throws TransformerException
1261       */
1262      public void transform(Source xmlSource, Result outputTarget, boolean shouldRelease)
1263              throws TransformerException
1264      {
1265    
1266        synchronized (m_reentryGuard)
1267        {
1268          SerializationHandler xoh = createSerializationHandler(outputTarget);
1269          this.setSerializationHandler(xoh);        
1270    
1271          m_outputTarget = outputTarget;
1272    
1273          transform(xmlSource, shouldRelease);
1274        }
1275      }
1276    
1277      /**
1278       * Process the source node to the output result, if the
1279       * processor supports the "http://xml.org/trax/features/dom/input"
1280       * feature.
1281       * %REVIEW% Do we need a Node version of this?
1282       * @param node  The input source node, which can be any valid DTM node.
1283       * @param outputTarget The output source target.
1284       *
1285       * @throws TransformerException
1286       */
1287      public void transformNode(int node, Result outputTarget)
1288              throws TransformerException
1289      {
1290        
1291    
1292        SerializationHandler xoh = createSerializationHandler(outputTarget);
1293        this.setSerializationHandler(xoh);
1294    
1295        m_outputTarget = outputTarget;
1296    
1297        transformNode(node);
1298      }
1299    
1300      /**
1301       * Process the source node to the output result, if the
1302       * processor supports the "http://xml.org/trax/features/dom/input"
1303       * feature.
1304       * %REVIEW% Do we need a Node version of this?
1305       * @param node  The input source node, which can be any valid DTM node.
1306       *
1307       * @throws TransformerException
1308       */
1309      public void transformNode(int node) throws TransformerException
1310      {
1311        //dml
1312        setExtensionsTable(getStylesheet());
1313        // Make sure we're not writing to the same output content handler.
1314        synchronized (m_serializationHandler)
1315        {
1316          m_hasBeenReset = false;
1317          
1318          XPathContext xctxt = getXPathContext();
1319          DTM dtm = xctxt.getDTM(node);
1320    
1321          try
1322          {
1323            pushGlobalVars(node);
1324    
1325            // ==========
1326            // Give the top-level templates a chance to pass information into 
1327            // the context (this is mainly for setting up tables for extensions).
1328            StylesheetRoot stylesheet = this.getStylesheet();
1329            int n = stylesheet.getGlobalImportCount();
1330    
1331            for (int i = 0; i < n; i++)
1332            {
1333              StylesheetComposed imported = stylesheet.getGlobalImport(i);
1334              int includedCount = imported.getIncludeCountComposed();
1335    
1336              for (int j = -1; j < includedCount; j++)
1337              {
1338                Stylesheet included = imported.getIncludeComposed(j);
1339    
1340                included.runtimeInit(this);
1341    
1342                for (ElemTemplateElement child = included.getFirstChildElem();
1343                        child != null; child = child.getNextSiblingElem())
1344                {
1345                  child.runtimeInit(this);
1346                }
1347              }
1348            }
1349            // ===========        
1350            // System.out.println("Calling applyTemplateToNode - "+Thread.currentThread().getName());
1351            DTMIterator dtmIter = new org.apache.xpath.axes.SelfIteratorNoPredicate();
1352            dtmIter.setRoot(node, xctxt);
1353            xctxt.pushContextNodeList(dtmIter);
1354            try
1355            {
1356              this.applyTemplateToNode(null, null, node);
1357            }
1358            finally
1359            {
1360              xctxt.popContextNodeList();
1361            }
1362            // m_stylesheetRoot.getStartRule().execute(this);
1363    
1364            // System.out.println("Done with applyTemplateToNode - "+Thread.currentThread().getName());
1365            if (null != m_serializationHandler)
1366            {
1367              m_serializationHandler.endDocument();
1368            }
1369          }
1370          catch (Exception se)
1371          {
1372    
1373            // System.out.println(Thread.currentThread().getName()+" threw an exception! "
1374            //                   +se.getMessage());
1375            // If an exception was thrown, we need to make sure that any waiting 
1376            // handlers can terminate, which I guess is best done by sending 
1377            // an endDocument.
1378            
1379            // SAXSourceLocator
1380            while(se instanceof org.apache.xml.utils.WrappedRuntimeException)
1381            {
1382              Exception e = ((org.apache.xml.utils.WrappedRuntimeException)se).getException();
1383              if(null != e)
1384                se = e;
1385            }
1386            
1387            if (null != m_serializationHandler)
1388            {
1389              try
1390              {
1391                if(se instanceof org.xml.sax.SAXParseException)
1392                  m_serializationHandler.fatalError((org.xml.sax.SAXParseException)se);
1393                else if(se instanceof TransformerException)
1394                {
1395                  TransformerException te = ((TransformerException)se);
1396                  SAXSourceLocator sl = new SAXSourceLocator( te.getLocator() );
1397                  m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(te.getMessage(), sl, te)); 
1398                }
1399                else
1400                {
1401                  m_serializationHandler.fatalError(new org.xml.sax.SAXParseException(se.getMessage(), new SAXSourceLocator(), se)); 
1402                }             
1403              }
1404              catch (Exception e){}
1405            }        
1406            
1407            if(se instanceof TransformerException)
1408            {
1409              m_errorHandler.fatalError((TransformerException)se);
1410            }
1411            else if(se instanceof org.xml.sax.SAXParseException)
1412            {
1413              m_errorHandler.fatalError(new TransformerException(se.getMessage(), 
1414                          new SAXSourceLocator((org.xml.sax.SAXParseException)se), 
1415                          se));
1416            }
1417            else
1418            {
1419              m_errorHandler.fatalError(new TransformerException(se));
1420            }
1421            
1422          }
1423          finally
1424          {
1425            this.reset();
1426          }
1427        }
1428      }
1429    
1430      /**
1431       * Get a SAX2 ContentHandler for the input.
1432       *
1433       * @return A valid ContentHandler, which should never be null, as
1434       * long as getFeature("http://xml.org/trax/features/sax/input")
1435       * returns true.
1436       */
1437      public ContentHandler getInputContentHandler()
1438      {
1439        return getInputContentHandler(false);
1440      }
1441    
1442      /**
1443       * Get a SAX2 ContentHandler for the input.
1444       *
1445       * @param doDocFrag true if a DocumentFragment should be created as
1446       * the root, rather than a Document.
1447       *
1448       * @return A valid ContentHandler, which should never be null, as
1449       * long as getFeature("http://xml.org/trax/features/sax/input")
1450       * returns true.
1451       */
1452      public ContentHandler getInputContentHandler(boolean doDocFrag)
1453      {
1454    
1455        if (null == m_inputContentHandler)
1456        {
1457    
1458          //      if(null == m_urlOfSource && null != m_stylesheetRoot)
1459          //        m_urlOfSource = m_stylesheetRoot.getBaseIdentifier();
1460          m_inputContentHandler = new TransformerHandlerImpl(this, doDocFrag,
1461                  m_urlOfSource);
1462        }
1463    
1464        return m_inputContentHandler;
1465      }
1466    
1467      /**
1468       * Get a SAX2 DeclHandler for the input.
1469       * @return A valid DeclHandler, which should never be null, as
1470       * long as getFeature("http://xml.org/trax/features/sax/input")
1471       * returns true.
1472       */
1473      public DeclHandler getInputDeclHandler()
1474      {
1475    
1476        if (m_inputContentHandler instanceof DeclHandler)
1477          return (DeclHandler) m_inputContentHandler;
1478        else
1479          return null;
1480      }
1481    
1482      /**
1483       * Get a SAX2 LexicalHandler for the input.
1484       * @return A valid LexicalHandler, which should never be null, as
1485       * long as getFeature("http://xml.org/trax/features/sax/input")
1486       * returns true.
1487       */
1488      public LexicalHandler getInputLexicalHandler()
1489      {
1490    
1491        if (m_inputContentHandler instanceof LexicalHandler)
1492          return (LexicalHandler) m_inputContentHandler;
1493        else
1494          return null;
1495      }
1496    
1497      /**
1498       * Set the output properties for the transformation.  These
1499       * properties will override properties set in the templates
1500       * with xsl:output.
1501       *
1502       * @param oformat A valid OutputProperties object (which will
1503       * not be mutated), or null.
1504       */
1505      public void setOutputFormat(OutputProperties oformat)
1506      {
1507        m_outputFormat = oformat;
1508      }
1509    
1510      /**
1511       * Get the output properties used for the transformation.
1512       *
1513       * @return the output format that was set by the user,
1514       * otherwise the output format from the stylesheet.
1515       */
1516      public OutputProperties getOutputFormat()
1517      {
1518    
1519        // Get the output format that was set by the user, otherwise get the 
1520        // output format from the stylesheet.
1521        OutputProperties format = (null == m_outputFormat)
1522                                  ? getStylesheet().getOutputComposed()
1523                                  : m_outputFormat;
1524    
1525        return format;
1526      }
1527    
1528      /**
1529       * Set a parameter for the templates.
1530       * 
1531       * @param name The name of the parameter.
1532       * @param namespace The namespace of the parameter.
1533       * @param value The value object.  This can be any valid Java object
1534       * -- it's up to the processor to provide the proper
1535       * coersion to the object, or simply pass it on for use
1536       * in extensions.
1537       */
1538      public void setParameter(String name, String namespace, Object value)
1539      {
1540    
1541        VariableStack varstack = getXPathContext().getVarStack();
1542        QName qname = new QName(namespace, name);
1543        XObject xobject = XObject.create(value, getXPathContext());
1544        
1545        StylesheetRoot sroot = m_stylesheetRoot;
1546        Vector vars = sroot.getVariablesAndParamsComposed();
1547        int i = vars.size();
1548        while (--i >= 0)
1549        {
1550          ElemVariable variable = (ElemVariable)vars.elementAt(i);
1551          if(variable.getXSLToken() == Constants.ELEMNAME_PARAMVARIABLE && 
1552             variable.getName().equals(qname))
1553          {
1554              varstack.setGlobalVariable(i, xobject);
1555          }
1556        }
1557      }
1558    
1559      /** NEEDSDOC Field m_userParams          */
1560      Vector m_userParams;
1561    
1562      /**
1563       * Set a parameter for the transformation.
1564       *
1565       * @param name The name of the parameter,
1566       *             which may have a namespace URI.
1567       * @param value The value object.  This can be any valid Java object
1568       * -- it's up to the processor to provide the proper
1569       * coersion to the object, or simply pass it on for use
1570       * in extensions.
1571       */
1572      public void setParameter(String name, Object value)
1573      {
1574        
1575        if (value == null) {
1576          throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_INVALID_SET_PARAM_VALUE, new Object[]{name}));
1577        }    
1578    
1579        StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
1580    
1581        try
1582        {
1583    
1584          // The first string might be the namespace, or it might be 
1585          // the local name, if the namespace is null.
1586          String s1 = tokenizer.nextToken();
1587          String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
1588    
1589          if (null == m_userParams)
1590            m_userParams = new Vector();
1591    
1592          if (null == s2)
1593          {
1594            replaceOrPushUserParam(new QName(s1), XObject.create(value, getXPathContext()));
1595            setParameter(s1, null, value);
1596          }
1597          else
1598          {
1599            replaceOrPushUserParam(new QName(s1, s2), XObject.create(value, getXPathContext()));
1600            setParameter(s2, s1, value);
1601          }
1602        }
1603        catch (java.util.NoSuchElementException nsee)
1604        {
1605    
1606          // Should throw some sort of an error.
1607        }
1608      }
1609    
1610      /**
1611       * NEEDSDOC Method replaceOrPushUserParam 
1612       *
1613       *
1614       * NEEDSDOC @param qname
1615       * NEEDSDOC @param xval
1616       */
1617      private void replaceOrPushUserParam(QName qname, XObject xval)
1618      {
1619    
1620        int n = m_userParams.size();
1621    
1622        for (int i = n - 1; i >= 0; i--)
1623        {
1624          Arg arg = (Arg) m_userParams.elementAt(i);
1625    
1626          if (arg.getQName().equals(qname))
1627          {
1628            m_userParams.setElementAt(new Arg(qname, xval, true), i);
1629    
1630            return;
1631          }
1632        }
1633    
1634        m_userParams.addElement(new Arg(qname, xval, true));
1635      }
1636    
1637      /**
1638       * Get a parameter that was explicitly set with setParameter
1639       * or setParameters.
1640       *
1641       *
1642       * NEEDSDOC @param name
1643       * @return A parameter that has been set with setParameter
1644       * or setParameters,
1645       * *not* all the xsl:params on the stylesheet (which require
1646       * a transformation Source to be evaluated).
1647       */
1648      public Object getParameter(String name)
1649      {
1650    
1651        try
1652        {
1653    
1654          // VariableStack varstack = getXPathContext().getVarStack();
1655          // The first string might be the namespace, or it might be 
1656          // the local name, if the namespace is null.
1657          QName qname = QName.getQNameFromString(name);
1658    
1659          if (null == m_userParams)
1660            return null;
1661    
1662          int n = m_userParams.size();
1663    
1664          for (int i = n - 1; i >= 0; i--)
1665          {
1666            Arg arg = (Arg) m_userParams.elementAt(i);
1667    
1668            if (arg.getQName().equals(qname))
1669            {
1670              return arg.getVal().object();
1671            }
1672          }
1673    
1674          return null;
1675        }
1676        catch (java.util.NoSuchElementException nsee)
1677        {
1678    
1679          // Should throw some sort of an error.
1680          return null;
1681        }
1682      }
1683      
1684      /**
1685       * Reset parameters that the user specified for the transformation.
1686       * Called during transformer.reset() after we have cleared the 
1687       * variable stack. We need to make sure that user params are
1688       * reset so that the transformer object can be reused. 
1689       */
1690      private void resetUserParameters()
1691      {
1692    
1693        try
1694        {
1695          
1696          if (null == m_userParams)
1697            return;
1698    
1699          int n = m_userParams.size();
1700          for (int i = n - 1; i >= 0; i--)
1701          {
1702            Arg arg = (Arg) m_userParams.elementAt(i);
1703            QName name = arg.getQName();
1704            // The first string might be the namespace, or it might be 
1705            // the local name, if the namespace is null.
1706            String s1 = name.getNamespace();
1707            String s2 = name.getLocalPart();
1708    
1709            setParameter(s2, s1, arg.getVal().object());
1710            
1711          }
1712          
1713        }
1714        catch (java.util.NoSuchElementException nsee)
1715        {
1716          // Should throw some sort of an error.
1717          
1718        }
1719      }
1720    
1721      /**
1722       * Set a bag of parameters for the transformation. Note that
1723       * these will not be additive, they will replace the existing
1724       * set of parameters.
1725       *
1726       * NEEDSDOC @param params
1727       */
1728      public void setParameters(Properties params)
1729      {
1730    
1731        clearParameters();
1732    
1733        Enumeration names = params.propertyNames();
1734    
1735        while (names.hasMoreElements())
1736        {
1737          String name = params.getProperty((String) names.nextElement());
1738          StringTokenizer tokenizer = new StringTokenizer(name, "{}", false);
1739    
1740          try
1741          {
1742    
1743            // The first string might be the namespace, or it might be 
1744            // the local name, if the namespace is null.
1745            String s1 = tokenizer.nextToken();
1746            String s2 = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
1747    
1748            if (null == s2)
1749              setParameter(s1, null, params.getProperty(name));
1750            else
1751              setParameter(s2, s1, params.getProperty(name));
1752          }
1753          catch (java.util.NoSuchElementException nsee)
1754          {
1755    
1756            // Should throw some sort of an error.
1757          }
1758        }
1759      }
1760    
1761      /**
1762       * Reset the parameters to a null list.
1763       */
1764      public void clearParameters()
1765      {
1766    
1767        synchronized (m_reentryGuard)
1768        {
1769          VariableStack varstack = new VariableStack();
1770    
1771          m_xcontext.setVarStack(varstack);
1772    
1773          m_userParams = null;
1774        }
1775      }
1776    
1777    
1778      /**
1779       * Internal -- push the global variables from the Stylesheet onto
1780       * the context's runtime variable stack.
1781       * <p>If we encounter a variable
1782       * that is already defined in the variable stack, we ignore it.  This
1783       * is because the second variable definition will be at a lower import
1784       * precedence.  Presumably, global"variables at the same import precedence
1785       * with the same name will have been caught during the recompose process.
1786       * <p>However, if we encounter a parameter that is already defined in the
1787       * variable stack, we need to see if this is a parameter whose value was
1788       * supplied by a setParameter call.  If so, we need to "receive" the one
1789       * already in the stack, ignoring this one.  If it is just an earlier
1790       * xsl:param or xsl:variable definition, we ignore it using the same
1791       * reasoning as explained above for the variable.
1792       *
1793       * @param contextNode The root of the source tree, can't be null.
1794       *
1795       * @throws TransformerException
1796       */
1797      protected void pushGlobalVars(int contextNode) throws TransformerException
1798      {
1799    
1800        XPathContext xctxt = m_xcontext;
1801        VariableStack vs = xctxt.getVarStack();
1802        StylesheetRoot sr = getStylesheet();
1803        Vector vars = sr.getVariablesAndParamsComposed();
1804        
1805        int i = vars.size();
1806        vs.link(i);
1807    
1808        while (--i >= 0)
1809        {
1810          ElemVariable v = (ElemVariable) vars.elementAt(i);
1811    
1812          // XObject xobj = v.getValue(this, contextNode);
1813          XObject xobj = new XUnresolvedVariable(v, contextNode, this,
1814                                         vs.getStackFrame(), 0, true);
1815          
1816          if(null == vs.elementAt(i))                               
1817            vs.setGlobalVariable(i, xobj);
1818        }
1819    
1820      }
1821    
1822      /**
1823       * Set an object that will be used to resolve URIs used in
1824       * document(), etc.
1825       * @param resolver An object that implements the URIResolver interface,
1826       * or null.
1827       */
1828      public void setURIResolver(URIResolver resolver)
1829      {
1830    
1831        synchronized (m_reentryGuard)
1832        {
1833          m_xcontext.getSourceTreeManager().setURIResolver(resolver);
1834        }
1835      }
1836    
1837      /**
1838       * Get an object that will be used to resolve URIs used in
1839       * document(), etc.
1840       *
1841       * @return An object that implements the URIResolver interface,
1842       * or null.
1843       */
1844      public URIResolver getURIResolver()
1845      {
1846        return m_xcontext.getSourceTreeManager().getURIResolver();
1847      }
1848    
1849      // ======== End Transformer Implementation ========  
1850    
1851      /**
1852       * Set the content event handler.
1853       *
1854       * NEEDSDOC @param handler
1855       * @throws java.lang.NullPointerException If the handler
1856       *            is null.
1857       * @see org.xml.sax.XMLReader#setContentHandler
1858       */
1859      public void setContentHandler(ContentHandler handler)
1860      {
1861    
1862        if (handler == null)
1863        {
1864          throw new NullPointerException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_CONTENT_HANDLER, null)); //"Null content handler");
1865        }
1866        else
1867        {
1868          m_outputContentHandler = handler;
1869    
1870          if (null == m_serializationHandler)
1871          {
1872            ToXMLSAXHandler h = new ToXMLSAXHandler();
1873            h.setContentHandler(handler);
1874            h.setTransformer(this);
1875            
1876            m_serializationHandler = h;
1877          }
1878          else
1879            m_serializationHandler.setContentHandler(handler);
1880        }
1881      }
1882    
1883      /**
1884       * Get the content event handler.
1885       *
1886       * @return The current content handler, or null if none was set.
1887       * @see org.xml.sax.XMLReader#getContentHandler
1888       */
1889      public ContentHandler getContentHandler()
1890      {
1891        return m_outputContentHandler;
1892      }
1893    
1894      /**
1895       * Given a stylesheet element, create a result tree fragment from it's
1896       * contents. The fragment will be built within the shared RTF DTM system
1897       * used as a variable stack.
1898       * @param templateParent The template element that holds the fragment.
1899       * @return the NodeHandle for the root node of the resulting RTF.
1900       *
1901       * @throws TransformerException
1902       * @xsl.usage advanced
1903       */
1904      public int transformToRTF(ElemTemplateElement templateParent)
1905              throws TransformerException
1906      {
1907        // Retrieve a DTM to contain the RTF. At this writing, this may be a
1908        // multi-document DTM (SAX2RTFDTM).
1909        DTM dtmFrag = m_xcontext.getRTFDTM();
1910        return transformToRTF(templateParent,dtmFrag);
1911      }
1912      
1913      /**
1914       * Given a stylesheet element, create a result tree fragment from it's
1915       * contents. The fragment will also use the shared DTM system, but will
1916       * obtain its space from the global variable pool rather than the dynamic
1917       * variable stack. This allows late binding of XUnresolvedVariables without
1918       * the risk that their content will be discarded when the variable stack
1919       * is popped.
1920       * 
1921       * @param templateParent The template element that holds the fragment.
1922       * @return the NodeHandle for the root node of the resulting RTF.
1923       *
1924       * @throws TransformerException
1925       * @xsl.usage advanced
1926       */
1927      public int transformToGlobalRTF(ElemTemplateElement templateParent)
1928              throws TransformerException
1929      {
1930        // Retrieve a DTM to contain the RTF. At this writing, this may be a
1931        // multi-document DTM (SAX2RTFDTM).
1932        DTM dtmFrag = m_xcontext.getGlobalRTFDTM();
1933        return transformToRTF(templateParent,dtmFrag);
1934      }
1935      
1936      /**
1937       * Given a stylesheet element, create a result tree fragment from it's
1938       * contents.
1939       * @param templateParent The template element that holds the fragment.
1940       * @param dtmFrag The DTM to write the RTF into
1941       * @return the NodeHandle for the root node of the resulting RTF.
1942       *
1943       * @throws TransformerException
1944       * @xsl.usage advanced
1945       */
1946      private int transformToRTF(ElemTemplateElement templateParent,DTM dtmFrag)
1947              throws TransformerException
1948      {
1949    
1950        XPathContext xctxt = m_xcontext;
1951        
1952        ContentHandler rtfHandler = dtmFrag.getContentHandler();
1953    
1954        // Obtain the ResultTreeFrag's root node.
1955        // NOTE: In SAX2RTFDTM, this value isn't available until after
1956        // the startDocument has been issued, so assignment has been moved
1957        // down a bit in the code.
1958        int resultFragment; // not yet reliably = dtmFrag.getDocument();
1959    
1960        // Save the current result tree handler.
1961        SerializationHandler savedRTreeHandler = this.m_serializationHandler;
1962     
1963    
1964        // And make a new handler for the RTF.
1965        ToSAXHandler h = new ToXMLSAXHandler();
1966        h.setContentHandler(rtfHandler);
1967        h.setTransformer(this);
1968        
1969        // Replace the old handler (which was already saved)
1970        m_serializationHandler = h;
1971     
1972        // use local variable for the current handler
1973        SerializationHandler rth = m_serializationHandler;
1974    
1975        try
1976        {
1977          rth.startDocument();
1978          
1979          // startDocument is "bottlenecked" in RTH. We need it acted upon immediately,
1980          // to set the DTM's state as in-progress, so that if the xsl:variable's body causes
1981          // further RTF activity we can keep that from bashing this DTM.
1982          rth.flushPending(); 
1983     
1984          try
1985          {
1986    
1987            // Do the transformation of the child elements.
1988            executeChildTemplates(templateParent, true);
1989    
1990            // Make sure everything is flushed!
1991            rth.flushPending();
1992            
1993            // Get the document ID. May not exist until the RTH has not only
1994            // received, but flushed, the startDocument, and may be invalid
1995            // again after the document has been closed (still debating that)
1996            // ... so waiting until just before the end seems simplest/safest. 
1997            resultFragment = dtmFrag.getDocument();      
1998          }
1999          finally
2000          {
2001            rth.endDocument();
2002          }
2003        }
2004        catch (org.xml.sax.SAXException se)
2005        {
2006          throw new TransformerException(se);
2007        }
2008        finally
2009        {
2010    
2011          // Restore the previous result tree handler.
2012          this.m_serializationHandler = savedRTreeHandler;
2013        }
2014    
2015        return resultFragment;
2016      }
2017    
2018      /**
2019       * Get the StringWriter pool, so that StringWriter
2020       * objects may be reused.
2021       *
2022       * @return The string writer pool, not null.
2023       * @xsl.usage internal
2024       */
2025      public ObjectPool getStringWriterPool()
2026      {
2027        return m_stringWriterObjectPool;
2028      }
2029    
2030      /**
2031       * Take the contents of a template element, process it, and
2032       * convert it to a string.
2033       *
2034       * @param elem The parent element whose children will be output
2035       * as a string.
2036       *
2037       * @return The stringized result of executing the elements children.
2038       *
2039       * @throws TransformerException
2040       * @xsl.usage advanced
2041       */
2042      public String transformToString(ElemTemplateElement elem)
2043              throws TransformerException
2044      {
2045        ElemTemplateElement firstChild = elem.getFirstChildElem();
2046        if(null == firstChild)
2047          return "";
2048        if(elem.hasTextLitOnly() && m_optimizer)
2049        {
2050          return ((ElemTextLiteral)firstChild).getNodeValue();
2051        }
2052    
2053        // Save the current result tree handler.
2054        SerializationHandler savedRTreeHandler = this.m_serializationHandler;
2055    
2056        // Create a Serializer object that will handle the SAX events 
2057        // and build the ResultTreeFrag nodes.
2058        StringWriter sw = (StringWriter) m_stringWriterObjectPool.getInstance();
2059    
2060        m_serializationHandler =
2061            (ToTextStream) m_textResultHandlerObjectPool.getInstance();
2062    
2063          if (null == m_serializationHandler)
2064          {
2065            // if we didn't get one from the pool, go make a new one
2066    
2067            
2068            Serializer serializer = org.apache.xml.serializer.SerializerFactory.getSerializer(
2069                m_textformat.getProperties());
2070            m_serializationHandler = (SerializationHandler) serializer;
2071          } 
2072    
2073            m_serializationHandler.setTransformer(this);
2074            m_serializationHandler.setWriter(sw);
2075     
2076     
2077        String result;
2078    
2079        try
2080        {
2081            /* Don't call startDocument, the SerializationHandler  will
2082             * generate its own internal startDocument call anyways
2083             */
2084          // this.m_serializationHandler.startDocument();
2085    
2086          // Do the transformation of the child elements.
2087          executeChildTemplates(elem, true);
2088            this.m_serializationHandler.endDocument();
2089    
2090          result = sw.toString();
2091        }
2092        catch (org.xml.sax.SAXException se)
2093        {
2094          throw new TransformerException(se);
2095        }
2096        finally
2097        {
2098          sw.getBuffer().setLength(0);
2099    
2100          try
2101          {
2102            sw.close();
2103          }
2104          catch (Exception ioe){}
2105    
2106          m_stringWriterObjectPool.freeInstance(sw);
2107          m_serializationHandler.reset();
2108          m_textResultHandlerObjectPool.freeInstance(m_serializationHandler);
2109    
2110          // Restore the previous result tree handler.
2111          m_serializationHandler = savedRTreeHandler;
2112        }
2113    
2114        return result;
2115      }
2116    
2117      /**
2118       * Given an element and mode, find the corresponding
2119       * template and process the contents.
2120       *
2121       * @param xslInstruction The calling element.
2122       * @param template The template to use if xsl:for-each, current template for apply-imports, or null.
2123       * @param child The source context node.
2124       * @throws TransformerException
2125       * @return true if applied a template, false if not.
2126       * @xsl.usage advanced
2127       */
2128      public boolean applyTemplateToNode(ElemTemplateElement xslInstruction,  // xsl:apply-templates or xsl:for-each
2129                                         ElemTemplate template, int child)
2130                                                 throws TransformerException
2131      {
2132    
2133        DTM dtm = m_xcontext.getDTM(child);
2134        short nodeType = dtm.getNodeType(child);
2135        boolean isDefaultTextRule = false;
2136        boolean isApplyImports = false;
2137        
2138        isApplyImports = ((xslInstruction == null)
2139                                    ? false
2140                                    : xslInstruction.getXSLToken()
2141                                      == Constants.ELEMNAME_APPLY_IMPORTS);        
2142    
2143        if (null == template || isApplyImports)
2144        {
2145          int maxImportLevel, endImportLevel=0;
2146    
2147          if (isApplyImports)
2148          {
2149            maxImportLevel =
2150              template.getStylesheetComposed().getImportCountComposed() - 1;
2151            endImportLevel =
2152              template.getStylesheetComposed().getEndImportCountComposed();
2153          }
2154          else
2155          {
2156            maxImportLevel = -1;
2157          }
2158    
2159          // If we're trying an xsl:apply-imports at the top level (ie there are no
2160          // imported stylesheets), we need to indicate that there is no matching template.
2161          // The above logic will calculate a maxImportLevel of -1 which indicates
2162          // that we should find any template.  This is because a value of -1 for
2163          // maxImportLevel has a special meaning.  But we don't want that.
2164          // We want to match -no- templates. See bugzilla bug 1170.
2165          if (isApplyImports && (maxImportLevel == -1))
2166          {
2167            template = null;
2168          }
2169          else
2170          {
2171    
2172            // Find the XSL template that is the best match for the 
2173            // element.        
2174            XPathContext xctxt = m_xcontext;
2175    
2176            try
2177            {
2178              xctxt.pushNamespaceContext(xslInstruction);
2179    
2180              QName mode = this.getMode();
2181              
2182              if (isApplyImports)
2183                template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode,
2184                      maxImportLevel, endImportLevel, m_quietConflictWarnings, dtm);
2185              else
2186                template = m_stylesheetRoot.getTemplateComposed(xctxt, child, mode,
2187                      m_quietConflictWarnings, dtm);
2188              
2189            }
2190            finally
2191            {
2192              xctxt.popNamespaceContext();
2193            }
2194          }
2195    
2196          // If that didn't locate a node, fall back to a default template rule.
2197          // See http://www.w3.org/TR/xslt#built-in-rule.
2198          if (null == template)
2199          {
2200            switch (nodeType)
2201            {
2202            case DTM.DOCUMENT_FRAGMENT_NODE :
2203            case DTM.ELEMENT_NODE :
2204              template = m_stylesheetRoot.getDefaultRule();
2205              break;
2206            case DTM.CDATA_SECTION_NODE :
2207            case DTM.TEXT_NODE :
2208            case DTM.ATTRIBUTE_NODE :
2209              template = m_stylesheetRoot.getDefaultTextRule();
2210              isDefaultTextRule = true;
2211              break;
2212            case DTM.DOCUMENT_NODE :
2213              template = m_stylesheetRoot.getDefaultRootRule();
2214              break;
2215            default :
2216    
2217              // No default rules for processing instructions and the like.
2218              return false;
2219            }
2220          }
2221        }
2222    
2223        // If we are processing the default text rule, then just clone 
2224        // the value directly to the result tree.
2225        try
2226        {
2227          pushElemTemplateElement(template);
2228          m_xcontext.pushCurrentNode(child);
2229          pushPairCurrentMatched(template, child);
2230          
2231          // Fix copy copy29 test.
2232          if (!isApplyImports) {
2233              DTMIterator cnl = new org.apache.xpath.NodeSetDTM(child, m_xcontext.getDTMManager());
2234              m_xcontext.pushContextNodeList(cnl);
2235          }
2236    
2237          if (isDefaultTextRule)
2238          {
2239            switch (nodeType)
2240            {
2241            case DTM.CDATA_SECTION_NODE :
2242            case DTM.TEXT_NODE :
2243              ClonerToResultTree.cloneToResultTree(child, nodeType, 
2244                                            dtm, getResultTreeHandler(), false);
2245              break;
2246            case DTM.ATTRIBUTE_NODE :
2247              dtm.dispatchCharactersEvents(child, getResultTreeHandler(), false);
2248              break;
2249            }
2250          }
2251          else
2252          {
2253    
2254            // Fire a trace event for the template.
2255             
2256            if (m_debug)
2257              getTraceManager().fireTraceEvent(template);
2258            // And execute the child templates.
2259            // 9/11/00: If template has been compiled, hand off to it
2260            // since much (most? all?) of the processing has been inlined.
2261            // (It would be nice if there was a single entry point that
2262            // worked for both... but the interpretive system works by
2263            // having the Tranformer execute the children, while the
2264            // compiled obviously has to run its own code. It's
2265            // also unclear that "execute" is really the right name for
2266            // that entry point.)
2267            m_xcontext.setSAXLocator(template);
2268            // m_xcontext.getVarStack().link();
2269            m_xcontext.getVarStack().link(template.m_frameSize);
2270            executeChildTemplates(template, true);
2271            
2272            if (m_debug)
2273              getTraceManager().fireTraceEndEvent(template);
2274          }
2275        }
2276        catch (org.xml.sax.SAXException se)
2277        {
2278          throw new TransformerException(se);
2279        }
2280        finally
2281        {
2282          if (!isDefaultTextRule)
2283            m_xcontext.getVarStack().unlink();
2284          m_xcontext.popCurrentNode();
2285          if (!isApplyImports) {
2286              m_xcontext.popContextNodeList();
2287          }
2288          popCurrentMatched();
2289          
2290          popElemTemplateElement();
2291        }
2292    
2293        return true;
2294      }
2295      
2296      
2297      /**
2298       * Execute each of the children of a template element.  This method
2299       * is only for extension use.
2300       *
2301       * @param elem The ElemTemplateElement that contains the children
2302       * that should execute.
2303       * NEEDSDOC @param context
2304       * @param mode The current mode.
2305       * @param handler The ContentHandler to where the result events
2306       * should be fed.
2307       *
2308       * @throws TransformerException
2309       * @xsl.usage advanced
2310       */
2311      public void executeChildTemplates(
2312              ElemTemplateElement elem, org.w3c.dom.Node context, QName mode, ContentHandler handler)
2313                throws TransformerException
2314      {
2315    
2316        XPathContext xctxt = m_xcontext;
2317    
2318        try
2319        {
2320          if(null != mode)
2321            pushMode(mode);
2322          xctxt.pushCurrentNode(xctxt.getDTMHandleFromNode(context));
2323          executeChildTemplates(elem, handler);
2324        }
2325        finally
2326        {
2327          xctxt.popCurrentNode();
2328          
2329          // I'm not sure where or why this was here.  It is clearly in 
2330          // error though, without a corresponding pushMode().
2331          if (null != mode)
2332            popMode();
2333        }
2334      }
2335    
2336      /**
2337       * Execute each of the children of a template element.
2338       *
2339       * @param elem The ElemTemplateElement that contains the children
2340       * that should execute.
2341       * @param shouldAddAttrs true if xsl:attributes should be executed.
2342       *
2343       * @throws TransformerException
2344       * @xsl.usage advanced
2345       */
2346      public void executeChildTemplates(
2347              ElemTemplateElement elem, boolean shouldAddAttrs)
2348                throws TransformerException
2349      {
2350    
2351        // Does this element have any children?
2352        ElemTemplateElement t = elem.getFirstChildElem();
2353    
2354        if (null == t)
2355          return;      
2356        
2357        if(elem.hasTextLitOnly() && m_optimizer)
2358        {      
2359          char[] chars = ((ElemTextLiteral)t).getChars();
2360          try
2361          {
2362            // Have to push stuff on for tooling...
2363            this.pushElemTemplateElement(t);
2364            m_serializationHandler.characters(chars, 0, chars.length);
2365          }
2366          catch(SAXException se)
2367          {
2368            throw new TransformerException(se);
2369          }
2370          finally
2371          {
2372            this.popElemTemplateElement();
2373          }
2374          return;
2375        }
2376    
2377    //    // Check for infinite loops if we have to.
2378    //    boolean check = (m_stackGuard.m_recursionLimit > -1);
2379    //
2380    //    if (check)
2381    //      getStackGuard().push(elem, xctxt.getCurrentNode());
2382    
2383        XPathContext xctxt = m_xcontext;
2384        xctxt.pushSAXLocatorNull();
2385        int currentTemplateElementsTop = m_currentTemplateElements.size();
2386        m_currentTemplateElements.push(null);
2387    
2388        try
2389        {
2390          // Loop through the children of the template, calling execute on 
2391          // each of them.
2392          for (; t != null; t = t.getNextSiblingElem())
2393          {
2394            if (!shouldAddAttrs
2395                    && t.getXSLToken() == Constants.ELEMNAME_ATTRIBUTE)
2396              continue;
2397    
2398            xctxt.setSAXLocator(t);
2399            m_currentTemplateElements.setElementAt(t,currentTemplateElementsTop);
2400            t.execute(this);
2401          }
2402        }
2403        catch(RuntimeException re)
2404        {
2405            TransformerException te = new TransformerException(re);
2406            te.setLocator(t);
2407            throw te;
2408        }
2409        finally
2410        {
2411          m_currentTemplateElements.pop();
2412          xctxt.popSAXLocator();
2413        }
2414    
2415        // Check for infinite loops if we have to
2416    //    if (check)
2417    //      getStackGuard().pop();
2418      }
2419        /**
2420          * Execute each of the children of a template element.
2421          *
2422          * @param elem The ElemTemplateElement that contains the children
2423          * that should execute.
2424          * @param handler The ContentHandler to where the result events
2425          * should be fed.
2426          *
2427          * @throws TransformerException
2428          * @xsl.usage advanced
2429          */
2430         public void executeChildTemplates(
2431                 ElemTemplateElement elem, ContentHandler handler)
2432                   throws TransformerException
2433         {
2434    
2435           SerializationHandler xoh = this.getSerializationHandler();
2436    
2437           // These may well not be the same!  In this case when calling
2438           // the Redirect extension, it has already set the ContentHandler
2439           // in the Transformer.
2440           SerializationHandler savedHandler = xoh;
2441    
2442           try
2443           {
2444             xoh.flushPending();
2445    
2446             // %REVIEW% Make sure current node is being pushed.
2447             LexicalHandler lex = null;
2448             if (handler instanceof LexicalHandler) {
2449                lex = (LexicalHandler) handler;
2450             }
2451             m_serializationHandler = new ToXMLSAXHandler(handler, lex, savedHandler.getEncoding());
2452             m_serializationHandler.setTransformer(this);
2453             executeChildTemplates(elem, true);
2454           }
2455           catch (TransformerException e)
2456           {
2457             throw e;
2458           }
2459           catch (SAXException se) {
2460             throw new TransformerException(se);
2461           }
2462           finally
2463           {
2464             m_serializationHandler = savedHandler;
2465        }
2466      }
2467    
2468      /**
2469       * Get the keys for the xsl:sort elements.
2470       * Note: Should this go into ElemForEach?
2471       *
2472       * @param foreach Valid ElemForEach element, not null.
2473       * @param sourceNodeContext The current node context in the source tree,
2474       * needed to evaluate the Attribute Value Templates.
2475       *
2476       * @return A Vector of NodeSortKeys, or null.
2477       *
2478       * @throws TransformerException
2479       * @xsl.usage advanced
2480       */
2481      public Vector processSortKeys(ElemForEach foreach, int sourceNodeContext)
2482              throws TransformerException
2483      {
2484    
2485        Vector keys = null;
2486        XPathContext xctxt = m_xcontext;
2487        int nElems = foreach.getSortElemCount();
2488    
2489        if (nElems > 0)
2490          keys = new Vector();
2491    
2492        // March backwards, collecting the sort keys.
2493        for (int i = 0; i < nElems; i++)
2494        {
2495          ElemSort sort = foreach.getSortElem(i);
2496          
2497          if (m_debug)
2498            getTraceManager().fireTraceEvent(sort);
2499         
2500          String langString =
2501            (null != sort.getLang())
2502            ? sort.getLang().evaluate(xctxt, sourceNodeContext, foreach) : null;
2503          String dataTypeString = sort.getDataType().evaluate(xctxt,
2504                                    sourceNodeContext, foreach);
2505    
2506          if (dataTypeString.indexOf(":") >= 0)
2507            System.out.println(
2508              "TODO: Need to write the hooks for QNAME sort data type");
2509          else if (!(dataTypeString.equalsIgnoreCase(Constants.ATTRVAL_DATATYPE_TEXT))
2510                   &&!(dataTypeString.equalsIgnoreCase(
2511                     Constants.ATTRVAL_DATATYPE_NUMBER)))
2512            foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2513                          new Object[]{ Constants.ATTRNAME_DATATYPE,
2514                                        dataTypeString });
2515    
2516          boolean treatAsNumbers =
2517            ((null != dataTypeString) && dataTypeString.equals(
2518            Constants.ATTRVAL_DATATYPE_NUMBER)) ? true : false;
2519          String orderString = sort.getOrder().evaluate(xctxt, sourceNodeContext,
2520                                 foreach);
2521    
2522          if (!(orderString.equalsIgnoreCase(Constants.ATTRVAL_ORDER_ASCENDING))
2523                  &&!(orderString.equalsIgnoreCase(
2524                    Constants.ATTRVAL_ORDER_DESCENDING)))
2525            foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2526                          new Object[]{ Constants.ATTRNAME_ORDER,
2527                                        orderString });
2528    
2529          boolean descending =
2530            ((null != orderString) && orderString.equals(
2531            Constants.ATTRVAL_ORDER_DESCENDING)) ? true : false;
2532          AVT caseOrder = sort.getCaseOrder();
2533          boolean caseOrderUpper;
2534    
2535          if (null != caseOrder)
2536          {
2537            String caseOrderString = caseOrder.evaluate(xctxt, sourceNodeContext,
2538                                                        foreach);
2539    
2540            if (!(caseOrderString.equalsIgnoreCase(Constants.ATTRVAL_CASEORDER_UPPER))
2541                    &&!(caseOrderString.equalsIgnoreCase(
2542                      Constants.ATTRVAL_CASEORDER_LOWER)))
2543              foreach.error(XSLTErrorResources.ER_ILLEGAL_ATTRIBUTE_VALUE,
2544                            new Object[]{ Constants.ATTRNAME_CASEORDER,
2545                                          caseOrderString });
2546    
2547            caseOrderUpper =
2548              ((null != caseOrderString) && caseOrderString.equals(
2549              Constants.ATTRVAL_CASEORDER_UPPER)) ? true : false;
2550          }
2551          else
2552          {
2553            caseOrderUpper = false;
2554          }
2555    
2556          keys.addElement(new NodeSortKey(this, sort.getSelect(), treatAsNumbers,
2557                                          descending, langString, caseOrderUpper,
2558                                          foreach));
2559          if (m_debug)
2560            getTraceManager().fireTraceEndEvent(sort);
2561         }
2562    
2563        return keys;
2564      }
2565    
2566      //==========================================================
2567      // SECTION: TransformState implementation
2568      //==========================================================
2569      
2570      /**
2571       * Get the stack of ElemTemplateElements.
2572       * 
2573       * @return A copy of stack that contains the xsl element instructions, 
2574       * the earliest called in index zero, and the latest called in index size()-1.
2575       */
2576      public Vector getElementCallstack()
2577      {
2578            Vector elems = new Vector();
2579            int nStackSize = m_currentTemplateElements.size();
2580            for(int i = 0; i < nStackSize; i++)
2581            {
2582                    ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements.elementAt(i);
2583                    if(null != elem)
2584                    {
2585                            elems.addElement(elem);
2586                    }
2587            }
2588            return elems;
2589      }
2590      
2591      /**
2592       * Get the count of how many elements are 
2593       * active.
2594       * @return The number of active elements on 
2595       * the currentTemplateElements stack.
2596       */
2597      public int getCurrentTemplateElementsCount()
2598      {
2599            return m_currentTemplateElements.size();
2600      }
2601      
2602      
2603      /**
2604       * Get the count of how many elements are 
2605       * active.
2606       * @return The number of active elements on 
2607       * the currentTemplateElements stack.
2608       */
2609      public ObjectStack getCurrentTemplateElements()
2610      {
2611            return m_currentTemplateElements;
2612      }
2613    
2614      /**
2615       * Push the current template element.
2616       *
2617       * @param elem The current ElemTemplateElement (may be null, and then
2618       * set via setCurrentElement).
2619       */
2620      public void pushElemTemplateElement(ElemTemplateElement elem)
2621      {
2622        m_currentTemplateElements.push(elem);
2623      }
2624    
2625      /**
2626       * Pop the current template element.
2627       */
2628      public void popElemTemplateElement()
2629      {
2630        m_currentTemplateElements.pop();
2631      }
2632    
2633      /**
2634       * Set the top of the current template elements
2635       * stack.
2636       *
2637       * @param e The current ElemTemplateElement about to
2638       * be executed.
2639       */
2640      public void setCurrentElement(ElemTemplateElement e)
2641      {
2642        m_currentTemplateElements.setTop(e);
2643      }
2644    
2645      /**
2646       * Retrieves the current ElemTemplateElement that is
2647       * being executed.
2648       *
2649       * @return The current ElemTemplateElement that is executing,
2650       * should not normally be null.
2651       */
2652      public ElemTemplateElement getCurrentElement()
2653      {
2654        return (m_currentTemplateElements.size() > 0) ? 
2655            (ElemTemplateElement) m_currentTemplateElements.peek() : null;
2656      }
2657    
2658      /**
2659       * This method retrieves the current context node
2660       * in the source tree.
2661       *
2662       * @return The current context node (should never be null?).
2663       */
2664      public int getCurrentNode()
2665      {
2666        return m_xcontext.getCurrentNode();
2667      }
2668      
2669      /**
2670       * Get the call stack of xsl:template elements.
2671       * 
2672       * @return A copy of stack that contains the xsl:template 
2673       * (ElemTemplate) instructions, the earliest called in index 
2674       * zero, and the latest called in index size()-1.
2675       */
2676      public Vector getTemplateCallstack()
2677      {
2678            Vector elems = new Vector();
2679            int nStackSize = m_currentTemplateElements.size();
2680            for(int i = 0; i < nStackSize; i++)
2681            {
2682                    ElemTemplateElement elem = (ElemTemplateElement) m_currentTemplateElements.elementAt(i);
2683                    if(null != elem && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE))
2684                    {
2685                            elems.addElement(elem);
2686                    }
2687            }
2688            return elems;
2689      }
2690    
2691    
2692      /**
2693       * This method retrieves the xsl:template
2694       * that is in effect, which may be a matched template
2695       * or a named template.
2696       *
2697       * <p>Please note that the ElemTemplate returned may
2698       * be a default template, and thus may not have a template
2699       * defined in the stylesheet.</p>
2700       *
2701       * @return The current xsl:template, should not be null.
2702       */
2703      public ElemTemplate getCurrentTemplate()
2704      {
2705    
2706        ElemTemplateElement elem = getCurrentElement();
2707    
2708        while ((null != elem)
2709               && (elem.getXSLToken() != Constants.ELEMNAME_TEMPLATE))
2710        {
2711          elem = elem.getParentElem();
2712        }
2713    
2714        return (ElemTemplate) elem;
2715      }
2716    
2717      /**
2718       * Push both the current xsl:template or xsl:for-each onto the
2719       * stack, along with the child node that was matched.
2720       * (Note: should this only be used for xsl:templates?? -sb)
2721       *
2722       * @param template xsl:template or xsl:for-each.
2723       * @param child The child that was matched.
2724       */
2725      public void pushPairCurrentMatched(ElemTemplateElement template, int child)
2726      {
2727        m_currentMatchTemplates.push(template);
2728        m_currentMatchedNodes.push(child);
2729      }
2730    
2731      /**
2732       * Pop the elements that were pushed via pushPairCurrentMatched.
2733       */
2734      public void popCurrentMatched()
2735      {
2736        m_currentMatchTemplates.pop();
2737        m_currentMatchedNodes.pop();
2738      }
2739    
2740      /**
2741       * This method retrieves the xsl:template
2742       * that was matched.  Note that this may not be
2743       * the same thing as the current template (which
2744       * may be from getCurrentElement()), since a named
2745       * template may be in effect.
2746       *
2747       * @return The pushed template that was pushed via pushPairCurrentMatched.
2748       */
2749      public ElemTemplate getMatchedTemplate()
2750      {
2751        return (ElemTemplate) m_currentMatchTemplates.peek();
2752      }
2753    
2754      /**
2755       * Retrieves the node in the source tree that matched
2756       * the template obtained via getMatchedTemplate().
2757       *
2758       * @return The matched node that corresponds to the
2759       * match attribute of the current xsl:template.
2760       */
2761      public int getMatchedNode()
2762      {
2763        return m_currentMatchedNodes.peepTail();
2764      }
2765    
2766      /**
2767       * Get the current context node list.
2768       *
2769       * @return A reset clone of the context node list.
2770       */
2771      public DTMIterator getContextNodeList()
2772      {
2773    
2774        try
2775        {
2776          DTMIterator cnl = m_xcontext.getContextNodeList();
2777    
2778          return (cnl == null) ? null : (DTMIterator) cnl.cloneWithReset();
2779        }
2780        catch (CloneNotSupportedException cnse)
2781        {
2782    
2783          // should never happen.
2784          return null;
2785        }
2786      }
2787    
2788      /**
2789       * Get the TrAX Transformer object in effect.
2790       *
2791       * @return This object.
2792       */
2793      public Transformer getTransformer()
2794      {
2795        return this;
2796      }
2797    
2798      //==========================================================
2799      // SECTION: Accessor Functions
2800      //==========================================================
2801    
2802      /**
2803       * Set the stylesheet for this processor.  If this is set, then the
2804       * process calls that take only the input .xml will use
2805       * this instead of looking for a stylesheet PI.  Also,
2806       * setting the stylesheet is needed if you are going
2807       * to use the processor as a SAX ContentHandler.
2808       *
2809       * @param stylesheetRoot A non-null StylesheetRoot object,
2810       * or null if you wish to clear the stylesheet reference.
2811       */
2812      public void setStylesheet(StylesheetRoot stylesheetRoot)
2813      {
2814        m_stylesheetRoot = stylesheetRoot;
2815      }
2816    
2817      /**
2818       * Get the current stylesheet for this processor.
2819       *
2820       * @return The stylesheet that is associated with this
2821       * transformer.
2822       */
2823      public final StylesheetRoot getStylesheet()
2824      {
2825        return m_stylesheetRoot;
2826      }
2827    
2828      /**
2829       * Get quietConflictWarnings property. If the quietConflictWarnings
2830       * property is set to true, warnings about pattern conflicts won't be
2831       * printed to the diagnostics stream.
2832       *
2833       * @return True if this transformer should not report
2834       * template match conflicts.
2835       */
2836      public boolean getQuietConflictWarnings()
2837      {
2838        return m_quietConflictWarnings;
2839      }
2840    
2841      /**
2842       * If the quietConflictWarnings property is set to
2843       * true, warnings about pattern conflicts won't be
2844       * printed to the diagnostics stream.
2845       * False by default.
2846       * (Currently setting this property will have no effect.)
2847       *
2848       * @param b true if conflict warnings should be suppressed.
2849       */
2850      public void setQuietConflictWarnings(boolean b)
2851      {
2852        m_quietConflictWarnings = b;
2853      }
2854    
2855      /**
2856       * Set the execution context for XPath.
2857       *
2858       * @param xcontext A non-null reference to the XPathContext
2859       * associated with this transformer.
2860       * @xsl.usage internal
2861       */
2862      public void setXPathContext(XPathContext xcontext)
2863      {
2864        m_xcontext = xcontext;
2865      }
2866    
2867      /**
2868       * Get the XPath context associated with this transformer.
2869       *
2870       * @return The XPathContext reference, never null.
2871       */
2872      public final XPathContext getXPathContext()
2873      {
2874        return m_xcontext;
2875      }
2876    
2877      /**
2878       * Get the object used to guard the stack from
2879       * recursion.
2880       *
2881       * @return The StackGuard object, which should never be null.
2882       * @xsl.usage internal
2883       */
2884      public StackGuard getStackGuard()
2885      {
2886        return m_stackGuard;
2887      }
2888    
2889      /**
2890       * Get the recursion limit.
2891       * Used for infinite loop check. If the value is -1, do not
2892       * check for infinite loops. Anyone who wants to enable that
2893       * check should change the value of this variable to be the
2894       * level of recursion that they want to check. Be careful setting
2895       * this variable, if the number is too low, it may report an
2896       * infinite loop situation, when there is none.
2897       * Post version 1.0.0, we'll make this a runtime feature.
2898       *
2899       * @return The limit on recursion, or -1 if no check is to be made.
2900       */
2901      public int getRecursionLimit()
2902      {
2903        return m_stackGuard.getRecursionLimit();
2904      }
2905    
2906      /**
2907       * Set the recursion limit.
2908       * Used for infinite loop check. If the value is -1, do not
2909       * check for infinite loops. Anyone who wants to enable that
2910       * check should change the value of this variable to be the
2911       * level of recursion that they want to check. Be careful setting
2912       * this variable, if the number is too low, it may report an
2913       * infinite loop situation, when there is none.
2914       * Post version 1.0.0, we'll make this a runtime feature.
2915       *
2916       * @param limit A number that represents the limit of recursion,
2917       * or -1 if no checking is to be done.
2918       */
2919      public void setRecursionLimit(int limit)
2920      {
2921        m_stackGuard.setRecursionLimit(limit);
2922      }
2923    
2924      /**
2925       * Get the SerializationHandler object.
2926       *
2927       * @return The current SerializationHandler, which may not
2928       * be the main result tree manager.
2929       */
2930      public SerializationHandler getResultTreeHandler()
2931      {
2932        return m_serializationHandler;
2933      }
2934    
2935      /**
2936       * Get the SerializationHandler object.
2937       *
2938       * @return The current SerializationHandler, which may not
2939       * be the main result tree manager.
2940       */
2941      public SerializationHandler getSerializationHandler()
2942      {
2943        return m_serializationHandler;
2944      }
2945      
2946      /**
2947       * Get the KeyManager object.
2948       *
2949       * @return A reference to the KeyManager object, which should
2950       * never be null.
2951       */
2952      public KeyManager getKeyManager()
2953      {
2954        return m_keyManager;
2955      }
2956    
2957      /**
2958       * Check to see if this is a recursive attribute definition.
2959       *
2960       * @param attrSet A non-null ElemAttributeSet reference.
2961       *
2962       * @return true if the attribute set is recursive.
2963       */
2964      public boolean isRecursiveAttrSet(ElemAttributeSet attrSet)
2965      {
2966    
2967        if (null == m_attrSetStack)
2968        {
2969          m_attrSetStack = new Stack();
2970        }
2971    
2972        if (!m_attrSetStack.empty())
2973        {
2974          int loc = m_attrSetStack.search(attrSet);
2975    
2976          if (loc > -1)
2977          {
2978            return true;
2979          }
2980        }
2981    
2982        return false;
2983      }
2984    
2985      /**
2986       * Push an executing attribute set, so we can check for
2987       * recursive attribute definitions.
2988       *
2989       * @param attrSet A non-null ElemAttributeSet reference.
2990       */
2991      public void pushElemAttributeSet(ElemAttributeSet attrSet)
2992      {
2993        m_attrSetStack.push(attrSet);
2994      }
2995    
2996      /**
2997       * Pop the current executing attribute set.
2998       */
2999      public void popElemAttributeSet()
3000      {
3001        m_attrSetStack.pop();
3002      }
3003    
3004      /**
3005       * Get the table of counters, for optimized xsl:number support.
3006       *
3007       * @return The CountersTable, never null.
3008       */
3009      public CountersTable getCountersTable()
3010      {
3011    
3012        if (null == m_countersTable)
3013          m_countersTable = new CountersTable();
3014    
3015        return m_countersTable;
3016      }
3017    
3018      /**
3019       * Tell if the current template rule is null, i.e. if we are
3020       * directly within an apply-templates.  Used for xsl:apply-imports.
3021       *
3022       * @return True if the current template rule is null.
3023       */
3024      public boolean currentTemplateRuleIsNull()
3025      {
3026        return ((!m_currentTemplateRuleIsNull.isEmpty())
3027                && (m_currentTemplateRuleIsNull.peek() == true));
3028      }
3029    
3030      /**
3031       * Push true if the current template rule is null, false
3032       * otherwise.
3033       *
3034       * @param b True if the we are executing an xsl:for-each
3035       * (or xsl:call-template?).
3036       */
3037      public void pushCurrentTemplateRuleIsNull(boolean b)
3038      {
3039        m_currentTemplateRuleIsNull.push(b);
3040      }
3041    
3042      /**
3043       * Push true if the current template rule is null, false
3044       * otherwise.
3045       */
3046      public void popCurrentTemplateRuleIsNull()
3047      {
3048        m_currentTemplateRuleIsNull.pop();
3049      }
3050    
3051      /**
3052       * Push a funcion result for the currently active EXSLT
3053       * <code>func:function</code>.
3054       * 
3055       * @param val the result of executing an EXSLT
3056       * <code>func:result</code> instruction for the current
3057       * <code>func:function</code>.
3058       */
3059      public void pushCurrentFuncResult(Object val) {
3060        m_currentFuncResult.push(val);
3061      }
3062    
3063      /**
3064       * Pops the result of the currently active EXSLT <code>func:function</code>.
3065       * 
3066       * @return the value of the <code>func:function</code>
3067       */
3068      public Object popCurrentFuncResult() {
3069        return m_currentFuncResult.pop();
3070      }
3071    
3072      /**
3073       * Determines whether an EXSLT <code>func:result</code> instruction has been
3074       * executed for the currently active EXSLT <code>func:function</code>.
3075       * 
3076       * @return <code>true</code> if and only if a <code>func:result</code>
3077       * instruction has been executed
3078       */
3079      public boolean currentFuncResultSeen() {
3080        return !m_currentFuncResult.empty()
3081                   && m_currentFuncResult.peek() != null;
3082      }
3083    
3084      /**
3085       * Return the message manager.
3086       *
3087       * @return The message manager, never null.
3088       */
3089      public MsgMgr getMsgMgr()
3090      {
3091    
3092        if (null == m_msgMgr)
3093          m_msgMgr = new MsgMgr(this);
3094    
3095        return m_msgMgr;
3096      }
3097    
3098      /**
3099       * Set the error event listener.
3100       *
3101       * @param listener The new error listener.
3102       * @throws IllegalArgumentException if
3103       */
3104      public void setErrorListener(ErrorListener listener)
3105              throws IllegalArgumentException
3106      {
3107    
3108        synchronized (m_reentryGuard)
3109        {
3110          if (listener == null)
3111            throw new IllegalArgumentException(XSLMessages.createMessage(XSLTErrorResources.ER_NULL_ERROR_HANDLER, null)); //"Null error handler");
3112    
3113          m_errorHandler = listener;
3114        }
3115      }
3116    
3117      /**
3118       * Get the current error event handler.
3119       *
3120       * @return The current error handler, which should never be null.
3121       */
3122      public ErrorListener getErrorListener()
3123      {
3124        return m_errorHandler;
3125      }
3126    
3127      /**
3128       * Get an instance of the trace manager for this transformation.
3129       * This object can be used to set trace listeners on various
3130       * events during the transformation.
3131       *
3132       * @return A reference to the TraceManager, never null.
3133       */
3134      public TraceManager getTraceManager()
3135      {
3136        return m_traceManager;
3137      }
3138    
3139      /**
3140       * Look up the value of a feature.
3141       *
3142       * <p>The feature name is any fully-qualified URI.  It is
3143       * possible for an TransformerFactory to recognize a feature name but
3144       * to be unable to return its value; this is especially true
3145       * in the case of an adapter for a SAX1 Parser, which has
3146       * no way of knowing whether the underlying parser is
3147       * validating, for example.</p>
3148       *
3149       * <h3>Open issues:</h3>
3150       * <dl>
3151       *    <dt><h4>Should getFeature be changed to hasFeature?</h4></dt>
3152       *    <dd>Keith Visco writes: Should getFeature be changed to hasFeature?
3153       *        It returns a boolean which indicated whether the "state"
3154       *        of feature is "true or false". I assume this means whether
3155       *        or not a feature is supported? I know SAX is using "getFeature",
3156       *        but to me "hasFeature" is cleaner.</dd>
3157       * </dl>
3158       *
3159       * @param name The feature name, which is a fully-qualified
3160       *        URI.
3161       * @return The current state of the feature (true or false).
3162       * @throws org.xml.sax.SAXNotRecognizedException When the
3163       *            TransformerFactory does not recognize the feature name.
3164       * @throws org.xml.sax.SAXNotSupportedException When the
3165       *            TransformerFactory recognizes the feature name but
3166       *            cannot determine its value at this time.
3167       *
3168       * @throws SAXNotRecognizedException
3169       * @throws SAXNotSupportedException
3170       */
3171      public boolean getFeature(String name)
3172              throws SAXNotRecognizedException, SAXNotSupportedException
3173      {
3174    
3175        if ("http://xml.org/trax/features/sax/input".equals(name))
3176          return true;
3177        else if ("http://xml.org/trax/features/dom/input".equals(name))
3178          return true;
3179    
3180        throw new SAXNotRecognizedException(name);
3181      }
3182    
3183      // %TODO% Doc
3184    
3185      /**
3186       * NEEDSDOC Method getMode 
3187       *
3188       *
3189       * NEEDSDOC (getMode) @return
3190       */
3191      public QName getMode()
3192      {
3193        return m_modes.isEmpty() ? null : (QName) m_modes.peek();
3194      }
3195    
3196      // %TODO% Doc
3197    
3198      /**
3199       * NEEDSDOC Method pushMode 
3200       *
3201       *
3202       * NEEDSDOC @param mode
3203       */
3204      public void pushMode(QName mode)
3205      {
3206        m_modes.push(mode);
3207      }
3208    
3209      // %TODO% Doc
3210    
3211      /**
3212       * NEEDSDOC Method popMode 
3213       *
3214       */
3215      public void popMode()
3216      {
3217        m_modes.pop();
3218      }
3219    
3220      /**
3221       * Called by SourceTreeHandler to start the transformation
3222       *  in a separate thread
3223       *
3224       * NEEDSDOC @param priority
3225       */
3226      public void runTransformThread(int priority)
3227      {
3228    
3229        // used in SourceTreeHandler
3230        Thread t = ThreadControllerWrapper.runThread(this, priority);
3231        this.setTransformThread(t);
3232      }
3233    
3234      /**
3235       * Called by this.transform() if isParserEventsOnMain()==false.
3236       *  Similar with runTransformThread(), but no priority is set
3237       *  and setTransformThread is not set.
3238       */
3239      public void runTransformThread()
3240      {
3241        ThreadControllerWrapper.runThread(this, -1);
3242      }
3243      
3244      /**
3245       * Called by CoRoutineSAXParser. Launches the CoroutineSAXParser
3246       * in a thread, and prepares it to invoke the parser from that thread
3247       * upon request. 
3248       *  
3249       */
3250      public static void runTransformThread(Runnable runnable)
3251      {
3252        ThreadControllerWrapper.runThread(runnable, -1);
3253      }
3254    
3255      /**
3256       * Used by SourceTreeHandler to wait until the transform
3257       *   completes
3258       *
3259       * @throws SAXException
3260       */
3261      public void waitTransformThread() throws SAXException
3262      {
3263    
3264        // This is called to make sure the task is done.
3265        // It is possible that the thread has been reused -
3266        // but for a different transformation. ( what if we 
3267        // recycle the transformer ? Not a problem since this is
3268        // still in use. )
3269        Thread transformThread = this.getTransformThread();
3270    
3271        if (null != transformThread)
3272        {
3273          try
3274          {
3275            ThreadControllerWrapper.waitThread(transformThread, this);
3276    
3277            if (!this.hasTransformThreadErrorCatcher())
3278            {
3279              Exception e = this.getExceptionThrown();
3280    
3281              if (null != e)
3282              {
3283                e.printStackTrace();
3284                throw new org.xml.sax.SAXException(e);
3285              }
3286            }
3287    
3288            this.setTransformThread(null);
3289          }
3290          catch (InterruptedException ie){}
3291        }
3292      }
3293    
3294      /**
3295       * Get the exception thrown by the secondary thread (normally
3296       * the transform thread).
3297       *
3298       * @return The thrown exception, or null if no exception was
3299       * thrown.
3300       */
3301      public Exception getExceptionThrown()
3302      {
3303        return m_exceptionThrown;
3304      }
3305    
3306      /**
3307       * Set the exception thrown by the secondary thread (normally
3308       * the transform thread).
3309       *
3310       * @param e The thrown exception, or null if no exception was
3311       * thrown.
3312       */
3313      public void setExceptionThrown(Exception e)
3314      {
3315        m_exceptionThrown = e;
3316      }
3317    
3318      /**
3319       * This is just a way to set the document for run().
3320       *
3321       * @param doc A non-null reference to the root of the
3322       * tree to be transformed.
3323       */
3324      public void setSourceTreeDocForThread(int doc)
3325      {
3326        m_doc = doc;
3327      }
3328    
3329      /**
3330       * Set the input source for the source tree, which is needed if the
3331       * parse thread is not the main thread, in order for the parse
3332       * thread's run method to get to the input source.
3333       *
3334       * @param source The input source for the source tree.
3335       */
3336      public void setXMLSource(Source source)
3337      {
3338        m_xmlSource = source;
3339      }
3340    
3341      /**
3342       * Tell if the transform method is completed.
3343       *
3344       * @return True if transformNode has completed, or
3345       * an exception was thrown.
3346       */
3347      public boolean isTransformDone()
3348      {
3349    
3350        synchronized (this)
3351        {
3352          return m_isTransformDone;
3353        }
3354      }
3355    
3356      /**
3357       * Set if the transform method is completed.
3358       *
3359       * @param done True if transformNode has completed, or
3360       * an exception was thrown.
3361       */
3362      public void setIsTransformDone(boolean done)
3363      {
3364    
3365        synchronized (this)
3366        {
3367          m_isTransformDone = done;
3368        }
3369      }
3370    
3371      /**
3372       * From a secondary thread, post the exception, so that
3373       * it can be picked up from the main thread.
3374       *
3375       * @param e The exception that was thrown.
3376       */
3377      void postExceptionFromThread(Exception e)
3378      {
3379    
3380        // Commented out in response to problem reported by Nicola Brown <Nicola.Brown@jacobsrimell.com>
3381        //    if(m_reportInPostExceptionFromThread)
3382        //    {
3383        //      // Consider re-throwing the exception if this flag is set.
3384        //      e.printStackTrace();
3385        //    }
3386        // %REVIEW Need DTM equivelent?    
3387        //    if (m_inputContentHandler instanceof SourceTreeHandler)
3388        //    {
3389        //      SourceTreeHandler sth = (SourceTreeHandler) m_inputContentHandler;
3390        //
3391        //      sth.setExceptionThrown(e);
3392        //    }
3393     //   ContentHandler ch = getContentHandler();
3394    
3395        //    if(ch instanceof SourceTreeHandler)
3396        //    {
3397        //      SourceTreeHandler sth = (SourceTreeHandler) ch;
3398        //      ((TransformerImpl)(sth.getTransformer())).postExceptionFromThread(e);
3399        //    }
3400        m_isTransformDone = true;
3401        m_exceptionThrown = e;
3402        ;  // should have already been reported via the error handler?
3403    
3404        synchronized (this)
3405        {
3406    
3407          // See message from me on 3/27/2001 to Patrick Moore.
3408          //      String msg = e.getMessage();
3409          // System.out.println(e.getMessage());
3410          // Is this really needed?  -sb
3411          notifyAll();
3412    
3413          //      if (null == msg)
3414          //      {
3415          //
3416          //        // m_throwNewError = false;
3417          //        e.printStackTrace();
3418          //      }
3419          // throw new org.apache.xml.utils.WrappedRuntimeException(e);
3420        }
3421      }
3422    
3423      /**
3424       * Run the transform thread.
3425       */
3426      public void run()
3427      {
3428    
3429        m_hasBeenReset = false;
3430    
3431        try
3432        {
3433    
3434          // int n = ((SourceTreeHandler)getInputContentHandler()).getDTMRoot();
3435          // transformNode(n);
3436          try
3437          {
3438            m_isTransformDone = false;
3439            
3440            // Should no longer be needed...
3441    //          if(m_inputContentHandler instanceof TransformerHandlerImpl)
3442    //          {
3443    //            TransformerHandlerImpl thi = (TransformerHandlerImpl)m_inputContentHandler;
3444    //            thi.waitForInitialEvents();
3445    //          }
3446    
3447            transformNode(m_doc);
3448            
3449          }
3450          catch (Exception e)
3451          {
3452            // e.printStackTrace();
3453    
3454            // Strange that the other catch won't catch this...
3455            if (null != m_transformThread)
3456              postExceptionFromThread(e);   // Assume we're on the main thread
3457            else 
3458              throw new RuntimeException(e.getMessage());
3459          }
3460          finally
3461          {
3462            m_isTransformDone = true;
3463    
3464            if (m_inputContentHandler instanceof TransformerHandlerImpl)
3465            {
3466              ((TransformerHandlerImpl) m_inputContentHandler).clearCoRoutine();
3467            }
3468    
3469            //        synchronized (this)
3470            //        {
3471            //          notifyAll();
3472            //        }
3473          }
3474        }
3475        catch (Exception e)
3476        {
3477    
3478          // e.printStackTrace();
3479          if (null != m_transformThread)
3480            postExceptionFromThread(e);
3481          else 
3482            throw new RuntimeException(e.getMessage());         // Assume we're on the main thread.
3483        }
3484      }
3485    
3486      // Fragment re-execution interfaces for a tool.
3487    
3488      /**
3489       * This will get a snapshot of the current executing context 
3490       *
3491       *
3492       * @return TransformSnapshot object, snapshot of executing context
3493       * @deprecated This is an internal tooling API that nobody seems to be using
3494       */
3495      public TransformSnapshot getSnapshot()
3496      {
3497        return new TransformSnapshotImpl(this);
3498      }
3499    
3500      /**
3501       * This will execute the following XSLT instructions
3502       * from the snapshot point, after the stylesheet execution
3503       * context has been reset from the snapshot point. 
3504       *
3505       * @param ts The snapshot of where to start execution
3506       *
3507       * @throws TransformerException
3508       * @deprecated This is an internal tooling API that nobody seems to be using
3509       */
3510      public void executeFromSnapshot(TransformSnapshot ts)
3511              throws TransformerException
3512      {
3513    
3514        ElemTemplateElement template = getMatchedTemplate();
3515        int child = getMatchedNode();
3516    
3517        pushElemTemplateElement(template);  //needed??
3518        m_xcontext.pushCurrentNode(child);  //needed??
3519        this.executeChildTemplates(template, true);  // getResultTreeHandler());
3520      }
3521    
3522      /**
3523       * This will reset the stylesheet execution context
3524       * from the snapshot point.
3525       *
3526       * @param ts The snapshot of where to start execution
3527       * @deprecated This is an internal tooling API that nobody seems to be using
3528       */
3529      public void resetToStylesheet(TransformSnapshot ts)
3530      {
3531        ((TransformSnapshotImpl) ts).apply(this);
3532      }
3533    
3534      /**
3535       * NEEDSDOC Method stopTransformation 
3536       *
3537       */
3538      public void stopTransformation(){}
3539    
3540      /**
3541       * Test whether whitespace-only text nodes are visible in the logical
3542       * view of <code>DTM</code>. Normally, this function
3543       * will be called by the implementation of <code>DTM</code>;
3544       * it is not normally called directly from
3545       * user code.
3546       *
3547       * @param elementHandle int Handle of the element.
3548       * @return one of NOTSTRIP, STRIP, or INHERIT.
3549       */
3550      public short getShouldStripSpace(int elementHandle, DTM dtm)
3551      {
3552    
3553        try
3554        {
3555          org.apache.xalan.templates.WhiteSpaceInfo info =
3556            m_stylesheetRoot.getWhiteSpaceInfo(m_xcontext, elementHandle, dtm);
3557    
3558          if (null == info)
3559          {
3560            return DTMWSFilter.INHERIT;
3561          }
3562          else
3563          {
3564    
3565            // System.out.println("getShouldStripSpace: "+info.getShouldStripSpace());
3566            return info.getShouldStripSpace()
3567                   ? DTMWSFilter.STRIP : DTMWSFilter.NOTSTRIP;
3568          }
3569        }
3570        catch (TransformerException se)
3571        {
3572          return DTMWSFilter.INHERIT;
3573        }
3574      }
3575      /**
3576       * Initializer method.
3577       *
3578       * @param transformer non-null transformer instance
3579       * @param realHandler Content Handler instance
3580       */
3581       public void init(ToXMLSAXHandler h,Transformer transformer, ContentHandler realHandler)
3582       {
3583          h.setTransformer(transformer);
3584          h.setContentHandler(realHandler);
3585       }
3586          
3587       public void setSerializationHandler(SerializationHandler xoh)
3588       {
3589          m_serializationHandler = xoh;
3590       }
3591       
3592       
3593         
3594            /**
3595             * Fire off characters, cdate events.
3596             * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, char[], int, int)
3597             */
3598            public void fireGenerateEvent(
3599                    int eventType,
3600                    char[] ch,
3601                    int start,
3602                    int length) {
3603                            
3604                    GenerateEvent ge = new GenerateEvent(this, eventType, ch, start, length);
3605                    m_traceManager.fireGenerateEvent(ge);                                   
3606            }
3607    
3608            /**
3609             * Fire off startElement, endElement events.
3610             * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, Attributes)
3611             */
3612            public void fireGenerateEvent(
3613                    int eventType,
3614                    String name,
3615                    Attributes atts) {
3616                            
3617                    GenerateEvent ge = new GenerateEvent(this, eventType, name, atts);
3618                    m_traceManager.fireGenerateEvent(ge);                                   
3619            }
3620    
3621            /**
3622             * Fire off processingInstruction events.
3623             * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String, String)
3624             */
3625            public void fireGenerateEvent(int eventType, String name, String data) {
3626                    GenerateEvent ge = new GenerateEvent(this, eventType, name,data);
3627                    m_traceManager.fireGenerateEvent(ge);                           
3628            }
3629    
3630            /**
3631             * Fire off comment and entity ref events.
3632             * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int, String)
3633             */
3634            public void fireGenerateEvent(int eventType, String data) {
3635                    GenerateEvent ge = new GenerateEvent(this, eventType, data);
3636                    m_traceManager.fireGenerateEvent(ge);           
3637            }
3638    
3639            /**
3640             * Fire off startDocument, endDocument events.
3641             * @see org.apache.xml.serializer.SerializerTrace#fireGenerateEvent(int)
3642             */
3643            public void fireGenerateEvent(int eventType) {
3644                    GenerateEvent ge = new GenerateEvent(this, eventType);
3645                    m_traceManager.fireGenerateEvent(ge);
3646            }
3647    
3648        /**
3649         * @see org.apache.xml.serializer.SerializerTrace#hasTraceListeners()
3650         */
3651        public boolean hasTraceListeners() {
3652            return m_traceManager.hasTraceListeners();
3653        }
3654    
3655        public boolean getDebug() {
3656            return m_debug;
3657        }
3658    
3659        public void setDebug(boolean b) {
3660            m_debug = b;
3661        }
3662    
3663        /**
3664         * @return Incremental flag
3665         */
3666        public boolean getIncremental() {
3667            return m_incremental;
3668        }
3669    
3670        /**
3671         * @return Optimization flag
3672         */
3673        public boolean getOptimize() {
3674            return m_optimizer;
3675        }
3676    
3677        /**
3678         * @return Source location flag
3679         */
3680        public boolean getSource_location() {
3681            return m_source_location;
3682        }
3683    
3684    }  // end TransformerImpl class
3685