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