src/xalanc/XSLT/ElemForEach.cpp (619 lines of code) (raw):

/* * 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 "ElemForEach.hpp" #include <algorithm> #include <cassert> #include <xercesc/sax/AttributeList.hpp> #include <xalanc/Include/STLHelper.hpp> #include <xalanc/PlatformSupport/DOMStringHelper.hpp> #include <xalanc/PlatformSupport/XalanMessageLoader.hpp> #include <xalanc/XPath/XPath.hpp> #include "ElemSort.hpp" #include "NodeSorter.hpp" #include "SelectionEvent.hpp" #include "StylesheetConstructionContext.hpp" #include "StylesheetExecutionContext.hpp" namespace XALAN_CPP_NAMESPACE { ElemForEach::ElemForEach( StylesheetConstructionContext& constructionContext, Stylesheet& stylesheetTree, const AttributeListType& atts, XalanFileLoc lineNumber, XalanFileLoc columnNumber) : ElemTemplateElement(constructionContext, stylesheetTree, lineNumber, columnNumber, StylesheetConstructionContext::ELEMNAME_FOR_EACH), m_selectPattern(0), m_sortElems(constructionContext.getMemoryManager()), m_sortElemsCount(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_SELECT)) { m_selectPattern = constructionContext.createXPath(getLocator(), atts.getValue(i), *this); } else if (isAttrOK( aname, atts, i, constructionContext) == false && processSpaceAttr( Constants::ELEMNAME_FOREACH_WITH_PREFIX_STRING.c_str(), aname, atts, i, constructionContext) == false) { error( constructionContext, XalanMessages::ElementHasIllegalAttribute_2Param, Constants::ELEMNAME_FOREACH_WITH_PREFIX_STRING.c_str(), aname); } } if (0 == m_selectPattern) { error( constructionContext, XalanMessages::ElementMustHaveAttribute_2Param, Constants::ELEMNAME_FOREACH_WITH_PREFIX_STRING, Constants::ATTRNAME_SELECT); } } ElemForEach::ElemForEach( StylesheetConstructionContext& constructionContext, Stylesheet& stylesheetTree, XalanFileLoc lineNumber, XalanFileLoc columnNumber, int xslToken) : ElemTemplateElement(constructionContext, stylesheetTree, lineNumber, columnNumber, xslToken), m_selectPattern(0), m_sortElems(constructionContext.getMemoryManager()), m_sortElemsCount(0) { } ElemForEach::~ElemForEach() { using std::for_each; MemoryManager& theManager = m_sortElems.getMemoryManager(); for_each(m_sortElems.begin(), m_sortElems.end(), DeleteFunctor<ElemSort>(theManager)); } void ElemForEach::processSortElement( StylesheetConstructionContext& constructionContext, Stylesheet& theStylesheet, const AttributeListType& atts, const Locator* locator) { const XalanFileLoc lineNumber = XalanLocator::getLineNumber(locator); const XalanFileLoc columnNumber = XalanLocator::getColumnNumber(locator); m_sortElems.reserve(m_sortElems.size() + 1); ElemSort* sortElem = ElemSort::create( constructionContext.getMemoryManager(), constructionContext, theStylesheet, atts, lineNumber, columnNumber); m_sortElems.push_back(sortElem); } const XalanDOMString& ElemForEach::getElementName() const { return Constants::ELEMNAME_FOREACH_WITH_PREFIX_STRING; } void ElemForEach::postConstruction( StylesheetConstructionContext& constructionContext, const NamespacesHandler& theParentHandler) { ElemTemplateElement::postConstruction(constructionContext, theParentHandler); m_sortElemsCount = m_sortElems.size(); } #if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION) const ElemTemplateElement* ElemForEach::startElement(StylesheetExecutionContext& executionContext) const { assert(executionContext.getCurrentNode() != 0); if (hasChildren() == true) { executionContext.pushCurrentTemplate(0); const NodeRefListBase * nodeList = createSelectedAndSortedNodeList( executionContext); executionContext.createAndPushNodesToTransformList(nodeList); executionContext.pushContextNodeList(*nodeList); XalanNode* currentNode = executionContext.getNextNodeToTransform(); if (currentNode == 0) { return 0; } executionContext.pushCurrentNode(currentNode); return beginExecuteChildren(executionContext); } return 0; } const ElemTemplateElement* ElemForEach::getNextChildElemToExecute( StylesheetExecutionContext& executionContext, const ElemTemplateElement* currentElem) const { if (hasDirectTemplate() != true) { ElemTemplateElement* nextElement = currentElem->getNextSiblingElem(); if (nextElement != 0) { return nextElement; } } executionContext.popCurrentNode(); XalanNode * nextNode = executionContext.getNextNodeToTransform(); if (nextNode == 0) { return 0; } executionContext.pushCurrentNode(nextNode); endExecuteChildren(executionContext); return beginExecuteChildren(executionContext); } void ElemForEach::endElement(StylesheetExecutionContext& executionContext) const { if (hasChildren() == true) { // Children only executed if there were selected nodes if(executionContext.getContextNodeList().getLength() > 0) { endExecuteChildren(executionContext); } executionContext.popNodesToTransformList(); executionContext.popContextNodeList(); releaseSelectedAndSortedNodeList(executionContext); executionContext.popCurrentTemplate(); } } const NodeRefListBase* ElemForEach::createSelectedAndSortedNodeList( StylesheetExecutionContext& executionContext) const { assert(m_selectPattern != 0); typedef StylesheetExecutionContext::SetAndRestoreCurrentStackFrameIndex SetAndRestoreCurrentStackFrameIndex; MutableNodeRefList& selectedNodeList = executionContext.createAndPushMutableNodeRefList(); XObjectPtr xobjectResult; const NodeRefListBase* nodesToTransform = 0; { xobjectResult = m_selectPattern->execute( *this, executionContext, selectedNodeList); if (xobjectResult.null() == true) { nodesToTransform = &selectedNodeList; } else { nodesToTransform = &xobjectResult->nodeset(); } } executionContext.pushXObjectPtr(xobjectResult); if(0 != executionContext.getTraceListeners()) { executionContext.fireSelectEvent( SelectionEvent( executionContext, executionContext.getCurrentNode(), *this, XalanDOMString("select", executionContext.getMemoryManager()), *m_selectPattern, *nodesToTransform)); } if (m_sortElemsCount > 0) { MutableNodeRefList& sortedNodeList = executionContext.createAndPushMutableNodeRefList(); if (nodesToTransform->getLength() > 1) { nodesToTransform = sortChildren( executionContext, *nodesToTransform, sortedNodeList); } } return nodesToTransform; } void ElemForEach::releaseSelectedAndSortedNodeList( StylesheetExecutionContext& executionContext) const { executionContext.popXObjectPtr(); executionContext.releaseAndPopMutableNodeRefList(); if (m_sortElemsCount > 0) { executionContext.releaseAndPopMutableNodeRefList(); } } const NodeRefListBase* ElemForEach::sortChildren( StylesheetExecutionContext& executionContext, const NodeRefListBase& selectedNodeList, MutableNodeRefList& sortedNodeList) const { typedef NodeSorter::NodeSortKeyVectorType NodeSortKeyVectorType; typedef StylesheetExecutionContext::SetAndRestoreCurrentStackFrameIndex SetAndRestoreCurrentStackFrameIndex; typedef StylesheetExecutionContext::ContextNodeListPushAndPop ContextNodeListPushAndPop; NodeSorter* sorter = executionContext.getNodeSorter(); NodeSortKeyVectorType& keys = sorter->getSortKeys(); assert(keys.empty() == true); CollectionClearGuard<NodeSortKeyVectorType> guard(keys); // Reserve the space now... keys.reserve(m_sortElemsCount); // Get some temporary strings to use for evaluting the AVTs... const StylesheetExecutionContext::GetCachedString theTemp1(executionContext); XalanDOMString& langString = theTemp1.get(); const StylesheetExecutionContext::GetCachedString theTemp2(executionContext); XalanDOMString& scratchString = theTemp2.get(); // March backwards, performing a sort on each xsl:sort child. // Probably not the most efficient method. for(SortElemsVectorType::size_type i = 0; i < m_sortElemsCount; i++) { const ElemSort* const sort = m_sortElems[i]; assert(sort != 0); const AVT* avt = sort->getLangAVT(); if(0 != avt) { avt->evaluate(langString, *this, executionContext); } avt = sort->getDataTypeAVT(); if(0 != avt) { avt->evaluate(scratchString, *this, executionContext); } bool treatAsNumbers = false; if (scratchString.empty() == false) { if (equals(scratchString, Constants::ATTRVAL_DATATYPE_NUMBER) == true) { treatAsNumbers = true; } else if (equals(scratchString, Constants::ATTRVAL_DATATYPE_TEXT) == false) { const XalanQNameByValue theQName(scratchString, executionContext.getMemoryManager(), this); if (theQName.getNamespace().length() == 0) { error( executionContext, XalanMessages::SortDataTypeMustBe, sort->getLocator()); } else { warn( executionContext, XalanMessages::SortHasUnknownDataType, sort->getLocator()); } } } scratchString.clear(); avt = sort->getOrderAVT(); if(0 != avt) { avt->evaluate(scratchString, *this, executionContext); } bool descending = false; if (scratchString.empty() == false) { if (equals(scratchString, Constants::ATTRVAL_ORDER_DESCENDING) == true) { descending = true; } else if (equals(scratchString, Constants::ATTRVAL_ORDER_ASCENDING) == false) { error( executionContext, XalanMessages::SortMustBeAscendOrDescend, sort->getLocator()); } } scratchString.clear(); avt = sort->getCaseOrderAVT(); if(0 != avt) { avt->evaluate(scratchString, *this, executionContext); } XalanCollationServices::eCaseOrder caseOrder = XalanCollationServices::eDefault; if (scratchString.empty() == false) { if (equals(scratchString, Constants::ATTRVAL_CASEORDER_UPPER) == true) { caseOrder = XalanCollationServices::eUpperFirst; } else if (equals(scratchString, Constants::ATTRVAL_CASEORDER_LOWER) == true) { caseOrder = XalanCollationServices::eLowerFirst; } else { error( executionContext, XalanMessages::SortCaseOrderMustBe, sort->getLocator()); } } scratchString.clear(); keys.push_back( NodeSortKey( executionContext, sort->getSelectPattern(), treatAsNumbers, descending, caseOrder, langString, *this)); } sortedNodeList = selectedNodeList; { ContextNodeListPushAndPop theContextNodeListPushAndPop( executionContext, selectedNodeList); sorter->sort(executionContext, sortedNodeList); } return &sortedNodeList; } #endif #if defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION) void ElemForEach::execute(StylesheetExecutionContext& executionContext) const { assert(m_selectPattern != 0); assert(executionContext.getCurrentNode() != 0); StylesheetExecutionContext::PushAndPopCurrentTemplate thePushAndPop(executionContext, 0); if (hasChildren() == true) { transformSelectedChildren( executionContext, this); } } void ElemForEach::transformSelectedChildren( StylesheetExecutionContext& executionContext, const ElemTemplateElement* theTemplate) const { assert(m_selectPattern != 0); assert(m_sortElemsCount == m_sortElems.size()); if (m_sortElemsCount == 0) { selectAndSortChildren( executionContext, theTemplate, 0, executionContext.getCurrentStackFrameIndex()); } else { typedef NodeSorter::NodeSortKeyVectorType NodeSortKeyVectorType; typedef StylesheetExecutionContext::BorrowReturnNodeSorter BorrowReturnNodeSorter; BorrowReturnNodeSorter sorter(executionContext); NodeSortKeyVectorType& keys = sorter->getSortKeys(); assert(keys.empty() == true); CollectionClearGuard<NodeSortKeyVectorType> guard(keys); // Reserve the space now... keys.reserve(m_sortElemsCount); // Get some temporary strings to use for evaluting the AVTs... StylesheetExecutionContext::GetCachedString theTemp1(executionContext); XalanDOMString& langString = theTemp1.get(); const StylesheetExecutionContext::GetCachedString theTemp2(executionContext); XalanDOMString& scratchString = theTemp2.get(); // March backwards, performing a sort on each xsl:sort child. // Probably not the most efficient method. for(SortElemsVectorType::size_type i = 0; i < m_sortElemsCount; i++) { const ElemSort* const sort = m_sortElems[i]; assert(sort != 0); const AVT* avt = sort->getLangAVT(); if(0 != avt) { avt->evaluate(langString, *this, executionContext); } avt = sort->getDataTypeAVT(); if(0 != avt) { avt->evaluate(scratchString, *this, executionContext); } bool treatAsNumbers = false; if (scratchString.empty() == false) { if (equals(scratchString, Constants::ATTRVAL_DATATYPE_NUMBER) == true) { treatAsNumbers = true; } else if (equals(scratchString, Constants::ATTRVAL_DATATYPE_TEXT) == false) { const XalanQNameByValue theQName(scratchString, executionContext.getMemoryManager(), this); if (theQName.getNamespace().length() == 0) { error( executionContext, XalanMessages::SortDataTypeMustBe); } else { warn( executionContext, XalanMessages::SortHasUnknownDataType); } } } clear(scratchString); avt = sort->getOrderAVT(); if(0 != avt) { avt->evaluate(scratchString, *this, executionContext); } bool descending = false; if (scratchString.empty() == false) { if (equals(scratchString, Constants::ATTRVAL_ORDER_DESCENDING) == true) { descending = true; } else if (equals(scratchString, Constants::ATTRVAL_ORDER_ASCENDING) == false) { error( executionContext, XalanMessages::SortMustBeAscendOrDescend); } } clear(scratchString); avt = sort->getCaseOrderAVT(); if(0 != avt) { avt->evaluate(scratchString, *this, executionContext); } XalanCollationServices::eCaseOrder caseOrder = XalanCollationServices::eDefault; if (scratchString.empty() == false) { if (equals(scratchString, Constants::ATTRVAL_CASEORDER_UPPER) == true) { caseOrder = XalanCollationServices::eUpperFirst; } else if (equals(scratchString, Constants::ATTRVAL_CASEORDER_LOWER) == true) { caseOrder = XalanCollationServices::eLowerFirst; } else { error( executionContext, XalanMessages::SortCaseOrderMustBe); } } clear(scratchString); keys.push_back( NodeSortKey( executionContext, sort->getSelectPattern(), treatAsNumbers, descending, caseOrder, langString, *this)); } selectAndSortChildren( executionContext, theTemplate, sorter.get(), executionContext.getCurrentStackFrameIndex()); } } void ElemForEach::selectAndSortChildren( StylesheetExecutionContext& executionContext, const ElemTemplateElement* theTemplate, NodeSorter* sorter, int selectStackFrameIndex) const { typedef StylesheetExecutionContext::SetAndRestoreCurrentStackFrameIndex SetAndRestoreCurrentStackFrameIndex; assert(m_selectPattern != 0); typedef XPathExecutionContext::BorrowReturnMutableNodeRefList BorrowReturnMutableNodeRefList; BorrowReturnMutableNodeRefList theGuard(executionContext); const NodeRefListBase* sourceNodes = 0; XObjectPtr xobjectResult; { SetAndRestoreCurrentStackFrameIndex theSetAndRestore( executionContext, selectStackFrameIndex); xobjectResult = m_selectPattern->execute( *this, executionContext, *theGuard); if (xobjectResult.null() == true) { sourceNodes = &*theGuard; } else { theGuard.release(); sourceNodes = &xobjectResult->nodeset(); } } if(0 != executionContext.getTraceListeners()) { executionContext.fireSelectEvent( SelectionEvent( executionContext, executionContext.getCurrentNode(), *this, XalanDOMString("select", executionContext.getMemoryManager()), *m_selectPattern, *sourceNodes)); } const NodeRefListBase::size_type nNodes = sourceNodes->getLength(); if (nNodes > 0) { // If there's not NodeSorter, or we've only selected one node, // then just do the transform... if (sorter == 0 || nNodes == 1) { transformSelectedChildren( executionContext, theTemplate, *sourceNodes, nNodes); } else { typedef StylesheetExecutionContext::SetAndRestoreCurrentStackFrameIndex SetAndRestoreCurrentStackFrameIndex; typedef StylesheetExecutionContext::ContextNodeListPushAndPop ContextNodeListPushAndPop; typedef StylesheetExecutionContext::BorrowReturnMutableNodeRefList BorrowReturnMutableNodeRefList; BorrowReturnMutableNodeRefList sortedSourceNodes(executionContext); *sortedSourceNodes = *sourceNodes; { SetAndRestoreCurrentStackFrameIndex theStackFrameSetAndRestore( executionContext, selectStackFrameIndex); ContextNodeListPushAndPop theContextNodeListPushAndPop( executionContext, *sourceNodes); sorter->sort(executionContext, *sortedSourceNodes); } transformSelectedChildren( executionContext, theTemplate, *sortedSourceNodes, nNodes); } } } void ElemForEach::transformSelectedChildren( StylesheetExecutionContext& executionContext, const ElemTemplateElement* theTemplate, const NodeRefListBase& sourceNodes, NodeRefListBase::size_type sourceNodesCount) const { if(executionContext.getTraceSelects() == true) { executionContext.traceSelect( *this, sourceNodes, m_selectPattern); } // Create an object to set and restore the context node list... const StylesheetExecutionContext::ContextNodeListPushAndPop theContextNodeLisPushAndPop( executionContext, sourceNodes); for(NodeRefListBase::size_type i = 0; i < sourceNodesCount; i++) { XalanNode* const childNode = sourceNodes.item(i); assert(childNode != 0); transformChild( executionContext, *this, theTemplate, childNode); } } #endif const XPath* ElemForEach::getXPath(XalanSize_t index) const { return index == 0 ? m_selectPattern : 0; } }