/*
 * 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 "XalanNamespacesStack.hpp"



#include "DOMServices.hpp"



namespace XALAN_CPP_NAMESPACE {



XalanNamespacesStack::PrefixResolverProxy::PrefixResolverProxy(
            const XalanNamespacesStack&     theStack,
            const XalanDOMString&           theURI) :
    m_stack(theStack),
    m_uri(theURI)
{
}



XalanNamespacesStack::PrefixResolverProxy::~PrefixResolverProxy()
{
}



const XalanDOMString*
XalanNamespacesStack::PrefixResolverProxy::getNamespaceForPrefix(const XalanDOMString&  prefix) const
{
    return m_stack.getNamespaceForPrefix(prefix);
}



const XalanDOMString&
XalanNamespacesStack::PrefixResolverProxy::getURI() const
{
    return m_uri;
}



XalanNamespacesStack::XalanNamespacesStackEntry::XalanNamespacesStackEntry(MemoryManager& theManager) :
    m_namespaces(theManager),
    m_position(m_namespaces.begin())
{
}



XalanNamespacesStack::XalanNamespacesStackEntry::XalanNamespacesStackEntry(const XalanNamespacesStackEntry&     theSource,
                                                                                        MemoryManager&      theManager) :
    m_namespaces(theSource.m_namespaces, theManager),
    m_position(m_namespaces.begin() + (const_iterator(theSource.m_position) - theSource.m_namespaces.begin()))
{
}



XalanNamespacesStack::XalanNamespacesStackEntry&
XalanNamespacesStack::XalanNamespacesStackEntry::set(const XalanNamespacesStackEntry&       theRHS,
                                                           MemoryManager&                   theManager)
{
    if (this != &theRHS)
    {
        XalanNamespacesStackEntry   theCopy(theRHS, theManager);

        swap(theCopy);
    }

    return *this;
}



XalanNamespacesStack::XalanNamespacesStackEntry::~XalanNamespacesStackEntry()
{
}



void
XalanNamespacesStack::XalanNamespacesStackEntry::swap(XalanNamespacesStackEntry&    theOther)
{
    m_namespaces.swap(theOther.m_namespaces);

    const iterator  theTemp = theOther.m_position;

    theOther.m_position = m_position;

    m_position = theTemp;
}



void
XalanNamespacesStack::XalanNamespacesStackEntry::addDeclaration(
                const XalanDOMString&       thePrefix,
                const XalanDOMChar*         theURI,
                XalanDOMString::size_type   theLength)
{
    // If we're at the end, we need to allocate something
    // new, then update m_position.
    if (m_position == m_namespaces.end())
    {
        m_namespaces.resize(m_namespaces.size() + 1);

        m_position = m_namespaces.end() - 1;
    }

    value_type&     theNamespace = *m_position;

    // Set the appropriate values...
    theNamespace.setPrefix(thePrefix);

    theNamespace.setURI(theURI, theLength);

    ++m_position;
}



const XalanDOMString*
XalanNamespacesStack::XalanNamespacesStackEntry::findEntry(
            const XalanDOMString&   theKey,
            MemberFunctionType      theKeyFunction,
            MemberFunctionType      theValueFunction) const
{
    if (m_namespaces.empty() == false)
    {
        const_iterator  i(m_position);

        do
        {
            // m_position is always pointed past the end, so
            // decrement first.
            --i;

            const value_type&   ns = (*i);

            const XalanDOMString&   thisKey = (ns.*theKeyFunction)();

            if(equals(theKey, thisKey))
            {
                return &(ns.*theValueFunction)();
            }
        } while (i != m_namespaces.begin());
    }

    return 0;
}
void
XalanNamespacesStack::XalanNamespacesStackEntry::clear()
{
    m_namespaces.clear();

    m_position = m_namespaces.begin();
}



XalanNamespacesStack::XalanNamespacesStack(MemoryManager& theManager) :
    m_resultNamespaces(theManager, 1),
    m_stackBegin(m_resultNamespaces.begin()),
    m_stackPosition(m_stackBegin),
    m_createNewContextStack(theManager)
{
    // m_resultNamespaces is initialized to a size of
    // 1, so we always have a dummy entry at the
    // beginning.  This makes the implementation
    // much simpler.
}



XalanNamespacesStack::~XalanNamespacesStack()
{
}



void
XalanNamespacesStack::addDeclaration(
            const XalanDOMString&       thePrefix,
            const XalanDOMChar*         theURI,
            XalanDOMString::size_type   theLength)
{
    assert(theURI != 0);
    assert(m_createNewContextStack.empty() == false);

    // Check to see if we need to create a new context and do so if necessary...
    if (m_createNewContextStack.back() == true)
    {
        ++m_stackPosition;

        if (m_stackPosition == m_resultNamespaces.end())
        {
            m_resultNamespaces.resize(m_resultNamespaces.size() + 1);

            m_stackPosition = m_resultNamespaces.end() - 1;
            m_stackBegin = m_resultNamespaces.begin();
        }

        m_createNewContextStack.back() = false;
    }

    value_type& theCurrentEntry = *m_stackPosition;

    // Add a new namespace declaration...
    theCurrentEntry.addDeclaration(thePrefix, theURI, theLength);
}



void
XalanNamespacesStack::pushContext()
{
    if (m_createNewContextStack.empty() == true)
    {
        m_createNewContextStack.reserve(eDefaultCreateNewContextStackSize);
    }

    m_createNewContextStack.push_back(true);
}



void
XalanNamespacesStack::popContext()
{
    assert(m_createNewContextStack.empty() == false);

    if (m_createNewContextStack.back() == false)
    {
        assert(m_resultNamespaces.empty() == false &&
               m_stackPosition != m_resultNamespaces.begin());

        (*m_stackPosition).reset();

        --m_stackPosition;
    }

    m_createNewContextStack.pop_back();
}



const XalanDOMString*
XalanNamespacesStack::findEntry(
            const XalanDOMString&   theKey,
            MemberFunctionType      theFunction) const
{
    if (m_stackPosition == m_stackBegin)
    {
        return 0;
    }
    else
    {
        NamespacesStackType::const_iterator     theBegin(m_stackBegin);
        NamespacesStackType::const_iterator     theEnd(m_stackPosition + 1);

        const XalanDOMString*   theValue = 0;

        do
        {
            theValue = ((*(--theEnd)).*theFunction)(theKey);

            if (theValue != 0)
            {
                break;
            }
        } while(theBegin != theEnd);

        return theValue;
    }
}



const XalanDOMString*
XalanNamespacesStack::getNamespaceForPrefix(const XalanDOMString&   thePrefix) const
{
    if(thePrefix == DOMServices::s_XMLString)
    {
        return &DOMServices::s_XMLNamespaceURI;
    }
    else if (thePrefix == DOMServices::s_XMLNamespace)
    {
        return &DOMServices::s_XMLNamespacePrefixURI;
    }
    else
    {
        return findEntry(thePrefix, &value_type::getNamespaceForPrefix);
    }
}



bool
XalanNamespacesStack::prefixIsPresentLocal(const XalanDOMString&    thePrefix)
{
    // Check to see if we need to create a new context.  If so, there are
    // no prefixes mapped at this level, so return false...
    if (m_createNewContextStack.back() == true)
    {
        return false;
    }
    else
    {
        return (*m_stackPosition).isPrefixPresent(thePrefix);
    }
}



void
XalanNamespacesStack::clear()
{
    // Since we always keep one dummy entry at the beginning,
    // swap with an OutputContextStackType instance of size 1.
    NamespacesStackType(m_resultNamespaces.getMemoryManager(), 1 ).swap(m_resultNamespaces);

    m_stackBegin = m_resultNamespaces.begin();

    m_stackPosition = m_stackBegin;

    m_createNewContextStack.clear();
}



}
