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: StylesheetRoot.java 476466 2006-11-18 08:22:31Z minchau $
20  */
21 package org.apache.xalan.templates;
22 
23 import java.text.DecimalFormatSymbols;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.Hashtable;
27 import java.util.Properties;
28 import java.util.Vector;
29 
30 import javax.xml.transform.ErrorListener;
31 import javax.xml.transform.Templates;
32 import javax.xml.transform.Transformer;
33 import javax.xml.transform.TransformerConfigurationException;
34 import javax.xml.transform.TransformerException;
35 
36 import org.apache.xalan.extensions.ExtensionNamespacesManager;
37 import org.apache.xalan.processor.XSLTSchema;
38 import org.apache.xalan.res.XSLMessages;
39 import org.apache.xalan.res.XSLTErrorResources;
40 
41 import org.apache.xalan.transformer.TransformerImpl;
42 import org.apache.xml.dtm.DTM;
43 import org.apache.xml.dtm.ref.ExpandedNameTable;
44 import org.apache.xml.utils.IntStack;
45 import org.apache.xml.utils.QName;
46 import org.apache.xpath.XPath;
47 import org.apache.xpath.XPathContext;
48 
49 /**
50  * This class represents the root object of the stylesheet tree.
51  * @xsl.usage general
52  */
53 public class StylesheetRoot extends StylesheetComposed
54         implements java.io.Serializable, Templates
55 {
56     static final long serialVersionUID = 3875353123529147855L;
57 
58     /**
59      * The flag for the setting of the optimize feature;
60      */
61     private boolean m_optimizer = true;
62 
63     /**
64      * The flag for the setting of the incremental feature;
65      */
66     private boolean m_incremental = false;
67 
68     /**
69      * The flag for the setting of the source_location feature;
70      */
71     private boolean m_source_location = false;
72 
73     /**
74      * State of the secure processing feature.
75      */
76     private boolean m_isSecureProcessing = false;
77 
78   /**
79    * Uses an XSL stylesheet document.
80    * @throws TransformerConfigurationException if the baseIdentifier can not be resolved to a URL.
81    */
StylesheetRoot(ErrorListener errorListener)82   public StylesheetRoot(ErrorListener errorListener) throws TransformerConfigurationException
83   {
84 
85     super(null);
86 
87     setStylesheetRoot(this);
88 
89     try
90     {
91       m_selectDefault = new XPath("node()", this, this, XPath.SELECT, errorListener);
92 
93       initDefaultRule(errorListener);
94     }
95     catch (TransformerException se)
96     {
97       throw new TransformerConfigurationException(XSLMessages.createMessage(XSLTErrorResources.ER_CANNOT_INIT_DEFAULT_TEMPLATES, null), se); //"Can't init default templates!", se);
98     }
99   }
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    */
StylesheetRoot(XSLTSchema schema, ErrorListener listener)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    */
isRoot()126   public boolean isRoot()
127   {
128     return true;
129   }
130 
131   /**
132    * Set the state of the secure processing feature.
133    */
setSecureProcessing(boolean flag)134   public void setSecureProcessing(boolean flag)
135   {
136     m_isSecureProcessing = flag;
137   }
138 
139   /**
140    * Return the state of the secure processing feature.
141    */
isSecureProcessing()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    */
getAvailableElements()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    */
getExtensionNamespacesManager()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    */
getExtensions()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    */
newTransformer()198   public Transformer newTransformer()
199   {
200     return new TransformerImpl(this);
201   }
202 
203 
getDefaultOutputProps()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    */
getOutputProperties()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    */
recompose()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    */
composeTemplates(ElemTemplateElement templ)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    */
addImports(Stylesheet stylesheet, boolean addToList, Vector importList)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    */
getGlobalImport(int i)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    */
getGlobalImportCount()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    */
getImportNumber(StylesheetComposed sheet)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    */
recomposeOutput(OutputProperties oprops)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    */
getOutputComposed()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    */
isOutputMethodSet()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    */
recomposeAttributeSets(ElemAttributeSet attrSet)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    */
getAttributeSetComposed(QName name)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    */
recomposeDecimalFormats(DecimalFormatProperties dfp)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    */
getDecimalFormatComposed(QName name)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    */
recomposeKeys(KeyDeclaration keyDecl)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    */
getKeysComposed()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    */
recomposeNamespaceAliases(NamespaceAlias nsAlias)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    */
getNamespaceAliasComposed(String uri)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    */
recomposeTemplates(ElemTemplate template)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    */
getTemplateListComposed()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    */
setTemplateListComposed(TemplateList templateList)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    */
getTemplateComposed(XPathContext xctxt, int targetNode, QName mode, boolean quietConflictWarnings, DTM dtm)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    */
getTemplateComposed(XPathContext xctxt, int targetNode, QName mode, int maxImportLevel, int endImportLevel, boolean quietConflictWarnings, DTM dtm)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    */
getTemplateComposed(QName qname)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    */
recomposeVariables(ElemVariable elemVar)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    */
getVariableOrParamComposed(QName qname)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    */
getVariablesAndParamsComposed()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    */
recomposeWhiteSpaceInfo(WhiteSpaceInfo wsi)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    */
shouldCheckWhitespace()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    */
getWhiteSpaceInfo( XPathContext support, int targetElement, DTM dtm)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    */
shouldStripWhiteSpace( XPathContext support, int targetElement)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    */
canStripWhiteSpace()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    */
getDefaultTextRule()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    */
getDefaultRule()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    */
getDefaultRootRule()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    */
getStartRule()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    */
initDefaultRule(ErrorListener errorListener)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 
QuickSort2(Vector v, int lo0, int hi0)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      */
initComposeState()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      */
getComposeState()1208     ComposeState getComposeState()
1209     {
1210       return m_composeState;
1211     }
1212 
1213     /**
1214      * Clear the compose state.
1215      */
clearComposeState()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      */
setExtensionHandlerClass(String handlerClassName)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      */
getExtensionHandlerClass()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     {
ComposeState()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        */
getQNameID(QName qname)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        */
addVariableName(final org.apache.xml.utils.QName qname)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 
resetStackFrameSize()1301       void resetStackFrameSize()
1302       {
1303         m_maxStackFrameSize = 0;
1304       }
1305 
getFrameSize()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        */
getCurrentStackFrameSize()1316       int getCurrentStackFrameSize()
1317       {
1318         return m_variableNames.size();
1319       }
1320 
1321       /**
1322        * Set the current size of the stack frame.
1323        */
setCurrentStackFrameSize(int sz)1324       void setCurrentStackFrameSize(int sz)
1325       {
1326         m_variableNames.setSize(sz);
1327       }
1328 
getGlobalsSize()1329       int getGlobalsSize()
1330       {
1331         return m_variables.size();
1332       }
1333 
1334       IntStack m_marks = new IntStack();
1335 
pushStackMark()1336       void pushStackMark()
1337       {
1338         m_marks.push(getCurrentStackFrameSize());
1339       }
1340 
popStackMark()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        */
getVariableNames()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      */
getOptimizer()1366     public boolean getOptimizer() {
1367         return m_optimizer;
1368     }
1369 
1370     /**
1371      * @param b Optimization flag
1372      */
setOptimizer(boolean b)1373     public void setOptimizer(boolean b) {
1374         m_optimizer = b;
1375     }
1376 
1377     /**
1378      * @return Incremental flag
1379      */
getIncremental()1380     public boolean getIncremental() {
1381         return m_incremental;
1382     }
1383 
1384     /**
1385      * @return source location flag
1386      */
getSource_location()1387     public boolean getSource_location() {
1388         return m_source_location;
1389     }
1390 
1391     /**
1392      * @param b Incremental flag
1393      */
setIncremental(boolean b)1394     public void setIncremental(boolean b) {
1395         m_incremental = b;
1396     }
1397 
1398     /**
1399      * @param b Source location flag
1400      */
setSource_location(boolean b)1401     public void setSource_location(boolean b) {
1402         m_source_location = b;
1403     }
1404 
1405 }
1406