sboag 99/12/16 03:19:20
Modified: src makexslt4j
src/org/apache/xalan/xpath/dtm DTMLiaison.java DTMProxy.java
src/org/apache/xalan/xpath/xml FormatterToHTML.java
src/org/apache/xalan/xslt Stylesheet.java
StylesheetHandler.java
Added: src/org/apache/xalan/xslt TemplateList.java
Log:
Fixed template conflicts when priority is different but pattern is the same.
Also broke template handling into a TemplateList class, in order to make it
easier to navigate around template management and template match resolution.
Revision Changes Path
1.5 +1 -0 xml-xalan/src/makexslt4j
Index: makexslt4j
===================================================================
RCS file: /home/cvs/xml-xalan/src/makexslt4j,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- makexslt4j 1999/12/03 08:43:11 1.4
+++ makexslt4j 1999/12/16 11:19:18 1.5
@@ -85,6 +85,7 @@
$(XSLT4JDIR)$(PATHSEP)XSLProcessorVersion.java \
$(XSLT4JDIR)$(PATHSEP)XSLTInputSource.java \
$(XSLT4JDIR)$(PATHSEP)XSLTResultTarget.java \
+ $(XSLT4JDIR)$(PATHSEP)TemplateList.java \
$(XSLT4JDIR)$(PATHSEP)ResultNameSpace.java \
$(TRACEDIR)$(PATHSEP)GenerateEvent.java \
$(TRACEDIR)$(PATHSEP)SelectionEvent.java \
1.8 +1 -1 xml-xalan/src/org/apache/xalan/xpath/dtm/DTMLiaison.java
Index: DTMLiaison.java
===================================================================
RCS file: /home/cvs/xml-xalan/src/org/apache/xalan/xpath/dtm/DTMLiaison.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- DTMLiaison.java 1999/12/16 06:10:45 1.7
+++ DTMLiaison.java 1999/12/16 11:19:19 1.8
@@ -274,7 +274,7 @@
{
try
{
- return ((org.apache.xalan.xpath.dtm.DTMProxy)n).getParentNode();
+ return ((org.apache.xalan.xpath.dtm.DTMProxy)n).getOwnerNode();
}
catch(ClassCastException cce)
{
1.5 +7 -0 xml-xalan/src/org/apache/xalan/xpath/dtm/DTMProxy.java
Index: DTMProxy.java
===================================================================
RCS file: /home/cvs/xml-xalan/src/org/apache/xalan/xpath/dtm/DTMProxy.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- DTMProxy.java 1999/12/16 09:06:05 1.4
+++ DTMProxy.java 1999/12/16 11:19:19 1.5
@@ -224,6 +224,13 @@
}
/** @see org.w3c.dom.Node */
+ public final Node getOwnerNode()
+ {
+ int newnode=dtm.getParent(node);
+ return (newnode==-1) ? null : dtm.getNode(newnode);
+ }
+
+ /** @see org.w3c.dom.Node */
public final NodeList getChildNodes()
{
throw new DTMException(DTMException.NOT_SUPPORTED_ERR);
1.17 +1 -2
xml-xalan/src/org/apache/xalan/xpath/xml/FormatterToHTML.java
Index: FormatterToHTML.java
===================================================================
RCS file:
/home/cvs/xml-xalan/src/org/apache/xalan/xpath/xml/FormatterToHTML.java,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- FormatterToHTML.java 1999/12/15 21:53:07 1.16
+++ FormatterToHTML.java 1999/12/16 11:19:19 1.17
@@ -356,8 +356,7 @@
m_attrCharsMap[(int)'\n'] = 'S';
// XSLT Spec: The html output method should not
// escape < characters occurring in attribute values.
- // (But I'll comment this out for Stefano for the moment.)
- // m_attrCharsMap[(int)'<'] = '\0';
+ m_attrCharsMap[(int)'<'] = '\0';
m_attrCharsMap[(int)'>'] = '\0';
m_charsMap[0x0A] = 'S';
m_charsMap[0x0D] = 'S';
1.17 +22 -467 xml-xalan/src/org/apache/xalan/xslt/Stylesheet.java
Index: Stylesheet.java
===================================================================
RCS file: /home/cvs/xml-xalan/src/org/apache/xalan/xslt/Stylesheet.java,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- Stylesheet.java 1999/12/16 03:50:35 1.16
+++ Stylesheet.java 1999/12/16 11:19:19 1.17
@@ -108,15 +108,6 @@
}
/**
- * 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();
-
- /**
* Table of attribute sets, keyed by set name.
* @serial
*/
@@ -165,19 +156,6 @@
* @serial
*/
boolean m_tablesAreInvalid = true;
-
- /**
- * Tells if the stylesheet is without an xsl:stylesheet
- * and xsl:template wrapper.
- * @serial
- */
- boolean m_isWrapperless = false;
-
- /**
- * The manufactured template if there is no wrapper.
- * @serial
- */
- ElemTemplate m_wrapperlessTemplate = null;
/**
* A dummy value for use in hash tables that have no use for the value.
@@ -242,25 +220,24 @@
Hashtable m_extensionNamespaces = new Hashtable();
/**
- * The first template of the template children.
- * @serial
- */
- ElemTemplateElement m_firstTemplate = null;
+ * The list of templates, and findTemplate support.
+ */
+ private TemplateList m_templateList;
/**
- * Get the first template of the template children.
+ * Get the list of templates.
*/
- public ElemTemplateElement getFirstTemplate()
+ public TemplateList getTemplateList()
{
- return m_firstTemplate;
+ return m_templateList;
}
/**
- * Set the first template of the template children.
+ * Set the list of templates.
*/
- public void setFirstTemplate(ElemTemplateElement v)
+ public void setTemplateList(TemplateList v)
{
- m_firstTemplate = v;
+ m_templateList = v;
}
/**
@@ -445,35 +422,6 @@
}
/**
- * 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
- */
- Hashtable m_namedTemplates = new Hashtable();
-
- /**
- * Get table of named Templates.
- * These are keyed on string macro names, and holding values
- * that are template elements in the XSL DOM tree.
- */
- public 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.
- */
- public void setNamedTemplates(Hashtable v)
- {
- m_namedTemplates = v;
- }
-
- /**
* Table for defined constants, keyed on the names.
* @serial
*/
@@ -679,6 +627,7 @@
IOException,
SAXException
{
+ m_templateList = new TemplateList(this);
m_includeStack = new Stack();
m_includeStack.push(processor.m_parserLiaison.getURLFromString(this.m_baseIdent,
null));
initXPath(processor, null);
@@ -1019,132 +968,6 @@
}
/**
- * Add a template to the template list.
- */
- void addTemplate(ElemTemplate template)
- {
- int pos = 0;
- if(null == m_firstTemplate)
- {
- m_firstTemplate = template;
- }
- else
- {
- ElemTemplateElement next = m_firstTemplate;
- while(null != next)
- {
- if(null == next.m_nextSibling)
- {
- next.m_nextSibling = template;
- template.m_nextSibling = null; // just to play it safe.
- break;
- }
- pos++;
- next = next.m_nextSibling;
- }
- }
- if(null != template.m_name)
- {
- m_namedTemplates.put(template.m_name, template);
- }
-
- if(null != template.m_matchPattern)
- {
- Vector strings = template.m_matchPattern.getTargetElementStrings();
- if(null != strings)
- {
- int nTargets = strings.size();
- for(int stringIndex = 0; stringIndex < nTargets; stringIndex++)
- {
- String target = (String)strings.elementAt(stringIndex);
-
- Object newMatchPat = new
MatchPattern2(template.m_matchPattern.m_currentPattern,
- template.m_matchPattern,
- template, pos,
- target, this);
-
- // See if there's already one there
- Object val = m_patternTable.get(target);
- if(null == val)
- {
- // System.out.println("putting: "+target);
- m_patternTable.put(target, newMatchPat);
- }
- else
- {
- // find the tail of the list
- MatchPattern2 matchPat = (MatchPattern2)val;
- ((MatchPattern2)newMatchPat).setNext(matchPat);
- m_patternTable.put(target, newMatchPat);
- /*
- MatchPattern2 next;
- while((next = matchPat.getNext()) != null)
- {
- matchPat = next;
- }
- // System.out.println("appending: "+target+" to
"+matchPat.getPattern());
- matchPat.setNext((MatchPattern2)newMatchPat);
- */
- }
- }
- }
- }
- }
-
- /**
- * Locate a macro via the "name" attribute.
- * @exception XSLProcessorException thrown if the active ProblemListener
and XMLParserLiaison decide
- * the error condition is severe enough to halt processing.
- */
- ElemTemplateElement findNamedTemplate(String name)
- throws XSLProcessorException
- {
- QName qname = new QName(name, m_namespaces);
-
- return findNamedTemplate(qname);
- }
-
- /**
- * Locate a macro via the "name" attribute.
- * @exception XSLProcessorException thrown if the active ProblemListener
and XMLParserLiaison decide
- * the error condition is severe enough to halt processing.
- */
- ElemTemplateElement findNamedTemplate(QName qname)
- throws XSLProcessorException
- {
- ElemTemplateElement namedTemplate = null;
- Object obj = m_namedTemplates.get(qname);
- if(null == obj)
- {
- int nImports = m_imports.size();
- for(int i = 0; i < nImports; i++)
- {
- Stylesheet stylesheet = (Stylesheet)m_imports.elementAt(i);
- namedTemplate = stylesheet.findNamedTemplate(qname);
- if(null != namedTemplate)
- {
- break;
- }
- }
- if((null == namedTemplate) && (null != m_stylesheetParent))
- {
- Stylesheet stylesheet = m_stylesheetParent.getPreviousImport(this);
- if(null != stylesheet)
- {
- namedTemplate = stylesheet.findNamedTemplate(qname);
- }
- }
- }
- else
- {
- namedTemplate = (ElemTemplateElement)obj;
- }
-
- return namedTemplate;
- }
-
-
- /**
* Set a top level variable, to be serialized with the rest of
* the stylesheet.
* @param var A top-level variable declared with xsl:variable or
xsl:param-variable.
@@ -1229,9 +1052,9 @@
java.io.FileNotFoundException,
java.io.IOException
{
- return findTemplate(transformContext, sourceTree, targetNode, null,
false, null);
+ return getTemplateList().findTemplate(transformContext, sourceTree,
targetNode, null, false, null);
}
-
+
/**
* Given a target element, find the template that best
* matches in the given XSL document, according
@@ -1253,240 +1076,22 @@
QName mode,
boolean useImports,
Stylesheet foundStylesheet[])
- throws SAXException,
- java.net.MalformedURLException,
- java.io.FileNotFoundException,
- java.io.IOException
- {
- ElemTemplate bestMatchedRule = null;
- MatchPattern2 bestMatchedPattern =null; // Syncs with bestMatchedRule
- if(m_isWrapperless)
- {
- return m_wrapperlessTemplate;
- }
- Vector conflicts = null;
- if(!useImports)
- {
- double highScore = XPath.MATCH_SCORE_NONE;
-
- MatchPattern2 matchPat = null;
- int targetNodeType = targetNode.getNodeType();
-
- switch(targetNodeType)
- {
- case Node.ELEMENT_NODE:
- // String targetName =
m_parserLiaison.getExpandedElementName((Element)targetNode);
- String targetName =
transformContext.m_parserLiaison.getLocalNameOfNode(targetNode);
- matchPat = locateMatchPatternList2(targetName, true);
- break;
-
- case Node.PROCESSING_INSTRUCTION_NODE:
- case Node.ATTRIBUTE_NODE:
- matchPat = locateMatchPatternList2(targetNode.getNodeName(), true);
- break;
-
- case Node.CDATA_SECTION_NODE:
- case Node.TEXT_NODE:
- matchPat = locateMatchPatternList2(XPath.PSEUDONAME_TEXT, false);
- break;
-
- case Node.COMMENT_NODE:
- matchPat = locateMatchPatternList2(XPath.PSEUDONAME_COMMENT, false);
- break;
-
- case Node.DOCUMENT_NODE:
- matchPat = locateMatchPatternList2(XPath.PSEUDONAME_ROOT, false);
- break;
-
- case Node.DOCUMENT_FRAGMENT_NODE:
- matchPat = locateMatchPatternList2(XPath.PSEUDONAME_ANY, false);
- break;
-
- default:
- {
- matchPat = locateMatchPatternList2(targetNode.getNodeName(),
false);
- }
- }
-
- String prevPat = null;
- while(null != matchPat)
- {
- ElemTemplate rule = matchPat.getTemplate();
- // We'll be needing to match rules according to what
- // mode we're in.
- QName ruleMode = rule.m_mode;
-
- // The logic here should be that if we are not in a mode AND
- // the rule does not have a node, then go ahead.
- // OR if we are in a mode, AND the rule has a node,
- // AND the rules match, then go ahead.
- if(((null == mode) && (null == ruleMode)) ||
- ((null != ruleMode) && (null != mode) && ruleMode.equals(mode)))
- {
- String patterns = matchPat.getPattern();
-
- if((null != patterns) && !((prevPat != null) &&
prevPat.equals(patterns)))
- {
- prevPat = patterns;
-
- // Date date1 = new Date();
- XPath xpath = matchPat.getExpression();
- // System.out.println("Testing score for:
"+targetNode.getNodeName()+
- // " against '"+xpath.m_currentPattern);
- double score =
xpath.getMatchScore(transformContext.getExecContext(), targetNode);
- // System.out.println("Score for: "+targetNode.getNodeName()+
- // " against '"+xpath.m_currentPattern+
- // "' returned "+score);
-
- if(XPath.MATCH_SCORE_NONE != score)
- {
- double priorityOfRule
- = (XPath.MATCH_SCORE_NONE != rule.m_priority)
- ? rule.m_priority : score;
- matchPat.m_priority = priorityOfRule;
- double priorityOfBestMatched = (null != bestMatchedPattern) ?
- bestMatchedPattern.m_priority :
- XPath.MATCH_SCORE_NONE;
- // System.out.println("priorityOfRule: "+priorityOfRule+",
priorityOfBestMatched: "+priorityOfBestMatched);
- if(priorityOfRule > priorityOfBestMatched)
- {
- if(null != conflicts)
- conflicts.removeAllElements();
- highScore = score;
- bestMatchedRule = rule;
- bestMatchedPattern = matchPat;
- }
- else if(priorityOfRule == priorityOfBestMatched)
- {
- if(null == conflicts)
- conflicts = new Vector(10);
- addObjectIfNotFound(bestMatchedPattern, conflicts);
- conflicts.addElement(matchPat);
- highScore = score;
- bestMatchedRule = rule;
- bestMatchedPattern = matchPat;
- }
- }
- // Date date2 = new Date();
- // m_totalTimePatternMatching+=(date2.getTime() -
date1.getTime());
- } // end if(null != patterns)
- } // end if if(targetModeString.equals(mode))
-
- MatchPattern2 nextMatchPat = matchPat.getNext();
-
- // We also have to consider wildcard matches.
- if((null == nextMatchPat) && !matchPat.m_targetString.equals("*") &&
- ((Node.ELEMENT_NODE == targetNodeType) ||
- (Node.ATTRIBUTE_NODE == targetNodeType) ||
- (Node.PROCESSING_INSTRUCTION_NODE == targetNodeType)))
- {
- nextMatchPat = (MatchPattern2)m_patternTable.get("*");
- }
- matchPat = nextMatchPat;
- }
- } // end if(!useImports)
-
- if(null == bestMatchedRule)
- {
- int nImports = m_imports.size();
- for(int i = 0; i < nImports; i++)
- {
- Stylesheet stylesheet = (Stylesheet)m_imports.elementAt(i);
- bestMatchedRule = stylesheet.findTemplate(transformContext,
sourceTree, targetNode, mode,
- false, foundStylesheet);
- if(null != bestMatchedRule)
- {
- break;
- }
- }
- }
-
- if(null != conflicts)
- {
- int nConflicts = conflicts.size();
- // System.out.println("nConflicts: "+nConflicts);
- String conflictsString = (!transformContext.m_quietConflictWarnings)
- ? "Specificity conflicts found: " : null;
- for(int i = 0; i < nConflicts; i++)
- {
- MatchPattern2 conflictPat = (MatchPattern2)conflicts.elementAt(i);
- if(0 != i)
- {
- if(!transformContext.m_quietConflictWarnings)
- conflictsString += ", ";
-
- // Find the furthest one towards the bottom of the document.
- if(conflictPat.m_posInStylesheet >
bestMatchedPattern.m_posInStylesheet)
- {
- bestMatchedPattern = conflictPat;
- }
- }
- else
- {
- bestMatchedPattern = conflictPat;
- }
- if(!transformContext.m_quietConflictWarnings)
- conflictsString += "\""+conflictPat.getPattern()+"\"";
- }
- bestMatchedRule = bestMatchedPattern.getTemplate();
- if(!transformContext.m_quietConflictWarnings)
- {
- //conflictsString += " ";
- //conflictsString += "Last found in stylesheet will be used.";
- transformContext.warn(XSLTErrorResources.WARNING0012, new Object[]
{conflictsString});
- }
- }
-
- if((null != bestMatchedPattern) && (null != foundStylesheet))
- {
- foundStylesheet[0] = bestMatchedPattern.m_stylesheet;
- }
-
- return bestMatchedRule;
- } // end findTemplate
-
- /**
- * Add object to vector if not already there.
- */
- void addObjectIfNotFound(Object obj, Vector v)
+ throws SAXException
{
- 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);
- }
+ return getTemplateList().findTemplate(transformContext,
+ sourceTree, targetNode,
+ mode, useImports, foundStylesheet);
}
/**
- * Given an element type, locate the start of a linked list of
- * possible template matches.
+ * Locate a macro via the "name" attribute.
+ * @exception XSLProcessorException thrown if the active ProblemListener
and XMLParserLiaison decide
+ * the error condition is severe enough to halt processing.
*/
- private MatchPattern2 locateMatchPatternList2(String sourceElementType,
boolean tryWildCard)
+ ElemTemplateElement findNamedTemplate(QName qname)
+ throws XSLProcessorException
{
- MatchPattern2 startMatchList = null;
- Object val = m_patternTable.get(sourceElementType);
- if(null != val)
- {
- startMatchList = (MatchPattern2)val;
- }
- else if(tryWildCard)
- {
- val = m_patternTable.get("*");
- if(null != val)
- {
- startMatchList = (MatchPattern2)val;
- }
- }
- return startMatchList;
+ return getTemplateList().findNamedTemplate(qname);
}
/**
@@ -1548,56 +1153,6 @@
}
}
}
-
- /**
- * A class to contain a match pattern and it's corresponding template.
- * This class also defines a node in a match pattern linked list.
- */
- class MatchPattern2 implements Serializable
- {
- /**
- * Construct a match pattern from a pattern and template.
- * @param pat For now a Nodelist that contains old-style element
patterns.
- * @param template The node that contains the template for this pattern.
- * @param isMatchPatternsOnly tells if pat param contains only match
- * patterns (for compatibility with old syntax).
- */
- MatchPattern2(String pat, XPath exp, ElemTemplate template, int
posInStylesheet,
- String targetString, Stylesheet stylesheet)
- {
- m_pattern = pat;
- m_template = template;
- m_posInStylesheet = posInStylesheet;
- m_targetString = targetString;
- m_stylesheet = stylesheet;
- m_expression = exp;
- }
-
- Stylesheet m_stylesheet;
-
- String m_targetString;
-
- XPath m_expression;
- public XPath getExpression() { return m_expression; }
-
- int m_posInStylesheet;
-
- /**
- * Transient... only used to track priority while
- * processing.
- */
- double m_priority = XPath.MATCH_SCORE_NONE;
-
- private String m_pattern;
- public String getPattern() { return m_pattern; }
-
- private ElemTemplate m_template; // ref to the corrisponding template
- public ElemTemplate getTemplate() { return m_template; }
-
- private MatchPattern2 m_next = null; // null when at end of list.
- public MatchPattern2 getNext() { return m_next; }
- public void setNext(MatchPattern2 mp) { m_next = mp; }
- }
/**
* Given a valid element key, return the corresponding node list.
1.11 +6 -6
xml-xalan/src/org/apache/xalan/xslt/StylesheetHandler.java
Index: StylesheetHandler.java
===================================================================
RCS file:
/home/cvs/xml-xalan/src/org/apache/xalan/xslt/StylesheetHandler.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- StylesheetHandler.java 1999/12/14 00:21:55 1.10
+++ StylesheetHandler.java 1999/12/16 11:19:19 1.11
@@ -415,7 +415,7 @@
{
m_stylesheet.initXSLTKeys();
m_stylesheet.m_stylesheetRoot.initDefaultRule();
- m_stylesheet.m_isWrapperless = false;
+ m_stylesheet.getTemplateList().setIsWrapperless(false);
}
Object obj = m_stylesheet.m_elementKeys.get(localName);
@@ -435,7 +435,7 @@
name, atts, lineNumber,
columnNumber);
m_elems.push(m_template);
m_inTemplate = true;
- m_stylesheet.addTemplate(m_template);
+ m_stylesheet.getTemplateList().addTemplate(m_template);
break;
case Constants.ELEMNAME_CSSSTYLECONVERSION:
@@ -563,7 +563,7 @@
// break;
case Constants.ELEMNAME_STYLESHEET:
- m_stylesheet.m_isWrapperless = false;
+ m_stylesheet.getTemplateList().setIsWrapperless(false);
m_foundStylesheet = true;
int nAttrs = atts.getLength();
boolean didSpecifiyIndent = false;
@@ -616,7 +616,7 @@
}
else if(!(isAttrOK(aname, atts, i) || processSpaceAttr(aname,
atts, i)))
{
- if(false == m_stylesheet.m_isWrapperless)
+ if(false == m_stylesheet.getTemplateList().getIsWrapperless())
{
throw new
SAXException(XSLMessages.createMessage(XSLTErrorResources.ERROR0066, new
Object[]{name, aname})); //"(StylesheetHandler) "+name+" has an illegal
attribute: "+aname);
}
@@ -1134,9 +1134,9 @@
m_template.appendChild(elem);
m_inTemplate = true;
- m_stylesheet.m_wrapperlessTemplate = m_template;
+ m_stylesheet.getTemplateList().setWrapperlessTemplate(m_template);
+ m_stylesheet.getTemplateList().setIsWrapperless(true);
m_foundStylesheet = true;
- m_stylesheet.m_isWrapperless = true;
if(name.equals("HTML"))
{
m_stylesheet.m_stylesheetRoot.m_indentResult = true;
1.1 xml-xalan/src/org/apache/xalan/xslt/TemplateList.java
Index: TemplateList.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "XSLT4J" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, Lotus
* Development Corporation., http://www.lotus.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.xalan.xslt;
import java.util.Hashtable;
import org.w3c.dom.Node;
import org.apache.xalan.xpath.xml.QName;
import org.xml.sax.SAXException;
import java.util.Vector;
import java.io.Serializable;
import org.apache.xalan.xpath.XPath;
import org.apache.xalan.xslt.res.XSLTErrorResources;
/**
* This class segregates the template list, and performs template
* finding services.
*/
public class TemplateList
{
/**
* Construct a TemplateList object.
*/
TemplateList(Stylesheet stylesheet)
{
m_stylesheet = stylesheet;
}
/**
* The stylesheet owner of the list.
*/
Stylesheet m_stylesheet;
/**
* The first template of the template children.
* @serial
*/
ElemTemplateElement m_firstTemplate = null;
/**
* Get the first template of the template children.
*/
public ElemTemplateElement getFirstTemplate()
{
return m_firstTemplate;
}
/**
* Set the first template of the template children.
*/
public void setFirstTemplate(ElemTemplateElement v)
{
m_firstTemplate = v;
}
/**
* 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
*/
Hashtable m_namedTemplates = new Hashtable();
/**
* Tells if the stylesheet is without an xsl:stylesheet
* and xsl:template wrapper.
* @serial
*/
private boolean m_isWrapperless = false;
/**
* The manufactured template if there is no wrapper.
* @serial
*/
private ElemTemplate m_wrapperlessTemplate = null;
/**
* 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();
/**
* Set if the stylesheet is without an xsl:stylesheet
* and xsl:template wrapper.
*/
void setIsWrapperless(boolean b)
{
m_isWrapperless = b;
}
/**
* Get if the stylesheet is without an xsl:stylesheet
* and xsl:template wrapper.
*/
boolean getIsWrapperless()
{
return m_isWrapperless;
}
/**
* Set the manufactured template if there is no wrapper.
* and xsl:template wrapper.
*/
void setWrapperlessTemplate(ElemTemplate t)
{
m_wrapperlessTemplate = t;
}
/**
* Get the manufactured template if there is no wrapper.
* and xsl:template wrapper.
*/
ElemTemplate getWrapperlessTemplate()
{
return m_wrapperlessTemplate;
}
/**
* Get table of named Templates.
* These are keyed on string macro names, and holding values
* that are template elements in the XSL DOM tree.
*/
public 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.
*/
public void setNamedTemplates(Hashtable v)
{
m_namedTemplates = v;
}
/**
* 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 stylesheetTree Where the XSL rules are to be found.
* @param sourceTree Where the targetElem is to be found.
* @param targetElem The element that needs a rule.
* @param mode A string indicating the display mode.
* @param useImports means that this is an xsl:apply-imports commend.
* @param foundStylesheet If non-null, the Stylesheet that the found
template
* belongs to will be returned in the foundStylesheet[0].
* @return Rule that best matches targetElem.
* @exception XSLProcessorException thrown if the active ProblemListener
and XMLParserLiaison decide
* the error condition is severe enough to halt processing.
*/
public ElemTemplate findTemplate(XSLTEngineImpl transformContext,
Node sourceTree,
Node targetNode,
QName mode,
boolean useImports,
Stylesheet foundStylesheet[])
throws SAXException
{
ElemTemplate bestMatchedRule = null;
MatchPattern2 bestMatchedPattern =null; // Syncs with bestMatchedRule
if(m_isWrapperless)
{
return m_wrapperlessTemplate;
}
Vector conflicts = null;
if(!useImports)
{
double highScore = XPath.MATCH_SCORE_NONE;
MatchPattern2 matchPat = null;
int targetNodeType = targetNode.getNodeType();
switch(targetNodeType)
{
case Node.ELEMENT_NODE:
// String targetName =
m_parserLiaison.getExpandedElementName((Element)targetNode);
String targetName =
transformContext.m_parserLiaison.getLocalNameOfNode(targetNode);
matchPat = locateMatchPatternList2(targetName, true);
break;
case Node.PROCESSING_INSTRUCTION_NODE:
case Node.ATTRIBUTE_NODE:
matchPat = locateMatchPatternList2(targetNode.getNodeName(), true);
break;
case Node.CDATA_SECTION_NODE:
case Node.TEXT_NODE:
matchPat = locateMatchPatternList2(XPath.PSEUDONAME_TEXT, false);
break;
case Node.COMMENT_NODE:
matchPat = locateMatchPatternList2(XPath.PSEUDONAME_COMMENT, false);
break;
case Node.DOCUMENT_NODE:
matchPat = locateMatchPatternList2(XPath.PSEUDONAME_ROOT, false);
break;
case Node.DOCUMENT_FRAGMENT_NODE:
matchPat = locateMatchPatternList2(XPath.PSEUDONAME_ANY, false);
break;
default:
{
matchPat = locateMatchPatternList2(targetNode.getNodeName(), false);
}
}
String prevPat = null;
MatchPattern2 prevMatchPat = null;
while(null != matchPat)
{
ElemTemplate rule = matchPat.getTemplate();
// We'll be needing to match rules according to what
// mode we're in.
QName ruleMode = rule.m_mode;
// The logic here should be that if we are not in a mode AND
// the rule does not have a node, then go ahead.
// OR if we are in a mode, AND the rule has a node,
// AND the rules match, then go ahead.
if(((null == mode) && (null == ruleMode)) ||
((null != ruleMode) && (null != mode) && ruleMode.equals(mode)))
{
String patterns = matchPat.getPattern();
if((null != patterns) && !((prevPat != null) &&
prevPat.equals(patterns) &&
(prevMatchPat.getTemplate().m_priority
== matchPat.getTemplate().m_priority)) )
{
prevMatchPat = matchPat;
prevPat = patterns;
// Date date1 = new Date();
XPath xpath = matchPat.getExpression();
// System.out.println("Testing score for:
"+targetNode.getNodeName()+
// " against '"+xpath.m_currentPattern);
double score =
xpath.getMatchScore(transformContext.getExecContext(), targetNode);
// System.out.println("Score for: "+targetNode.getNodeName()+
// " against '"+xpath.m_currentPattern+
// "' returned "+score);
if(XPath.MATCH_SCORE_NONE != score)
{
double priorityOfRule
= (XPath.MATCH_SCORE_NONE != rule.m_priority)
? rule.m_priority : score;
matchPat.m_priority = priorityOfRule;
double priorityOfBestMatched = (null != bestMatchedPattern) ?
bestMatchedPattern.m_priority :
XPath.MATCH_SCORE_NONE;
// System.out.println("priorityOfRule: "+priorityOfRule+",
priorityOfBestMatched: "+priorityOfBestMatched);
if(priorityOfRule > priorityOfBestMatched)
{
if(null != conflicts)
conflicts.removeAllElements();
highScore = score;
bestMatchedRule = rule;
bestMatchedPattern = matchPat;
}
else if(priorityOfRule == priorityOfBestMatched)
{
if(null == conflicts)
conflicts = new Vector(10);
addObjectIfNotFound(bestMatchedPattern, conflicts);
conflicts.addElement(matchPat);
highScore = score;
bestMatchedRule = rule;
bestMatchedPattern = matchPat;
}
}
// Date date2 = new Date();
// m_totalTimePatternMatching+=(date2.getTime() -
date1.getTime());
} // end if(null != patterns)
} // end if if(targetModeString.equals(mode))
MatchPattern2 nextMatchPat = matchPat.getNext();
// We also have to consider wildcard matches.
if((null == nextMatchPat) && !matchPat.m_targetString.equals("*") &&
((Node.ELEMENT_NODE == targetNodeType) ||
(Node.ATTRIBUTE_NODE == targetNodeType) ||
(Node.PROCESSING_INSTRUCTION_NODE == targetNodeType)))
{
nextMatchPat = (MatchPattern2)m_patternTable.get("*");
}
matchPat = nextMatchPat;
}
} // end if(!useImports)
if(null == bestMatchedRule)
{
int nImports = m_stylesheet.m_imports.size();
for(int i = 0; i < nImports; i++)
{
Stylesheet stylesheet =
(Stylesheet)m_stylesheet.m_imports.elementAt(i);
bestMatchedRule = stylesheet.findTemplate(transformContext,
sourceTree, targetNode, mode,
false, foundStylesheet);
if(null != bestMatchedRule)
{
break;
}
}
}
if(null != conflicts)
{
int nConflicts = conflicts.size();
// System.out.println("nConflicts: "+nConflicts);
String conflictsString = (!transformContext.m_quietConflictWarnings)
? "" : null;
for(int i = 0; i < nConflicts; i++)
{
MatchPattern2 conflictPat = (MatchPattern2)conflicts.elementAt(i);
if(0 != i)
{
if(!transformContext.m_quietConflictWarnings)
conflictsString += ", ";
// Find the furthest one towards the bottom of the document.
if(conflictPat.m_posInStylesheet >
bestMatchedPattern.m_posInStylesheet)
{
bestMatchedPattern = conflictPat;
}
}
else
{
bestMatchedPattern = conflictPat;
}
if(!transformContext.m_quietConflictWarnings)
conflictsString += "\""+conflictPat.getPattern()+"\"";
}
bestMatchedRule = bestMatchedPattern.getTemplate();
if(!transformContext.m_quietConflictWarnings)
{
//conflictsString += " ";
//conflictsString += "Last found in stylesheet will be used.";
transformContext.warn(XSLTErrorResources.WARNING0012, new Object[]
{conflictsString});
}
}
if((null != bestMatchedPattern) && (null != foundStylesheet))
{
foundStylesheet[0] = bestMatchedPattern.m_stylesheet;
}
return bestMatchedRule;
} // end findTemplate
/**
* Add object to vector if not already there.
*/
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);
}
}
/**
* Given an element type, locate the start of a linked list of
* possible template matches.
*/
private MatchPattern2 locateMatchPatternList2(String sourceElementType,
boolean tryWildCard)
{
MatchPattern2 startMatchList = null;
Object val = m_patternTable.get(sourceElementType);
if(null != val)
{
startMatchList = (MatchPattern2)val;
}
else if(tryWildCard)
{
val = m_patternTable.get("*");
if(null != val)
{
startMatchList = (MatchPattern2)val;
}
}
return startMatchList;
}
/**
* Add a template to the template list.
*/
void addTemplate(ElemTemplate template)
{
int pos = 0;
if(null == m_firstTemplate)
{
m_firstTemplate = template;
}
else
{
ElemTemplateElement next = m_firstTemplate;
while(null != next)
{
if(null == next.m_nextSibling)
{
next.m_nextSibling = template;
template.m_nextSibling = null; // just to play it safe.
break;
}
pos++;
next = next.m_nextSibling;
}
}
if(null != template.m_name)
{
m_namedTemplates.put(template.m_name, template);
}
if(null != template.m_matchPattern)
{
Vector strings = template.m_matchPattern.getTargetElementStrings();
if(null != strings)
{
int nTargets = strings.size();
for(int stringIndex = 0; stringIndex < nTargets; stringIndex++)
{
String target = (String)strings.elementAt(stringIndex);
Object newMatchPat = new
MatchPattern2(template.m_matchPattern.m_currentPattern,
template.m_matchPattern,
template, pos,
target, m_stylesheet);
// See if there's already one there
Object val = m_patternTable.get(target);
if(null == val)
{
// System.out.println("putting: "+target);
m_patternTable.put(target, newMatchPat);
}
else
{
// find the tail of the list
MatchPattern2 matchPat = (MatchPattern2)val;
((MatchPattern2)newMatchPat).setNext(matchPat);
m_patternTable.put(target, newMatchPat);
/*
MatchPattern2 next;
while((next = matchPat.getNext()) != null)
{
matchPat = next;
}
// System.out.println("appending: "+target+" to
"+matchPat.getPattern());
matchPat.setNext((MatchPattern2)newMatchPat);
*/
}
}
}
}
}
/**
* Locate a macro via the "name" attribute.
* @exception XSLProcessorException thrown if the active ProblemListener
and XMLParserLiaison decide
* the error condition is severe enough to halt processing.
*/
ElemTemplateElement findNamedTemplate(String name)
throws XSLProcessorException
{
QName qname = new QName(name, m_stylesheet.m_namespaces);
return findNamedTemplate(qname);
}
/**
* Locate a macro via the "name" attribute.
* @exception XSLProcessorException thrown if the active ProblemListener
and XMLParserLiaison decide
* the error condition is severe enough to halt processing.
*/
ElemTemplateElement findNamedTemplate(QName qname)
throws XSLProcessorException
{
ElemTemplateElement namedTemplate = null;
Object obj = m_namedTemplates.get(qname);
if(null == obj)
{
int nImports = m_stylesheet.m_imports.size();
for(int i = 0; i < nImports; i++)
{
Stylesheet stylesheet =
(Stylesheet)m_stylesheet.m_imports.elementAt(i);
namedTemplate = stylesheet.findNamedTemplate(qname);
if(null != namedTemplate)
{
break;
}
}
if((null == namedTemplate) && (null != m_stylesheet.m_stylesheetParent))
{
Stylesheet stylesheet =
m_stylesheet.m_stylesheetParent.getPreviousImport(m_stylesheet);
if(null != stylesheet)
{
namedTemplate = stylesheet.findNamedTemplate(qname);
}
}
}
else
{
namedTemplate = (ElemTemplateElement)obj;
}
return namedTemplate;
}
/**
* A class to contain a match pattern and it's corresponding template.
* This class also defines a node in a match pattern linked list.
*/
class MatchPattern2 implements Serializable
{
/**
* Construct a match pattern from a pattern and template.
* @param pat For now a Nodelist that contains old-style element patterns.
* @param template The node that contains the template for this pattern.
* @param isMatchPatternsOnly tells if pat param contains only match
* patterns (for compatibility with old syntax).
*/
MatchPattern2(String pat, XPath exp, ElemTemplate template, int
posInStylesheet,
String targetString, Stylesheet stylesheet)
{
m_pattern = pat;
m_template = template;
m_posInStylesheet = posInStylesheet;
m_targetString = targetString;
m_stylesheet = stylesheet;
m_expression = exp;
}
Stylesheet m_stylesheet;
String m_targetString;
XPath m_expression;
public XPath getExpression() { return m_expression; }
int m_posInStylesheet;
/**
* Transient... only used to track priority while
* processing.
*/
double m_priority = XPath.MATCH_SCORE_NONE;
private String m_pattern;
public String getPattern() { return m_pattern; }
private ElemTemplate m_template; // ref to the corrisponding template
public ElemTemplate getTemplate() { return m_template; }
private MatchPattern2 m_next = null; // null when at end of list.
public MatchPattern2 getNext() { return m_next; }
public void setNext(MatchPattern2 mp) { m_next = mp; }
}
}