• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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: TemplateList.java 468643 2006-10-28 06:56:03Z minchau $
20  */
21 package org.apache.xalan.templates;
22 
23 import java.util.Enumeration;
24 import java.util.Hashtable;
25 import java.util.Vector;
26 
27 import javax.xml.transform.TransformerException;
28 
29 import org.apache.xalan.res.XSLTErrorResources;
30 import org.apache.xml.dtm.DTM;
31 import org.apache.xml.utils.QName;
32 import org.apache.xpath.Expression;
33 import org.apache.xpath.XPath;
34 import org.apache.xpath.XPathContext;
35 import org.apache.xpath.compiler.PsuedoNames;
36 import org.apache.xpath.patterns.NodeTest;
37 import org.apache.xpath.patterns.StepPattern;
38 import org.apache.xpath.patterns.UnionPattern;
39 
40 /**
41  * Encapsulates a template list, and helps locate individual templates.
42  * @xsl.usage advanced
43  */
44 public class TemplateList implements java.io.Serializable
45 {
46     static final long serialVersionUID = 5803675288911728791L;
47 
48   /**
49    * Construct a TemplateList object. Needs to be public so it can
50    * be invoked from the CompilingStylesheetHandler.
51    */
TemplateList()52   public TemplateList()
53   {
54     super();
55   }
56 
57   /**
58    * Add a template to the table of named templates and/or the table of templates
59    * with match patterns.  This routine should
60    * be called in decreasing order of precedence but it checks nonetheless.
61    *
62    * @param template
63    */
setTemplate(ElemTemplate template)64   public void setTemplate(ElemTemplate template)
65   {
66     XPath matchXPath = template.getMatch();
67 
68     if (null == template.getName() && null == matchXPath)
69     {
70       template.error(XSLTErrorResources.ER_NEED_NAME_OR_MATCH_ATTRIB,
71           new Object[]{ "xsl:template" });
72     }
73 
74     if (null != template.getName())
75     {
76       ElemTemplate existingTemplate = (ElemTemplate) m_namedTemplates.get(template.getName());
77       if (null == existingTemplate)
78       {
79         m_namedTemplates.put(template.getName(), template);
80       }
81       else
82       {
83         int existingPrecedence =
84                         existingTemplate.getStylesheetComposed().getImportCountComposed();
85         int newPrecedence = template.getStylesheetComposed().getImportCountComposed();
86         if (newPrecedence > existingPrecedence)
87         {
88           // This should never happen
89           m_namedTemplates.put(template.getName(), template);
90         }
91         else if (newPrecedence == existingPrecedence)
92           template.error(XSLTErrorResources.ER_DUPLICATE_NAMED_TEMPLATE,
93                        new Object[]{ template.getName() });
94       }
95     }
96 
97 
98 
99     if (null != matchXPath)
100     {
101       Expression matchExpr = matchXPath.getExpression();
102 
103       if (matchExpr instanceof StepPattern)
104       {
105         insertPatternInTable((StepPattern) matchExpr, template);
106       }
107       else if (matchExpr instanceof UnionPattern)
108       {
109         UnionPattern upat = (UnionPattern) matchExpr;
110         StepPattern[] pats = upat.getPatterns();
111         int n = pats.length;
112 
113         for (int i = 0; i < n; i++)
114         {
115           insertPatternInTable(pats[i], template);
116         }
117       }
118       else
119       {
120 
121         // TODO: assert error
122       }
123     }
124   }
125 
126   /** Flag to indicate whether in DEBUG mode          */
127   final static boolean DEBUG = false;
128 
129   /**
130    * Dump all patterns and elements that match those patterns
131    *
132    */
dumpAssociationTables()133   void dumpAssociationTables()
134   {
135 
136     Enumeration associations = m_patternTable.elements();
137 
138     while (associations.hasMoreElements())
139     {
140       TemplateSubPatternAssociation head =
141         (TemplateSubPatternAssociation) associations.nextElement();
142 
143       while (null != head)
144       {
145         System.out.print("(" + head.getTargetString() + ", "
146                          + head.getPattern() + ")");
147 
148         head = head.getNext();
149       }
150 
151       System.out.println("\n.....");
152     }
153 
154     TemplateSubPatternAssociation head = m_wildCardPatterns;
155 
156     System.out.print("wild card list: ");
157 
158     while (null != head)
159     {
160       System.out.print("(" + head.getTargetString() + ", "
161                        + head.getPattern() + ")");
162 
163       head = head.getNext();
164     }
165 
166     System.out.println("\n.....");
167   }
168 
169   /**
170    * After all templates have been added, this function
171    * should be called.
172    */
compose(StylesheetRoot sroot)173   public void compose(StylesheetRoot sroot)
174   {
175 
176     if (DEBUG)
177     {
178       System.out.println("Before wildcard insert...");
179       dumpAssociationTables();
180     }
181 
182     if (null != m_wildCardPatterns)
183     {
184       Enumeration associations = m_patternTable.elements();
185 
186       while (associations.hasMoreElements())
187       {
188         TemplateSubPatternAssociation head =
189           (TemplateSubPatternAssociation) associations.nextElement();
190         TemplateSubPatternAssociation wild = m_wildCardPatterns;
191 
192         while (null != wild)
193         {
194           try
195           {
196             head = insertAssociationIntoList(
197               head, (TemplateSubPatternAssociation) wild.clone(), true);
198           }
199           catch (CloneNotSupportedException cnse){}
200 
201           wild = wild.getNext();
202         }
203       }
204     }
205 
206     if (DEBUG)
207     {
208       System.out.println("After wildcard insert...");
209       dumpAssociationTables();
210     }
211   }
212 
213   /**
214    * Insert the given TemplateSubPatternAssociation into the the linked
215    * list.  Sort by import precedence, then priority, then by document order.
216    *
217    * @param head The first TemplateSubPatternAssociation in the linked list.
218    * @param item The item that we want to insert into the proper place.
219    * @param isWildCardInsert <code>true</code> if we are inserting a wild card
220    *             template onto this list.
221    * @return the new head of the list.
222    */
223   private TemplateSubPatternAssociation
insertAssociationIntoList(TemplateSubPatternAssociation head, TemplateSubPatternAssociation item, boolean isWildCardInsert)224               insertAssociationIntoList(TemplateSubPatternAssociation head,
225                                          TemplateSubPatternAssociation item,
226                                          boolean isWildCardInsert)
227   {
228 
229     // Sort first by import level (higher level is at front),
230     // then by priority (highest priority is at front),
231     // then by document order (later in document is at front).
232 
233     double priority = getPriorityOrScore(item);
234     double workPriority;
235     int importLevel = item.getImportLevel();
236     int docOrder = item.getDocOrderPos();
237     TemplateSubPatternAssociation insertPoint = head;
238     TemplateSubPatternAssociation next;
239     boolean insertBefore;         // true means insert before insertPoint; otherwise after
240                                   // This can only be true if insertPoint is pointing to
241                                   // the first or last template.
242 
243     // Spin down so that insertPoint points to:
244     // (a) the template immediately _before_ the first template on the chain with
245     // a precedence that is either (i) less than ours or (ii) the same as ours but
246     // the template document position is less than ours
247     // -or-
248     // (b) the last template on the chain if no such template described in (a) exists.
249     // If we are pointing to the first template or the last template (that is, case b),
250     // we need to determine whether to insert before or after the template.  Otherwise,
251     // we always insert after the insertPoint.
252 
253     while (true)
254     {
255       next = insertPoint.getNext();
256       if (null == next)
257         break;
258       else
259       {
260         workPriority = getPriorityOrScore(next);
261         if (importLevel > next.getImportLevel())
262           break;
263         else if (importLevel < next.getImportLevel())
264           insertPoint = next;
265         else if (priority > workPriority)               // import precedence is equal
266           break;
267         else if (priority < workPriority)
268           insertPoint = next;
269         else if (docOrder >= next.getDocOrderPos())     // priorities, import are equal
270           break;
271         else
272           insertPoint = next;
273       }
274     }
275 
276     if ( (null == next) || (insertPoint == head) )      // insert point is first or last
277     {
278       workPriority = getPriorityOrScore(insertPoint);
279       if (importLevel > insertPoint.getImportLevel())
280         insertBefore = true;
281       else if (importLevel < insertPoint.getImportLevel())
282         insertBefore = false;
283       else if (priority > workPriority)
284         insertBefore = true;
285       else if (priority < workPriority)
286         insertBefore = false;
287       else if (docOrder >= insertPoint.getDocOrderPos())
288         insertBefore = true;
289       else
290         insertBefore = false;
291     }
292     else
293       insertBefore = false;
294 
295     // System.out.println("appending: "+target+" to "+matchPat.getPattern());
296 
297     if (isWildCardInsert)
298     {
299       if (insertBefore)
300       {
301         item.setNext(insertPoint);
302 
303         String key = insertPoint.getTargetString();
304 
305         item.setTargetString(key);
306         putHead(key, item);
307         return item;
308       }
309       else
310       {
311         item.setNext(next);
312         insertPoint.setNext(item);
313         return head;
314       }
315     }
316     else
317     {
318       if (insertBefore)
319       {
320         item.setNext(insertPoint);
321 
322         if (insertPoint.isWild() || item.isWild())
323           m_wildCardPatterns = item;
324         else
325           putHead(item.getTargetString(), item);
326         return item;
327       }
328       else
329       {
330         item.setNext(next);
331         insertPoint.setNext(item);
332         return head;
333       }
334     }
335   }
336 
337   /**
338    * Add a template to the template list.
339    *
340    * @param pattern
341    * @param template
342    */
insertPatternInTable(StepPattern pattern, ElemTemplate template)343   private void insertPatternInTable(StepPattern pattern, ElemTemplate template)
344   {
345 
346     String target = pattern.getTargetString();
347 
348     if (null != target)
349     {
350       String pstring = template.getMatch().getPatternString();
351       TemplateSubPatternAssociation association =
352         new TemplateSubPatternAssociation(template, pattern, pstring);
353 
354       // See if there's already one there
355       boolean isWildCard = association.isWild();
356       TemplateSubPatternAssociation head = isWildCard
357                                            ? m_wildCardPatterns
358                                            : getHead(target);
359 
360       if (null == head)
361       {
362         if (isWildCard)
363           m_wildCardPatterns = association;
364         else
365           putHead(target, association);
366       }
367       else
368       {
369         insertAssociationIntoList(head, association, false);
370       }
371     }
372   }
373 
374   /**
375    * Given a match pattern and template association, return the
376    * score of that match.  This score or priority can always be
377    * statically calculated.
378    *
379    * @param matchPat The match pattern to template association.
380    *
381    * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST},
382    *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE},
383    *         {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD},
384    *         {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or
385    *         {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}, or
386    *         the value defined by the priority attribute of the template.
387    *
388    */
getPriorityOrScore(TemplateSubPatternAssociation matchPat)389   private double getPriorityOrScore(TemplateSubPatternAssociation matchPat)
390   {
391 
392     double priority = matchPat.getTemplate().getPriority();
393 
394     if (priority == XPath.MATCH_SCORE_NONE)
395     {
396       Expression ex = matchPat.getStepPattern();
397 
398       if (ex instanceof NodeTest)
399       {
400         return ((NodeTest) ex).getDefaultScore();
401       }
402     }
403 
404     return priority;
405   }
406 
407   /**
408    * Locate a named template.
409    *
410    * @param qname  Qualified name of the template.
411    *
412    * @return Template argument with the requested name, or null if not found.
413    */
getTemplate(QName qname)414   public ElemTemplate getTemplate(QName qname)
415   {
416     return (ElemTemplate) m_namedTemplates.get(qname);
417   }
418 
419   /**
420    * Get the head of the most likely list of associations to check, based on
421    * the name and type of the targetNode argument.
422    *
423    * @param xctxt The XPath runtime context.
424    * @param targetNode The target node that will be checked for a match.
425    * @param dtm The dtm owner for the target node.
426    *
427    * @return The head of a linked list that contains all possible match pattern to
428    * template associations.
429    */
getHead(XPathContext xctxt, int targetNode, DTM dtm)430   public TemplateSubPatternAssociation getHead(XPathContext xctxt,
431                                                int targetNode, DTM dtm)
432   {
433     short targetNodeType = dtm.getNodeType(targetNode);
434     TemplateSubPatternAssociation head;
435 
436     switch (targetNodeType)
437     {
438     case DTM.ELEMENT_NODE :
439     case DTM.ATTRIBUTE_NODE :
440       head = (TemplateSubPatternAssociation) m_patternTable.get(
441         dtm.getLocalName(targetNode));
442       break;
443     case DTM.TEXT_NODE :
444     case DTM.CDATA_SECTION_NODE :
445       head = m_textPatterns;
446       break;
447     case DTM.ENTITY_REFERENCE_NODE :
448     case DTM.ENTITY_NODE :
449       head = (TemplateSubPatternAssociation) m_patternTable.get(
450         dtm.getNodeName(targetNode)); // %REVIEW% I think this is right
451       break;
452     case DTM.PROCESSING_INSTRUCTION_NODE :
453       head = (TemplateSubPatternAssociation) m_patternTable.get(
454         dtm.getLocalName(targetNode));
455       break;
456     case DTM.COMMENT_NODE :
457       head = m_commentPatterns;
458       break;
459     case DTM.DOCUMENT_NODE :
460     case DTM.DOCUMENT_FRAGMENT_NODE :
461       head = m_docPatterns;
462       break;
463     case DTM.NOTATION_NODE :
464     default :
465       head = (TemplateSubPatternAssociation) m_patternTable.get(
466         dtm.getNodeName(targetNode)); // %REVIEW% I think this is right
467     }
468 
469     return (null == head) ? m_wildCardPatterns : head;
470   }
471 
472   /**
473    * Given a target element, find the template that best
474    * matches in the given XSL document, according
475    * to the rules specified in the xsl draft.  This variation of getTemplate
476    * assumes the current node and current expression node have already been
477    * pushed.
478    *
479    * @param xctxt
480    * @param targetNode
481    * @param mode A string indicating the display mode.
482    * @param maxImportLevel The maximum importCountComposed that we should consider or -1
483    *        if we should consider all import levels.  This is used by apply-imports to
484    *        access templates that have been overridden.
485    * @param quietConflictWarnings
486    * @return Rule that best matches targetElem.
487    * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
488    * the error condition is severe enough to halt processing.
489    *
490    * @throws TransformerException
491    */
getTemplateFast(XPathContext xctxt, int targetNode, int expTypeID, QName mode, int maxImportLevel, boolean quietConflictWarnings, DTM dtm)492   public ElemTemplate getTemplateFast(XPathContext xctxt,
493                                 int targetNode,
494                                 int expTypeID,
495                                 QName mode,
496                                 int maxImportLevel,
497                                 boolean quietConflictWarnings,
498                                 DTM dtm)
499             throws TransformerException
500   {
501 
502     TemplateSubPatternAssociation head;
503 
504     switch (dtm.getNodeType(targetNode))
505     {
506     case DTM.ELEMENT_NODE :
507     case DTM.ATTRIBUTE_NODE :
508       head = (TemplateSubPatternAssociation) m_patternTable.get(
509         dtm.getLocalNameFromExpandedNameID(expTypeID));
510       break;
511     case DTM.TEXT_NODE :
512     case DTM.CDATA_SECTION_NODE :
513       head = m_textPatterns;
514       break;
515     case DTM.ENTITY_REFERENCE_NODE :
516     case DTM.ENTITY_NODE :
517       head = (TemplateSubPatternAssociation) m_patternTable.get(
518         dtm.getNodeName(targetNode)); // %REVIEW% I think this is right
519       break;
520     case DTM.PROCESSING_INSTRUCTION_NODE :
521       head = (TemplateSubPatternAssociation) m_patternTable.get(
522         dtm.getLocalName(targetNode));
523       break;
524     case DTM.COMMENT_NODE :
525       head = m_commentPatterns;
526       break;
527     case DTM.DOCUMENT_NODE :
528     case DTM.DOCUMENT_FRAGMENT_NODE :
529       head = m_docPatterns;
530       break;
531     case DTM.NOTATION_NODE :
532     default :
533       head = (TemplateSubPatternAssociation) m_patternTable.get(
534         dtm.getNodeName(targetNode)); // %REVIEW% I think this is right
535     }
536 
537     if(null == head)
538     {
539       head = m_wildCardPatterns;
540       if(null == head)
541         return null;
542     }
543 
544     // XSLT functions, such as xsl:key, need to be able to get to
545     // current ElemTemplateElement via a cast to the prefix resolver.
546     // Setting this fixes bug idkey03.
547     xctxt.pushNamespaceContextNull();
548     try
549     {
550       do
551       {
552         if ( (maxImportLevel > -1) && (head.getImportLevel() > maxImportLevel) )
553         {
554           continue;
555         }
556         ElemTemplate template = head.getTemplate();
557         xctxt.setNamespaceContext(template);
558 
559         if ((head.m_stepPattern.execute(xctxt, targetNode, dtm, expTypeID) != NodeTest.SCORE_NONE)
560                 && head.matchMode(mode))
561         {
562           if (quietConflictWarnings)
563             checkConflicts(head, xctxt, targetNode, mode);
564 
565           return template;
566         }
567       }
568       while (null != (head = head.getNext()));
569     }
570     finally
571     {
572       xctxt.popNamespaceContext();
573     }
574 
575     return null;
576   }  // end findTemplate
577 
578   /**
579    * Given a target element, find the template that best
580    * matches in the given XSL document, according
581    * to the rules specified in the xsl draft.
582    *
583    * @param xctxt
584    * @param targetNode
585    * @param mode A string indicating the display mode.
586    * @param quietConflictWarnings
587    * @return Rule that best matches targetElem.
588    * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
589    * the error condition is severe enough to halt processing.
590    *
591    * @throws TransformerException
592    */
getTemplate(XPathContext xctxt, int targetNode, QName mode, boolean quietConflictWarnings, DTM dtm)593   public ElemTemplate getTemplate(XPathContext xctxt,
594                                 int targetNode,
595                                 QName mode,
596                                 boolean quietConflictWarnings,
597                                 DTM dtm)
598             throws TransformerException
599   {
600 
601     TemplateSubPatternAssociation head = getHead(xctxt, targetNode, dtm);
602 
603     if (null != head)
604     {
605       // XSLT functions, such as xsl:key, need to be able to get to
606       // current ElemTemplateElement via a cast to the prefix resolver.
607       // Setting this fixes bug idkey03.
608       xctxt.pushNamespaceContextNull();
609       xctxt.pushCurrentNodeAndExpression(targetNode, targetNode);
610       try
611       {
612         do
613         {
614           ElemTemplate template = head.getTemplate();
615           xctxt.setNamespaceContext(template);
616 
617           if ((head.m_stepPattern.execute(xctxt, targetNode) != NodeTest.SCORE_NONE)
618                   && head.matchMode(mode))
619           {
620             if (quietConflictWarnings)
621               checkConflicts(head, xctxt, targetNode, mode);
622 
623             return template;
624           }
625         }
626         while (null != (head = head.getNext()));
627       }
628       finally
629       {
630         xctxt.popCurrentNodeAndExpression();
631         xctxt.popNamespaceContext();
632       }
633     }
634 
635     return null;
636   }  // end findTemplate
637 
638   /**
639    * Given a target element, find the template that best
640    * matches in the given XSL document, according
641    * to the rules specified in the xsl draft.
642    *
643    * @param xctxt
644    * @param targetNode
645    * @param mode A string indicating the display mode.
646    * @param maxImportLevel The maximum importCountComposed that we should consider or -1
647    *        if we should consider all import levels.  This is used by apply-imports to
648    *        access templates that have been overridden.
649    * @param endImportLevel The count of composed imports
650    * @param quietConflictWarnings
651    * @return Rule that best matches targetElem.
652    * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide
653    * the error condition is severe enough to halt processing.
654    *
655    * @throws TransformerException
656    */
getTemplate(XPathContext xctxt, int targetNode, QName mode, int maxImportLevel, int endImportLevel, boolean quietConflictWarnings, DTM dtm)657   public ElemTemplate getTemplate(XPathContext xctxt,
658                                 int targetNode,
659                                 QName mode,
660                                 int maxImportLevel, int endImportLevel,
661                                 boolean quietConflictWarnings,
662                                 DTM dtm)
663             throws TransformerException
664   {
665 
666     TemplateSubPatternAssociation head = getHead(xctxt, targetNode, dtm);
667 
668     if (null != head)
669     {
670       // XSLT functions, such as xsl:key, need to be able to get to
671       // current ElemTemplateElement via a cast to the prefix resolver.
672       // Setting this fixes bug idkey03.
673       xctxt.pushNamespaceContextNull();
674       xctxt.pushCurrentNodeAndExpression(targetNode, targetNode);
675       try
676       {
677         do
678         {
679           if ( (maxImportLevel > -1) && (head.getImportLevel() > maxImportLevel))
680           {
681             continue;
682           }
683           if (head.getImportLevel()<= maxImportLevel - endImportLevel)
684             return null;
685           ElemTemplate template = head.getTemplate();
686           xctxt.setNamespaceContext(template);
687 
688           if ((head.m_stepPattern.execute(xctxt, targetNode) != NodeTest.SCORE_NONE)
689                   && head.matchMode(mode))
690           {
691             if (quietConflictWarnings)
692               checkConflicts(head, xctxt, targetNode, mode);
693 
694             return template;
695           }
696         }
697         while (null != (head = head.getNext()));
698       }
699       finally
700       {
701         xctxt.popCurrentNodeAndExpression();
702         xctxt.popNamespaceContext();
703       }
704     }
705 
706     return null;
707   }  // end findTemplate
708 
709   /**
710    * Get a TemplateWalker for use by a compiler.  See the documentation for
711    * the TreeWalker inner class for further details.
712    */
getWalker()713   public TemplateWalker getWalker()
714   {
715     return new TemplateWalker();
716   }
717 
718   /**
719    * Check for match conflicts, and warn the stylesheet author.
720    *
721    * @param head Template pattern
722    * @param xctxt Current XPath context
723    * @param targetNode Node matching the pattern
724    * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>.
725    */
checkConflicts(TemplateSubPatternAssociation head, XPathContext xctxt, int targetNode, QName mode)726   private void checkConflicts(TemplateSubPatternAssociation head,
727                               XPathContext xctxt, int targetNode, QName mode)
728   {
729 
730     // TODO: Check for conflicts.
731   }
732 
733   /**
734    * Add object to vector if not already there.
735    *
736    * @param obj
737    * @param v
738    */
addObjectIfNotFound(Object obj, Vector v)739   private void addObjectIfNotFound(Object obj, Vector v)
740   {
741 
742     int n = v.size();
743     boolean addIt = true;
744 
745     for (int i = 0; i < n; i++)
746     {
747       if (v.elementAt(i) == obj)
748       {
749         addIt = false;
750 
751         break;
752       }
753     }
754 
755     if (addIt)
756     {
757       v.addElement(obj);
758     }
759   }
760 
761   /**
762    * Keyed on string macro names, and holding values
763    * that are macro elements in the XSL DOM tree.
764    * Initialized in initMacroLookupTable, and used in
765    * findNamedTemplate.
766    * @serial
767    */
768   private Hashtable m_namedTemplates = new Hashtable(89);
769 
770   /**
771    * This table is keyed on the target elements
772    * of patterns, and contains linked lists of
773    * the actual patterns that match the target element
774    * to some degree of specifity.
775    * @serial
776    */
777   private Hashtable m_patternTable = new Hashtable(89);
778 
779   /** Wildcard patterns.
780    *  @serial          */
781   private TemplateSubPatternAssociation m_wildCardPatterns = null;
782 
783   /** Text Patterns.
784    *  @serial          */
785   private TemplateSubPatternAssociation m_textPatterns = null;
786 
787   /** Root document Patterns.
788    *  @serial          */
789   private TemplateSubPatternAssociation m_docPatterns = null;
790 
791   /** Comment Patterns.
792    *  @serial          */
793   private TemplateSubPatternAssociation m_commentPatterns = null;
794 
795   /**
796    * Get table of named Templates.
797    * These are keyed on template names, and holding values
798    * that are template elements.
799    *
800    * @return A Hashtable dictionary that contains {@link java.lang.String}s
801    * as the keys, and {@link org.apache.xalan.templates.ElemTemplate}s as the
802    * values.
803    */
getNamedTemplates()804   private Hashtable getNamedTemplates()
805   {
806     return m_namedTemplates;
807   }
808 
809   /**
810    * Set table of named Templates.
811    * These are keyed on string macro names, and holding values
812    * that are template elements in the XSL DOM tree.
813    *
814    * @param v Hashtable dictionary that contains {@link java.lang.String}s
815    * as the keys, and {@link org.apache.xalan.templates.ElemTemplate}s as the
816    * values.
817    */
setNamedTemplates(Hashtable v)818   private void setNamedTemplates(Hashtable v)
819   {
820     m_namedTemplates = v;
821   }
822 
823   /**
824    * Get the head of the assocation list that is keyed by target.
825    *
826    * @param key The name of a node.
827    *
828    * @return The head of a linked list that contains all possible match pattern to
829    * template associations for the given key.
830    */
getHead(String key)831   private TemplateSubPatternAssociation getHead(String key)
832   {
833     return (TemplateSubPatternAssociation) m_patternTable.get(key);
834   }
835 
836   /**
837    * Get the head of the assocation list that is keyed by target.
838    *
839    * @param key
840    * @param assoc
841    */
putHead(String key, TemplateSubPatternAssociation assoc)842   private void putHead(String key, TemplateSubPatternAssociation assoc)
843   {
844 
845     if (key.equals(PsuedoNames.PSEUDONAME_TEXT))
846       m_textPatterns = assoc;
847     else if (key.equals(PsuedoNames.PSEUDONAME_ROOT))
848       m_docPatterns = assoc;
849     else if (key.equals(PsuedoNames.PSEUDONAME_COMMENT))
850       m_commentPatterns = assoc;
851 
852     m_patternTable.put(key, assoc);
853   }
854 
855   /**
856    * An inner class used by a compiler to iterate over all of the ElemTemplates
857    * stored in this TemplateList.  The compiler can replace returned templates
858    * with their compiled equivalent.
859    */
860   public class TemplateWalker
861   {
862     private Enumeration hashIterator;
863     private boolean inPatterns;
864     private TemplateSubPatternAssociation curPattern;
865 
866     private Hashtable m_compilerCache = new Hashtable();
867 
TemplateWalker()868     private TemplateWalker()
869     {
870       hashIterator = m_patternTable.elements();
871       inPatterns = true;
872       curPattern = null;
873     }
874 
next()875     public ElemTemplate next()
876     {
877 
878       ElemTemplate retValue = null;
879       ElemTemplate ct;
880 
881       while (true)
882       {
883         if (inPatterns)
884         {
885           if (null != curPattern)
886             curPattern = curPattern.getNext();
887 
888           if (null != curPattern)
889             retValue = curPattern.getTemplate();
890           else
891           {
892             if (hashIterator.hasMoreElements())
893             {
894               curPattern = (TemplateSubPatternAssociation) hashIterator.nextElement();
895               retValue =  curPattern.getTemplate();
896             }
897             else
898             {
899               inPatterns = false;
900               hashIterator = m_namedTemplates.elements();
901             }
902           }
903         }
904 
905         if (!inPatterns)
906         {
907           if (hashIterator.hasMoreElements())
908             retValue = (ElemTemplate) hashIterator.nextElement();
909           else
910             return null;
911         }
912 
913         ct = (ElemTemplate) m_compilerCache.get(new Integer(retValue.getUid()));
914         if (null == ct)
915         {
916           m_compilerCache.put(new Integer(retValue.getUid()), retValue);
917           return retValue;
918         }
919       }
920     }
921   }
922 
923 }
924