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