#ifndef SVGListProperty_h
#define SVGListProperty_h
#include "SVGException.h"
#include "SVGPropertyTearOff.h"
#include "SVGPropertyTraits.h"
namespace WebCore {
enum ListModification {
ListModificationUnknown = 0,
ListModificationInsert = 1,
ListModificationReplace = 2,
ListModificationRemove = 3,
ListModificationAppend = 4
};
template<typename PropertyType>
class SVGAnimatedListPropertyTearOff;
template<typename PropertyType>
class SVGListProperty : public SVGProperty {
public:
typedef SVGListProperty<PropertyType> Self;
typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType;
typedef SVGPropertyTearOff<ListItemType> ListItemTearOff;
typedef RefPtr<ListItemTearOff> PtrListItemTearOff;
typedef SVGAnimatedListPropertyTearOff<PropertyType> AnimatedListPropertyTearOff;
typedef typename SVGAnimatedListPropertyTearOff<PropertyType>::ListWrapperCache ListWrapperCache;
bool canAlterList(ExceptionCode& ec) const
{
if (m_role == AnimValRole) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return false;
}
return true;
}
static void detachListWrappersAndResize(ListWrapperCache* wrappers, unsigned newListSize = 0)
{
ASSERT(wrappers);
for (auto& item : *wrappers) {
if (item)
item->detachWrapper();
}
if (newListSize)
wrappers->fill(0, newListSize);
else
wrappers->clear();
}
void detachListWrappers(unsigned newListSize)
{
detachListWrappersAndResize(m_wrappers, newListSize);
}
void setValuesAndWrappers(PropertyType* values, ListWrapperCache* wrappers, bool shouldOwnValues)
{
ASSERT(m_values);
ASSERT(m_wrappers);
ASSERT(m_role == AnimValRole);
if (m_ownsValues)
delete m_values;
m_values = values;
m_ownsValues = shouldOwnValues;
m_wrappers = wrappers;
ASSERT(m_values->size() == m_wrappers->size());
}
void clearValues(ExceptionCode& ec)
{
if (!canAlterList(ec))
return;
m_values->clear();
commitChange();
}
void clearValuesAndWrappers(ExceptionCode& ec)
{
if (!canAlterList(ec))
return;
detachListWrappers(0);
m_values->clear();
commitChange();
}
unsigned numberOfItems() const
{
return m_values->size();
}
ListItemType initializeValues(const ListItemType& newItem, ExceptionCode& ec)
{
if (!canAlterList(ec))
return ListItemType();
processIncomingListItemValue(newItem, 0);
m_values->clear();
m_values->append(newItem);
commitChange();
return newItem;
}
PtrListItemTearOff initializeValuesAndWrappers(PtrListItemTearOff newItem, ExceptionCode& ec)
{
ASSERT(m_wrappers);
if (!canAlterList(ec))
return nullptr;
if (!newItem) {
ec = SVGException::SVG_WRONG_TYPE_ERR;
return nullptr;
}
ASSERT(m_values->size() == m_wrappers->size());
processIncomingListItemWrapper(newItem, 0);
detachListWrappers(0);
m_values->clear();
m_values->append(newItem->propertyReference());
m_wrappers->append(newItem);
commitChange();
return newItem;
}
bool canGetItem(unsigned index, ExceptionCode& ec)
{
if (index >= m_values->size()) {
ec = INDEX_SIZE_ERR;
return false;
}
return true;
}
ListItemType getItemValues(unsigned index, ExceptionCode& ec)
{
if (!canGetItem(index, ec))
return ListItemType();
return m_values->at(index);
}
PtrListItemTearOff getItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, unsigned index, ExceptionCode& ec)
{
ASSERT(m_wrappers);
if (!canGetItem(index, ec))
return nullptr;
ASSERT(m_values->size() == m_wrappers->size());
RefPtr<ListItemTearOff> wrapper = m_wrappers->at(index);
if (!wrapper) {
wrapper = ListItemTearOff::create(animatedList, UndefinedRole, m_values->at(index));
m_wrappers->at(index) = wrapper;
}
return wrapper.release();
}
ListItemType insertItemBeforeValues(const ListItemType& newItem, unsigned index, ExceptionCode& ec)
{
if (!canAlterList(ec))
return ListItemType();
if (index > m_values->size())
index = m_values->size();
if (!processIncomingListItemValue(newItem, &index)) {
return newItem;
}
m_values->insert(index, newItem);
commitChange();
return newItem;
}
PtrListItemTearOff insertItemBeforeValuesAndWrappers(PtrListItemTearOff newItem, unsigned index, ExceptionCode& ec)
{
ASSERT(m_wrappers);
if (!canAlterList(ec))
return nullptr;
if (!newItem) {
ec = SVGException::SVG_WRONG_TYPE_ERR;
return nullptr;
}
if (index > m_values->size())
index = m_values->size();
ASSERT(m_values->size() == m_wrappers->size());
if (!processIncomingListItemWrapper(newItem, &index))
return newItem.release();
m_values->insert(index, newItem->propertyReference());
m_wrappers->insert(index, newItem);
commitChange();
return newItem;
}
bool canReplaceItem(unsigned index, ExceptionCode& ec)
{
if (!canAlterList(ec))
return false;
if (index >= m_values->size()) {
ec = INDEX_SIZE_ERR;
return false;
}
return true;
}
ListItemType replaceItemValues(const ListItemType& newItem, unsigned index, ExceptionCode& ec)
{
if (!canReplaceItem(index, ec))
return ListItemType();
if (!processIncomingListItemValue(newItem, &index)) {
return newItem;
}
if (m_values->isEmpty()) {
ec = INDEX_SIZE_ERR;
return ListItemType();
}
m_values->at(index) = newItem;
commitChange();
return newItem;
}
PtrListItemTearOff replaceItemValuesAndWrappers(PtrListItemTearOff newItem, unsigned index, ExceptionCode& ec)
{
ASSERT(m_wrappers);
if (!canReplaceItem(index, ec))
return nullptr;
if (!newItem) {
ec = SVGException::SVG_WRONG_TYPE_ERR;
return nullptr;
}
ASSERT(m_values->size() == m_wrappers->size());
if (!processIncomingListItemWrapper(newItem, &index))
return newItem;
if (m_values->isEmpty()) {
ASSERT(m_wrappers->isEmpty());
ec = INDEX_SIZE_ERR;
return nullptr;
}
RefPtr<ListItemTearOff> oldItem = m_wrappers->at(index);
if (oldItem)
oldItem->detachWrapper();
m_values->at(index) = newItem->propertyReference();
m_wrappers->at(index) = newItem;
commitChange();
return newItem;
}
bool canRemoveItem(unsigned index, ExceptionCode& ec)
{
if (!canAlterList(ec))
return false;
if (index >= m_values->size()) {
ec = INDEX_SIZE_ERR;
return false;
}
return true;
}
ListItemType removeItemValues(unsigned index, ExceptionCode& ec)
{
if (!canRemoveItem(index, ec))
return ListItemType();
ListItemType oldItem = m_values->at(index);
m_values->remove(index);
commitChange();
return oldItem;
}
PtrListItemTearOff removeItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, unsigned index, ExceptionCode& ec)
{
ASSERT(m_wrappers);
if (!canRemoveItem(index, ec))
return nullptr;
ASSERT(m_values->size() == m_wrappers->size());
RefPtr<ListItemTearOff> oldItem = m_wrappers->at(index);
if (!oldItem)
oldItem = ListItemTearOff::create(animatedList, UndefinedRole, m_values->at(index));
oldItem->detachWrapper();
m_wrappers->remove(index);
m_values->remove(index);
commitChange();
return oldItem.release();
}
ListItemType appendItemValues(const ListItemType& newItem, ExceptionCode& ec)
{
if (!canAlterList(ec))
return ListItemType();
processIncomingListItemValue(newItem, 0);
m_values->append(newItem);
commitChange(ListModificationAppend);
return newItem;
}
PtrListItemTearOff appendItemValuesAndWrappers(PtrListItemTearOff newItem, ExceptionCode& ec)
{
ASSERT(m_wrappers);
if (!canAlterList(ec))
return nullptr;
if (!newItem) {
ec = SVGException::SVG_WRONG_TYPE_ERR;
return nullptr;
}
ASSERT(m_values->size() == m_wrappers->size());
processIncomingListItemWrapper(newItem, 0);
m_values->append(newItem->propertyReference());
m_wrappers->append(newItem);
commitChange(ListModificationAppend);
return newItem;
}
PropertyType& values()
{
ASSERT(m_values);
return *m_values;
}
ListWrapperCache& wrappers() const
{
ASSERT(m_wrappers);
return *m_wrappers;
}
protected:
SVGListProperty(SVGPropertyRole role, PropertyType& values, ListWrapperCache* wrappers)
: m_role(role)
, m_ownsValues(false)
, m_values(&values)
, m_wrappers(wrappers)
{
}
virtual ~SVGListProperty()
{
if (m_ownsValues)
delete m_values;
}
virtual void commitChange() = 0;
virtual void commitChange(ListModification)
{
commitChange();
}
virtual bool processIncomingListItemValue(const ListItemType& newItem, unsigned* indexToModify) = 0;
virtual bool processIncomingListItemWrapper(RefPtr<ListItemTearOff>& newItem, unsigned* indexToModify) = 0;
SVGPropertyRole m_role;
bool m_ownsValues;
PropertyType* m_values;
ListWrapperCache* m_wrappers;
};
}
#endif // SVGListProperty_h