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 */
21package org.apache.xalan.templates;
22
23import java.text.DecimalFormatSymbols;
24import java.util.ArrayList;
25import java.util.HashMap;
26import java.util.Hashtable;
27import java.util.Properties;
28import java.util.Vector;
29
30import javax.xml.transform.ErrorListener;
31import javax.xml.transform.Templates;
32import javax.xml.transform.Transformer;
33import javax.xml.transform.TransformerConfigurationException;
34import javax.xml.transform.TransformerException;
35
36import org.apache.xalan.extensions.ExtensionNamespacesManager;
37import org.apache.xalan.processor.XSLTSchema;
38import org.apache.xalan.res.XSLMessages;
39import org.apache.xalan.res.XSLTErrorResources;
40
41import org.apache.xalan.transformer.TransformerImpl;
42import org.apache.xml.dtm.DTM;
43import org.apache.xml.dtm.ref.ExpandedNameTable;
44import org.apache.xml.utils.IntStack;
45import org.apache.xml.utils.QName;
46import org.apache.xpath.XPath;
47import org.apache.xpath.XPathContext;
48
49/**
50 * This class represents the root object of the stylesheet tree.
51 * @xsl.usage general
52 */
53public 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   */
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   */
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}
1406