| /* |
| * 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 "VariablesStack.hpp" |
| |
| |
| |
| #include <algorithm> |
| |
| |
| |
| #include <xalanc/PlatformSupport/XalanMessageLoader.hpp> |
| |
| |
| |
| #include "ElemVariable.hpp" |
| #include "StylesheetExecutionContext.hpp" |
| |
| |
| |
| namespace XALAN_CPP_NAMESPACE { |
| |
| |
| |
| VariablesStack::VariablesStack(MemoryManager& theManager) : |
| m_stack(theManager), |
| m_globalStackFrameIndex(~0u), |
| m_globalStackFrameMarked(false), |
| m_currentStackFrameIndex(0), |
| m_guardStack(theManager), |
| m_elementFrameStack(theManager) |
| { |
| m_stack.reserve(eDefaultStackSize); |
| } |
| |
| |
| |
| VariablesStack::~VariablesStack() |
| { |
| } |
| |
| |
| |
| void |
| VariablesStack::reset() |
| { |
| while(m_stack.empty() == false) |
| { |
| pop(); |
| } |
| |
| m_stack.clear(); |
| m_guardStack.clear(); |
| m_elementFrameStack.clear(); |
| |
| m_globalStackFrameMarked = false; |
| m_globalStackFrameIndex = ~0u; |
| } |
| |
| |
| |
| bool |
| VariablesStack::elementFrameAlreadyPushed(const ElemTemplateElement* elem) const |
| { |
| const VariableStackStackType::size_type nElems = m_stack.size(); |
| assert(nElems > 0); |
| |
| // There is guaranteed to be a context marker at |
| // the bottom of the stack, so i should stop at |
| // 1. |
| for(VariableStackStackType::size_type i = nElems - 1; i > 0; --i) |
| { |
| const StackEntry& theEntry = m_stack[i]; |
| |
| if(theEntry.getType() == StackEntry::eElementFrameMarker) |
| { |
| if(theEntry.getElement() == elem) |
| { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| |
| |
| void |
| VariablesStack::pushContextMarker() |
| { |
| push(StackEntry()); |
| } |
| |
| |
| |
| void |
| VariablesStack::popContextMarker() |
| { |
| VariableStackStackType::iterator i = m_stack.end(); |
| |
| for(; ;) |
| { |
| assert(i != m_stack.begin()); |
| |
| const StackEntry& theEntry = *--i; |
| assert(theEntry == back()); |
| |
| const StackEntry::eType type = theEntry.getType(); |
| assert(type < StackEntry::eNextValue && type >= 0); |
| |
| pop(); |
| |
| if (type == StackEntry::eContextMarker) |
| { |
| break; |
| } |
| } |
| |
| // This introduced a regression into 1.7, because of all the |
| // external fiddling with the stack frame index. We need to |
| // re-write that code so no external meddling is necessary, |
| // so we can track the stack frame index properly. In particular, |
| // this is an issue with xsl:apply-templates, because a context |
| // marker is pushed before its select attribute is evaluated, even |
| // though the select expression really should be evaluated in |
| // the previous stack context. |
| // m_currentStackFrameIndex = size_type(m_stack.size()); |
| } |
| |
| |
| |
| void |
| VariablesStack::push(const StackEntry& theEntry) |
| { |
| assert(theEntry.getType() < StackEntry::eNextValue && theEntry.getType() >= 0); |
| |
| if(m_currentStackFrameIndex == m_stack.size()) |
| { |
| ++m_currentStackFrameIndex; |
| } |
| |
| m_stack.push_back(theEntry); |
| |
| // Increment the global stack frame index as long as we're pushing variables, and |
| // it already hasn't been marked. This is a temporary work-around for problems |
| // with evaluating top-level variables as they're pushed, rather than as they're |
| // used. |
| if (m_globalStackFrameMarked == false && theEntry.getType() == StackEntry::eVariable) |
| { |
| m_globalStackFrameIndex = m_currentStackFrameIndex; |
| } |
| } |
| |
| |
| |
| void |
| VariablesStack::pop() |
| { |
| assert(m_stack.empty() == false); |
| |
| if(m_currentStackFrameIndex == m_stack.size()) |
| { |
| --m_currentStackFrameIndex; |
| } |
| |
| m_stack.pop_back(); |
| } |
| |
| |
| |
| void |
| VariablesStack::PushParamFunctor::operator()(const VariablesStack::ParamsVectorType::value_type& theEntry) const |
| { |
| assert(theEntry.m_qname != 0); |
| |
| if (theEntry.m_value.null() == false) |
| { |
| m_variablesStack.push(VariablesStack::StackEntry(theEntry.m_qname, theEntry.m_value, true)); |
| } |
| else |
| { |
| assert(theEntry.m_variable != 0); |
| |
| m_variablesStack.push(VariablesStack::StackEntry(theEntry.m_qname, theEntry.m_variable, true)); |
| } |
| } |
| |
| |
| |
| VariablesStack::CommitPushParams::CommitPushParams(VariablesStack& theVariablesStack) : |
| m_variablesStack(&theVariablesStack), |
| m_stackSize(theVariablesStack.getStackSize()) |
| { |
| } |
| |
| |
| |
| VariablesStack::CommitPushParams::~CommitPushParams() |
| { |
| if (m_variablesStack != 0) |
| { |
| while(m_variablesStack->getStackSize() > m_stackSize) |
| { |
| m_variablesStack->pop(); |
| } |
| } |
| } |
| |
| |
| |
| void |
| VariablesStack::pushParams(const ParamsVectorType& theParams) |
| { |
| // This object will push the params and pop them |
| // if we don't call it's commit() member function. So |
| // if an exception is thrown while transferring the |
| // parameters, the stack stays in a consistent state. |
| CommitPushParams thePusher(*this); |
| |
| using std::for_each; |
| |
| for_each(theParams.begin(), theParams.end(), PushParamFunctor(*this)); |
| |
| thePusher.commit(); |
| } |
| |
| |
| |
| void |
| VariablesStack::pushVariable( |
| const XalanQName& name, |
| const ElemVariable* var, |
| const ElemTemplateElement* e) |
| { |
| if(elementFrameAlreadyPushed(e) == false) |
| { |
| pushElementFrame(e); |
| } |
| |
| push(StackEntry(&name, var)); |
| } |
| |
| |
| |
| void |
| VariablesStack::pushVariable( |
| const XalanQName& name, |
| const XObjectPtr& val, |
| const ElemTemplateElement* e) |
| { |
| if(elementFrameAlreadyPushed(e) == false) |
| { |
| XalanDOMString theBuffer(m_stack.getMemoryManager()); |
| |
| throw InvalidStackContextException(theBuffer); |
| } |
| |
| push(StackEntry(&name, val)); |
| } |
| |
| |
| |
| void |
| VariablesStack::start() |
| { |
| } |
| |
| |
| |
| void |
| VariablesStack::resetParams() |
| { |
| const size_type nElems = getCurrentStackFrameIndex(); |
| assert(nElems > 0); |
| |
| // There is guaranteed to be a context marker at |
| // the bottom of the stack, so i should stop at |
| // 1. |
| for(size_type i = nElems - 1; i > 0; --i) |
| { |
| StackEntry& theEntry = m_stack[i]; |
| |
| if(theEntry.getType() == StackEntry::eContextMarker) |
| { |
| break; |
| } |
| else |
| { |
| theEntry.deactivate(); |
| } |
| } |
| } |
| |
| |
| |
| void |
| VariablesStack::markGlobalStackFrame() |
| { |
| m_globalStackFrameIndex = size_type(m_stack.size()); |
| |
| m_globalStackFrameMarked = true; |
| |
| pushContextMarker(); |
| } |
| |
| |
| |
| void |
| VariablesStack::unmarkGlobalStackFrame() |
| { |
| popContextMarker(); |
| |
| m_globalStackFrameIndex = ~0u; |
| |
| m_globalStackFrameMarked = false; |
| } |
| |
| |
| |
| const XObjectPtr |
| VariablesStack::findXObject( |
| const XalanQName& name, |
| StylesheetExecutionContext& executionContext, |
| bool fIsParam, |
| bool fSearchGlobalSpace, |
| bool& fNameFound) |
| { |
| typedef VariableStackStackType::size_type size_type; |
| |
| // findEntry() returns an index into the stack. We should |
| // _never_ take the address of anything in the stack, since |
| // the address could change at unexpected times. |
| const size_type theEntryIndex = |
| findEntry(name, fIsParam, fSearchGlobalSpace); |
| |
| if (theEntryIndex == m_stack.size()) |
| { |
| fNameFound = false; |
| |
| return XObjectPtr(); |
| } |
| else |
| { |
| assert(theEntryIndex < m_stack.size()); |
| |
| fNameFound = true; |
| |
| assert(m_stack[theEntryIndex].getType() == StackEntry::eVariable || |
| m_stack[theEntryIndex].getType() == StackEntry::eParam || |
| m_stack[theEntryIndex].getType() == StackEntry::eActiveParam); |
| |
| const XObjectPtr& theValue = m_stack[theEntryIndex].getValue(); |
| |
| if (theValue.null() == false) |
| { |
| return theValue; |
| } |
| else |
| { |
| const ElemVariable* const var = m_stack[theEntryIndex].getVariable(); |
| |
| XObjectPtr theNewValue; |
| |
| if (var != 0) |
| { |
| XalanNode* const doc = executionContext.getRootDocument(); |
| assert(doc != 0); |
| |
| using std::find; |
| |
| // See if the ElemVariable instance is already being evaluated... |
| if (find(m_guardStack.begin(), m_guardStack.end(), var) != m_guardStack.end()) |
| { |
| const StylesheetExecutionContext::GetCachedString theGuard(executionContext); |
| |
| executionContext.problem( |
| StylesheetExecutionContext::eXSLTProcessor, |
| StylesheetExecutionContext::eError, |
| XalanMessageLoader::getMessage( |
| theGuard.get(), |
| XalanMessages::CircularVariableDefWasDetected), |
| var->getLocator(), |
| doc); |
| } |
| |
| m_guardStack.push_back(var); |
| |
| #if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION) |
| executionContext.pushContextMarker(); |
| #else |
| // We need to set up a stack frame for the variable's execution... |
| typedef StylesheetExecutionContext::PushAndPopContextMarker PushAndPopContextMarker; |
| |
| const PushAndPopContextMarker theContextMarkerPushPop(executionContext); |
| #endif |
| |
| theNewValue = var->getValue(executionContext, doc); |
| assert(theNewValue.null() == false); |
| |
| #if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION) |
| executionContext.popContextMarker(); |
| #endif |
| |
| assert(m_guardStack.empty() == false); |
| |
| m_guardStack.pop_back(); |
| |
| m_stack[theEntryIndex].setValue(theNewValue); |
| m_stack[theEntryIndex].activate(); |
| } |
| |
| return theNewValue; |
| } |
| } |
| } |
| |
| |
| |
| VariablesStack::size_type |
| VariablesStack::findEntry( |
| const XalanQName& qname, |
| bool fIsParam, |
| bool fSearchGlobalSpace) |
| { |
| size_type theEntryIndex = size_type(m_stack.size()); |
| |
| const size_type nElems = getCurrentStackFrameIndex(); |
| |
| // There is guaranteed to be a context marker at |
| // the bottom of the stack, so i should stop at |
| // 1. |
| for(size_type i = nElems - 1; i > 0; --i) |
| { |
| StackEntry& theEntry = m_stack[i]; |
| |
| const StackEntry::eType theType = theEntry.getType(); |
| |
| if(theType == StackEntry::eVariable || |
| theType == StackEntry::eActiveParam) |
| { |
| assert(theEntry.getName() != 0); |
| |
| if(theEntry.getName()->equals(qname)) |
| { |
| theEntryIndex = i; |
| |
| break; |
| } |
| } |
| else if (theType == StackEntry::eParam) |
| { |
| if (fIsParam == true) |
| { |
| if(theEntry.getName()->equals(qname)) |
| { |
| theEntry.activate(); |
| |
| theEntryIndex = i; |
| |
| break; |
| } |
| } |
| } |
| else if(theType == StackEntry::eContextMarker) |
| { |
| break; |
| } |
| } |
| |
| if(theEntryIndex == m_stack.size() && fIsParam == false && true == fSearchGlobalSpace && m_globalStackFrameIndex > 1) |
| { |
| // Look in the global space |
| for(size_type i = m_globalStackFrameIndex - 1; i > 0; i--) |
| { |
| StackEntry& theEntry = m_stack[i]; |
| |
| const StackEntry::eType theType = theEntry.getType(); |
| |
| if(theType == StackEntry::eVariable) |
| { |
| assert(theEntry.getName() != 0); |
| |
| if(theEntry.getName()->equals(qname)) |
| { |
| theEntryIndex = i; |
| |
| break; |
| } |
| } |
| else if(theType == StackEntry::eContextMarker) |
| { |
| break; |
| } |
| } |
| } |
| |
| return theEntryIndex; |
| } |
| |
| |
| |
| void |
| VariablesStack::pushElementFrame(const ElemTemplateElement* elem) |
| { |
| push(StackEntry(elem)); |
| |
| #if !defined(NDEBUG) |
| m_elementFrameStack.push_back(elem); |
| #endif |
| } |
| |
| |
| |
| class EnsurePop |
| { |
| public: |
| |
| EnsurePop(VariablesStack& theVariablesStack) : |
| m_variablesStack(theVariablesStack) |
| { |
| } |
| |
| ~EnsurePop() |
| { |
| m_variablesStack.pop(); |
| } |
| |
| private: |
| |
| VariablesStack& m_variablesStack; |
| }; |
| |
| |
| |
| void |
| VariablesStack::popElementFrame() |
| { |
| const VariableStackStackType::size_type nElems = m_stack.size(); |
| assert(nElems > 0); |
| |
| // There is guaranteed to be a context marker at |
| // the bottom of the stack, so i should stop at |
| // 1. |
| for(VariableStackStackType::size_type i = nElems - 1; i > 0; --i) |
| { |
| const StackEntry& theEntry = m_stack[i]; |
| |
| // Guarantee that it will be popped when we're done. |
| const EnsurePop theEnsurePop(*this); |
| |
| if(theEntry.getType() == StackEntry::eContextMarker) |
| { |
| XalanDOMString theBuffer(m_stack.getMemoryManager()); |
| |
| throw InvalidStackContextException(theBuffer); |
| } |
| else if (theEntry.getType() == StackEntry::eElementFrameMarker) |
| { |
| #if !defined(NDEBUG) |
| const ElemTemplateElement* const theElement = |
| theEntry.getElement(); |
| |
| if (m_elementFrameStack.empty() == true) |
| { |
| XalanDOMString theBuffer(m_stack.getMemoryManager()); |
| |
| throw InvalidStackContextException(theBuffer); |
| } |
| |
| const ElemTemplateElement* const theStackBack = |
| m_elementFrameStack.back(); |
| |
| m_elementFrameStack.pop_back(); |
| |
| if (theElement != theStackBack) |
| { |
| XalanDOMString theBuffer(m_stack.getMemoryManager()); |
| |
| throw InvalidStackContextException(theBuffer); |
| } |
| #endif |
| |
| break; |
| } |
| } |
| } |
| |
| |
| |
| VariablesStack::StackEntry::StackEntry() : |
| m_type(eContextMarker), |
| m_qname(0), |
| m_value(), |
| m_variable(0), |
| m_element(0) |
| { |
| } |
| |
| |
| |
| VariablesStack::StackEntry::StackEntry( |
| const XalanQName* name, |
| const XObjectPtr& val, |
| bool isParam) : |
| m_type(isParam == true ? eParam : eVariable), |
| m_qname(name), |
| m_value(val), |
| m_variable(0), |
| m_element(0) |
| { |
| } |
| |
| |
| |
| VariablesStack::StackEntry::StackEntry( |
| const XalanQName* name, |
| const ElemVariable* var, |
| bool isParam) : |
| m_type(isParam == true ? eParam : eVariable), |
| m_qname(name), |
| m_value(), |
| m_variable(var), |
| m_element(0) |
| { |
| } |
| |
| |
| |
| VariablesStack::StackEntry::StackEntry(const ElemTemplateElement* elem) : |
| m_type(eElementFrameMarker), |
| m_qname(0), |
| m_value(), |
| m_variable(0), |
| m_element(elem) |
| { |
| } |
| |
| |
| |
| VariablesStack::StackEntry::StackEntry(const StackEntry& theSource) : |
| m_type(theSource.m_type), |
| m_qname(theSource.m_qname), |
| m_value(theSource.m_value), |
| m_variable(theSource.m_variable), |
| m_element(theSource.m_element) |
| { |
| } |
| |
| |
| |
| VariablesStack::StackEntry::~StackEntry() |
| { |
| } |
| |
| |
| |
| VariablesStack::StackEntry& |
| VariablesStack::StackEntry::operator=(const StackEntry& theRHS) |
| { |
| if (this != &theRHS) |
| { |
| m_type = theRHS.m_type; |
| |
| m_qname = theRHS.m_qname; |
| |
| m_value = theRHS.m_value; |
| |
| m_variable = theRHS.m_variable; |
| |
| m_element = theRHS.m_element; |
| } |
| |
| return *this; |
| } |
| |
| |
| |
| // Equality for StackEntry instances is probably bogus, |
| // so it might be worthwhile to just get rid of this. |
| bool |
| VariablesStack::StackEntry::operator==(const StackEntry& theRHS) const |
| { |
| bool fResult = false; |
| |
| if (m_type == theRHS.m_type) |
| { |
| if (m_type == eContextMarker) |
| { |
| if (&theRHS == this) |
| { |
| fResult = true; |
| } |
| } |
| else if (m_type == eVariable || m_type == eParam || m_type == eActiveParam) |
| { |
| // We only need to compare the variable related members... |
| if (m_value == theRHS.m_value || |
| m_variable == theRHS.m_variable) |
| { |
| fResult = true; |
| } |
| } |
| else if (m_type == eElementFrameMarker) |
| { |
| if (m_element == theRHS.m_element) |
| { |
| fResult = true; |
| } |
| } |
| else |
| { |
| assert(0); |
| } |
| } |
| |
| return fResult; |
| } |
| |
| |
| |
| void |
| VariablesStack::StackEntry::activate() |
| { |
| if (m_type == eParam) |
| { |
| m_type = eActiveParam; |
| } |
| } |
| |
| |
| |
| void |
| VariablesStack::StackEntry::deactivate() |
| { |
| if (m_type == eActiveParam) |
| { |
| m_type = eParam; |
| } |
| } |
| |
| |
| const XalanDOMChar VariablesStack::InvalidStackContextException::m_type[] = |
| { |
| XalanUnicode::charLetter_I, |
| XalanUnicode::charLetter_n, |
| XalanUnicode::charLetter_v, |
| XalanUnicode::charLetter_a, |
| XalanUnicode::charLetter_l, |
| XalanUnicode::charLetter_i, |
| XalanUnicode::charLetter_d, |
| XalanUnicode::charLetter_S, |
| XalanUnicode::charLetter_t, |
| XalanUnicode::charLetter_a, |
| XalanUnicode::charLetter_c, |
| XalanUnicode::charLetter_k, |
| XalanUnicode::charLetter_C, |
| XalanUnicode::charLetter_o, |
| XalanUnicode::charLetter_n, |
| XalanUnicode::charLetter_t, |
| XalanUnicode::charLetter_e, |
| XalanUnicode::charLetter_x, |
| XalanUnicode::charLetter_t, |
| XalanUnicode::charLetter_E, |
| XalanUnicode::charLetter_x, |
| XalanUnicode::charLetter_c, |
| XalanUnicode::charLetter_e, |
| XalanUnicode::charLetter_p, |
| XalanUnicode::charLetter_t, |
| XalanUnicode::charLetter_i, |
| XalanUnicode::charLetter_o, |
| XalanUnicode::charLetter_n, |
| 0 |
| }; |
| |
| |
| |
| VariablesStack::InvalidStackContextException::InvalidStackContextException(XalanDOMString& theResult) : |
| XSLTProcessorException( |
| theResult.getMemoryManager(), |
| XalanMessageLoader::getMessage( |
| theResult, |
| XalanMessages::InvalidStackContext)) |
| { |
| } |
| |
| |
| |
| VariablesStack::InvalidStackContextException::~InvalidStackContextException() |
| { |
| } |
| |
| |
| |
| } |