001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: StylesheetRoot.java 476466 2006-11-18 08:22:31Z minchau $
020     */
021    package org.apache.xalan.templates;
022    
023    import java.text.DecimalFormatSymbols;
024    import java.util.ArrayList;
025    import java.util.HashMap;
026    import java.util.Hashtable;
027    import java.util.Properties;
028    import java.util.Vector;
029    
030    import javax.xml.transform.ErrorListener;
031    import javax.xml.transform.Templates;
032    import javax.xml.transform.Transformer;
033    import javax.xml.transform.TransformerConfigurationException;
034    import javax.xml.transform.TransformerException;
035    
036    import org.apache.xalan.extensions.ExtensionNamespacesManager;
037    import org.apache.xalan.processor.XSLTSchema;
038    import org.apache.xalan.res.XSLMessages;
039    import org.apache.xalan.res.XSLTErrorResources;
040    
041    import org.apache.xalan.transformer.TransformerImpl;
042    import org.apache.xml.dtm.DTM;
043    import org.apache.xml.dtm.ref.ExpandedNameTable;
044    import org.apache.xml.utils.IntStack;
045    import org.apache.xml.utils.QName;
046    import org.apache.xpath.XPath;
047    import org.apache.xpath.XPathContext;
048    
049    /**
050     * This class represents the root object of the stylesheet tree.
051     * @xsl.usage general
052     */
053    public class StylesheetRoot extends StylesheetComposed
054            implements java.io.Serializable, Templates
055    {
056        static final long serialVersionUID = 3875353123529147855L;
057        
058        /**
059         * The flag for the setting of the optimize feature;
060         */    
061        private boolean m_optimizer = true;
062    
063        /**
064         * The flag for the setting of the incremental feature;
065         */    
066        private boolean m_incremental = false;
067    
068        /**
069         * The flag for the setting of the source_location feature;
070         */  
071        private boolean m_source_location = false;
072    
073        /**
074         * State of the secure processing feature.
075         */
076        private boolean m_isSecureProcessing = false;
077    
078      /**
079       * Uses an XSL stylesheet document.
080       * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL.
081       */
082      public StylesheetRoot(ErrorListener errorListener) throws TransformerConfigurationException
083      {
084    
085        super(null);
086    
087        setStylesheetRoot(this);
088    
089        try
090        {
091          m_selectDefault = new XPath("node()", this, this, XPath.SELECT, errorListener);
092    
093          initDefaultRule(errorListener);
094        }
095        catch (TransformerException se)
096        {
097          throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_INIT_DEFAULT_TEMPLATES, null), se); //"Can't init default templates!", se);
098        }
099      }
100    
101      /**
102       * The schema used when creating this StylesheetRoot
103       * @serial
104       */
105      private HashMap m_availElems;
106      
107      /**
108       * Creates a StylesheetRoot and retains a pointer to the schema used to create this
109       * StylesheetRoot.  The schema may be needed later for an element-available() function call.
110       * 
111       * @param schema The schema used to create this stylesheet
112       * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL.
113       */
114      public StylesheetRoot(XSLTSchema schema, ErrorListener listener) throws TransformerConfigurationException
115      {
116    
117        this(listener);
118        m_availElems = schema.getElemsAvailable();
119      }
120    
121      /**
122       * Tell if this is the root of the stylesheet tree.
123       *
124       * @return True since this is the root of the stylesheet tree.
125       */
126      public boolean isRoot()
127      {
128        return true;
129      }
130    
131      /**
132       * Set the state of the secure processing feature.
133       */
134      public void setSecureProcessing(boolean flag)
135      {
136        m_isSecureProcessing = flag;
137      }
138      
139      /**
140       * Return the state of the secure processing feature.
141       */
142      public boolean isSecureProcessing()
143      {
144        return m_isSecureProcessing;
145      }
146    
147      /**
148       * Get the hashtable of available elements.
149       *
150       * @return table of available elements, keyed by qualified names, and with 
151       * values of the same qualified names.
152       */
153      public HashMap getAvailableElements()
154      {
155        return m_availElems;
156      }
157      
158      private transient ExtensionNamespacesManager m_extNsMgr = null;
159      
160      /**
161       * Only instantiate an ExtensionNamespacesManager if one is called for
162       * (i.e., if the stylesheet contains  extension functions and/or elements).
163       */
164      public ExtensionNamespacesManager getExtensionNamespacesManager()
165      {
166         if (m_extNsMgr == null)
167           m_extNsMgr = new ExtensionNamespacesManager();
168         return m_extNsMgr;
169      }
170      
171      /**
172       * Get the vector of extension namespaces. Used to provide
173       * the extensions table access to a list of extension
174       * namespaces encountered during composition of a stylesheet.
175       */
176      public Vector getExtensions()
177      {
178        return m_extNsMgr != null ? m_extNsMgr.getExtensions() : null;
179      }  
180    
181    /*
182      public void runtimeInit(TransformerImpl transformer) throws TransformerException
183      {
184        System.out.println("StylesheetRoot.runtimeInit()");
185          
186      //    try{throw new Exception("StylesheetRoot.runtimeInit()");} catch(Exception e){e.printStackTrace();}
187    
188        }
189    */  
190    
191      //============== Templates Interface ================
192    
193      /**
194       * Create a new transformation context for this Templates object.
195       *
196       * @return A Transformer instance, never null.
197       */
198      public Transformer newTransformer()
199      {
200        return new TransformerImpl(this);
201      }
202      
203    
204      public Properties getDefaultOutputProps()
205      {
206        return m_outputProperties.getProperties();
207      }
208      
209      /**
210       * Get the static properties for xsl:output.  The object returned will
211       * be a clone of the internal values, and thus it can be mutated
212       * without mutating the Templates object, and then handed in to
213       * the process method.
214       *
215       * <p>For XSLT, Attribute Value Templates attribute values will
216       * be returned unexpanded (since there is no context at this point).</p>
217       *
218       * @return A Properties object, not null.
219       */
220      public Properties getOutputProperties()
221      {    
222        return (Properties)getDefaultOutputProps().clone();
223      }
224    
225      //============== End Templates Interface ================
226    
227      /**
228       * Recompose the values of all "composed" properties, meaning
229       * properties that need to be combined or calculated from
230       * the combination of imported and included stylesheets.  This
231       * method determines the proper import precedence of all imported
232       * stylesheets.  It then iterates through all of the elements and 
233       * properties in the proper order and triggers the individual recompose
234       * methods.
235       *
236       * @throws TransformerException
237       */
238      public void recompose() throws TransformerException
239      {
240        // Now we make a Vector that is going to hold all of the recomposable elements
241    
242          Vector recomposableElements = new Vector();
243    
244        // First, we build the global import tree.
245    
246        if (null == m_globalImportList)
247        {
248    
249          Vector importList = new Vector();
250    
251          addImports(this, true, importList);            
252    
253          // Now we create an array and reverse the order of the importList vector.
254          // We built the importList vector backwards so that we could use addElement
255          // to append to the end of the vector instead of constantly pushing new
256          // stylesheets onto the front of the vector and having to shift the rest
257          // of the vector each time.
258    
259          m_globalImportList = new StylesheetComposed[importList.size()];
260    
261          for (int i =  0, j= importList.size() -1; i < importList.size(); i++)
262          {  
263            m_globalImportList[j] = (StylesheetComposed) importList.elementAt(i);
264            // Build the global include list for this stylesheet.
265            // This needs to be done ahead of the recomposeImports
266            // because we need the info from the composed includes. 
267            m_globalImportList[j].recomposeIncludes(m_globalImportList[j]);
268            // Calculate the number of this import.    
269            m_globalImportList[j--].recomposeImports();        
270          }
271        }    
272        // Next, we walk the import tree and add all of the recomposable elements to the vector.
273        int n = getGlobalImportCount();
274    
275        for (int i = 0; i < n; i++)
276        {
277          StylesheetComposed imported = getGlobalImport(i);
278          imported.recompose(recomposableElements);
279        }
280    
281        // We sort the elements into ascending order.
282    
283        QuickSort2(recomposableElements, 0, recomposableElements.size() - 1);
284    
285        // We set up the global variables that will hold the recomposed information.
286    
287    
288        m_outputProperties = new OutputProperties(org.apache.xml.serializer.Method.UNKNOWN);
289    //  m_outputProperties = new OutputProperties(Method.XML);
290        
291        m_attrSets = new HashMap();
292        m_decimalFormatSymbols = new Hashtable();
293        m_keyDecls = new Vector();
294        m_namespaceAliasComposed = new Hashtable();
295        m_templateList = new TemplateList();
296        m_variables = new Vector();
297    
298        // Now we sequence through the sorted elements, 
299        // calling the recompose() function on each one.  This will call back into the
300        // appropriate routine here to actually do the recomposition.
301        // Note that we're going backwards, encountering the highest precedence items first.
302        for (int i = recomposableElements.size() - 1; i >= 0; i--)
303          ((ElemTemplateElement) recomposableElements.elementAt(i)).recompose(this);
304        
305    /*
306     * Backing out REE again, as it seems to cause some new failures
307     * which need to be investigated. -is
308     */      
309        // This has to be done before the initialization of the compose state, because 
310        // eleminateRedundentGlobals will add variables to the m_variables vector, which 
311        // it then copied in the ComposeState constructor.
312        
313    //    if(true && org.apache.xalan.processor.TransformerFactoryImpl.m_optimize)
314    //    {
315    //          RedundentExprEliminator ree = new RedundentExprEliminator();
316    //          callVisitors(ree);
317    //          ree.eleminateRedundentGlobals(this);
318    //    }
319              
320        initComposeState();
321    
322        // Need final composition of TemplateList.  This adds the wild cards onto the chains.
323        m_templateList.compose(this);
324        
325        // Need to clear check for properties at the same import level.
326        m_outputProperties.compose(this);
327        m_outputProperties.endCompose(this);
328        
329        // Now call the compose() method on every element to give it a chance to adjust
330        // based on composed values.
331        
332        n = getGlobalImportCount();
333    
334        for (int i = 0; i < n; i++)
335        {
336          StylesheetComposed imported = this.getGlobalImport(i);
337          int includedCount = imported.getIncludeCountComposed();
338          for (int j = -1; j < includedCount; j++)
339          {
340            Stylesheet included = imported.getIncludeComposed(j);
341            composeTemplates(included);
342          }
343        }
344        // Attempt to register any remaining unregistered extension namespaces.
345        if (m_extNsMgr != null)
346          m_extNsMgr.registerUnregisteredNamespaces();
347    
348        clearComposeState();
349      }
350    
351      /**
352       * Call the compose function for each ElemTemplateElement.
353       *
354       * @param templ non-null reference to template element that will have 
355       * the composed method called on it, and will have it's children's composed 
356       * methods called.
357       */
358      void composeTemplates(ElemTemplateElement templ) throws TransformerException
359      {
360    
361        templ.compose(this);
362    
363        for (ElemTemplateElement child = templ.getFirstChildElem();
364                child != null; child = child.getNextSiblingElem())
365        {
366          composeTemplates(child);
367        }
368        
369        templ.endCompose(this);
370      }
371    
372      /**
373       * The combined list of imports.  The stylesheet with the highest
374       * import precedence will be at element 0.  The one with the lowest
375       * import precedence will be at element length - 1.
376       * @serial
377       */
378      private StylesheetComposed[] m_globalImportList;
379    
380      /**
381       * Add the imports in the given sheet to the working importList vector.
382       * The will be added from highest import precedence to
383       * least import precedence.  This is a post-order traversal of the
384       * import tree as described in <a href="http://www.w3.org/TR/xslt.html#import">the
385       * XSLT Recommendation</a>.
386       * <p>For example, suppose</p>
387       * <p>stylesheet A imports stylesheets B and C in that order;</p>
388       * <p>stylesheet B imports stylesheet D;</p>
389       * <p>stylesheet C imports stylesheet E.</p>
390       * <p>Then the order of import precedence (highest first) is
391       * A, C, E, B, D.</p>
392       *
393       * @param stylesheet Stylesheet to examine for imports.
394       * @param addToList  <code>true</code> if this template should be added to the import list
395       * @param importList The working import list.  Templates are added here in the reverse
396       *        order of priority.  When we're all done, we'll reverse this to the correct
397       *        priority in an array.
398       */
399      protected void addImports(Stylesheet stylesheet, boolean addToList, Vector importList)
400      {
401    
402        // Get the direct imports of this sheet.
403    
404        int n = stylesheet.getImportCount();
405    
406        if (n > 0)
407        {
408          for (int i = 0; i < n; i++)
409          {
410            Stylesheet imported = stylesheet.getImport(i);
411    
412            addImports(imported, true, importList);
413          }
414        }
415    
416        n = stylesheet.getIncludeCount();
417    
418        if (n > 0)
419        {
420          for (int i = 0; i < n; i++)
421          {
422            Stylesheet included = stylesheet.getInclude(i);
423    
424            addImports(included, false, importList);
425          }
426        }
427    
428        if (addToList)
429          importList.addElement(stylesheet);
430    
431      }
432    
433      /**
434       * Get a stylesheet from the global import list. 
435       * TODO: JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH COUNT.
436       * 
437       * @param i Index of stylesheet to get from global import list 
438       *
439       * @return The stylesheet at the given index 
440       */
441      public StylesheetComposed getGlobalImport(int i)
442      {
443        return m_globalImportList[i];
444      }
445    
446      /**
447       * Get the total number of imports in the global import list.
448       * 
449       * @return The total number of imported stylesheets, including
450       * the root stylesheet, thus the number will always be 1 or
451       * greater.
452       * TODO: JKESS PROPOSES SPECIAL-CASE FOR NO IMPORT LIST, TO MATCH DESCRIPTION.
453       */
454      public int getGlobalImportCount()
455      {
456              return (m_globalImportList!=null)
457                            ? m_globalImportList.length 
458                              : 1;
459      }
460    
461      /**
462       * Given a stylesheet, return the number of the stylesheet
463       * in the global import list.
464       * @param sheet The stylesheet which will be located in the
465       * global import list.
466       * @return The index into the global import list of the given stylesheet,
467       * or -1 if it is not found (which should never happen).
468       */
469      public int getImportNumber(StylesheetComposed sheet)
470      {
471    
472        if (this == sheet)
473          return 0;
474    
475        int n = getGlobalImportCount();
476    
477        for (int i = 0; i < n; i++)
478        {
479          if (sheet == getGlobalImport(i))
480            return i;
481        }
482    
483        return -1;
484      }
485    
486      /**
487       * This will be set up with the default values, and then the values
488       * will be set as stylesheets are encountered.
489       * @serial
490       */
491      private OutputProperties m_outputProperties;
492    
493      /**
494       * Recompose the output format object from the included elements.
495       *
496       * @param oprops non-null reference to xsl:output properties representation.
497       */
498      void recomposeOutput(OutputProperties oprops)
499        throws TransformerException
500      {
501        
502        m_outputProperties.copyFrom(oprops);
503      }
504    
505      /**
506       * Get the combined "xsl:output" property with the properties
507       * combined from the included stylesheets.  If a xsl:output
508       * is not declared in this stylesheet or an included stylesheet,
509       * look in the imports.
510       * Please note that this returns a reference to the OutputProperties
511       * object, not a cloned object, like getOutputProperties does.
512       * @see <a href="http://www.w3.org/TR/xslt#output">output in XSLT Specification</a>
513       *
514       * @return non-null reference to composed output properties object.
515       */
516      public OutputProperties getOutputComposed()
517      {
518    
519        // System.out.println("getOutputComposed.getIndent: "+m_outputProperties.getIndent());
520        // System.out.println("getOutputComposed.getIndenting: "+m_outputProperties.getIndenting());
521        return m_outputProperties;
522      }
523    
524      /** Flag indicating whether an output method has been set by the user.
525       *  @serial           */
526      private boolean m_outputMethodSet = false;
527    
528      /**
529       * Find out if an output method has been set by the user.
530       *
531       * @return Value indicating whether an output method has been set by the user
532       * @xsl.usage internal
533       */
534      public boolean isOutputMethodSet()
535      {
536        return m_outputMethodSet;
537      }
538    
539      /**
540       * Composed set of all included and imported attribute set properties.
541       * Each entry is a vector of ElemAttributeSet objects.
542       * @serial
543       */
544      private HashMap m_attrSets;
545    
546      /**
547       * Recompose the attribute-set declarations.
548       *
549       * @param attrSet An attribute-set to add to the hashtable of attribute sets.
550       */
551      void recomposeAttributeSets(ElemAttributeSet attrSet)
552      {
553        ArrayList attrSetList = (ArrayList) m_attrSets.get(attrSet.getName());
554    
555        if (null == attrSetList)
556        {
557          attrSetList = new ArrayList();
558    
559          m_attrSets.put(attrSet.getName(), attrSetList);
560        }
561    
562        attrSetList.add(attrSet);
563      }
564    
565      /**
566       * Get a list "xsl:attribute-set" properties that match the qname.
567       * @see <a href="http://www.w3.org/TR/xslt#attribute-sets">attribute-sets in XSLT Specification</a>
568       *
569       * @param name Qualified name of attribute set properties to get
570       *
571       * @return A vector of attribute sets matching the given name
572       *
573       * @throws ArrayIndexOutOfBoundsException
574       */
575      public ArrayList getAttributeSetComposed(QName name)
576              throws ArrayIndexOutOfBoundsException
577      {
578        return (ArrayList) m_attrSets.get(name);
579      }
580    
581      /**
582       * Table of DecimalFormatSymbols, keyed by QName.
583       * @serial
584       */
585      private Hashtable m_decimalFormatSymbols;
586    
587      /**
588       * Recompose the decimal-format declarations.
589       *
590       * @param dfp A DecimalFormatProperties to add to the hashtable of decimal formats.
591       */
592      void recomposeDecimalFormats(DecimalFormatProperties dfp)
593      {
594        DecimalFormatSymbols oldDfs =
595                      (DecimalFormatSymbols) m_decimalFormatSymbols.get(dfp.getName());
596        if (null == oldDfs)
597        {
598          m_decimalFormatSymbols.put(dfp.getName(), dfp.getDecimalFormatSymbols());
599        }
600        else if (!dfp.getDecimalFormatSymbols().equals(oldDfs))
601        {
602          String themsg;
603          if (dfp.getName().equals(new QName("")))
604          {
605            // "Only one default xsl:decimal-format declaration is allowed."
606            themsg = XSLMessages.createWarning(
607                              XSLTErrorResources.WG_ONE_DEFAULT_XSLDECIMALFORMAT_ALLOWED,
608                              new Object[0]);
609          }
610          else
611          {
612            // "xsl:decimal-format names must be unique. Name {0} has been duplicated."
613            themsg = XSLMessages.createWarning(
614                              XSLTErrorResources.WG_XSLDECIMALFORMAT_NAMES_MUST_BE_UNIQUE,
615                              new Object[] {dfp.getName()});
616          }
617    
618          error(themsg);   // Should we throw TransformerException instead?
619        }
620    
621      }
622    
623      /**
624       * Given a valid element decimal-format name, return the
625       * decimalFormatSymbols with that name.
626       * <p>It is an error to declare either the default decimal-format or
627       * a decimal-format with a given name more than once (even with
628       * different import precedence), unless it is declared every
629       * time with the same value for all attributes (taking into
630       * account any default values).</p>
631       * <p>Which means, as far as I can tell, the decimal-format
632       * properties are not additive.</p>
633       *
634       * @param name Qualified name of the decimal format to find 
635       * @return DecimalFormatSymbols object matching the given name or
636       * null if name is not found.
637       */
638      public DecimalFormatSymbols getDecimalFormatComposed(QName name)
639      {
640        return (DecimalFormatSymbols) m_decimalFormatSymbols.get(name);
641      }
642    
643      /**
644       * A list of all key declarations visible from this stylesheet and all
645       * lesser stylesheets.
646       * @serial
647       */
648      private Vector m_keyDecls;
649    
650      /**
651       * Recompose the key declarations.
652       *
653       * @param keyDecl A KeyDeclaration to be added to the vector of key declarations.
654       */
655      void recomposeKeys(KeyDeclaration keyDecl)
656      {
657        m_keyDecls.addElement(keyDecl);
658      }
659    
660      /**
661       * Get the composed "xsl:key" properties.
662       * @see <a href="http://www.w3.org/TR/xslt#key">key in XSLT Specification</a>
663       *
664       * @return A vector of the composed "xsl:key" properties.
665       */
666      public Vector getKeysComposed()
667      {
668        return m_keyDecls;
669      }
670    
671      /**
672       * Composed set of all namespace aliases.
673       * @serial
674       */
675      private Hashtable m_namespaceAliasComposed;
676    
677      /**
678       * Recompose the namespace-alias declarations.
679       *
680       * @param nsAlias A NamespaceAlias object to add to the hashtable of namespace aliases.
681       */
682      void recomposeNamespaceAliases(NamespaceAlias nsAlias)
683      {
684        m_namespaceAliasComposed.put(nsAlias.getStylesheetNamespace(),
685                                     nsAlias);
686      }
687    
688      /**
689       * Get the "xsl:namespace-alias" property.
690       * Return the NamespaceAlias for a given namespace uri.
691       * @see <a href="http://www.w3.org/TR/xslt#literal-result-element">literal-result-element in XSLT Specification</a>
692       *
693       * @param uri non-null reference to namespace that is to be aliased.
694       *
695       * @return NamespaceAlias that matches uri, or null if no match.
696       */
697      public NamespaceAlias getNamespaceAliasComposed(String uri)
698      {
699        return (NamespaceAlias) ((null == m_namespaceAliasComposed) 
700                        ? null : m_namespaceAliasComposed.get(uri));
701      }
702    
703      /**
704       * The "xsl:template" properties.
705       * @serial
706       */
707      private TemplateList m_templateList;
708    
709      /**
710       * Recompose the template declarations.
711       *
712       * @param template An ElemTemplate object to add to the template list.
713       */
714      void recomposeTemplates(ElemTemplate template)
715      {
716        m_templateList.setTemplate(template);
717      }
718    
719      /**
720       * Accessor method to retrieve the <code>TemplateList</code> associated with
721       * this StylesheetRoot.
722       * 
723       * @return The composed <code>TemplateList</code>.
724       */
725      public final TemplateList getTemplateListComposed()
726      {
727        return m_templateList;
728      }
729    
730      /**
731       * Mutator method to set the <code>TemplateList</code> associated with this
732       * StylesheetRoot.  This method should only be used by the compiler.  Normally,
733       * the template list is built during the recompose process and should not be
734       * altered by the user.
735       * @param templateList The new <code>TemplateList</code> for this StylesheetRoot.
736       */
737      public final void setTemplateListComposed(TemplateList templateList)
738      {
739        m_templateList = templateList;
740      }
741    
742      /**
743       * Get an "xsl:template" property by node match. This looks in the imports as
744       * well as this stylesheet.
745       * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
746       *
747       * @param xctxt non-null reference to XPath runtime execution context.
748       * @param targetNode non-null reference of node that the template must match.
749       * @param mode qualified name of the node, or null.
750       * @param quietConflictWarnings true if conflict warnings should not be reported.
751       *
752       * @return reference to ElemTemplate that is the best match for targetNode, or 
753       *         null if no match could be made.
754       *
755       * @throws TransformerException
756       */
757      public ElemTemplate getTemplateComposed(XPathContext xctxt,
758                                              int targetNode,
759                                              QName mode,
760                                              boolean quietConflictWarnings,
761                                              DTM dtm)
762                throws TransformerException
763      {
764        return m_templateList.getTemplate(xctxt, targetNode, mode, 
765                                          quietConflictWarnings,
766                                          dtm);
767      }
768      
769      /**
770       * Get an "xsl:template" property by node match. This looks in the imports as
771       * well as this stylesheet.
772       * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
773       *
774       * @param xctxt non-null reference to XPath runtime execution context.
775       * @param targetNode non-null reference of node that the template must match.
776       * @param mode qualified name of the node, or null.
777       * @param maxImportLevel The maximum importCountComposed that we should consider or -1
778       *        if we should consider all import levels.  This is used by apply-imports to
779       *        access templates that have been overridden.
780       * @param endImportLevel The count of composed imports
781       * @param quietConflictWarnings true if conflict warnings should not be reported.
782       *
783       * @return reference to ElemTemplate that is the best match for targetNode, or 
784       *         null if no match could be made.
785       *
786       * @throws TransformerException
787       */
788      public ElemTemplate getTemplateComposed(XPathContext xctxt,
789                                              int targetNode,
790                                              QName mode,
791                                              int maxImportLevel, int endImportLevel,
792                                              boolean quietConflictWarnings,
793                                              DTM dtm)
794                throws TransformerException
795      {
796        return m_templateList.getTemplate(xctxt, targetNode, mode, 
797                                          maxImportLevel, endImportLevel,
798                                          quietConflictWarnings,
799                                          dtm);
800      }
801    
802      /**
803       * Get an "xsl:template" property. This looks in the imports as
804       * well as this stylesheet.
805       * @see <a href="http://www.w3.org/TR/xslt#section-Defining-Template-Rules">section-Defining-Template-Rules in XSLT Specification</a>
806       *
807       * @param qname non-null reference to qualified name of template.
808       *
809       * @return reference to named template, or null if not found.
810       */
811      public ElemTemplate getTemplateComposed(QName qname)
812      {
813        return m_templateList.getTemplate(qname);
814      }
815      
816      /**
817       * Composed set of all variables and params.
818       * @serial
819       */
820      private Vector m_variables;
821    
822      /**
823       * Recompose the top level variable and parameter declarations.
824       *
825       * @param elemVar A top level variable or parameter to be added to the Vector.
826       */
827      void recomposeVariables(ElemVariable elemVar)
828      {
829        // Don't overide higher priority variable        
830        if (getVariableOrParamComposed(elemVar.getName()) == null)
831        {
832          elemVar.setIsTopLevel(true);        // Mark as a top-level variable or param
833          elemVar.setIndex(m_variables.size());
834          m_variables.addElement(elemVar);
835        }
836      }
837    
838      /**
839       * Get an "xsl:variable" property.
840       * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
841       *
842       * @param qname Qualified name of variable or param
843       *
844       * @return The ElemVariable with the given qualified name
845       */
846      public ElemVariable getVariableOrParamComposed(QName qname)
847      {
848        if (null != m_variables)
849        {
850          int n = m_variables.size();
851    
852          for (int i = 0; i < n; i++)
853          {
854            ElemVariable var = (ElemVariable)m_variables.elementAt(i);
855            if(var.getName().equals(qname))
856              return var;
857          }
858        }
859    
860        return null;
861      }
862    
863      /**
864       * Get all global "xsl:variable" properties in scope for this stylesheet.
865       * @see <a href="http://www.w3.org/TR/xslt#top-level-variables">top-level-variables in XSLT Specification</a>
866       *
867       * @return Vector of all variables and params in scope
868       */
869      public Vector getVariablesAndParamsComposed()
870      {
871        return m_variables;
872      }
873    
874      /**
875       * A list of properties that specify how to do space
876       * stripping. This uses the same exact mechanism as Templates.
877       * @serial
878       */
879      private TemplateList m_whiteSpaceInfoList;
880    
881      /**
882       * Recompose the strip-space and preserve-space declarations.
883       *
884       * @param wsi A WhiteSpaceInfo element to add to the list of WhiteSpaceInfo elements.
885       */
886      void recomposeWhiteSpaceInfo(WhiteSpaceInfo wsi)
887      {
888        if (null == m_whiteSpaceInfoList)
889          m_whiteSpaceInfoList = new TemplateList();
890    
891        m_whiteSpaceInfoList.setTemplate(wsi);
892      }
893    
894      /**
895       * Check to see if the caller should bother with check for
896       * whitespace nodes.
897       *
898       * @return Whether the caller should bother with check for
899       * whitespace nodes.
900       */
901      public boolean shouldCheckWhitespace()
902      {
903        return null != m_whiteSpaceInfoList;
904      }
905    
906      /**
907       * Get information about whether or not an element should strip whitespace.
908       * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
909       *
910       * @param support The XPath runtime state.
911       * @param targetElement Element to check
912       *
913       * @return WhiteSpaceInfo for the given element
914       *
915       * @throws TransformerException
916       */
917      public WhiteSpaceInfo getWhiteSpaceInfo(
918              XPathContext support, int targetElement, DTM dtm) throws TransformerException
919      {
920    
921        if (null != m_whiteSpaceInfoList)
922          return (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support,
923                  targetElement, null, false, dtm);
924        else
925          return null;
926      }
927      
928      /**
929       * Get information about whether or not an element should strip whitespace.
930       * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
931       *
932       * @param support The XPath runtime state.
933       * @param targetElement Element to check
934       *
935       * @return true if the whitespace should be stripped.
936       *
937       * @throws TransformerException
938       */
939      public boolean shouldStripWhiteSpace(
940              XPathContext support, int targetElement) throws TransformerException
941      {
942        if (null != m_whiteSpaceInfoList)
943        {
944          while(DTM.NULL != targetElement)
945          {
946            DTM dtm = support.getDTM(targetElement);
947            WhiteSpaceInfo info = (WhiteSpaceInfo) m_whiteSpaceInfoList.getTemplate(support,
948                    targetElement, null, false, dtm);
949            if(null != info)
950              return info.getShouldStripSpace();
951            
952            int parent = dtm.getParent(targetElement);
953            if(DTM.NULL != parent && DTM.ELEMENT_NODE == dtm.getNodeType(parent))
954              targetElement = parent;
955            else
956              targetElement = DTM.NULL;
957          }
958        }
959        return false;
960      }
961      
962      /**
963       * Get information about whether or not whitespace can be stripped.
964       * @see <a href="http://www.w3.org/TR/xslt#strip">strip in XSLT Specification</a>
965       *
966       * @return true if the whitespace can be stripped.
967       */
968      public boolean canStripWhiteSpace()
969      {
970        return (null != m_whiteSpaceInfoList);
971      }
972      
973    
974    
975      /**
976       * The default template to use for text nodes if we don't find
977       * anything else.  This is initialized in initDefaultRule().
978       * @serial
979       * @xsl.usage advanced
980       */
981      private ElemTemplate m_defaultTextRule;
982    
983      /**
984       * Get the default template for text.
985       *
986       * @return the default template for text.
987       * @xsl.usage advanced
988       */
989      public final ElemTemplate getDefaultTextRule()
990      {
991        return m_defaultTextRule;
992      }
993    
994      /**
995       * The default template to use if we don't find anything
996       * else.  This is initialized in initDefaultRule().
997       * @serial
998       * @xsl.usage advanced
999       */
1000      private ElemTemplate m_defaultRule;
1001    
1002      /**
1003       * Get the default template for elements.
1004       *
1005       * @return the default template for elements.
1006       * @xsl.usage advanced
1007       */
1008      public final ElemTemplate getDefaultRule()
1009      {
1010        return m_defaultRule;
1011      }
1012    
1013      /**
1014       * The default template to use for the root if we don't find
1015       * anything else.  This is initialized in initDefaultRule().
1016       * We kind of need this because the defaultRule isn't good
1017       * enough because it doesn't supply a document context.
1018       * For now, I default the root document element to "HTML".
1019       * Don't know if this is really a good idea or not.
1020       * I suspect it is not.
1021       * @serial
1022       * @xsl.usage advanced
1023       */
1024      private ElemTemplate m_defaultRootRule;
1025    
1026      /**
1027       * Get the default template for a root node.
1028       *
1029       * @return The default template for a root node.
1030       * @xsl.usage advanced
1031       */
1032      public final ElemTemplate getDefaultRootRule()
1033      {
1034        return m_defaultRootRule;
1035      }
1036      
1037      /**
1038       * The start rule to kick off the transformation.
1039       * @serial
1040       * @xsl.usage advanced
1041       */
1042      private ElemTemplate m_startRule;
1043    
1044      /**
1045       * Get the default template for a root node.
1046       *
1047       * @return The default template for a root node.
1048       * @xsl.usage advanced
1049       */
1050      public final ElemTemplate getStartRule()
1051      {
1052        return m_startRule;
1053      }
1054    
1055    
1056      /**
1057       * Used for default selection.
1058       * @serial
1059       */
1060      XPath m_selectDefault;
1061    
1062      /**
1063       * Create the default rule if needed.
1064       *
1065       * @throws TransformerException
1066       */
1067      private void initDefaultRule(ErrorListener errorListener) throws TransformerException
1068      {
1069    
1070        // Then manufacture a default
1071        m_defaultRule = new ElemTemplate();
1072    
1073        m_defaultRule.setStylesheet(this);
1074    
1075        XPath defMatch = new XPath("*", this, this, XPath.MATCH, errorListener);
1076    
1077        m_defaultRule.setMatch(defMatch);
1078    
1079        ElemApplyTemplates childrenElement = new ElemApplyTemplates();
1080    
1081        childrenElement.setIsDefaultTemplate(true);
1082        childrenElement.setSelect(m_selectDefault);
1083        m_defaultRule.appendChild(childrenElement);
1084        
1085        m_startRule = m_defaultRule;
1086    
1087        // -----------------------------
1088        m_defaultTextRule = new ElemTemplate();
1089    
1090        m_defaultTextRule.setStylesheet(this);
1091    
1092        defMatch = new XPath("text() | @*", this, this, XPath.MATCH, errorListener);
1093    
1094        m_defaultTextRule.setMatch(defMatch);
1095    
1096        ElemValueOf elemValueOf = new ElemValueOf();
1097    
1098        m_defaultTextRule.appendChild(elemValueOf);
1099    
1100        XPath selectPattern = new XPath(".", this, this, XPath.SELECT, errorListener);
1101    
1102        elemValueOf.setSelect(selectPattern);
1103    
1104        //--------------------------------
1105        m_defaultRootRule = new ElemTemplate();
1106    
1107        m_defaultRootRule.setStylesheet(this);
1108    
1109        defMatch = new XPath("/", this, this, XPath.MATCH, errorListener);
1110    
1111        m_defaultRootRule.setMatch(defMatch);
1112    
1113        childrenElement = new ElemApplyTemplates();
1114    
1115        childrenElement.setIsDefaultTemplate(true);
1116        m_defaultRootRule.appendChild(childrenElement);
1117        childrenElement.setSelect(m_selectDefault);
1118      }
1119    
1120      /**
1121       * This is a generic version of C.A.R Hoare's Quick Sort
1122       * algorithm.  This will handle arrays that are already
1123       * sorted, and arrays with duplicate keys.  It was lifted from
1124       * the NodeSorter class but should probably be eliminated and replaced
1125       * with a call to Collections.sort when we migrate to Java2.<BR>
1126       *
1127       * If you think of a one dimensional array as going from
1128       * the lowest index on the left to the highest index on the right
1129       * then the parameters to this function are lowest index or
1130       * left and highest index or right.  The first time you call
1131       * this function it will be with the parameters 0, a.length - 1.
1132       *
1133       * @param v       a vector of ElemTemplateElement elements 
1134       * @param lo0     left boundary of partition
1135       * @param hi0     right boundary of partition
1136       *
1137       */
1138    
1139      private void QuickSort2(Vector v, int lo0, int hi0)
1140        {
1141          int lo = lo0;
1142          int hi = hi0;
1143    
1144          if ( hi0 > lo0)
1145          {
1146            // Arbitrarily establishing partition element as the midpoint of
1147            // the array.
1148            ElemTemplateElement midNode = (ElemTemplateElement) v.elementAt( ( lo0 + hi0 ) / 2 );
1149    
1150            // loop through the array until indices cross
1151            while( lo <= hi )
1152            {
1153              // find the first element that is greater than or equal to
1154              // the partition element starting from the left Index.
1155              while( (lo < hi0) && (((ElemTemplateElement) v.elementAt(lo)).compareTo(midNode) < 0) )
1156              {
1157                ++lo;
1158              } // end while
1159    
1160              // find an element that is smaller than or equal to
1161              // the partition element starting from the right Index.
1162              while( (hi > lo0) && (((ElemTemplateElement) v.elementAt(hi)).compareTo(midNode) > 0) )          {
1163                --hi;
1164              }
1165    
1166              // if the indexes have not crossed, swap
1167              if( lo <= hi )
1168              {
1169                ElemTemplateElement node = (ElemTemplateElement) v.elementAt(lo);
1170                v.setElementAt(v.elementAt(hi), lo);
1171                v.setElementAt(node, hi);
1172    
1173                ++lo;
1174                --hi;
1175              }
1176            }
1177    
1178            // If the right index has not reached the left side of array
1179            // must now sort the left partition.
1180            if( lo0 < hi )
1181            {
1182              QuickSort2( v, lo0, hi );
1183            }
1184    
1185            // If the left index has not reached the right side of array
1186            // must now sort the right partition.
1187            if( lo < hi0 )
1188            {
1189              QuickSort2( v, lo, hi0 );
1190            }
1191          }
1192        } // end QuickSort2  */
1193        
1194        private transient ComposeState m_composeState;
1195        
1196        /**
1197         * Initialize a new ComposeState.
1198         */
1199        void initComposeState()
1200        {
1201          m_composeState = new ComposeState();
1202        }
1203    
1204        /**
1205         * Return class to track state global state during the compose() operation.
1206         * @return ComposeState reference, or null if endCompose has been called.
1207         */
1208        ComposeState getComposeState()
1209        {
1210          return m_composeState;
1211        }
1212        
1213        /**
1214         * Clear the compose state.
1215         */
1216        private void clearComposeState()
1217        {
1218          m_composeState = null;
1219        }
1220    
1221        private String m_extensionHandlerClass = 
1222            "org.apache.xalan.extensions.ExtensionHandlerExsltFunction";
1223        
1224        /**
1225         * This internal method allows the setting of the java class
1226         * to handle the extension function (if other than the default one).
1227         * 
1228         * @xsl.usage internal
1229         */
1230        public String setExtensionHandlerClass(String handlerClassName) {
1231            String oldvalue = m_extensionHandlerClass;
1232            m_extensionHandlerClass = handlerClassName;
1233            return oldvalue;
1234        } 
1235        /**
1236         * 
1237         * @xsl.usage internal
1238         */
1239        public String getExtensionHandlerClass() {
1240            return m_extensionHandlerClass;
1241        }
1242            
1243        /**
1244         * Class to track state global state during the compose() operation.
1245         */
1246        class ComposeState
1247        {
1248          ComposeState()
1249          {
1250            int size = m_variables.size();
1251            for (int i = 0; i < size; i++) 
1252            {
1253              ElemVariable ev = (ElemVariable)m_variables.elementAt(i);
1254              m_variableNames.addElement(ev.getName());
1255            }
1256            
1257          }
1258          
1259          private ExpandedNameTable m_ent = new ExpandedNameTable();
1260          
1261          /**
1262           * Given a qualified name, return an integer ID that can be 
1263           * quickly compared.
1264           *
1265           * @param qname a qualified name object, must not be null.
1266           *
1267           * @return the expanded-name id of the qualified name.
1268           */
1269          public int getQNameID(QName qname)
1270          {
1271            
1272            return m_ent.getExpandedTypeID(qname.getNamespace(), 
1273                                           qname.getLocalName(),
1274                                           // The type doesn't matter for our 
1275                                           // purposes. 
1276                                           org.apache.xml.dtm.DTM.ELEMENT_NODE);
1277          }
1278          
1279          /**
1280           * A Vector of the current params and QNames within the current template.
1281           * Set by ElemTemplate and used by ProcessorVariable.
1282           */
1283          private java.util.Vector m_variableNames = new java.util.Vector();
1284                
1285          /**
1286           * Add the name of a qualified name within the template.  The position in 
1287           * the vector is its ID.
1288           * @param qname A qualified name of a param or variable, should be non-null.
1289           * @return the index where the variable was added.
1290           */
1291          int addVariableName(final org.apache.xml.utils.QName qname)
1292          {
1293            int pos = m_variableNames.size();
1294            m_variableNames.addElement(qname);
1295            int frameSize = m_variableNames.size() - getGlobalsSize();
1296            if(frameSize > m_maxStackFrameSize)
1297              m_maxStackFrameSize++;
1298            return pos;
1299          }
1300          
1301          void resetStackFrameSize()
1302          {
1303            m_maxStackFrameSize = 0;
1304          }
1305          
1306          int getFrameSize()
1307          {
1308            return m_maxStackFrameSize;
1309          }
1310          
1311          /**
1312           * Get the current size of the stack frame.  Use this to record the position 
1313           * in a template element at startElement, so that it can be popped 
1314           * at endElement.
1315           */
1316          int getCurrentStackFrameSize()
1317          {
1318            return m_variableNames.size();
1319          }
1320          
1321          /**
1322           * Set the current size of the stack frame.
1323           */
1324          void setCurrentStackFrameSize(int sz)
1325          {
1326            m_variableNames.setSize(sz);
1327          }
1328          
1329          int getGlobalsSize()
1330          {
1331            return m_variables.size();
1332          }
1333          
1334          IntStack m_marks = new IntStack();
1335          
1336          void pushStackMark()
1337          {
1338            m_marks.push(getCurrentStackFrameSize());
1339          }
1340          
1341          void popStackMark()
1342          {
1343            int mark = m_marks.pop();
1344            setCurrentStackFrameSize(mark);
1345          }
1346          
1347          /**
1348           * Get the Vector of the current params and QNames to be collected 
1349           * within the current template.
1350           * @return A reference to the vector of variable names.  The reference 
1351           * returned is owned by this class, and so should not really be mutated, or 
1352           * stored anywhere.
1353           */
1354          java.util.Vector getVariableNames()
1355          {
1356            return m_variableNames;
1357          }
1358          
1359          private int m_maxStackFrameSize;
1360    
1361        }
1362        
1363        /**
1364         * @return Optimization flag
1365         */
1366        public boolean getOptimizer() {
1367            return m_optimizer;
1368        }
1369    
1370        /**
1371         * @param b Optimization flag
1372         */
1373        public void setOptimizer(boolean b) {
1374            m_optimizer = b;
1375        }
1376    
1377        /**
1378         * @return Incremental flag
1379         */
1380        public boolean getIncremental() {
1381            return m_incremental;
1382        }
1383    
1384        /**
1385         * @return source location flag
1386         */
1387        public boolean getSource_location() {
1388            return m_source_location;
1389        }
1390    
1391        /**
1392         * @param b Incremental flag
1393         */
1394        public void setIncremental(boolean b) {
1395            m_incremental = b;
1396        }
1397    
1398        /**
1399         * @param b Source location flag
1400         */
1401        public void setSource_location(boolean b) {
1402            m_source_location = b;
1403        }
1404    
1405    }