| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2000 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 1999, International |
| * Business Machines, Inc., http://www.ibm.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| |
| |
| |
| // Class header file. |
| #include "VariablesStack.hpp" |
| |
| |
| |
| #include <algorithm> |
| |
| |
| |
| #include "ElemVariable.hpp" |
| #include "StylesheetExecutionContext.hpp" |
| |
| |
| |
| VariablesStack::VariablesStack() : |
| m_stack(), |
| m_globalStackFrameIndex(-1), |
| m_globalStackFrameMarked(false), |
| m_currentStackFrameIndex(0), |
| m_guardStack() |
| { |
| m_stack.reserve(eDefaultStackSize); |
| } |
| |
| |
| |
| VariablesStack::~VariablesStack() |
| { |
| } |
| |
| |
| |
| void |
| VariablesStack::reset() |
| { |
| while(m_stack.size() > 0) |
| { |
| pop(); |
| } |
| |
| m_stack.clear(); |
| m_guardStack.clear(); |
| |
| m_globalStackFrameMarked = false; |
| m_globalStackFrameIndex = -1; |
| } |
| |
| |
| |
| 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() |
| { |
| const VariableStackStackType::size_type nElems = m_stack.size(); |
| |
| for(VariableStackStackType::size_type i = nElems; i > 0 && m_stack.empty() == false; --i) |
| { |
| const StackEntry& theEntry = m_stack[i - 1]; |
| assert(theEntry == back()); |
| |
| const StackEntry::eType type = theEntry.getType(); |
| assert(type < StackEntry::eNextValue && type >= 0); |
| |
| pop(); |
| |
| if (type == StackEntry::eContextMarker) |
| { |
| break; |
| } |
| } |
| } |
| |
| |
| |
| class CommitPushElementFrame |
| { |
| public: |
| |
| CommitPushElementFrame( |
| VariablesStack& theVariableStack, |
| const ElemTemplateElement* targetTemplate) : |
| m_variableStack(&theVariableStack), |
| m_targetTemplate(targetTemplate) |
| { |
| theVariableStack.pushElementFrame(targetTemplate); |
| } |
| |
| ~CommitPushElementFrame() |
| { |
| if (m_variableStack != 0) |
| { |
| m_variableStack->popElementFrame(m_targetTemplate); |
| } |
| } |
| |
| void |
| commit() |
| { |
| m_variableStack = 0; |
| } |
| |
| private: |
| |
| VariablesStack* m_variableStack; |
| |
| const ElemTemplateElement* const m_targetTemplate; |
| }; |
| |
| |
| |
| 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(); |
| } |
| |
| |
| |
| const void |
| VariablesStack::PushParamFunctor::operator()(const VariablesStack::ParamsVectorType::value_type& theEntry) |
| { |
| 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)); |
| } |
| } |
| |
| |
| |
| void |
| VariablesStack::pushParams( |
| const ParamsVectorType& theParams, |
| const ElemTemplateElement* targetTemplate) |
| { |
| // This object will push an element marker, and pop it |
| // if we don't call it's commit() member function. So |
| // if an exception is thrown while transferring the |
| // parameters, the element marker will be popped. |
| // This keeps the stack in a consistent state. |
| // It will also delete things left in the temp stack |
| // as well. |
| CommitPushElementFrame thePusher(*this, |
| targetTemplate); |
| |
| #if !defined (XALAN_NO_NAMESPACES) |
| using std::for_each; |
| #endif |
| |
| 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) |
| { |
| throw InvalidStackContextException(); |
| } |
| |
| push(StackEntry(&name, val)); |
| } |
| |
| |
| |
| void |
| VariablesStack::start() |
| { |
| } |
| |
| |
| |
| void |
| VariablesStack::resetParams() |
| { |
| const unsigned int nElems = getCurrentStackFrameIndex(); |
| |
| // There is guaranteed to be a context marker at |
| // the bottom of the stack, so i should stop at |
| // 1. |
| for(unsigned int 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 = m_stack.size(); |
| |
| m_globalStackFrameMarked = true; |
| |
| pushContextMarker(); |
| } |
| |
| |
| |
| void |
| VariablesStack::unmarkGlobalStackFrame() |
| { |
| popContextMarker(); |
| |
| m_globalStackFrameIndex = -1; |
| |
| 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. |
| 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); |
| |
| #if !defined (XALAN_NO_NAMESPACES) |
| using std::find; |
| #endif |
| |
| // See if the ElemVariable instance is already being evaluated... |
| if (find(m_guardStack.begin(), m_guardStack.end(), var) != m_guardStack.end()) |
| { |
| executionContext.error( |
| "A circular variable definition was detected", |
| doc, |
| var->getLocator()); |
| } |
| |
| m_guardStack.push_back(var); |
| |
| // We need to set up a stack frame for the variable's execution... |
| typedef StylesheetExecutionContext::PushAndPopContextMarker PushAndPopContextMarker; |
| |
| const PushAndPopContextMarker theContextMarkerPushPop(executionContext); |
| |
| theNewValue = var->getValue(executionContext, doc); |
| assert(theNewValue.null() == false); |
| |
| assert(m_guardStack.empty() == false); |
| |
| m_guardStack.pop_back(); |
| |
| m_stack[theEntryIndex].setValue(theNewValue); |
| m_stack[theEntryIndex].activate(); |
| } |
| |
| return theNewValue; |
| } |
| } |
| } |
| |
| |
| |
| VariablesStack::VariableStackStackType::size_type |
| VariablesStack::findEntry( |
| const XalanQName& qname, |
| bool fIsParam, |
| bool fSearchGlobalSpace) |
| { |
| typedef VariableStackStackType::size_type size_type; |
| |
| size_type theEntryIndex = m_stack.size(); |
| |
| const unsigned int nElems = getCurrentStackFrameIndex(); |
| |
| // There is guaranteed to be a context marker at |
| // the bottom of the stack, so i should stop at |
| // 1. |
| for(unsigned int 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 = size_type(i); |
| |
| break; |
| } |
| } |
| else if (theType == StackEntry::eParam) |
| { |
| if (fIsParam == true) |
| { |
| if(theEntry.getName()->equals(qname)) |
| { |
| theEntry.activate(); |
| |
| theEntryIndex = size_type(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(unsigned int 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 = size_type(i); |
| |
| break; |
| } |
| } |
| else if(theType == StackEntry::eContextMarker) |
| { |
| break; |
| } |
| } |
| } |
| |
| return theEntryIndex; |
| } |
| |
| |
| |
| void |
| VariablesStack::pushElementFrame(const ElemTemplateElement* elem) |
| { |
| push(StackEntry(elem)); |
| } |
| |
| |
| |
| class EnsurePop |
| { |
| public: |
| |
| EnsurePop(VariablesStack& theVariablesStack) : |
| m_variablesStack(theVariablesStack) |
| { |
| } |
| |
| ~EnsurePop() |
| { |
| m_variablesStack.pop(); |
| } |
| |
| private: |
| |
| VariablesStack& m_variablesStack; |
| }; |
| |
| |
| |
| void |
| VariablesStack::popElementFrame(const ElemTemplateElement* elem) |
| { |
| 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. |
| EnsurePop theEnsurePop(*this); |
| |
| if(theEntry.getType() == StackEntry::eContextMarker) |
| { |
| throw InvalidStackContextException(); |
| } |
| else if (theEntry.getType() == StackEntry::eElementFrameMarker) |
| { |
| const ElemTemplateElement* const theElement = |
| theEntry.getElement(); |
| |
| if (theElement != elem) |
| { |
| throw InvalidStackContextException(); |
| } |
| |
| 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; |
| } |
| } |
| |
| |
| |
| VariablesStack::InvalidStackContextException::InvalidStackContextException() : |
| XSLTProcessorException(TranscodeFromLocalCodePage("Invalid stack context"), |
| TranscodeFromLocalCodePage("InvalidStackContextException")) |
| { |
| } |
| |
| |
| |
| VariablesStack::InvalidStackContextException::~InvalidStackContextException() |
| { |
| } |