blob: 6ca7678a4c6e230c8c7df2d8ad3f6a4c0652f825 [file] [log] [blame]
/*
* 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.
*/
#include "Stylesheet.hpp"
#include <algorithm>
#include <xercesc/sax/AttributeList.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xalanc/Include/STLHelper.hpp>
#include <xalanc/XalanDOM/XalanDOMException.hpp>
#include <xalanc/PlatformSupport/XalanMessageLoader.hpp>
#include <xalanc/DOMSupport/DOMServices.hpp>
#include <xalanc/XMLSupport/XMLParserLiaison.hpp>
#include <xalanc/XPath/XObject.hpp>
#include <xalanc/XPath/XPath.hpp>
#include <xalanc/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 "XalanMatchPatternData.hpp"
namespace XALAN_CPP_NAMESPACE {
const XalanDOMString Stylesheet::s_emptyString(XalanMemMgrs::getDummyMemMgr());
const Stylesheet::PatternTableVectorType Stylesheet::s_emptyTemplateList(XalanMemMgrs::getDummyMemMgr());
const XalanQNameByReference Stylesheet::s_emptyQName;
Stylesheet::Stylesheet(
StylesheetRoot& root,
const XalanDOMString& baseIdentifier,
StylesheetConstructionContext& constructionContext) :
PrefixResolver(),
m_stylesheetRoot(root),
m_baseIdent(baseIdentifier,constructionContext.getMemoryManager()),
m_keyDeclarations(constructionContext.getMemoryManager()),
m_whitespaceElements(constructionContext.getMemoryManager()),
m_XSLTNamespaceURI(constructionContext.getXSLTNamespaceURI(),constructionContext.getMemoryManager()),
m_imports(constructionContext.getMemoryManager()),
m_importsSize(0),
m_namespaces(constructionContext.getMemoryManager()),
m_namespaceDecls(constructionContext.getMemoryManager()),
m_isWrapperless(false),
m_extensionNamespaces(constructionContext.getMemoryManager()),
m_firstTemplate(0),
m_includeStack(constructionContext.getMemoryManager()),
m_namedTemplates(constructionContext.getMemoryManager()),
m_topLevelVariables(constructionContext.getMemoryManager()),
m_XSLTVerDeclared(1.0L),
m_elementPatternTable(constructionContext.getMemoryManager()),
m_elementPatternTableEnd(m_elementPatternTable.end()),
m_elementAnyPatternList(constructionContext.getMemoryManager()),
m_attributePatternTable(constructionContext.getMemoryManager()),
m_attributePatternTableEnd(m_attributePatternTable.end()),
m_attributeAnyPatternList(constructionContext.getMemoryManager()),
m_textPatternList(constructionContext.getMemoryManager()),
m_commentPatternList(constructionContext.getMemoryManager()),
m_rootPatternList(constructionContext.getMemoryManager()),
m_piPatternList(constructionContext.getMemoryManager()),
m_nodePatternList(constructionContext.getMemoryManager()),
m_patternCount(0),
m_elemDecimalFormats(constructionContext.getMemoryManager()),
m_namespacesHandler(constructionContext.getMemoryManager())
{
if (m_baseIdent.empty() == true)
{
m_includeStack.push_back(m_baseIdent);
}
else
{
try
{
const GetCachedString theGuard(constructionContext);
XalanDOMString& urlString = theGuard.get();
constructionContext.getURLStringFromString(m_baseIdent, urlString);
if (urlString.empty() == false)
{
m_includeStack.push_back(urlString);
m_baseIdent = urlString;
}
}
catch(const xercesc::XMLPlatformUtilsException&)
{
// 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::create(
MemoryManager& theManager,
StylesheetRoot& root,
const XalanDOMString& baseIdentifier,
StylesheetConstructionContext& constructionContext)
{
Stylesheet* theInstance;
return XalanConstruct(
theManager,
theInstance,
root,
baseIdentifier,
constructionContext);
}
Stylesheet::~Stylesheet()
{
using std::for_each;
// Clean up all entries in the imports vector.
for_each(
m_imports.begin(),
m_imports.end(),
DeleteFunctor<Stylesheet>(m_imports.getMemoryManager()));
// Clean up the decimal formats vector
for_each(
m_elemDecimalFormats.begin(),
m_elemDecimalFormats.end(),
DeleteFunctor<ElemDecimalFormat>(m_elemDecimalFormats.getMemoryManager()));
for_each(
m_extensionNamespaces.begin(),
m_extensionNamespaces.end(),
makeMapValueDeleteFunctor(m_extensionNamespaces));
}
void
Stylesheet::error(
StylesheetConstructionContext& theContext,
XalanMessages::Codes theErrorCode,
const Locator* theLocator,
const XalanDOMChar* theParam1,
const XalanDOMChar* theParam2,
const XalanDOMChar* theParam3) const
{
const GetCachedString theGuard(theContext);
theContext.problem(
StylesheetConstructionContext::eXSLTProcessor,
StylesheetConstructionContext::eError,
XalanMessageLoader::getMessage(
theGuard.get(),
theErrorCode,
theParam1,
theParam2,
theParam3),
theLocator,
0);
}
ElemTemplateElement*
Stylesheet::initWrapperless(
StylesheetConstructionContext& constructionContext,
const Locator* locator)
{
if (m_isWrapperless == true)
{
error(
constructionContext,
XalanMessages::StylesheetHasWrapperlessTemplate,
locator);
}
assert(m_firstTemplate == 0);
m_isWrapperless = true;
AttributeListImpl templateAttrs(constructionContext.getMemoryManager());
templateAttrs.addAttribute(Constants::ATTRNAME_NAME.c_str(),
Constants::ATTRTYPE_CDATA.c_str(),
Constants::ATTRVAL_SIMPLE.c_str());
ElemTemplateElement* const theNewTemplate =
constructionContext.createElement(
StylesheetConstructionContext::ELEMNAME_TEMPLATE,
*this,
templateAttrs,
locator);
theNewTemplate->addToStylesheet(constructionContext, *this);
assert(m_firstTemplate == theNewTemplate);
return theNewTemplate;
}
void
Stylesheet::processKeyElement(
const PrefixResolver& nsContext,
const AttributeListType& atts,
const Locator* locator,
StylesheetConstructionContext& constructionContext)
{
const XalanQName* theQName = 0;
XPath* matchAttr = 0;
XPath* useAttr = 0;
const XalanSize_t nAttrs = atts.getLength();
for(XalanSize_t i = 0; i < nAttrs; i++)
{
const XalanDOMChar* const aname = atts.getName(i);
if (equals(aname, Constants::ATTRNAME_NAME))
{
theQName = constructionContext.createXalanQName(atts.getValue(i), m_namespaces, locator);
// $$$TODO: It's not clear of this code will ever be executed, since
// constructing an invalid QName is not possibly right now.
assert(theQName->isValid() == true);
if (theQName->isValid() == false)
{
error(
constructionContext,
XalanMessages::AttributeValueNotValidQName_2Param,
locator,
Constants::ATTRNAME_NAME.c_str(),
atts.getValue(i));
}
}
else if(equals(aname, Constants::ATTRNAME_MATCH))
{
const GetCachedString theGuard(constructionContext);
XalanDOMString& theBuffer = theGuard.get();
theBuffer.assign(atts.getValue(i));
matchAttr =
constructionContext.createMatchPattern(
0,
theBuffer,
nsContext,
false,
false);
}
else if(equals(aname, Constants::ATTRNAME_USE))
{
useAttr =
constructionContext.createXPath(
0,
atts.getValue(i),
nsContext,
false,
false);
}
else if (isAttrOK(aname, atts, i, constructionContext) == false)
{
error(
constructionContext,
XalanMessages::ElementHasIllegalAttribute_2Param,
locator,
Constants::ELEMNAME_KEY_WITH_PREFIX_STRING.c_str(),
aname);
}
}
if (0 == theQName)
{
error(
constructionContext,
XalanMessages::ElementRequiresAttribute_2Param,
locator,
Constants::ELEMNAME_KEY_WITH_PREFIX_STRING.c_str(),
Constants::ATTRNAME_NAME.c_str());
}
if (0 == matchAttr)
{
error(
constructionContext,
XalanMessages::ElementRequiresAttribute_2Param,
locator,
Constants::ELEMNAME_KEY_WITH_PREFIX_STRING.c_str(),
Constants::ATTRNAME_MATCH.c_str());
}
if (0 == useAttr)
{
error(
constructionContext,
XalanMessages::ElementRequiresAttribute_2Param,
locator,
Constants::ELEMNAME_KEY_WITH_PREFIX_STRING.c_str(),
Constants::ATTRNAME_USE.c_str());
}
m_keyDeclarations.push_back(
KeyDeclaration(
*theQName,
*matchAttr,
*useAttr,
m_baseIdent,
XalanLocator::getLineNumber(locator),
XalanLocator::getColumnNumber(locator)));
}
void
Stylesheet::pushNamespaces(const AttributeListType& atts)
{
const XalanSize_t nAttrs = atts.getLength();
NamespaceVectorType namespaces(getMemoryManager());
XalanDOMString prefix(getMemoryManager());
for(XalanSize_t 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)
{
if (isPrefix == false)
{
prefix.clear();
}
else
{
substring(aname, prefix, DOMServices::s_XMLNamespaceWithSeparatorLength);
}
const NameSpace newNS(prefix, XalanDOMString(value, getMemoryManager()), getMemoryManager());
namespaces.push_back(newNS);
}
}
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;
};
static void
addToList(
Stylesheet::PatternTableVectorType& theList,
const XalanMatchPatternData* thePattern)
{
assert(thePattern != 0);
const double thePatternPriority = thePattern->getPriorityOrDefault();
const XalanSize_t thePatternPosition = thePattern->getPosition();
typedef Stylesheet::PatternTableVectorType PatternTableListType;
typedef PatternTableListType::iterator iterator;
iterator theCurrent = theList.begin();
const iterator theEnd = theList.end();
while (theCurrent != theEnd)
{
const double theCurrentPriority = (*theCurrent)->getPriorityOrDefault();
if (thePatternPriority > theCurrentPriority)
{
break;
}
else if (thePatternPriority == theCurrentPriority &&
thePatternPosition > (*theCurrent)->getPosition())
{
break;
}
++theCurrent;
}
theList.insert(theCurrent, thePattern);
}
static void
addToTable(
Stylesheet::PatternTableMapType& theTable,
const Stylesheet::PatternTableVectorType& theList)
{
typedef Stylesheet::PatternTableMapType PatternTableMapType;
typedef Stylesheet::PatternTableVectorType PatternTableListType;
PatternTableMapType::iterator theCurrentTable = theTable.begin();
const PatternTableMapType::iterator theTableEnd = theTable.end();
const PatternTableListType::const_iterator theListEnd = theList.end();
while(theCurrentTable != theTableEnd)
{
PatternTableListType::const_iterator theCurrent = theList.begin();
while(theCurrent != theListEnd)
{
addToList((*theCurrentTable).second, *theCurrent);
++theCurrent;
}
++theCurrentTable;
}
}
void
Stylesheet::addWhitespaceElement(const XalanSpaceNodeTester& theTester)
{
typedef WhitespaceElementsVectorType::iterator iterator;
const XPath::eMatchScore theMatchScore = theTester.getMatchScore();
iterator i = m_whitespaceElements.begin();
while(i != m_whitespaceElements.end())
{
if (theMatchScore >= (*i).getMatchScore())
{
break;
}
else
{
++i;
}
}
m_whitespaceElements.insert(i, theTester);
}
void
Stylesheet::postConstruction(StylesheetConstructionContext& constructionContext)
{
KeyDeclarationVectorType::size_type theKeyDeclarationsCount = 0;
WhitespaceElementsVectorType::size_type theWhitespaceElementsCount = 0;
{
m_importsSize = m_imports.size();
// Call postConstruction() on any imported stylesheets, in reverse order,
// so namespace aliases are processed properly. Also, get any key
// declarations and preserve/strip space information.
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());
theKeyDeclarationsCount += (*i)->m_keyDeclarations.size();
theWhitespaceElementsCount += (*i)->m_whitespaceElements.size();
++i;
}
}
{
// Call postConstruction() on any imported stylesheets, in import order,
// and process preserve/strip space information.
const StylesheetVectorType::iterator theEnd = m_imports.end();
StylesheetVectorType::iterator i = m_imports.begin();
m_keyDeclarations.reserve(
m_keyDeclarations.size() + theKeyDeclarationsCount);
m_whitespaceElements.reserve(
m_whitespaceElements.size() + theWhitespaceElementsCount);
while(i != theEnd)
{
m_keyDeclarations.insert(
m_keyDeclarations.end(),
(*i)->m_keyDeclarations.begin(),
(*i)->m_keyDeclarations.end());
{
KeyDeclarationVectorType temp(getMemoryManager());
temp.swap((*i)->m_keyDeclarations);
}
m_whitespaceElements.insert(
m_whitespaceElements.end(),
(*i)->m_whitespaceElements.begin(),
(*i)->m_whitespaceElements.end());
{
WhitespaceElementsVectorType temp(getMemoryManager());
temp.swap((*i)->m_whitespaceElements);
}
++i;
}
}
// Call postConstruction() on our own namespaces handler...
m_namespacesHandler.postConstruction(constructionContext);
{
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);
}
}
addToTable(m_elementPatternTable, m_elementAnyPatternList);
addToTable(m_attributePatternTable, m_attributeAnyPatternList);
}
bool
Stylesheet::isAttrOK(
const XalanDOMChar* attrName,
const AttributeListType& /* atts */,
XalanSize_t /* 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 XalanDOMString::size_type indexOfNSSep = indexOf(attrName, XalanUnicode::charColon);
if(indexOfNSSep < length(attrName))
{
const GetCachedString theGuard(constructionContext);
XalanDOMString& prefix = theGuard.get();
prefix.assign(attrName, indexOfNSSep);
const XalanDOMString* ns = getNamespaceForPrefixFromStack(prefix);
attrOK = ns != 0 && !ns->empty() && *ns != constructionContext.getXSLTNamespaceURI();
}
else if (m_XSLTVerDeclared > constructionContext.getXSLTVersionSupported())
{
attrOK = true;
}
}
return attrOK;
}
const XalanDOMString*
Stylesheet::getNamespaceFromStack(const XalanDOMChar* nodeName,
XalanDOMString& theBuffer) const
{
assert(nodeName != 0);
const XalanDOMString::size_type indexOfNSSep = indexOf(nodeName, XalanUnicode::charColon);
if (indexOfNSSep == length(nodeName))
{
return getNamespaceForPrefixFromStack(s_emptyString);
}
else
{
theBuffer.assign(nodeName, indexOfNSSep);
return getNamespaceForPrefixFromStack(theBuffer);
}
}
const XalanDOMString*
Stylesheet::getNamespaceForPrefix(
const XalanDOMString& prefix,
StylesheetConstructionContext& constructionContext) const
{
const XalanDOMString* const theURI = getNamespaceForPrefix(prefix);
if (theURI == 0)
{
error(
constructionContext,
XalanMessages::PrefixIsNotDeclared_1Param,
constructionContext.getLocatorFromStack(),
prefix.c_str());
}
return theURI;
}
const XalanDOMString*
Stylesheet::getNamespaceForPrefix(
const XalanDOMChar* prefix,
StylesheetConstructionContext& constructionContext) const
{
const GetCachedString theGuard(constructionContext);
XalanDOMString& theTemp = theGuard.get();
theTemp.assign(prefix);
return getNamespaceForPrefix(theTemp, constructionContext);
}
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
{
error(
constructionContext,
XalanMessages::AttributeMustBe_3Params,
constructionContext.getLocatorFromStack(),
aname,
Constants::ATTRVAL_YES.c_str(),
Constants::ATTRVAL_NO.c_str());
return false;
}
}
void
Stylesheet::addTemplate(
ElemTemplate* theTemplate,
StylesheetConstructionContext& constructionContext)
{
assert(theTemplate != 0);
if (m_isWrapperless == true)
{
if (m_firstTemplate != 0)
{
error(
constructionContext,
XalanMessages::StylesheetHasWrapperlessTemplate,
theTemplate->getLocator());
}
else
{
m_firstTemplate = theTemplate;
}
}
else 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;
}
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->getNameAttribute();
if (theName.isEmpty() == false)
{
if (m_namedTemplates.find(theName) == m_namedTemplates.end())
{
m_namedTemplates[theName] = theTemplate;
}
else
{
const GetCachedString theGuard(constructionContext);
error(
constructionContext,
XalanMessages::StylesheetHasDuplicateNamedTemplate_1Param,
theTemplate->getLocator(),
theName.format(theGuard.get()).c_str());
}
}
// 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::TargetDataVectorType TargetDataVectorType;
TargetDataVectorType data(constructionContext.getMemoryManager());
xp->getTargetData(data);
TargetDataVectorType::size_type nTargets =
data.size();
if (nTargets != 0)
{
const GetCachedString theGuard(constructionContext);
XalanDOMString& tempString = theGuard.get();
for (TargetDataVectorType::size_type i = 0; i < nTargets; ++i)
{
assert(data[i].getString() != 0);
tempString = data[i].getString();
const XalanMatchPatternData* const newMatchPat =
constructionContext.createXalanMatchPatternData(
*theTemplate,
m_patternCount,
tempString,
*xp,
xp->getExpression().getCurrentPattern(),
data[i].getDefaultPriority());
++m_patternCount;
// Always put things on the front of the list, so
// templates later in the stylesheet are always
// selected first.
if (equals(tempString, XPath::PSEUDONAME_TEXT) == true)
{
addToList(m_textPatternList, newMatchPat);
}
else if (equals(tempString, XPath::PSEUDONAME_COMMENT) == true)
{
addToList(m_commentPatternList, newMatchPat);
}
else if (equals(tempString, XPath::PSEUDONAME_ROOT) == true)
{
addToList(m_rootPatternList, newMatchPat);
}
else if (equals(tempString, XPath::PSEUDONAME_PI) == true)
{
addToList(m_piPatternList, newMatchPat);
}
else if (equals(tempString, XPath::PSEUDONAME_NODE) == true)
{
addToList(m_nodePatternList, newMatchPat);
addToList(m_elementAnyPatternList, newMatchPat);
addToList(m_attributeAnyPatternList, newMatchPat);
addToList(m_commentPatternList, newMatchPat);
addToList(m_textPatternList, newMatchPat);
addToList(m_piPatternList, newMatchPat);
}
else if (equals(tempString, XPath::PSEUDONAME_ANY) == true)
{
if (data[i].getTargetType() == XPath::TargetData::eElement)
{
addToList(m_elementAnyPatternList, newMatchPat);
}
else if (data[i].getTargetType() == XPath::TargetData::eAttribute)
{
addToList(m_attributeAnyPatternList, newMatchPat);
}
else if (data[i].getTargetType() == XPath::TargetData::eAny)
{
addToList(m_elementAnyPatternList, newMatchPat);
addToList(m_attributeAnyPatternList, newMatchPat);
}
}
else
{
if (data[i].getTargetType() == XPath::TargetData::eElement)
{
addToList(m_elementPatternTable[tempString], newMatchPat);
}
else if (data[i].getTargetType() == XPath::TargetData::eAttribute)
{
addToList(m_attributePatternTable[tempString], newMatchPat);
}
}
}
}
}
}
const ElemTemplate*
Stylesheet::findNamedTemplate(const XalanQName& qname) const
{
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 importsCount = m_imports.size();
for(StylesheetVectorType::size_type i = 0; i < importsCount; ++i)
{
const Stylesheet* const stylesheet = m_imports[i];
namedTemplate = stylesheet->findNamedTemplate(qname);
if(0 != namedTemplate)
break;
}
return namedTemplate;
}
}
void
Stylesheet::addObjectIfNotFound(
const XalanMatchPatternData* thePattern,
PatternTableVectorType& theVector)
{
using std::find;
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 XalanMatchPatternData* thePattern,
const XalanMatchPatternData* thePatternArray[],
XalanSize_t& thePatternArraySize)
{
assert(thePattern != 0 && thePatternArray != 0);
if (thePatternArraySize == 0)
{
thePatternArray[0] = thePattern;
++thePatternArraySize;
}
else
{
XalanSize_t i = 0;
while(i < thePatternArraySize)
{
if (thePatternArray[i] != thePattern)
{
++i;
}
else
{
break;
}
}
if (i == thePatternArraySize)
{
thePatternArray[thePatternArraySize++] = thePattern;
}
}
}
inline const Stylesheet::PatternTableVectorType*
Stylesheet::locateElementMatchPatternDataList(const XalanDOMString& theName) const
{
assert(m_elementPatternTableEnd == m_elementPatternTable.end());
const PatternTableMapType::const_iterator i =
m_elementPatternTable.find(theName);
if (i != m_elementPatternTableEnd)
{
return &(*i).second;
}
else
{
return &m_elementAnyPatternList;
}
}
inline const Stylesheet::PatternTableVectorType*
Stylesheet::locateAttributeMatchPatternDataList(const XalanDOMString& theName) const
{
assert(m_attributePatternTableEnd == m_attributePatternTable.end());
const PatternTableMapType::const_iterator i =
m_attributePatternTable.find(theName);
if (i != m_attributePatternTableEnd)
{
return &(*i).second;
}
else
{
return &m_attributeAnyPatternList;
}
}
inline const Stylesheet::PatternTableVectorType*
Stylesheet::locateMatchPatternDataList(
const XalanNode& theNode,
XalanNode::NodeType targetNodeType) const
{
assert(theNode.getNodeType() == targetNodeType);
switch(targetNodeType)
{
case XalanNode::ELEMENT_NODE:
return locateElementMatchPatternDataList(DOMServices::getLocalNameOfNode(theNode));
break;
case XalanNode::PROCESSING_INSTRUCTION_NODE:
return &m_piPatternList;
break;
case XalanNode::ATTRIBUTE_NODE:
if ((DOMServices::isNamespaceDeclaration(static_cast<const XalanAttr&>(theNode)) == true))
{
return &s_emptyTemplateList;
}
else
{
return locateAttributeMatchPatternDataList(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:
case XalanNode::DOCUMENT_FRAGMENT_NODE:
return &m_rootPatternList;
break;
default:
break;
}
return &m_nodePatternList;
}
inline const ElemTemplate*
Stylesheet::findTemplateInImports(
StylesheetExecutionContext& executionContext,
XalanNode* targetNode,
XalanNode::NodeType targetNodeType,
const XalanQName& mode) const
{
assert(targetNode->getNodeType() == targetNodeType);
assert(m_importsSize == m_imports.size());
for(StylesheetVectorType::size_type i = 0; i < m_importsSize; i++)
{
const Stylesheet* const stylesheet =
m_imports[i];
const ElemTemplate* const bestMatchedRule =
stylesheet->findTemplate(
executionContext,
targetNode,
targetNodeType,
mode,
false);
if(bestMatchedRule != 0)
{
return bestMatchedRule;
}
}
return 0;
}
const ElemTemplate*
Stylesheet::findTemplate(
StylesheetExecutionContext& executionContext,
XalanNode* targetNode,
XalanNode::NodeType targetNodeType,
const XalanQName& mode,
bool onlyUseImports) const
{
assert(targetNode != 0);
assert(targetNode->getNodeType() == targetNodeType);
if(m_isWrapperless == true)
{
return m_firstTemplate;
}
else if (onlyUseImports == true)
{
return findTemplateInImports(executionContext, targetNode, targetNodeType, mode);
}
else
{
const ElemTemplate* bestMatchedRule = 0;
if (executionContext.getQuietConflictWarnings() == true)
{
// Points to the current list of match patterns. Note
// that this may point to more than one table.
const PatternTableVectorType* matchPatternList =
locateMatchPatternDataList(*targetNode, targetNodeType);
assert(matchPatternList != 0);
PatternTableVectorType::const_iterator theCurrentEntry =
matchPatternList->begin();
const PatternTableVectorType::const_iterator theTableEnd =
matchPatternList->end();
while (theCurrentEntry != theTableEnd)
{
const XalanMatchPatternData* matchPat = *theCurrentEntry;
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 XPath* const xpath = matchPat->getExpression();
XPath::eMatchScore score =
xpath->getMatchScore(targetNode, *this, executionContext);
if(XPath::eMatchScoreNone != score)
{
bestMatchedRule = rule;
break;
}
}
++theCurrentEntry;
}
if(0 == bestMatchedRule)
{
bestMatchedRule = findTemplateInImports(executionContext, targetNode, targetNodeType, mode);
}
}
else
{
const PatternTableVectorType* matchPatternList =
locateMatchPatternDataList(*targetNode, targetNodeType);
assert(matchPatternList != 0);
PatternTableVectorType::const_iterator theCurrentEntry =
matchPatternList->begin();
const PatternTableVectorType::const_iterator theTableEnd =
matchPatternList->end();
if (theCurrentEntry != theTableEnd)
{
const XalanMatchPatternData* bestMatchedPattern = 0; // Syncs with bestMatchedRule
const double matchScoreNoneValue =
XPath::getMatchScoreValue(XPath::eMatchScoreNone);
double bestMatchPatPriority = matchScoreNoneValue;
XalanSize_t nConflicts = 0;
// Use a stack-based array when possible...
const XalanMatchPatternData* conflictsArray[100];
XalanVector<const XalanMatchPatternData*> conflictsVector(executionContext.getMemoryManager());
const XalanMatchPatternData** conflicts = 0;
const XalanDOMString* prevPat = 0;
const XalanMatchPatternData* prevMatchPat = 0;
do
{
const XalanMatchPatternData* 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(!patterns->empty() &&
!(prevMatchPat != 0 &&
(prevPat != 0 && equals(*prevPat, *patterns)) &&
prevMatchPat->getTemplate()->getPriority() == matchPat->getTemplate()->getPriority()))
{
prevPat = patterns;
prevMatchPat = matchPat;
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.resize(m_patternCount);
conflicts = conflictsVector.begin();
}
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);
if(nConflicts > 0)
{
assert(conflicts != 0 && nConflicts <= m_patternCount);
// Since the templates are arranged by their
// priority and reverse order in the stylesheet,
// the template with the highest priority that
// was last in the stylesheet will be the
// first one in the array.
bestMatchedPattern = conflicts[0];
bestMatchedRule = bestMatchedPattern->getTemplate();
const StylesheetExecutionContext::GetCachedString theGuard(executionContext);
executionContext.problem(
StylesheetExecutionContext::eXSLTProcessor,
StylesheetExecutionContext::eWarning,
XalanMessageLoader::getMessage(
theGuard.get(),
XalanMessages::ConflictsFound),
bestMatchedRule->getLocator(),
targetNode);
}
}
if (0 == bestMatchedRule)
{
bestMatchedRule = findTemplateInImports(executionContext, targetNode, targetNodeType, mode);
}
}
return bestMatchedRule;
}
}
void
Stylesheet::processExtensionNamespace(
StylesheetConstructionContext& theConstructionContext,
const XalanDOMString& uri)
{
XalanMemMgrAutoPtr<ExtensionNSHandler> theGuard(
theConstructionContext.getMemoryManager(),
ExtensionNSHandler::create(
uri,
theConstructionContext.getMemoryManager()));
m_extensionNamespaces.insert(uri, theGuard.get());
theGuard.release();
m_namespacesHandler.addExtensionNamespaceURI(theConstructionContext, 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 =
StylesheetConstructionContext::ELEMNAME_PARAM == 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->getNameAttribute()))
{
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->getNameAttribute(),
var,
var->getParentNodeElem());
}
}
}
void
Stylesheet::processNSAliasElement(
const XalanDOMChar* name,
const AttributeListType& atts,
StylesheetConstructionContext& constructionContext)
{
const XalanSize_t nAttrs = atts.getLength();
const XalanDOMString* stylesheetNamespace = 0;
const XalanDOMString* resultNamespace = 0;
for (XalanSize_t i = 0; i < nAttrs; i++)
{
const XalanDOMChar* const aname = atts.getName(i);
if (aname == Constants::ATTRNAME_STYLESHEET_PREFIX)
{
const XalanDOMChar* const value = atts.getValue(i);
if (equals(value, Constants::ATTRVAL_DEFAULT_PREFIX) == true)
{
stylesheetNamespace =
getNamespaceForPrefix(
DOMServices::s_emptyString,
constructionContext);
}
else
{
stylesheetNamespace =
getNamespaceForPrefix(
value,
constructionContext);
}
}
else if (aname == Constants::ATTRNAME_RESULT_PREFIX)
{
const XalanDOMChar* const value = atts.getValue(i);
if (equals(value, Constants::ATTRVAL_DEFAULT_PREFIX) == true)
{
resultNamespace =
getNamespaceForPrefix(
DOMServices::s_emptyString,
constructionContext);
}
else
{
resultNamespace =
getNamespaceForPrefix(
value,
constructionContext);
}
}
else if (!isAttrOK(aname, atts, i, constructionContext))
{
error(
constructionContext,
XalanMessages::ElementHasIllegalAttribute_2Param,
constructionContext.getLocatorFromStack(),
name,
aname);
}
}
// Build a table of aliases, the key is the stylesheet uri and the
// value is the result uri
if (stylesheetNamespace == 0)
{
error(
constructionContext,
XalanMessages::ElementMustHaveAttribute_2Param,
constructionContext.getLocatorFromStack(),
name,
Constants::ATTRNAME_STYLESHEET_PREFIX.c_str());
}
else if (resultNamespace == 0)
{
error(
constructionContext,
XalanMessages::ElementMustHaveAttribute_2Param,
constructionContext.getLocatorFromStack(),
name,
Constants::ATTRNAME_RESULT_PREFIX.c_str());
}
else
{
assert(stylesheetNamespace->empty() == false && resultNamespace->empty() == false);
m_namespacesHandler.setNamespaceAlias(
constructionContext,
*stylesheetNamespace,
*resultNamespace);
}
}
void
Stylesheet::processDecimalFormatElement(
StylesheetConstructionContext& constructionContext,
const AttributeListType& atts,
const Locator* locator)
{
const XalanFileLoc lineNumber =
XalanLocator::getLineNumber(locator);
const XalanFileLoc columnNumber =
XalanLocator::getColumnNumber(locator);
m_elemDecimalFormats.reserve(m_elemDecimalFormats.size() + 1);
ElemDecimalFormat* theInstance;
XalanConstruct(
constructionContext.getMemoryManager(),
theInstance,
constructionContext,
*this,
atts,
lineNumber,
columnNumber);
m_elemDecimalFormats.push_back(theInstance);
}
const XalanDecimalFormatSymbols*
Stylesheet::getDecimalFormatSymbols(const XalanQName& theQName) const
{
const XalanDecimalFormatSymbols* dfs = 0;
const ElemDecimalFormatVectorType::size_type theSize =
m_elemDecimalFormats.size();
if (theSize > 0)
{
// Start from the top of the stack
for (ElemDecimalFormatVectorType::size_type i = theSize; i > 0; --i)
{
assert(m_elemDecimalFormats[i - 1] != 0);
const ElemDecimalFormat* const theCurrent =
m_elemDecimalFormats[i - 1];
assert(theCurrent != 0);
if (theCurrent->getQName().equals(theQName) == true)
{
dfs = &theCurrent->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(theQName);
if (dfs != 0)
{
break;
}
}
}
return dfs;
}
const XalanDOMString*
Stylesheet::getNamespaceForPrefix(const XalanDOMString& prefix) const
{
return XalanQName::getNamespaceForPrefix(m_namespaceDecls, prefix);
}
const XalanDOMString&
Stylesheet::getURI() const
{
return m_baseIdent;
}
}