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