Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/apache/xalan/transformer/TransformerImpl.java


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