blob: d1a6388332340b2f2386dc699162350aefde9d40 [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 "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;
}
}