blob: a6084ca88eabd6ca03afbddc3ba1a707303d2233 [file] [log] [blame]
/*
* 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 "Xalan" 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 apache@apache.org.
*
* 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, International
* Business Machines, Inc., http://www.ibm.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* $ Id: $
*
*/
#include "Stylesheet.hpp"
#include <algorithm>
#include <sax/AttributeList.hpp>
#include <Include/STLHelper.hpp>
#include <XalanDOM/XalanDOMException.hpp>
#include <DOMSupport/DOMServices.hpp>
#include <XMLSupport/XMLParserLiaison.hpp>
#include <XPath/ElementPrefixResolverProxy.hpp>
#include <XPath/XObject.hpp>
#include <XPath/XPath.hpp>
#include <XPath/XalanQNameByReference.hpp>
#include "Constants.hpp"
#include "ElemAttributeSet.hpp"
#include "ElemDecimalFormat.hpp"
#include "ElemTemplate.hpp"
#include "ElemTemplateElement.hpp"
#include "ElemVariable.hpp"
#include "ExtensionNSHandler.hpp"
#include "KeyTable.hpp"
#include "StylesheetConstructionContext.hpp"
#include "StylesheetExecutionContext.hpp"
#include "StylesheetRoot.hpp"
const Stylesheet::NamespaceVectorType Stylesheet::s_emptyNamespace;
const XalanDOMString Stylesheet::s_emptyString;
const XalanQNameByReference Stylesheet::s_emptyQName;
const XalanEmptyNamedNodeMap Stylesheet::s_fakeAttributes;
Stylesheet::Stylesheet(
StylesheetRoot& root,
const XalanDOMString& baseIdentifier,
StylesheetConstructionContext& constructionContext) :
XalanDocument(),
PrefixResolver(),
m_stylesheetRoot(root),
m_baseIdent(baseIdentifier),
m_keyDeclarations(),
m_XSLTNamespaceURI(constructionContext.getXSLTNamespaceURI()),
m_whitespacePreservingElements(),
m_whitespaceStrippingElements(),
m_imports(),
m_importsSize(0),
m_namespaces(),
m_namespaceDecls(),
m_isWrapperless(false),
m_wrapperlessTemplate(0),
m_extensionNamespaces(),
m_firstTemplate(0),
m_includeStack(),
m_namedTemplates(),
m_topLevelVariables(),
m_XSLTVerDeclared(1.0L),
m_isRoot(&root == this ? true: false),
m_patternTable(),
m_patternTableEnd(m_patternTable.end()),
m_textPatternList(),
m_commentPatternList(),
m_rootPatternList(),
m_piPatternList(),
m_nodePatternList(),
m_anyPatternList(),
m_matchPattern2Container(),
m_patternCount(0),
m_attributeSets(),
m_attributeSetsSize(0),
m_surrogateChildren(*this),
m_elemDecimalFormats(),
m_prefixAliases(),
m_namespacesHandler()
{
if (length(m_baseIdent) == 0)
{
m_includeStack.push_back(m_baseIdent);
}
else
{
try
{
const XalanDOMString urlString = constructionContext.getURLStringFromString(m_baseIdent);
if (length(urlString) != 0)
{
m_includeStack.push_back(urlString);
m_baseIdent = urlString;
}
}
catch(const XMLException&)
{
// Assume that any exception here relates to get the urlString from
// m_baseIdent. We'll assume that it's just a fake base identifier
// since the parser will throw the real error if the base identifier
// can't be resolved.
m_includeStack.push_back(baseIdentifier);
}
}
}
Stylesheet::~Stylesheet()
{
#if !defined(XALAN_NO_NAMESPACES)
using std::for_each;
#endif
// Clean up all entries in the imports vector.
for_each(m_imports.begin(),
m_imports.end(),
DeleteFunctor<Stylesheet>());
// Clean up the atribute sets vector
for_each(m_attributeSets.begin(),
m_attributeSets.end(),
DeleteFunctor<ElemAttributeSet>());
// Clean up the top-level variables vector
for_each(m_topLevelVariables.begin(),
m_topLevelVariables.end(),
DeleteFunctor<ElemVariable>());
// Clean up the decimal formats vector
for_each(m_elemDecimalFormats.begin(),
m_elemDecimalFormats.end(),
DeleteFunctor<ElemDecimalFormat>());
// Clean up the extension namespaces vector
for_each(m_extensionNamespaces.begin(),
m_extensionNamespaces.end(),
MapValueDeleteFunctor<ExtensionNamespacesMapType>());
delete m_wrapperlessTemplate;
delete m_firstTemplate;
}
void
Stylesheet::processKeyElement(
ElemTemplateElement* nsContext,
const AttributeList& atts,
StylesheetConstructionContext& constructionContext)
{
const XalanDOMChar* nameAttr = 0;
XPath* matchAttr = 0;
XPath* useAttr = 0;
const unsigned int nAttrs = atts.getLength();
for(unsigned int i = 0; i < nAttrs; i++)
{
const XalanDOMChar* const aname = atts.getName(i);
if (equals(aname, Constants::ATTRNAME_NAME))
{
nameAttr = atts.getValue(i);
}
else if(equals(aname, Constants::ATTRNAME_MATCH))
{
matchAttr =
constructionContext.createMatchPattern(
0,
XalanDOMString(atts.getValue(i)),
*nsContext);
}
else if(equals(aname, Constants::ATTRNAME_USE))
{
useAttr =
constructionContext.createXPath(
0,
atts.getValue(i),
*nsContext);
}
else if (isAttrOK(aname, atts, i, constructionContext) == false)
{
constructionContext.error(
TranscodeFromLocalCodePage("xsl:key, unrecognized keyword '") +
aname +
TranscodeFromLocalCodePage("'!"));
}
}
if(0 == nameAttr)
constructionContext.error(TranscodeFromLocalCodePage("xsl:key requires a ") + Constants::ATTRNAME_NAME + " attribute!");
if(0 == matchAttr)
constructionContext.error(TranscodeFromLocalCodePage("xsl:key requires a ") + Constants::ATTRNAME_MATCH + " attribute!");
if(0 == useAttr)
constructionContext.error(TranscodeFromLocalCodePage("xsl:key requires a ") + Constants::ATTRNAME_USE + " attribute!");
m_keyDeclarations.push_back(KeyDeclaration(XalanDOMString(nameAttr), *matchAttr, *useAttr));
}
void
Stylesheet::pushNamespaces(const AttributeList& atts)
{
const unsigned int nAttrs = atts.getLength();
NamespaceVectorType namespaces;
for(unsigned int i = 0; i < nAttrs; i++)
{
const XalanDOMChar* const aname = atts.getName(i);
const XalanDOMChar* const value = atts.getValue(i);
const bool isPrefix = startsWith(aname, DOMServices::s_XMLNamespaceWithSeparator);
if (equals(aname, DOMServices::s_XMLNamespace) || isPrefix)
{
const XalanDOMString p = isPrefix ? substring(aname, 6) : XalanDOMString();
namespaces.push_back(NameSpace(p, XalanDOMString(value)));
}
}
m_namespaces.push_back(namespaces);
}
class attrSetCompare
{
public:
attrSetCompare(const ElemAttributeSet& theAttrSet) :
m_attrSet(theAttrSet)
{
}
bool
operator()(const ElemAttributeSet* theRHS) const
{
assert(theRHS != 0);
return m_attrSet == *theRHS;
}
private:
const ElemAttributeSet& m_attrSet;
};
void
Stylesheet::postConstruction(StylesheetConstructionContext& constructionContext)
{
{
m_importsSize = m_imports.size();
// Call postConstruction() on any imported stylesheets, the get any aliases
// in reverse order, to preserve import precedence. Also, get any key declarations.
const StylesheetVectorType::reverse_iterator theEnd = m_imports.rend();
StylesheetVectorType::reverse_iterator i = m_imports.rbegin();
while(i != theEnd)
{
(*i)->postConstruction(constructionContext);
m_namespacesHandler.copyNamespaceAliases((*i)->getNamespacesHandler());
// $$ ToDo: Should we clear the imported stylesheet's key
// declarations after we copy them?
m_keyDeclarations.insert(
m_keyDeclarations.end(),
(*i)->m_keyDeclarations.begin(),
(*i)->m_keyDeclarations.end());
++i;
}
}
// Call postConstruction() on our own namespaces handler...
m_namespacesHandler.postConstruction();
{
for (ElemTemplateElement* node = m_firstTemplate;
node != 0;
node = node->getNextSiblingElem())
{
node->postConstruction(constructionContext, m_namespacesHandler);
}
}
{
for (ElemVariableVectorType::iterator it = m_topLevelVariables.begin();
it != m_topLevelVariables.end();
++it)
{
(*it)->postConstruction(constructionContext, m_namespacesHandler);
}
}
{
for (ElemTemplateElement* node = m_wrapperlessTemplate;
node != 0;
node = node->getNextSiblingElem())
{
node->postConstruction(constructionContext, m_namespacesHandler);
}
}
{
#if !defined(XALAN_NO_NAMESPACES)
using std::find_if;
#endif
for (AttributeSetVectorType::size_type i = 0; i < m_attributeSets.size(); ++i)
{
ElemAttributeSet* const theCurrent = m_attributeSets[i];
assert(theCurrent != 0);
for(;;)
{
// Look for duplicate sets...
const AttributeSetVectorType::iterator theResult =
find_if(
m_attributeSets.begin() + (i + 1),
m_attributeSets.end(),
attrSetCompare(*theCurrent));
// Did we find it?
if(theResult == m_attributeSets.end())
{
break;
}
else
{
theCurrent->adopt(**theResult);
delete *theResult;
m_attributeSets.erase(theResult);
}
}
theCurrent->postConstruction(constructionContext, m_namespacesHandler);
}
// Now that we're done with removing duplicates, cache the size...
m_attributeSetsSize = m_attributeSets.size();
}
// OK, now we need to add everything template that matches "node()"
// to the end of the text, comment, and PI template lists.
PatternTableListType::iterator theBegin = m_nodePatternList.begin();
PatternTableListType::iterator theEnd = m_nodePatternList.end();
if (theBegin != theEnd)
{
m_textPatternList.insert(
m_textPatternList.end(),
theBegin,
theEnd);
m_commentPatternList.insert(
m_commentPatternList.end(),
theBegin,
theEnd);
m_piPatternList.insert(
m_piPatternList.end(),
theBegin,
theEnd);
}
m_nodePatternList.insert(
theEnd,
m_anyPatternList.begin(),
m_anyPatternList.end());
theBegin = m_nodePatternList.begin();
theEnd = m_nodePatternList.end();
if (theBegin != theEnd)
{
PatternTableMapType::iterator i =
m_patternTable.begin();
while(i != m_patternTable.end())
{
PatternTableListType& theTable = (*i).second;
theTable.insert(
theTable.end(),
theBegin,
theEnd);
++i;
}
}
m_patternCount = m_matchPattern2Container.size();
}
bool
Stylesheet::isAttrOK(
const XalanDOMChar* attrName,
const AttributeList& /* atts */,
int /* which */,
StylesheetConstructionContext& constructionContext) const
{
// Namespace declarations are OK by definition
bool attrOK = equals(attrName, DOMServices::s_XMLNamespace) ||
startsWith(attrName, DOMServices::s_XMLNamespaceWithSeparator);
if(!attrOK)
{
// Others are OK if their prefix has been
// bound to a non-null Namespace URI other than XSLT's
const unsigned int indexOfNSSep = indexOf(attrName, XalanUnicode::charColon);
if(indexOfNSSep < length(attrName))
{
const XalanDOMString prefix = substring(attrName, 0, indexOfNSSep);
const XalanDOMString* ns = getNamespaceForPrefixFromStack(prefix);
attrOK = ns != 0 && !::isEmpty(*ns) && !equals(*ns, constructionContext.getXSLTNamespaceURI());
}
else
{
attrOK = false;
}
}
return attrOK;
}
const XalanDOMString*
Stylesheet::getNamespaceFromStack(const XalanDOMChar* nodeName) const
{
assert(nodeName != 0);
const unsigned int indexOfNSSep = indexOf(nodeName, XalanUnicode::charColon);
const XalanDOMString prefix =
indexOfNSSep < length(nodeName) ?
substring(nodeName, 0, indexOfNSSep) :
XalanDOMString();
return getNamespaceForPrefixFromStack(prefix);
}
bool
Stylesheet::getYesOrNo(
const XalanDOMChar* aname,
const XalanDOMChar* val,
StylesheetConstructionContext& constructionContext) const
{
if(equals(val, Constants::ATTRVAL_YES))
{
return true;
}
else if(equals(val, Constants::ATTRVAL_NO))
{
return false;
}
else
{
constructionContext.error(XalanDOMString(val) + " is unknown value for " + aname);
return false;
}
}
void
Stylesheet::addTemplate(
ElemTemplate* theTemplate,
StylesheetConstructionContext& constructionContext)
{
assert(theTemplate != 0);
unsigned int pos = 0;
if(0 == m_firstTemplate)
{
m_firstTemplate = theTemplate;
}
else
{
ElemTemplateElement* next = m_firstTemplate;
// Find the last one, then append the new one.
while(0 != next)
{
if(0 == next->getNextSiblingElem())
{
next->setNextSiblingElem(theTemplate);
theTemplate->setNextSiblingElem(0); // just to play it safe.
theTemplate->setPreviousSiblingElem(next);
break;
}
pos++;
next = next->getNextSiblingElem();
}
}
// If it's a named template, then we need to
// and it to the map of named templates.
const XalanQName& theName = theTemplate->getName();
if(theName.isEmpty() == false)
{
if (m_namedTemplates.find(theName) == m_namedTemplates.end())
{
m_namedTemplates[theName] = theTemplate;
}
else
{
// This is an error...
XalanDOMString theMessage(TranscodeFromLocalCodePage("The stylesheet already has a template with the name "));
const XalanDOMString& theNamespace = theName.getNamespace();
if (length(theNamespace) != 0)
{
theMessage += theNamespace;
theMessage += DOMServices::s_XMLNamespaceSeparatorString;
}
theMessage += theName.getLocalPart();
constructionContext.error(theMessage, 0, theTemplate);
}
}
// Now, process the match pattern associated with the
// template.
const XPath* const xp = theTemplate->getMatchPattern();
if(0 != xp)
{
/* Each string has a list of pattern tables associated with it; if the
* string is not in the map, then create a list of pattern tables with one
* entry for the string, otherwise add to the existing pattern table list
* for that string
*/
typedef XPath::TargetElementStringsVectorType TargetElementStringsVectorType;
TargetElementStringsVectorType strings;
xp->getTargetElementStrings(strings);
TargetElementStringsVectorType::size_type nTargets =
strings.size();
if(nTargets != 0)
{
for(TargetElementStringsVectorType::size_type stringIndex = 0;
stringIndex < nTargets; stringIndex++)
{
const XalanDOMString& target = strings[stringIndex];
m_matchPattern2Container.push_back(
MatchPattern2(
*theTemplate,
pos,
target,
*xp,
xp->getExpression().getCurrentPattern()));
const MatchPattern2* const newMatchPat =
&m_matchPattern2Container.back();
// Always put things on the front of the list, so
// templates later in the stylesheet are always
// selected first.
if (equals(target, XPath::PSEUDONAME_TEXT) == true)
{
m_textPatternList.insert(
m_textPatternList.begin(),
newMatchPat);
}
else if (equals(target, XPath::PSEUDONAME_COMMENT) == true)
{
m_commentPatternList.insert(
m_commentPatternList.begin(),
newMatchPat);
}
else if (equals(target, XPath::PSEUDONAME_ROOT) == true)
{
m_rootPatternList.insert(
m_rootPatternList.begin(),
newMatchPat);
}
else if (equals(target, XPath::PSEUDONAME_PI) == true)
{
m_piPatternList.insert(
m_piPatternList.begin(),
newMatchPat);
}
else if (equals(target, XPath::PSEUDONAME_NODE) == true)
{
m_nodePatternList.insert(
m_nodePatternList.begin(),
newMatchPat);
}
else if (equals(target, XPath::PSEUDONAME_ANY) == true)
{
m_anyPatternList.insert(
m_anyPatternList.begin(),
newMatchPat);
}
else
{
// Put it in the map.
PatternTableListType& theTable =
m_patternTable[target];
theTable.insert(
theTable.begin(),
newMatchPat);
}
}
}
}
}
const ElemTemplate*
Stylesheet::findNamedTemplate(const XalanQName& qname) const
{
ElemTemplateMapType::const_iterator it = m_namedTemplates.find(qname);
if(it != m_namedTemplates.end())
{
return (*it).second;
}
else
{
const ElemTemplate* namedTemplate = 0;
// Look for the template in the imports
const StylesheetVectorType::size_type nImports = m_imports.size();
for(StylesheetVectorType::size_type i = 0; i < nImports; ++i)
{
const Stylesheet* const stylesheet = m_imports[i];
namedTemplate = stylesheet->findNamedTemplate(qname);
if(0 != namedTemplate)
break;
}
return namedTemplate;
}
}
void
Stylesheet::addObjectIfNotFound(
const MatchPattern2* thePattern,
PatternTableVectorType& theVector)
{
#if !defined(XALAN_NO_NAMESPACES)
using std::find;
#endif
const PatternTableVectorType::const_iterator theResult =
find(
theVector.begin(),
theVector.end(),
thePattern);
// Did we find it?
if(theResult == theVector.end())
{
theVector.push_back(thePattern);
}
}
inline void
Stylesheet::addObjectIfNotFound(
const MatchPattern2* thePattern,
const MatchPattern2* thePatternArray[],
unsigned int& thePatternArraySize)
{
if (thePatternArraySize == 0)
{
thePatternArray[0] = thePattern;
++thePatternArraySize;
}
else
{
unsigned int i = 0;
while(i < thePatternArraySize)
{
if (thePatternArray[i] != thePattern)
{
++i;
}
else
{
break;
}
}
if (i == thePatternArraySize)
{
thePatternArray[thePatternArraySize++] = thePattern;
}
}
}
inline const Stylesheet::PatternTableListType*
Stylesheet::locateMatchPatternList2(const XalanDOMString& theName) const
{
assert(m_patternTableEnd == m_patternTable.end());
const PatternTableMapType::const_iterator i =
m_patternTable.find(theName);
if (i != m_patternTableEnd)
{
return &(*i).second;
}
else
{
return &m_nodePatternList;
}
}
inline const Stylesheet::PatternTableListType*
Stylesheet::locateMatchPatternList2(const XalanNode& theNode) const
{
switch(theNode.getNodeType())
{
case XalanNode::ELEMENT_NODE:
return locateMatchPatternList2(DOMServices::getLocalNameOfNode(theNode));
break;
case XalanNode::PROCESSING_INSTRUCTION_NODE:
return &m_piPatternList;
break;
case XalanNode::ATTRIBUTE_NODE:
return locateMatchPatternList2(DOMServices::getLocalNameOfNode(theNode));
break;
case XalanNode::CDATA_SECTION_NODE:
case XalanNode::TEXT_NODE:
return &m_textPatternList;
break;
case XalanNode::COMMENT_NODE:
return &m_commentPatternList;
break;
case XalanNode::DOCUMENT_NODE:
return &m_rootPatternList;
break;
case XalanNode::DOCUMENT_FRAGMENT_NODE:
return &m_anyPatternList;
break;
}
return locateMatchPatternList2(theNode.getNodeName());
}
const ElemTemplate*
Stylesheet::findTemplate(
StylesheetExecutionContext& executionContext,
XalanNode* targetNode,
const XalanQName& mode,
bool onlyUseImports) const
{
assert(targetNode != 0);
assert(m_patternCount == m_matchPattern2Container.size());
if(m_isWrapperless == true)
{
return m_wrapperlessTemplate;
}
else
{
const ElemTemplate* bestMatchedRule = 0;
const MatchPattern2* bestMatchedPattern = 0; // Syncs with bestMatchedRule
const double matchScoreNoneValue =
XPath::getMatchScoreValue(XPath::eMatchScoreNone);
double bestMatchPatPriority = matchScoreNoneValue;
unsigned int nConflicts = 0;
// Use a stack-based array when possible...
const MatchPattern2* conflictsArray[100];
XalanArrayAutoPtr<const MatchPattern2*> conflictsVector;
const MatchPattern2** conflicts = 0;
if(onlyUseImports == false)
{
// Points to the current list of match patterns. Note
// that this may point to more than one table.
const PatternTableListType* matchPatternList =
locateMatchPatternList2(*targetNode);
assert(matchPatternList != 0);
PatternTableListType::const_iterator theCurrentEntry =
matchPatternList->begin();
const PatternTableListType::const_iterator theTableEnd =
matchPatternList->end();
if (theCurrentEntry != theTableEnd)
{
const XalanDOMString* prevPat = 0;
const MatchPattern2* prevMatchPat = 0;
double prevMatchPatPriority = matchScoreNoneValue;
do
{
const MatchPattern2* matchPat = *theCurrentEntry;
double matchPatPriority = matchScoreNoneValue;
assert(matchPat != 0);
const ElemTemplate* const rule = matchPat->getTemplate();
assert(rule != 0);
// We'll be needing to match rules according to what
// mode we're in.
const XalanQName& ruleMode = rule->getMode();
// 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.
const bool haveMode = !mode.isEmpty();
const bool haveRuleMode = !ruleMode.isEmpty();
if ((!haveMode && !haveRuleMode) ||
(haveMode && haveRuleMode && ruleMode.equals(mode)))
{
const XalanDOMString* patterns = matchPat->getPattern();
assert(patterns != 0);
if(!isEmpty(*patterns) &&
!(prevMatchPat != 0 &&
(prevPat != 0 && equals(*prevPat, *patterns)) &&
prevMatchPat->getTemplate()->getPriority() == matchPat->getTemplate()->getPriority()))
{
prevPat = patterns;
prevMatchPat = matchPat;
prevMatchPatPriority = matchPatPriority;
matchPatPriority = matchScoreNoneValue;
const XPath* const xpath = matchPat->getExpression();
XPath::eMatchScore score =
xpath->getMatchScore(targetNode, *this, executionContext);
if(XPath::eMatchScoreNone != score)
{
const double priorityVal = rule->getPriority();
const double priorityOfRule
= (matchScoreNoneValue != priorityVal)
? priorityVal : XPath::getMatchScoreValue(score);
matchPatPriority = priorityOfRule;
const double priorityOfBestMatched =
(0 != bestMatchedPattern) ?
bestMatchPatPriority :
matchScoreNoneValue;
if(priorityOfRule > priorityOfBestMatched)
{
nConflicts = 0;
bestMatchedRule = rule;
bestMatchedPattern = matchPat;
bestMatchPatPriority = matchPatPriority;
}
else if(priorityOfRule == priorityOfBestMatched)
{
if (conflicts == 0)
{
if (m_patternCount > sizeof(conflictsArray) / sizeof(conflictsArray[0]))
{
conflictsVector.reset(new const MatchPattern2*[m_patternCount]);
conflicts = conflictsVector.get();
}
else
{
conflicts = conflictsArray;
}
}
assert(conflicts != 0);
// Add the best matched pattern so far.
addObjectIfNotFound(bestMatchedPattern, conflicts, nConflicts);
// Add the pattern that caused the conflict...
conflicts[nConflicts++] = matchPat;
bestMatchedRule = rule;
bestMatchedPattern = matchPat;
bestMatchPatPriority = matchPatPriority;
}
}
}
}
++theCurrentEntry;
} while(theCurrentEntry != theTableEnd);
}
} // end if(useImports == false)
if(0 == bestMatchedRule)
{
assert(m_importsSize == m_imports.size());
for(unsigned int i = 0; i < m_importsSize; i++)
{
const Stylesheet* const stylesheet =
m_imports[i];
bestMatchedRule = stylesheet->findTemplate(executionContext,
targetNode,
mode,
false);
if(0 != bestMatchedRule)
break;
}
}
if(nConflicts > 0)
{
assert(conflicts != 0);
const bool quietConflictWarnings = executionContext.getQuietConflictWarnings();
XalanDOMString conflictsString;
if (quietConflictWarnings == false)
{
conflictsString = XALAN_STATIC_UCODE_STRING("Specificity conflicts found: ");
}
for(unsigned int i = 0; i < nConflicts; i++)
{
const MatchPattern2* const conflictPat = conflicts[i];
if(0 != i)
{
if(quietConflictWarnings == false)
{
conflictsString += XALAN_STATIC_UCODE_STRING(", ");
}
// Find the furthest one towards the bottom of the document.
if(conflictPat->getPositionInStylesheet() >
bestMatchedPattern->getPositionInStylesheet())
{
bestMatchedPattern = conflictPat;
}
}
else
{
bestMatchedPattern = conflictPat;
}
if(quietConflictWarnings == false)
{
conflictsString += XalanDOMString(XALAN_STATIC_UCODE_STRING("\"")) +
*conflictPat->getPattern() +
XalanDOMString(XALAN_STATIC_UCODE_STRING("\""));
}
}
bestMatchedRule = bestMatchedPattern->getTemplate();
if(quietConflictWarnings == false)
{
conflictsString += XALAN_STATIC_UCODE_STRING(" ");
conflictsString += XALAN_STATIC_UCODE_STRING("Last found in stylesheet will be used.");
executionContext.warn(conflictsString);
}
}
return bestMatchedRule;
}
}
void
Stylesheet::addExtensionNamespace(
const XalanDOMString& uri,
ExtensionNSHandler* nsh)
{
m_extensionNamespaces.insert(ExtensionNamespacesMapType::value_type(uri, nsh));
m_namespacesHandler.addExtensionNamespaceURI(uri);
}
void
Stylesheet::pushTopLevelVariables(
StylesheetExecutionContext& executionContext,
const ParamVectorType& topLevelParams) const
{
{
// First, push any imports...
const StylesheetVectorType::const_reverse_iterator rend = m_imports.rend();
for(StylesheetVectorType::const_reverse_iterator i = m_imports.rbegin(); i != rend; ++i)
{
const Stylesheet* const stylesheet = *i;
assert(stylesheet != 0);
stylesheet->pushTopLevelVariables(executionContext, topLevelParams);
}
}
const ParamVectorType::size_type nVars = m_topLevelVariables.size();
for(ParamVectorType::size_type i = 0; i < nVars; ++i)
{
ElemVariable* const var = m_topLevelVariables[i];
bool isParam =
Constants::ELEMNAME_PARAMVARIABLE == var->getXSLToken();
if(isParam == true)
{
isParam = false;
const ParamVectorType::size_type n = topLevelParams.size();
for(ParamVectorType::size_type k = 0; k < n; k++)
{
const ParamVectorType::value_type& arg = topLevelParams[k];
if(arg.getName().equals(var->getName()))
{
isParam = true;
if (arg.getXObject().null() == false)
{
executionContext.pushVariable(
arg.getName(),
arg.getXObject(),
0);
}
else
{
executionContext.pushVariable(
arg.getName(),
0,
arg.getExpression(),
executionContext.getRootDocument(),
*this);
}
break;
}
}
}
if (isParam == false)
{
executionContext.pushVariable(var->getName(),
var,
var->getParentNodeElem());
}
}
}
void
Stylesheet::processNSAliasElement(
const XalanDOMChar* name,
const AttributeList& atts,
StylesheetConstructionContext& constructionContext)
{
const unsigned int nAttrs = atts.getLength();
const XalanDOMString* stylesheetNamespace = &DOMServices::s_emptyString;
const XalanDOMString* resultNamespace = &DOMServices::s_emptyString;
const XalanDOMString dummy;
for(unsigned int i = 0; i < nAttrs; i++)
{
const XalanDOMChar* const aname = atts.getName(i);
if(equals(aname, Constants::ATTRNAME_STYLESHEET_PREFIX) == true)
{
const XalanDOMChar* const value = atts.getValue(i);
if (equals(value, Constants::ATTRVAL_DEFAULT_PREFIX) == true)
{
stylesheetNamespace = getNamespaceForPrefix(dummy);
}
else
{
stylesheetNamespace = getNamespaceForPrefix(XalanDOMString(value));
}
}
else if(equals(aname, Constants::ATTRNAME_RESULT_PREFIX))
{
const XalanDOMChar* const value = atts.getValue(i);
if (equals(value, Constants::ATTRVAL_DEFAULT_PREFIX) == true)
{
resultNamespace = getNamespaceForPrefix(dummy);
}
else
{
resultNamespace = getNamespaceForPrefix(XalanDOMString(value));
}
}
else if(!isAttrOK(aname, atts, i, constructionContext))
{
constructionContext.error(XalanDOMString(name) + " has an illegal attribute: " + aname);
}
}
// Build a table of aliases, the key is the stylesheet uri and the
// value is the result uri
if (length(*stylesheetNamespace) == 0 ||
length(*resultNamespace) == 0)
{
constructionContext.error("Missing namespace URI for specified prefix");
}
else
{
#if 1
// $$$ ToDo: Enable other code. Perhaps an error?
m_prefixAliases[*stylesheetNamespace] = *resultNamespace;
m_namespacesHandler.setNamespaceAlias(*stylesheetNamespace, *resultNamespace);
#else
const PrefixAliasesMapType::iterator i =
m_prefixAliases.find(*stylesheetNamespace);
if (i != m_prefixAliases.end())
{
// $$$ ToDo: This could also be an error?
(*i).second = *resultNamespace;
}
else
{
m_prefixAliases.insert(PrefixAliasesMapType::value_type(*stylesheetNamespace, *resultNamespace));
}
#endif
}
}
XalanDOMString
Stylesheet::getAliasNamespaceURI(const XalanDOMChar* uri) const
{
assert(uri != 0);
return getAliasNamespaceURI(XalanDOMString(uri));
}
XalanDOMString
Stylesheet::getAliasNamespaceURI(const XalanDOMString& uri) const
{
const StringToStringMapType::const_iterator i =
m_prefixAliases.find(uri);
if (i != m_prefixAliases.end())
{
assert(length((*i).second) > 0);
return (*i).second;
}
else
{
XalanDOMString theResult;
const StylesheetVectorType::size_type nImports =
m_imports.size();
for(StylesheetVectorType::size_type i = 0; i < nImports; ++i)
{
theResult = m_imports[i]->getAliasNamespaceURI(uri);
if(length(theResult) != 0)
{
break;
}
}
return theResult;
}
}
const XalanDecimalFormatSymbols*
Stylesheet::getDecimalFormatSymbols(const XalanDOMString& name) const
{
const XalanDecimalFormatSymbols* dfs = 0;
const ElemDecimalFormatVectorType::size_type theSize =
m_elemDecimalFormats.size();
if(theSize > 0)
{
// Start from the top of the stack
for (int i = theSize - 1; i >= 0; --i)
{
assert(m_elemDecimalFormats[i] != 0);
if (equals(m_elemDecimalFormats[i]->getName(), name) == true)
{
dfs = &m_elemDecimalFormats[i]->getDecimalFormatSymbols();
break;
}
}
}
// If dfs is null at this point, it should
// mean there wasn't an xsl:decimal-format declared
// with the given name. So go up the import hierarchy
// and see if one of the imported stylesheets declared
// it.
if(dfs == 0)
{
for(StylesheetVectorType::size_type i = 0; i < m_importsSize; ++i)
{
dfs = m_imports[i]->getDecimalFormatSymbols(name);
if(dfs != 0)
{
break;
}
}
}
return dfs;
}
void
Stylesheet::applyAttrSets(
const QNameVectorType& attributeSetsNames,
StylesheetExecutionContext& executionContext,
XalanNode* sourceNode) const
{
const QNameVectorType::size_type nNames = attributeSetsNames.size();
if(0 != nNames)
{
assert(m_importsSize == m_imports.size());
// Process up the import chain...
for(StylesheetVectorType::size_type i = 0; i < m_importsSize; i++)
{
const Stylesheet* const stylesheet = m_imports[i];
stylesheet->applyAttrSets(
attributeSetsNames,
executionContext,
sourceNode);
}
for(QNameVectorType::size_type j = 0; j < nNames; j++)
{
const XalanQName& qname = attributeSetsNames[j];
assert(m_attributeSetsSize == m_attributeSets.size());
for(StylesheetVectorType::size_type k = 0; k < m_attributeSetsSize; k++)
{
const ElemAttributeSet* const attrSet = m_attributeSets[k];
assert(attrSet != 0);
if(qname.equals(attrSet->getQName()))
{
attrSet->execute(executionContext);
}
}
}
}
}
const XalanDOMString&
Stylesheet::getNodeName() const
{
return s_emptyString;
}
const XalanDOMString&
Stylesheet::getNodeValue() const
{
return s_emptyString;
}
Stylesheet::NodeType
Stylesheet::getNodeType() const
{
return XalanNode::DOCUMENT_NODE;
}
XalanNode*
Stylesheet::getParentNode() const
{
return 0;
}
const XalanNodeList*
Stylesheet::getChildNodes() const
{
return &m_surrogateChildren;
}
XalanNode*
Stylesheet::getFirstChild() const
{
return 0;
}
XalanNode*
Stylesheet::getLastChild() const
{
return 0;
}
XalanNode*
Stylesheet::getPreviousSibling() const
{
return 0;
}
XalanNode*
Stylesheet::getNextSibling() const
{
return 0;
}
const XalanNamedNodeMap*
Stylesheet::getAttributes() const
{
return &s_fakeAttributes;
}
XalanDocument*
Stylesheet::getOwnerDocument() const
{
return 0;
}
#if defined(XALAN_NO_COVARIANT_RETURN_TYPE)
XalanNode*
#else
Stylesheet*
#endif
Stylesheet::cloneNode(bool /* deep */) const
{
//should not be called
assert(false);
return 0;
}
XalanNode*
Stylesheet::insertBefore(
XalanNode* /* newChild */,
XalanNode* /* refChild */)
{
//should not be called
assert(false);
return 0;
}
XalanNode*
Stylesheet::replaceChild(
XalanNode* /* newChild */,
XalanNode* /* oldChild */)
{
//should not be called
assert(false);
return 0;
}
XalanNode*
Stylesheet::removeChild(XalanNode* /* oldChild */)
{
//should not be called
assert(false);
return 0;
}
XalanNode*
Stylesheet::appendChild(XalanNode* /* oldChild */)
{
//should not be called
assert(false);
return 0;
}
bool
Stylesheet::hasChildNodes() const
{
// $$$ ToDo: Is this always true?
return true;
}
void
Stylesheet::setNodeValue(const XalanDOMString& /* nodeValue */)
{
throw XalanDOMException(XalanDOMException::NO_MODIFICATION_ALLOWED_ERR);
}
void
Stylesheet::normalize()
{
}
bool
Stylesheet::supports(
const XalanDOMString& /* feature */,
const XalanDOMString& /* version */) const
{
return false;
}
const XalanDOMString&
Stylesheet::getNamespaceURI() const
{
// $$ ToDo: Is this the same value as PrefixResolver::getURI()?
return s_emptyString;
}
const XalanDOMString&
Stylesheet::getPrefix() const
{
return s_emptyString;
}
const XalanDOMString&
Stylesheet::getLocalName() const
{
return s_emptyString;
}
void
Stylesheet::setPrefix(const XalanDOMString& /* prefix */)
{
throw XalanDOMException(XalanDOMException::NO_MODIFICATION_ALLOWED_ERR);
}
unsigned long
Stylesheet::getIndex() const
{
return 0;
}
XalanElement*
Stylesheet::createElement(const XalanDOMString& /* tagName */)
{
//should not be called
assert(false);
return 0;
}
XalanDocumentFragment*
Stylesheet::createDocumentFragment()
{
//should not be called
assert(false);
return 0;
}
XalanText*
Stylesheet::createTextNode(const XalanDOMString& /* data */)
{
//should not be called
assert(false);
return 0;
}
XalanComment*
Stylesheet::createComment(const XalanDOMString& /* data */)
{
//should not be called
assert(false);
return 0;
}
XalanCDATASection*
Stylesheet::createCDATASection(const XalanDOMString& /* data */)
{
//should not be called
assert(false);
return 0;
}
XalanProcessingInstruction*
Stylesheet::createProcessingInstruction(
const XalanDOMString& /* target */,
const XalanDOMString& /* data */)
{
//should not be called
assert(false);
return 0;
}
XalanAttr*
Stylesheet::createAttribute(const XalanDOMString& /* name */)
{
//should not be called
assert(false);
return 0;
}
XalanEntityReference*
Stylesheet::createEntityReference(const XalanDOMString& /* name */)
{
//should not be called
assert(false);
return 0;
}
XalanDocumentType*
Stylesheet::getDoctype() const
{
//should not be called
assert(false);
return 0;
}
XalanDOMImplementation*
Stylesheet::getImplementation() const
{
//should not be called
assert(false);
return 0;
}
XalanElement*
Stylesheet::getDocumentElement() const
{
// $$$ ToDo: Is this correct?
return m_wrapperlessTemplate != 0 ? m_wrapperlessTemplate : m_firstTemplate;
}
XalanNodeList*
Stylesheet::getElementsByTagName(const XalanDOMString& /* name */) const
{
return 0;
}
XalanNodeList*
Stylesheet::getElementsByTagNameNS(
const XalanDOMString& /* namespaceURI */,
const XalanDOMString& /* localName */) const
{
return 0;
}
XalanNode*
Stylesheet::importNode(
XalanNode* /* importedNode */,
bool /* deep */)
{
//should not be called
assert(false);
return 0;
}
XalanElement*
Stylesheet::createElementNS(
const XalanDOMString& /* namespaceURI */,
const XalanDOMString& /* qualifiedName */)
{
//should not be called
assert(false);
return 0;
}
XalanAttr*
Stylesheet::createAttributeNS(
const XalanDOMString& /* namespaceURI */,
const XalanDOMString& /* qualifiedName */)
{
//should not be called
assert(false);
return 0;
}
XalanElement*
Stylesheet::getElementById(const XalanDOMString& /* elementId */) const
{
//should not be called
assert(false);
return 0;
}
bool
Stylesheet::isIndexed() const
{
// This member functionshould not be called
assert(false);
return false;
}
unsigned long
Stylesheet::getNumber() const
{
// This member functionshould not be called
assert(false);
return 0;
}
const XalanDOMString*
Stylesheet::getNamespaceForPrefix(const XalanDOMString& prefix) const
{
return XalanQName::getNamespaceForPrefix(m_namespaceDecls, prefix);
}
const XalanDOMString&
Stylesheet::getURI() const
{
return m_baseIdent;
}