Home | History | Annotate | Download | only in templates
      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    */
     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