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 */
21package org.apache.xalan.templates;
22
23import java.util.Enumeration;
24import java.util.Hashtable;
25import java.util.Vector;
26
27import javax.xml.transform.TransformerException;
28
29import org.apache.xalan.res.XSLTErrorResources;
30import org.apache.xml.dtm.DTM;
31import org.apache.xml.utils.QName;
32import org.apache.xpath.Expression;
33import org.apache.xpath.XPath;
34import org.apache.xpath.XPathContext;
35import org.apache.xpath.compiler.PsuedoNames;
36import org.apache.xpath.patterns.NodeTest;
37import org.apache.xpath.patterns.StepPattern;
38import org.apache.xpath.patterns.UnionPattern;
39
40/**
41 * Encapsulates a template list, and helps locate individual templates.
42 * @xsl.usage advanced
43 */
44public 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   */
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   */
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   */
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   */
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
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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
868    private TemplateWalker()
869    {
870      hashIterator = m_patternTable.elements();
871      inPatterns = true;
872      curPattern = null;
873    }
874
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