| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * $Id: TemplateList.java 468643 2006-10-28 06:56:03Z minchau $ |
| */ |
| package org.apache.xalan.templates; |
| |
| import java.util.Enumeration; |
| import java.util.Hashtable; |
| import java.util.Vector; |
| |
| import javax.xml.transform.TransformerException; |
| |
| import org.apache.xalan.res.XSLTErrorResources; |
| import org.apache.xml.dtm.DTM; |
| import org.apache.xml.utils.QName; |
| import org.apache.xpath.Expression; |
| import org.apache.xpath.XPath; |
| import org.apache.xpath.XPathContext; |
| import org.apache.xpath.compiler.PsuedoNames; |
| import org.apache.xpath.patterns.NodeTest; |
| import org.apache.xpath.patterns.StepPattern; |
| import org.apache.xpath.patterns.UnionPattern; |
| |
| /** |
| * Encapsulates a template list, and helps locate individual templates. |
| * @xsl.usage advanced |
| */ |
| public class TemplateList implements java.io.Serializable |
| { |
| static final long serialVersionUID = 5803675288911728791L; |
| |
| /** |
| * Construct a TemplateList object. Needs to be public so it can |
| * be invoked from the CompilingStylesheetHandler. |
| */ |
| public TemplateList() |
| { |
| super(); |
| } |
| |
| /** |
| * Add a template to the table of named templates and/or the table of templates |
| * with match patterns. This routine should |
| * be called in decreasing order of precedence but it checks nonetheless. |
| * |
| * @param template |
| */ |
| public void setTemplate(ElemTemplate template) |
| { |
| XPath matchXPath = template.getMatch(); |
| |
| if (null == template.getName() && null == matchXPath) |
| { |
| template.error(XSLTErrorResources.ER_NEED_NAME_OR_MATCH_ATTRIB, |
| new Object[]{ "xsl:template" }); |
| } |
| |
| if (null != template.getName()) |
| { |
| ElemTemplate existingTemplate = (ElemTemplate) m_namedTemplates.get(template.getName()); |
| if (null == existingTemplate) |
| { |
| m_namedTemplates.put(template.getName(), template); |
| } |
| else |
| { |
| int existingPrecedence = |
| existingTemplate.getStylesheetComposed().getImportCountComposed(); |
| int newPrecedence = template.getStylesheetComposed().getImportCountComposed(); |
| if (newPrecedence > existingPrecedence) |
| { |
| // This should never happen |
| m_namedTemplates.put(template.getName(), template); |
| } |
| else if (newPrecedence == existingPrecedence) |
| template.error(XSLTErrorResources.ER_DUPLICATE_NAMED_TEMPLATE, |
| new Object[]{ template.getName() }); |
| } |
| } |
| |
| |
| |
| if (null != matchXPath) |
| { |
| Expression matchExpr = matchXPath.getExpression(); |
| |
| if (matchExpr instanceof StepPattern) |
| { |
| insertPatternInTable((StepPattern) matchExpr, template); |
| } |
| else if (matchExpr instanceof UnionPattern) |
| { |
| UnionPattern upat = (UnionPattern) matchExpr; |
| StepPattern[] pats = upat.getPatterns(); |
| int n = pats.length; |
| |
| for (int i = 0; i < n; i++) |
| { |
| insertPatternInTable(pats[i], template); |
| } |
| } |
| else |
| { |
| |
| // TODO: assert error |
| } |
| } |
| } |
| |
| /** Flag to indicate whether in DEBUG mode */ |
| final static boolean DEBUG = false; |
| |
| /** |
| * Dump all patterns and elements that match those patterns |
| * |
| */ |
| void dumpAssociationTables() |
| { |
| |
| Enumeration associations = m_patternTable.elements(); |
| |
| while (associations.hasMoreElements()) |
| { |
| TemplateSubPatternAssociation head = |
| (TemplateSubPatternAssociation) associations.nextElement(); |
| |
| while (null != head) |
| { |
| System.out.print("(" + head.getTargetString() + ", " |
| + head.getPattern() + ")"); |
| |
| head = head.getNext(); |
| } |
| |
| System.out.println("\n....."); |
| } |
| |
| TemplateSubPatternAssociation head = m_wildCardPatterns; |
| |
| System.out.print("wild card list: "); |
| |
| while (null != head) |
| { |
| System.out.print("(" + head.getTargetString() + ", " |
| + head.getPattern() + ")"); |
| |
| head = head.getNext(); |
| } |
| |
| System.out.println("\n....."); |
| } |
| |
| /** |
| * After all templates have been added, this function |
| * should be called. |
| */ |
| public void compose(StylesheetRoot sroot) |
| { |
| |
| if (DEBUG) |
| { |
| System.out.println("Before wildcard insert..."); |
| dumpAssociationTables(); |
| } |
| |
| if (null != m_wildCardPatterns) |
| { |
| Enumeration associations = m_patternTable.elements(); |
| |
| while (associations.hasMoreElements()) |
| { |
| TemplateSubPatternAssociation head = |
| (TemplateSubPatternAssociation) associations.nextElement(); |
| TemplateSubPatternAssociation wild = m_wildCardPatterns; |
| |
| while (null != wild) |
| { |
| try |
| { |
| head = insertAssociationIntoList( |
| head, (TemplateSubPatternAssociation) wild.clone(), true); |
| } |
| catch (CloneNotSupportedException cnse){} |
| |
| wild = wild.getNext(); |
| } |
| } |
| } |
| |
| if (DEBUG) |
| { |
| System.out.println("After wildcard insert..."); |
| dumpAssociationTables(); |
| } |
| } |
| |
| /** |
| * Insert the given TemplateSubPatternAssociation into the the linked |
| * list. Sort by import precedence, then priority, then by document order. |
| * |
| * @param head The first TemplateSubPatternAssociation in the linked list. |
| * @param item The item that we want to insert into the proper place. |
| * @param isWildCardInsert <code>true</code> if we are inserting a wild card |
| * template onto this list. |
| * @return the new head of the list. |
| */ |
| private TemplateSubPatternAssociation |
| insertAssociationIntoList(TemplateSubPatternAssociation head, |
| TemplateSubPatternAssociation item, |
| boolean isWildCardInsert) |
| { |
| |
| // Sort first by import level (higher level is at front), |
| // then by priority (highest priority is at front), |
| // then by document order (later in document is at front). |
| |
| double priority = getPriorityOrScore(item); |
| double workPriority; |
| int importLevel = item.getImportLevel(); |
| int docOrder = item.getDocOrderPos(); |
| TemplateSubPatternAssociation insertPoint = head; |
| TemplateSubPatternAssociation next; |
| boolean insertBefore; // true means insert before insertPoint; otherwise after |
| // This can only be true if insertPoint is pointing to |
| // the first or last template. |
| |
| // Spin down so that insertPoint points to: |
| // (a) the template immediately _before_ the first template on the chain with |
| // a precedence that is either (i) less than ours or (ii) the same as ours but |
| // the template document position is less than ours |
| // -or- |
| // (b) the last template on the chain if no such template described in (a) exists. |
| // If we are pointing to the first template or the last template (that is, case b), |
| // we need to determine whether to insert before or after the template. Otherwise, |
| // we always insert after the insertPoint. |
| |
| while (true) |
| { |
| next = insertPoint.getNext(); |
| if (null == next) |
| break; |
| else |
| { |
| workPriority = getPriorityOrScore(next); |
| if (importLevel > next.getImportLevel()) |
| break; |
| else if (importLevel < next.getImportLevel()) |
| insertPoint = next; |
| else if (priority > workPriority) // import precedence is equal |
| break; |
| else if (priority < workPriority) |
| insertPoint = next; |
| else if (docOrder >= next.getDocOrderPos()) // priorities, import are equal |
| break; |
| else |
| insertPoint = next; |
| } |
| } |
| |
| if ( (null == next) || (insertPoint == head) ) // insert point is first or last |
| { |
| workPriority = getPriorityOrScore(insertPoint); |
| if (importLevel > insertPoint.getImportLevel()) |
| insertBefore = true; |
| else if (importLevel < insertPoint.getImportLevel()) |
| insertBefore = false; |
| else if (priority > workPriority) |
| insertBefore = true; |
| else if (priority < workPriority) |
| insertBefore = false; |
| else if (docOrder >= insertPoint.getDocOrderPos()) |
| insertBefore = true; |
| else |
| insertBefore = false; |
| } |
| else |
| insertBefore = false; |
| |
| // System.out.println("appending: "+target+" to "+matchPat.getPattern()); |
| |
| if (isWildCardInsert) |
| { |
| if (insertBefore) |
| { |
| item.setNext(insertPoint); |
| |
| String key = insertPoint.getTargetString(); |
| |
| item.setTargetString(key); |
| putHead(key, item); |
| return item; |
| } |
| else |
| { |
| item.setNext(next); |
| insertPoint.setNext(item); |
| return head; |
| } |
| } |
| else |
| { |
| if (insertBefore) |
| { |
| item.setNext(insertPoint); |
| |
| if (insertPoint.isWild() || item.isWild()) |
| m_wildCardPatterns = item; |
| else |
| putHead(item.getTargetString(), item); |
| return item; |
| } |
| else |
| { |
| item.setNext(next); |
| insertPoint.setNext(item); |
| return head; |
| } |
| } |
| } |
| |
| /** |
| * Add a template to the template list. |
| * |
| * @param pattern |
| * @param template |
| */ |
| private void insertPatternInTable(StepPattern pattern, ElemTemplate template) |
| { |
| |
| String target = pattern.getTargetString(); |
| |
| if (null != target) |
| { |
| String pstring = template.getMatch().getPatternString(); |
| TemplateSubPatternAssociation association = |
| new TemplateSubPatternAssociation(template, pattern, pstring); |
| |
| // See if there's already one there |
| boolean isWildCard = association.isWild(); |
| TemplateSubPatternAssociation head = isWildCard |
| ? m_wildCardPatterns |
| : getHead(target); |
| |
| if (null == head) |
| { |
| if (isWildCard) |
| m_wildCardPatterns = association; |
| else |
| putHead(target, association); |
| } |
| else |
| { |
| insertAssociationIntoList(head, association, false); |
| } |
| } |
| } |
| |
| /** |
| * Given a match pattern and template association, return the |
| * score of that match. This score or priority can always be |
| * statically calculated. |
| * |
| * @param matchPat The match pattern to template association. |
| * |
| * @return {@link org.apache.xpath.patterns.NodeTest#SCORE_NODETEST}, |
| * {@link org.apache.xpath.patterns.NodeTest#SCORE_NONE}, |
| * {@link org.apache.xpath.patterns.NodeTest#SCORE_NSWILD}, |
| * {@link org.apache.xpath.patterns.NodeTest#SCORE_QNAME}, or |
| * {@link org.apache.xpath.patterns.NodeTest#SCORE_OTHER}, or |
| * the value defined by the priority attribute of the template. |
| * |
| */ |
| private double getPriorityOrScore(TemplateSubPatternAssociation matchPat) |
| { |
| |
| double priority = matchPat.getTemplate().getPriority(); |
| |
| if (priority == XPath.MATCH_SCORE_NONE) |
| { |
| Expression ex = matchPat.getStepPattern(); |
| |
| if (ex instanceof NodeTest) |
| { |
| return ((NodeTest) ex).getDefaultScore(); |
| } |
| } |
| |
| return priority; |
| } |
| |
| /** |
| * Locate a named template. |
| * |
| * @param qname Qualified name of the template. |
| * |
| * @return Template argument with the requested name, or null if not found. |
| */ |
| public ElemTemplate getTemplate(QName qname) |
| { |
| return (ElemTemplate) m_namedTemplates.get(qname); |
| } |
| |
| /** |
| * Get the head of the most likely list of associations to check, based on |
| * the name and type of the targetNode argument. |
| * |
| * @param xctxt The XPath runtime context. |
| * @param targetNode The target node that will be checked for a match. |
| * @param dtm The dtm owner for the target node. |
| * |
| * @return The head of a linked list that contains all possible match pattern to |
| * template associations. |
| */ |
| public TemplateSubPatternAssociation getHead(XPathContext xctxt, |
| int targetNode, DTM dtm) |
| { |
| short targetNodeType = dtm.getNodeType(targetNode); |
| TemplateSubPatternAssociation head; |
| |
| switch (targetNodeType) |
| { |
| case DTM.ELEMENT_NODE : |
| case DTM.ATTRIBUTE_NODE : |
| head = (TemplateSubPatternAssociation) m_patternTable.get( |
| dtm.getLocalName(targetNode)); |
| break; |
| case DTM.TEXT_NODE : |
| case DTM.CDATA_SECTION_NODE : |
| head = m_textPatterns; |
| break; |
| case DTM.ENTITY_REFERENCE_NODE : |
| case DTM.ENTITY_NODE : |
| head = (TemplateSubPatternAssociation) m_patternTable.get( |
| dtm.getNodeName(targetNode)); // %REVIEW% I think this is right |
| break; |
| case DTM.PROCESSING_INSTRUCTION_NODE : |
| head = (TemplateSubPatternAssociation) m_patternTable.get( |
| dtm.getLocalName(targetNode)); |
| break; |
| case DTM.COMMENT_NODE : |
| head = m_commentPatterns; |
| break; |
| case DTM.DOCUMENT_NODE : |
| case DTM.DOCUMENT_FRAGMENT_NODE : |
| head = m_docPatterns; |
| break; |
| case DTM.NOTATION_NODE : |
| default : |
| head = (TemplateSubPatternAssociation) m_patternTable.get( |
| dtm.getNodeName(targetNode)); // %REVIEW% I think this is right |
| } |
| |
| return (null == head) ? m_wildCardPatterns : head; |
| } |
| |
| /** |
| * Given a target element, find the template that best |
| * matches in the given XSL document, according |
| * to the rules specified in the xsl draft. This variation of getTemplate |
| * assumes the current node and current expression node have already been |
| * pushed. |
| * |
| * @param xctxt |
| * @param targetNode |
| * @param mode A string indicating the display mode. |
| * @param maxImportLevel The maximum importCountComposed that we should consider or -1 |
| * if we should consider all import levels. This is used by apply-imports to |
| * access templates that have been overridden. |
| * @param quietConflictWarnings |
| * @return Rule that best matches targetElem. |
| * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide |
| * the error condition is severe enough to halt processing. |
| * |
| * @throws TransformerException |
| */ |
| public ElemTemplate getTemplateFast(XPathContext xctxt, |
| int targetNode, |
| int expTypeID, |
| QName mode, |
| int maxImportLevel, |
| boolean quietConflictWarnings, |
| DTM dtm) |
| throws TransformerException |
| { |
| |
| TemplateSubPatternAssociation head; |
| |
| switch (dtm.getNodeType(targetNode)) |
| { |
| case DTM.ELEMENT_NODE : |
| case DTM.ATTRIBUTE_NODE : |
| head = (TemplateSubPatternAssociation) m_patternTable.get( |
| dtm.getLocalNameFromExpandedNameID(expTypeID)); |
| break; |
| case DTM.TEXT_NODE : |
| case DTM.CDATA_SECTION_NODE : |
| head = m_textPatterns; |
| break; |
| case DTM.ENTITY_REFERENCE_NODE : |
| case DTM.ENTITY_NODE : |
| head = (TemplateSubPatternAssociation) m_patternTable.get( |
| dtm.getNodeName(targetNode)); // %REVIEW% I think this is right |
| break; |
| case DTM.PROCESSING_INSTRUCTION_NODE : |
| head = (TemplateSubPatternAssociation) m_patternTable.get( |
| dtm.getLocalName(targetNode)); |
| break; |
| case DTM.COMMENT_NODE : |
| head = m_commentPatterns; |
| break; |
| case DTM.DOCUMENT_NODE : |
| case DTM.DOCUMENT_FRAGMENT_NODE : |
| head = m_docPatterns; |
| break; |
| case DTM.NOTATION_NODE : |
| default : |
| head = (TemplateSubPatternAssociation) m_patternTable.get( |
| dtm.getNodeName(targetNode)); // %REVIEW% I think this is right |
| } |
| |
| if(null == head) |
| { |
| head = m_wildCardPatterns; |
| if(null == head) |
| return null; |
| } |
| |
| // XSLT functions, such as xsl:key, need to be able to get to |
| // current ElemTemplateElement via a cast to the prefix resolver. |
| // Setting this fixes bug idkey03. |
| xctxt.pushNamespaceContextNull(); |
| try |
| { |
| do |
| { |
| if ( (maxImportLevel > -1) && (head.getImportLevel() > maxImportLevel) ) |
| { |
| continue; |
| } |
| ElemTemplate template = head.getTemplate(); |
| xctxt.setNamespaceContext(template); |
| |
| if ((head.m_stepPattern.execute(xctxt, targetNode, dtm, expTypeID) != NodeTest.SCORE_NONE) |
| && head.matchMode(mode)) |
| { |
| if (quietConflictWarnings) |
| checkConflicts(head, xctxt, targetNode, mode); |
| |
| return template; |
| } |
| } |
| while (null != (head = head.getNext())); |
| } |
| finally |
| { |
| xctxt.popNamespaceContext(); |
| } |
| |
| return null; |
| } // end findTemplate |
| |
| /** |
| * Given a target element, find the template that best |
| * matches in the given XSL document, according |
| * to the rules specified in the xsl draft. |
| * |
| * @param xctxt |
| * @param targetNode |
| * @param mode A string indicating the display mode. |
| * @param quietConflictWarnings |
| * @return Rule that best matches targetElem. |
| * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide |
| * the error condition is severe enough to halt processing. |
| * |
| * @throws TransformerException |
| */ |
| public ElemTemplate getTemplate(XPathContext xctxt, |
| int targetNode, |
| QName mode, |
| boolean quietConflictWarnings, |
| DTM dtm) |
| throws TransformerException |
| { |
| |
| TemplateSubPatternAssociation head = getHead(xctxt, targetNode, dtm); |
| |
| if (null != head) |
| { |
| // XSLT functions, such as xsl:key, need to be able to get to |
| // current ElemTemplateElement via a cast to the prefix resolver. |
| // Setting this fixes bug idkey03. |
| xctxt.pushNamespaceContextNull(); |
| xctxt.pushCurrentNodeAndExpression(targetNode, targetNode); |
| try |
| { |
| do |
| { |
| ElemTemplate template = head.getTemplate(); |
| xctxt.setNamespaceContext(template); |
| |
| if ((head.m_stepPattern.execute(xctxt, targetNode) != NodeTest.SCORE_NONE) |
| && head.matchMode(mode)) |
| { |
| if (quietConflictWarnings) |
| checkConflicts(head, xctxt, targetNode, mode); |
| |
| return template; |
| } |
| } |
| while (null != (head = head.getNext())); |
| } |
| finally |
| { |
| xctxt.popCurrentNodeAndExpression(); |
| xctxt.popNamespaceContext(); |
| } |
| } |
| |
| return null; |
| } // end findTemplate |
| |
| /** |
| * Given a target element, find the template that best |
| * matches in the given XSL document, according |
| * to the rules specified in the xsl draft. |
| * |
| * @param xctxt |
| * @param targetNode |
| * @param mode A string indicating the display mode. |
| * @param maxImportLevel The maximum importCountComposed that we should consider or -1 |
| * if we should consider all import levels. This is used by apply-imports to |
| * access templates that have been overridden. |
| * @param endImportLevel The count of composed imports |
| * @param quietConflictWarnings |
| * @return Rule that best matches targetElem. |
| * @throws XSLProcessorException thrown if the active ProblemListener and XPathContext decide |
| * the error condition is severe enough to halt processing. |
| * |
| * @throws TransformerException |
| */ |
| public ElemTemplate getTemplate(XPathContext xctxt, |
| int targetNode, |
| QName mode, |
| int maxImportLevel, int endImportLevel, |
| boolean quietConflictWarnings, |
| DTM dtm) |
| throws TransformerException |
| { |
| |
| TemplateSubPatternAssociation head = getHead(xctxt, targetNode, dtm); |
| |
| if (null != head) |
| { |
| // XSLT functions, such as xsl:key, need to be able to get to |
| // current ElemTemplateElement via a cast to the prefix resolver. |
| // Setting this fixes bug idkey03. |
| xctxt.pushNamespaceContextNull(); |
| xctxt.pushCurrentNodeAndExpression(targetNode, targetNode); |
| try |
| { |
| do |
| { |
| if ( (maxImportLevel > -1) && (head.getImportLevel() > maxImportLevel)) |
| { |
| continue; |
| } |
| if (head.getImportLevel()<= maxImportLevel - endImportLevel) |
| return null; |
| ElemTemplate template = head.getTemplate(); |
| xctxt.setNamespaceContext(template); |
| |
| if ((head.m_stepPattern.execute(xctxt, targetNode) != NodeTest.SCORE_NONE) |
| && head.matchMode(mode)) |
| { |
| if (quietConflictWarnings) |
| checkConflicts(head, xctxt, targetNode, mode); |
| |
| return template; |
| } |
| } |
| while (null != (head = head.getNext())); |
| } |
| finally |
| { |
| xctxt.popCurrentNodeAndExpression(); |
| xctxt.popNamespaceContext(); |
| } |
| } |
| |
| return null; |
| } // end findTemplate |
| |
| /** |
| * Get a TemplateWalker for use by a compiler. See the documentation for |
| * the TreeWalker inner class for further details. |
| */ |
| public TemplateWalker getWalker() |
| { |
| return new TemplateWalker(); |
| } |
| |
| /** |
| * Check for match conflicts, and warn the stylesheet author. |
| * |
| * @param head Template pattern |
| * @param xctxt Current XPath context |
| * @param targetNode Node matching the pattern |
| * @param mode reference, which may be null, to the <a href="http://www.w3.org/TR/xslt#modes">current mode</a>. |
| */ |
| private void checkConflicts(TemplateSubPatternAssociation head, |
| XPathContext xctxt, int targetNode, QName mode) |
| { |
| |
| // TODO: Check for conflicts. |
| } |
| |
| /** |
| * Add object to vector if not already there. |
| * |
| * @param obj |
| * @param v |
| */ |
| private void addObjectIfNotFound(Object obj, Vector v) |
| { |
| |
| int n = v.size(); |
| boolean addIt = true; |
| |
| for (int i = 0; i < n; i++) |
| { |
| if (v.elementAt(i) == obj) |
| { |
| addIt = false; |
| |
| break; |
| } |
| } |
| |
| if (addIt) |
| { |
| v.addElement(obj); |
| } |
| } |
| |
| /** |
| * Keyed on string macro names, and holding values |
| * that are macro elements in the XSL DOM tree. |
| * Initialized in initMacroLookupTable, and used in |
| * findNamedTemplate. |
| * @serial |
| */ |
| private Hashtable m_namedTemplates = new Hashtable(89); |
| |
| /** |
| * This table is keyed on the target elements |
| * of patterns, and contains linked lists of |
| * the actual patterns that match the target element |
| * to some degree of specifity. |
| * @serial |
| */ |
| private Hashtable m_patternTable = new Hashtable(89); |
| |
| /** Wildcard patterns. |
| * @serial */ |
| private TemplateSubPatternAssociation m_wildCardPatterns = null; |
| |
| /** Text Patterns. |
| * @serial */ |
| private TemplateSubPatternAssociation m_textPatterns = null; |
| |
| /** Root document Patterns. |
| * @serial */ |
| private TemplateSubPatternAssociation m_docPatterns = null; |
| |
| /** Comment Patterns. |
| * @serial */ |
| private TemplateSubPatternAssociation m_commentPatterns = null; |
| |
| /** |
| * Get table of named Templates. |
| * These are keyed on template names, and holding values |
| * that are template elements. |
| * |
| * @return A Hashtable dictionary that contains {@link java.lang.String}s |
| * as the keys, and {@link org.apache.xalan.templates.ElemTemplate}s as the |
| * values. |
| */ |
| private Hashtable getNamedTemplates() |
| { |
| return m_namedTemplates; |
| } |
| |
| /** |
| * Set table of named Templates. |
| * These are keyed on string macro names, and holding values |
| * that are template elements in the XSL DOM tree. |
| * |
| * @param v Hashtable dictionary that contains {@link java.lang.String}s |
| * as the keys, and {@link org.apache.xalan.templates.ElemTemplate}s as the |
| * values. |
| */ |
| private void setNamedTemplates(Hashtable v) |
| { |
| m_namedTemplates = v; |
| } |
| |
| /** |
| * Get the head of the assocation list that is keyed by target. |
| * |
| * @param key The name of a node. |
| * |
| * @return The head of a linked list that contains all possible match pattern to |
| * template associations for the given key. |
| */ |
| private TemplateSubPatternAssociation getHead(String key) |
| { |
| return (TemplateSubPatternAssociation) m_patternTable.get(key); |
| } |
| |
| /** |
| * Get the head of the assocation list that is keyed by target. |
| * |
| * @param key |
| * @param assoc |
| */ |
| private void putHead(String key, TemplateSubPatternAssociation assoc) |
| { |
| |
| if (key.equals(PsuedoNames.PSEUDONAME_TEXT)) |
| m_textPatterns = assoc; |
| else if (key.equals(PsuedoNames.PSEUDONAME_ROOT)) |
| m_docPatterns = assoc; |
| else if (key.equals(PsuedoNames.PSEUDONAME_COMMENT)) |
| m_commentPatterns = assoc; |
| |
| m_patternTable.put(key, assoc); |
| } |
| |
| /** |
| * An inner class used by a compiler to iterate over all of the ElemTemplates |
| * stored in this TemplateList. The compiler can replace returned templates |
| * with their compiled equivalent. |
| */ |
| public class TemplateWalker |
| { |
| private Enumeration hashIterator; |
| private boolean inPatterns; |
| private TemplateSubPatternAssociation curPattern; |
| |
| private Hashtable m_compilerCache = new Hashtable(); |
| |
| private TemplateWalker() |
| { |
| hashIterator = m_patternTable.elements(); |
| inPatterns = true; |
| curPattern = null; |
| } |
| |
| public ElemTemplate next() |
| { |
| |
| ElemTemplate retValue = null; |
| ElemTemplate ct; |
| |
| while (true) |
| { |
| if (inPatterns) |
| { |
| if (null != curPattern) |
| curPattern = curPattern.getNext(); |
| |
| if (null != curPattern) |
| retValue = curPattern.getTemplate(); |
| else |
| { |
| if (hashIterator.hasMoreElements()) |
| { |
| curPattern = (TemplateSubPatternAssociation) hashIterator.nextElement(); |
| retValue = curPattern.getTemplate(); |
| } |
| else |
| { |
| inPatterns = false; |
| hashIterator = m_namedTemplates.elements(); |
| } |
| } |
| } |
| |
| if (!inPatterns) |
| { |
| if (hashIterator.hasMoreElements()) |
| retValue = (ElemTemplate) hashIterator.nextElement(); |
| else |
| return null; |
| } |
| |
| ct = (ElemTemplate) m_compilerCache.get(new Integer(retValue.getUid())); |
| if (null == ct) |
| { |
| m_compilerCache.put(new Integer(retValue.getUid()), retValue); |
| return retValue; |
| } |
| } |
| } |
| } |
| |
| } |