/*
 * Copyright 1999-2004 The Apache Software Foundation.
 *
 * Licensed 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 "AttributeListImpl.hpp"



#include <algorithm>
#include <cassert>



#include <xalanc/Include/XalanMemMgrAutoPtr.hpp>
#include <xalanc/Include/STLHelper.hpp>



#include "AttributeVectorEntry.hpp"



XALAN_CPP_NAMESPACE_BEGIN



AttributeListImpl::AttributeListImpl(MemoryManagerType&      theManager) :
	AttributeListType(),
	m_AttributeVector(theManager),
	m_cacheVector(theManager)
{
}



AttributeListImpl::~AttributeListImpl()
{
	// Clean up everything...
	clear();

	assert(m_AttributeVector.empty() == true);

	deleteEntries(m_cacheVector);
}



AttributeListImpl::AttributeListImpl(const AttributeListImpl&	theSource,
                                     MemoryManagerType&      theManager) :
	AttributeListType(),
	m_AttributeVector(theManager),
    m_cacheVector(theManager)
{
	// Use the assignment operator to do the dirty work...
	*this = theSource;

	assert(getLength() == theSource.getLength());
}



AttributeListImpl::AttributeListImpl(const AttributeListType&	theSource,
                                     MemoryManagerType&      theManager) :
	AttributeListType(),
	m_AttributeVector(theManager),
        m_cacheVector(theManager)

{
	// Use the assignment operator to do the dirty work...
	*this = theSource;

	assert(getLength() == theSource.getLength());
}



void
AttributeListImpl::deleteEntries(AttributeVectorType&	theVector)
{
	// Delete all of the objects in the vector.
	XALAN_STD_QUALIFIER for_each(
		theVector.begin(),
		theVector.end(),
        DeleteFunctor<AttributeVectorEntry>(theVector.getMemoryManager()));
}



AttributeListImpl&
AttributeListImpl::operator=(const AttributeListImpl&	theRHS)
{
	if (this != &theRHS)
	{
		// Note that we can't chain up to our base class operator=()
		// because it's private.

		// Some temporary structures to hold everything
		// until we're done.
		AttributeVectorType		tempVector(getMemoryManager());

		const unsigned int	theLength = theRHS.getLength();

		if (theLength > 0)
		{
			// Reserve the appropriate capacity right now...
			tempVector.reserve(theLength);

			// This will delete everything in tempVector when we're done...
			CollectionDeleteGuard<AttributeVectorType,
								  DeleteFunctor<AttributeVectorEntry> >		theGuard(tempVector);

			typedef AttributeVectorType::const_iterator		const_iterator;

			const const_iterator	theEnd = theRHS.m_AttributeVector.end();

			// Copy the vector entries, and build the index map...
			for(const_iterator i = theRHS.m_AttributeVector.begin(); i != theEnd; ++i)
			{
				AttributeVectorEntry* const		theEntry = *i;

				assert(theEntry != 0);

				// Add the item...
				tempVector.push_back(
					getNewEntry(
						&*theEntry->m_Name.begin(),
						&*theEntry->m_Type.begin(),
						&*theEntry->m_Value.begin()));
			}

			// OK, we're safe, so swap the contents of the
			// containers.  This is guaranteed not to throw.
			m_AttributeVector.swap(tempVector);
		}

		assert(getLength() == theLength);
	}

	return *this;
}



AttributeListImpl&
AttributeListImpl::operator=(const AttributeListType&	theRHS)
{
	if (this != &theRHS)
	{
		// Note that we can't chain up to our base class operator=()
		// because it's private.

		// Add all of the attributes to this temp list,
		// then swap at the end.  This means we're exception
		// safe and don't need any try blocks.
		AttributeListImpl	theTempList(getMemoryManager());

		const unsigned int	theLength = theRHS.getLength();

		theTempList.reserve(theLength);

		// Add each attribute.
		for(unsigned int i = 0; i < theLength; i++)
		{
			theTempList.addAttribute(
					theRHS.getName(i),
					theRHS.getType(i),
					theRHS.getValue(i));
		}

		// Now that the temp list is built, swap everything. This is
		// guaranteed not to throw.
		swap(theTempList);
	}

	return *this;
}



unsigned int
AttributeListImpl::getLength() const
{
	// Make sure the mismatch between Xerces and vector<> doesn't cause a problem...
	assert(m_AttributeVector.size() == unsigned(m_AttributeVector.size()));

	return unsigned(m_AttributeVector.size());
}



const XMLCh*
AttributeListImpl::getName(const unsigned int index) const
{
	assert(index < getLength());

	return &*m_AttributeVector[index]->m_Name.begin();
}



const XMLCh*
AttributeListImpl::getType(const unsigned int index) const
{
	assert(index < getLength());

	return &*m_AttributeVector[index]->m_Type.begin();
}



const XMLCh*
AttributeListImpl::getValue(const unsigned int index) const
{
	assert(index < getLength());

	return &*m_AttributeVector[index]->m_Value.begin();
}

const XMLCh*
AttributeListImpl::	getValue(const char* const /*name*/) const
{
    assert(0);

    return 0;
}

#if defined(XALAN_NEEDS_EXPLICIT_TEMPLATE_INSTANTIATION)
bool
AttributeListImpl::NameCompareFunctor::operator()(const AttributeVectorEntry*	theEntry) const
{
	return equals(&*theEntry->m_Name.begin(), m_name);
}
#else
struct NameCompareFunctor
{
	NameCompareFunctor(const XMLCh*		theName) :
		m_name(theName)
	{
	}

	bool
	operator()(const AttributeVectorEntry*	theEntry) const
	{
		return equals(&*theEntry->m_Name.begin(), m_name);
	}

private:

	const XMLCh* const	m_name;
};
#endif



const XMLCh*
AttributeListImpl::getType(const XMLCh* const name) const
{
	assert(name != 0);

	const AttributeVectorType::const_iterator	i =
		XALAN_STD_QUALIFIER find_if(
			m_AttributeVector.begin(),
			m_AttributeVector.end(),
			NameCompareFunctor(name));

	if (i != m_AttributeVector.end())
	{
		// Found it, so return a pointer to the type.
		return &*(*i)->m_Type.begin();
	}
	else
	{
		return 0;
	}
}


const XMLCh*
AttributeListImpl::getValue(const XMLCh* const name) const
{
	assert(name != 0);

	const AttributeVectorType::const_iterator	i =
		XALAN_STD_QUALIFIER find_if(
			m_AttributeVector.begin(),
			m_AttributeVector.end(),
			NameCompareFunctor(name));

	if (i != m_AttributeVector.end())
	{
		// Found it, so return a pointer to the value.
		return &*(*i)->m_Value.begin();
	}
	else
	{
		return 0;
	}
}



void
AttributeListImpl::clear()
{
	m_cacheVector.insert(m_cacheVector.end(), m_AttributeVector.begin(), m_AttributeVector.end());

	// Clear everything out.
	m_AttributeVector.clear();
}



bool
AttributeListImpl::addAttribute(
			const XMLCh*	name,
			const XMLCh*	type,
			const XMLCh*	value)
{
	assert(name != 0);
	assert(type != 0);
	assert(value != 0);

	bool	fResult = false;

	XALAN_USING_STD(find_if)
	XALAN_USING_STD(copy)

	typedef AttributeVectorEntry::XMLChVectorType	XMLChVectorType;

	// Update the attribute, if it's already there...
	const AttributeVectorType::const_iterator	i =
		find_if(
			m_AttributeVector.begin(),
			m_AttributeVector.end(),
			NameCompareFunctor(name));

	if (i != m_AttributeVector.end())
	{
		// This is a special optimization for type, since it's (almost) always "CDATA".
		if (equals(type, &*(*i)->m_Type.begin()) == false)
		{
			// If necessary, create the a new vector and swap them.  Otherwise,
			// just copy the new data in.
			const XMLCh* const	theNewTypeEnd = AttributeVectorEntry::endArray(type) + 1;

			if ((*i)->m_Type.capacity() < XMLChVectorType::size_type(theNewTypeEnd - type))
			{
				XMLChVectorType		theNewType(type, theNewTypeEnd, getMemoryManager());

				theNewType.swap((*i)->m_Type);
			}
			else
			{
				copy(type, theNewTypeEnd, (*i)->m_Type.begin());
			}
		}

		const XMLCh* const	theNewValueEnd = AttributeVectorEntry::endArray(value) + 1;

		const XMLChVectorType::size_type	theNewSize =
						XMLChVectorType::size_type(theNewValueEnd - value);

		// If necessary, create the a new vector and swap them.  Otherwise,
		// just copy the new data in.
		if ((*i)->m_Value.capacity() < theNewSize)
		{
			XMLChVectorType		theNewValue(value, theNewValueEnd, getMemoryManager());

			theNewValue.swap((*i)->m_Value); 
		}
		else
		{
			(*i)->m_Value.resize(theNewSize);

			copy(value, theNewValueEnd, (*i)->m_Value.begin());
		}
	}
	else
	{
		if (m_AttributeVector.capacity() == 0)
		{
			m_AttributeVector.reserve(eDefaultVectorSize);
		}
        
        typedef XalanMemMgrAutoPtr<AttributeVectorEntry,true> AutoPtr;

		AutoPtr	theEntry(getMemoryManager(), getNewEntry(name, type, value));

		// Add the new one.
		m_AttributeVector.push_back(theEntry.get());


		theEntry.release();

		fResult = true;
	}

	return fResult;
}



AttributeVectorEntry*
AttributeListImpl::getNewEntry(
			const XMLCh*	name,
			const XMLCh*	type,
			const XMLCh*	value)
{
	if (m_cacheVector.empty() == true)
	{
        return AttributeVectorEntry::create(name, value, type,getMemoryManager());
	}
	else
	{
		AttributeVectorEntry* const		theEntry =
			m_cacheVector.back();

		theEntry->clear();

		assert(theEntry->m_Name.empty() == true && theEntry->m_Value.empty() == true && theEntry->m_Type.empty() == true);

		theEntry->m_Name.insert(theEntry->m_Name.begin(), name, AttributeVectorEntry::endArray(name) + 1);
		theEntry->m_Value.insert(theEntry->m_Value.begin(), value, AttributeVectorEntry::endArray(value) + 1);
		theEntry->m_Type.insert(theEntry->m_Type.begin(), type, AttributeVectorEntry::endArray(type) + 1);

		m_cacheVector.pop_back();

		return theEntry;
	}
}



bool
AttributeListImpl::removeAttribute(const XMLCh*		name)
{
	assert(name != 0);

	bool	fResult = false;

	// Update the attribute, if it's already there...
	const AttributeVectorType::iterator		i =
		XALAN_STD_QUALIFIER find_if(
			m_AttributeVector.begin(),
			m_AttributeVector.end(),
			NameCompareFunctor(name));

	if (i != m_AttributeVector.end())
	{
		m_cacheVector.push_back(*i);

		m_AttributeVector.erase(i);

		fResult = true;
	}

	return fResult;
}



XALAN_CPP_NAMESPACE_END
