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