| /* |
| * 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. |
| */ |
| // Class header file |
| #include "KeyTable.hpp" |
| |
| |
| |
| #include <memory> |
| |
| |
| |
| #include <xalanc/XalanDOM/XalanElement.hpp> |
| #include <xalanc/XalanDOM/XalanNamedNodeMap.hpp> |
| #include <xalanc/XalanDOM/XalanNode.hpp> |
| #include <xalanc/XalanDOM/XalanNodeList.hpp> |
| |
| |
| |
| #include <xalanc/PlatformSupport/XalanMessageLoader.hpp> |
| |
| |
| |
| #include <xalanc/DOMSupport/DOMServices.hpp> |
| |
| |
| |
| #include <xalanc/XPath/XPath.hpp> |
| |
| |
| |
| #include "KeyDeclaration.hpp" |
| #include "StylesheetExecutionContext.hpp" |
| #include "XSLTProcessorException.hpp" |
| |
| |
| |
| namespace XALAN_CPP_NAMESPACE { |
| |
| |
| |
| const MutableNodeRefList KeyTable::s_dummyList(XalanMemMgrs::getDummyMemMgr()); |
| |
| |
| |
| KeyTable::KeyTable( |
| XalanNode* startNode, |
| const PrefixResolver& resolver, |
| const KeyDeclarationVectorType& keyDeclarations, |
| StylesheetExecutionContext& executionContext) : |
| m_keys(executionContext.getMemoryManager()) |
| { |
| XalanNode* pos = startNode; |
| |
| m_allKeys=keyDeclarations; |
| |
| const KeyDeclarationVectorType::size_type nDeclarations = |
| keyDeclarations.size(); |
| |
| // Do a non-recursive pre-walk over the tree. |
| while (0 != pos) |
| { |
| // We're going to have to walk the attribute list |
| // if it's an element, so get the attributes. |
| const XalanNamedNodeMap* attrs = 0; |
| |
| XalanSize_t nNodes = 1; |
| XalanSize_t nAttrNodes = 0; |
| |
| if (XalanNode::ELEMENT_NODE == pos->getNodeType()) |
| { |
| attrs = pos->getAttributes(); |
| |
| nAttrNodes = attrs->getLength(); |
| |
| if (0 == nAttrNodes) |
| { |
| attrs = 0; |
| } |
| else |
| { |
| nNodes += nAttrNodes; |
| } |
| } |
| |
| // Walk the primary node, and each of the attributes. |
| // This loop is a little strange... it is meant to always |
| // execute once, then execute for each of the attributes. |
| XalanNode* testNode = pos; |
| XalanSize_t nodeIndex = 0; |
| |
| for (XalanSize_t i = 0; i < nNodes; ++i) |
| { |
| // Walk through each of the declarations made with xsl:key |
| for (KeyDeclarationVectorType::size_type i = 0; i < nDeclarations; ++i) |
| { |
| const KeyDeclaration& kd = keyDeclarations[i]; |
| |
| // See if our node matches the given key declaration according to |
| // the match attribute on xsl:key. |
| assert(kd.getMatchPattern() != 0); |
| |
| const XPath::eMatchScore score = |
| kd.getMatchPattern()->getMatchScore( |
| testNode, |
| resolver, |
| executionContext); |
| |
| if (score != XPath::eMatchScoreNone) |
| { |
| processKeyDeclaration( |
| m_keys, |
| kd, |
| testNode, |
| resolver, |
| executionContext); |
| } |
| } |
| |
| if (0 != attrs && nodeIndex < nAttrNodes) |
| { |
| testNode = attrs->item(nodeIndex); |
| |
| ++nodeIndex; |
| } |
| } |
| |
| // The rest of this is getting the next pre-walk position in |
| // the tree. |
| XalanNode* nextNode = pos->getFirstChild(); |
| |
| while(0 == nextNode) |
| { |
| if(startNode == pos) |
| { |
| break; |
| } |
| else |
| { |
| nextNode = pos->getNextSibling(); |
| |
| if(0 == nextNode) |
| { |
| pos = pos->getParentNode(); |
| |
| if((startNode == pos) || (0 == pos)) |
| { |
| nextNode = 0; |
| break; |
| } |
| } |
| } |
| } |
| |
| pos = nextNode; |
| } // while(0 != pos) |
| |
| if (m_keys.empty() == false) |
| { |
| const KeysMapType::iterator theEnd = m_keys.end(); |
| KeysMapType::iterator theCurrent = m_keys.begin(); |
| assert(theCurrent != theEnd); |
| |
| do |
| { |
| NodeListMapType& theCurrentNodeListMap = (*theCurrent).second; |
| |
| if (theCurrentNodeListMap.empty() == false) |
| { |
| const NodeListMapType::iterator theEnd = theCurrentNodeListMap.end(); |
| NodeListMapType::iterator theCurrent = theCurrentNodeListMap.begin(); |
| assert(theCurrent != theEnd); |
| |
| do |
| { |
| (*theCurrent).second.setDocumentOrder(); |
| |
| ++theCurrent; |
| } |
| while(theCurrent != theEnd); |
| } |
| |
| ++theCurrent; |
| } |
| while(theCurrent != theEnd); |
| } |
| } // end constructor |
| |
| |
| |
| KeyTable* |
| KeyTable::create( |
| MemoryManager& theManager, |
| XalanNode* startNode, |
| const PrefixResolver& resolver, |
| const KeyDeclarationVectorType& keyDeclarations, |
| StylesheetExecutionContext& executionContext) |
| { |
| typedef KeyTable ThisType; |
| |
| XalanAllocationGuard theGuard(theManager, theManager.allocate(sizeof(ThisType))); |
| |
| ThisType* const theResult = |
| new (theGuard.get()) ThisType( |
| startNode, |
| resolver, |
| keyDeclarations, |
| executionContext); |
| theGuard.release(); |
| |
| return theResult; |
| } |
| |
| |
| |
| KeyTable::~KeyTable() |
| { |
| } |
| |
| |
| |
| const MutableNodeRefList* |
| KeyTable::getNodeSetByKey( |
| const XalanQName& qname, |
| const XalanDOMString& ref) const |
| { |
| const KeysMapType::const_iterator i = m_keys.find(qname); |
| |
| if (i != m_keys.end()) |
| { |
| const NodeListMapType& theMap = (*i).second; |
| |
| const NodeListMapType::const_iterator j = theMap.find(ref); |
| |
| if (j != theMap.end()) |
| { |
| return &(*j).second; |
| } |
| else |
| { |
| return &s_dummyList; |
| } |
| } |
| else |
| { |
| size_type nDeclarations=m_allKeys.size(); |
| for (KeyDeclarationVectorType::size_type i = 0; i < nDeclarations; ++i) |
| { |
| if (*m_allKeys[i].getQName()==qname) |
| { |
| return &s_dummyList; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| inline void |
| addIfNotFound( |
| StylesheetExecutionContext& executionContext, |
| MutableNodeRefList& theNodeList, |
| XalanNode* theNode) |
| { |
| theNodeList.addNodeInDocOrder(theNode, executionContext); |
| } |
| |
| |
| static const NodeRefList theEmptyList(XalanMemMgrs::getDummyMemMgr()); |
| |
| void |
| KeyTable::processKeyDeclaration( |
| KeysMapType& theKeys, |
| const KeyDeclaration& kd, |
| XalanNode* testNode, |
| const PrefixResolver& resolver, |
| StylesheetExecutionContext& executionContext) |
| { |
| // Query from the node, according the the select pattern in the |
| // use attribute in xsl:key. |
| assert(kd.getUse() != 0); |
| |
| const XObjectPtr xuse(kd.getUse()->execute(testNode, resolver, theEmptyList, executionContext)); |
| |
| if(xuse->getType() != XObject::eTypeNodeSet) |
| { |
| assert(kd.getQName() != 0); |
| |
| addIfNotFound( |
| executionContext, |
| theKeys[*kd.getQName()][xuse->str(executionContext)], |
| testNode); |
| } |
| else |
| { |
| const NodeRefListBase& nl = xuse->nodeset(); |
| |
| // Use each node in the node list as a key value that we'll be |
| // able to use to look up the given node. |
| const NodeRefListBase::size_type nUseValues = nl.getLength(); |
| |
| const StylesheetExecutionContext::GetCachedString theGuard(executionContext); |
| |
| XalanDOMString& nodeData = theGuard.get(); |
| |
| // Use each node in the node list as a key value that we'll be |
| // able to use to look up the given node. |
| for (NodeRefListBase::size_type i = 0; i < nUseValues; ++i) |
| { |
| // Get the string value of the node to use as the result of the |
| // expression. |
| assert(nl.item(i) != 0); |
| |
| DOMServices::getNodeData(*nl.item(i), executionContext, nodeData); |
| |
| assert(kd.getQName() != 0); |
| |
| addIfNotFound( |
| executionContext, |
| theKeys[*kd.getQName()][nodeData], |
| testNode); |
| |
| nodeData.clear(); |
| } |
| } |
| } |
| |
| |
| |
| } |