#ifndef SVGListPropertyHelper_h
#define SVGListPropertyHelper_h
#include "bindings/v8/ExceptionMessages.h"
#include "bindings/v8/ExceptionStatePlaceholder.h"
#include "core/dom/ExceptionCode.h"
#include "core/svg/properties/SVGProperty.h"
#include "wtf/PassRefPtr.h"
#include "wtf/Vector.h"
namespace WebCore {
template<typename Derived, typename ItemProperty>
class SVGListPropertyHelper : public SVGPropertyBase {
public:
typedef ItemProperty ItemPropertyType;
SVGListPropertyHelper()
: SVGPropertyBase(Derived::classType())
{
}
~SVGListPropertyHelper()
{
clear();
}
ItemPropertyType* at(size_t index)
{
ASSERT(index < m_values.size());
ASSERT(m_values.at(index)->ownerList() == this);
return m_values.at(index).get();
}
const ItemPropertyType* at(size_t index) const
{
return const_cast<SVGListPropertyHelper<Derived, ItemProperty>*>(this)->at(index);
}
class ConstIterator {
private:
typedef typename Vector<RefPtr<ItemPropertyType> >::const_iterator WrappedType;
public:
ConstIterator(WrappedType it)
: m_it(it)
{
}
ConstIterator& operator++() { ++m_it; return *this; }
bool operator==(const ConstIterator& o) const { return m_it == o.m_it; }
bool operator!=(const ConstIterator& o) const { return m_it != o.m_it; }
PassRefPtr<ItemPropertyType> operator*() { return *m_it; }
PassRefPtr<ItemPropertyType> operator->() { return *m_it; }
private:
WrappedType m_it;
};
ConstIterator begin() const
{
return ConstIterator(m_values.begin());
}
ConstIterator lastAppended() const
{
return ConstIterator(m_values.begin() + m_values.size() - 1);
}
ConstIterator end() const
{
return ConstIterator(m_values.end());
}
void append(PassRefPtr<ItemPropertyType> passNewItem)
{
RefPtr<ItemPropertyType> newItem = passNewItem;
ASSERT(newItem);
m_values.append(newItem);
newItem->setOwnerList(this);
}
bool operator==(const Derived&) const;
bool operator!=(const Derived& other) const
{
return !(*this == other);
}
bool isEmpty() const
{
return !length();
}
size_t length() const
{
return m_values.size();
}
void clear();
PassRefPtr<ItemPropertyType> initialize(PassRefPtr<ItemPropertyType>);
PassRefPtr<ItemPropertyType> getItem(size_t, ExceptionState&);
PassRefPtr<ItemPropertyType> insertItemBefore(PassRefPtr<ItemPropertyType>, size_t);
PassRefPtr<ItemPropertyType> removeItem(size_t, ExceptionState&);
PassRefPtr<ItemPropertyType> appendItem(PassRefPtr<ItemPropertyType>);
PassRefPtr<ItemPropertyType> replaceItem(PassRefPtr<ItemPropertyType>, size_t, ExceptionState&);
protected:
void deepCopy(PassRefPtr<Derived>);
private:
inline bool checkIndexBound(size_t, ExceptionState&);
bool removeFromOldOwnerListAndAdjustIndex(PassRefPtr<ItemPropertyType>, size_t* indexToModify);
size_t findItem(PassRefPtr<ItemPropertyType>);
Vector<RefPtr<ItemPropertyType> > m_values;
static PassRefPtr<Derived> toDerived(PassRefPtr<SVGPropertyBase> passBase)
{
if (!passBase)
return nullptr;
RefPtr<SVGPropertyBase> base = passBase;
ASSERT(base->type() == Derived::classType());
return static_pointer_cast<Derived>(base);
}
};
template<typename Derived, typename ItemProperty>
bool SVGListPropertyHelper<Derived, ItemProperty>::operator==(const Derived& other) const
{
if (length() != other.length())
return false;
size_t size = length();
for (size_t i = 0; i < size; ++i) {
if (*at(i) != *other.at(i))
return false;
}
return true;
}
template<typename Derived, typename ItemProperty>
void SVGListPropertyHelper<Derived, ItemProperty>::clear()
{
typename Vector<RefPtr<ItemPropertyType> >::const_iterator it = m_values.begin();
typename Vector<RefPtr<ItemPropertyType> >::const_iterator itEnd = m_values.end();
for (; it != itEnd; ++it) {
ASSERT((*it)->ownerList() == this);
(*it)->setOwnerList(0);
}
m_values.clear();
}
template<typename Derived, typename ItemProperty>
PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::initialize(PassRefPtr<ItemProperty> passNewItem)
{
RefPtr<ItemPropertyType> newItem = passNewItem;
removeFromOldOwnerListAndAdjustIndex(newItem, 0);
clear();
append(newItem);
return newItem.release();
}
template<typename Derived, typename ItemProperty>
PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::getItem(size_t index, ExceptionState& exceptionState)
{
if (!checkIndexBound(index, exceptionState))
return nullptr;
ASSERT(index < m_values.size());
ASSERT(m_values.at(index)->ownerList() == this);
return m_values.at(index);
}
template<typename Derived, typename ItemProperty>
PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::insertItemBefore(PassRefPtr<ItemProperty> passNewItem, size_t index)
{
if (index > m_values.size())
index = m_values.size();
RefPtr<ItemPropertyType> newItem = passNewItem;
if (!removeFromOldOwnerListAndAdjustIndex(newItem, &index)) {
return newItem.release();
}
m_values.insert(index, newItem);
newItem->setOwnerList(this);
return newItem.release();
}
template<typename Derived, typename ItemProperty>
PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::removeItem(size_t index, ExceptionState& exceptionState)
{
if (index >= m_values.size()) {
exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("index", index, m_values.size()));
return nullptr;
}
ASSERT(m_values.at(index)->ownerList() == this);
RefPtr<ItemPropertyType> oldItem = m_values.at(index);
m_values.remove(index);
oldItem->setOwnerList(0);
return oldItem.release();
}
template<typename Derived, typename ItemProperty>
PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::appendItem(PassRefPtr<ItemProperty> passNewItem)
{
RefPtr<ItemPropertyType> newItem = passNewItem;
removeFromOldOwnerListAndAdjustIndex(newItem, 0);
append(newItem);
return newItem.release();
}
template<typename Derived, typename ItemProperty>
PassRefPtr<ItemProperty> SVGListPropertyHelper<Derived, ItemProperty>::replaceItem(PassRefPtr<ItemProperty> passNewItem, size_t index, ExceptionState& exceptionState)
{
if (!checkIndexBound(index, exceptionState))
return nullptr;
RefPtr<ItemPropertyType> newItem = passNewItem;
if (!removeFromOldOwnerListAndAdjustIndex(newItem, &index)) {
return newItem.release();
}
if (m_values.isEmpty()) {
exceptionState.throwDOMException(IndexSizeError, String::format("Failed to replace the provided item at index %zu.", index));
return nullptr;
}
RefPtr<ItemPropertyType>& position = m_values[index];
ASSERT(position->ownerList() == this);
position->setOwnerList(0);
position = newItem;
newItem->setOwnerList(this);
return newItem.release();
}
template<typename Derived, typename ItemProperty>
bool SVGListPropertyHelper<Derived, ItemProperty>::checkIndexBound(size_t index, ExceptionState& exceptionState)
{
if (index >= m_values.size()) {
exceptionState.throwDOMException(IndexSizeError, ExceptionMessages::indexExceedsMaximumBound("index", index, m_values.size()));
return false;
}
return true;
}
template<typename Derived, typename ItemProperty>
bool SVGListPropertyHelper<Derived, ItemProperty>::removeFromOldOwnerListAndAdjustIndex(PassRefPtr<ItemPropertyType> passItem, size_t* indexToModify)
{
RefPtr<ItemPropertyType> item = passItem;
ASSERT(item);
RefPtr<Derived> ownerList = toDerived(item->ownerList());
if (!ownerList)
return true;
bool livesInOtherList = ownerList.get() != this;
size_t indexToRemove = ownerList->findItem(item);
ASSERT(indexToRemove != WTF::kNotFound);
if (!livesInOtherList && indexToModify && indexToRemove == *indexToModify)
return false;
ownerList->removeItem(indexToRemove, ASSERT_NO_EXCEPTION);
if (!indexToModify)
return true;
if (!livesInOtherList) {
size_t& index = *indexToModify;
if (static_cast<size_t>(indexToRemove) < index)
--index;
}
return true;
}
template<typename Derived, typename ItemProperty>
size_t SVGListPropertyHelper<Derived, ItemProperty>::findItem(PassRefPtr<ItemPropertyType> item)
{
return m_values.find(item);
}
template<typename Derived, typename ItemProperty>
void SVGListPropertyHelper<Derived, ItemProperty>::deepCopy(PassRefPtr<Derived> passFrom)
{
RefPtr<Derived> from = passFrom;
clear();
typename Vector<RefPtr<ItemPropertyType> >::const_iterator it = from->m_values.begin();
typename Vector<RefPtr<ItemPropertyType> >::const_iterator itEnd = from->m_values.end();
for (; it != itEnd; ++it) {
append((*it)->clone());
}
}
}
#endif