summaryrefslogtreecommitdiff
path: root/datamodel/dmattribute.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'datamodel/dmattribute.cpp')
-rw-r--r--datamodel/dmattribute.cpp3265
1 files changed, 3265 insertions, 0 deletions
diff --git a/datamodel/dmattribute.cpp b/datamodel/dmattribute.cpp
new file mode 100644
index 0000000..ba29592
--- /dev/null
+++ b/datamodel/dmattribute.cpp
@@ -0,0 +1,3265 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dmattributeinternal.h"
+#include "datamodel/dmelement.h"
+#include "dmelementdictionary.h"
+#include "datamodel/idatamodel.h"
+#include "datamodel.h"
+#include "tier1/uniqueid.h"
+#include "Color.h"
+#include "mathlib/vector.h"
+#include "tier1/utlstring.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/KeyValues.h"
+#include "tier1/mempool.h"
+#include "mathlib/vmatrix.h"
+#include "datamodel/dmattributevar.h"
+#include <ctype.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Tests equality
+//-----------------------------------------------------------------------------
+template< class T >
+bool IsAttributeEqual( const T& src1, const T& src2 )
+{
+ return src1 == src2;
+}
+
+template< class T >
+bool IsAttributeEqual( const CUtlVector<T> &src1, const CUtlVector<T> &src2 )
+{
+ if ( src1.Count() != src2.Count() )
+ return false;
+
+ for ( int i=0; i < src1.Count(); i++ )
+ {
+ if ( !( src1[i] == src2[i] ) )
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Typesafety check for element handles
+//-----------------------------------------------------------------------------
+static inline bool IsA( DmElementHandle_t hElement, UtlSymId_t type )
+{
+ // treat NULL, deleted, and unloaded elements as being of any type -
+ // when set, undeleted or loaded, this should be checked again
+ CDmElement *pElement = g_pDataModel->GetElement( hElement );
+ return pElement ? pElement->IsA( type ) : true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Element attributes are never directly unserialized
+//-----------------------------------------------------------------------------
+static bool Serialize( CUtlBuffer &buf, DmElementHandle_t src )
+{
+ Assert( 0 );
+ return false;
+}
+
+static bool Unserialize( CUtlBuffer &buf, DmElementHandle_t &dest )
+{
+ Assert( 0 );
+ return false;
+}
+
+static bool Serialize( CUtlBuffer &buf, const DmUnknownAttribute_t& src )
+{
+ Assert( 0 );
+ return false;
+}
+
+static bool Unserialize( CUtlBuffer &buf, DmUnknownAttribute_t &dest )
+{
+ Assert( 0 );
+ return false;
+}
+
+#include "tier1/utlbufferutil.h"
+
+//-----------------------------------------------------------------------------
+// Internal interface for dealing with generic attribute operations
+//-----------------------------------------------------------------------------
+abstract_class IDmAttributeOp
+{
+public:
+ virtual void* CreateAttributeData() = 0;
+ virtual void DestroyAttributeData( void *pData ) = 0;
+ virtual void SetDefaultValue( void *pData ) = 0;
+ virtual int DataSize() = 0;
+ virtual int ValueSize() = 0;
+ virtual bool SerializesOnMultipleLines() = 0;
+ virtual bool SkipUnserialize( CUtlBuffer& buf ) = 0;
+ virtual const char *AttributeTypeName() = 0;
+
+ virtual void SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue ) = 0;
+ virtual void SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue ) = 0;
+ virtual void Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue ) = 0;
+ virtual void SetToDefaultValue( CDmAttribute *pAttribute ) = 0;
+ virtual bool Serialize( const CDmAttribute *pAttribute, CUtlBuffer &buf ) = 0;
+ virtual bool Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf ) = 0;
+ virtual bool SerializeElement( const CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf ) = 0;
+ virtual bool UnserializeElement( CDmAttribute *pAttribute, CUtlBuffer &buf ) = 0;
+ virtual bool UnserializeElement( CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf ) = 0;
+ virtual void OnUnserializationFinished( CDmAttribute *pAttribute ) = 0;
+};
+
+
+//-----------------------------------------------------------------------------
+// Global table of generic attribute operations looked up by type
+//-----------------------------------------------------------------------------
+static IDmAttributeOp* s_pAttrInfo[ AT_TYPE_COUNT ];
+
+
+//-----------------------------------------------------------------------------
+//
+// Implementation of IDmAttributeOp for single-valued attributes
+//
+//-----------------------------------------------------------------------------
+template< class T >
+class CDmAttributeOp : public IDmAttributeOp
+{
+public:
+ virtual void* CreateAttributeData();
+ virtual void DestroyAttributeData( void *pData );
+ virtual void SetDefaultValue( void *pData );
+ virtual int DataSize();
+ virtual int ValueSize();
+ virtual bool SerializesOnMultipleLines();
+ virtual bool SkipUnserialize( CUtlBuffer& buf );
+ virtual const char *AttributeTypeName();
+
+ virtual void SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue );
+ virtual void SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue );
+ virtual void Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue );
+ virtual void SetToDefaultValue( CDmAttribute *pAttribute );
+ virtual bool Serialize( const CDmAttribute *pData, CUtlBuffer &buf );
+ virtual bool Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf );
+ virtual bool SerializeElement( const CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf );
+ virtual bool UnserializeElement( CDmAttribute *pAttribute, CUtlBuffer &buf );
+ virtual bool UnserializeElement( CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf );
+ virtual void OnUnserializationFinished( CDmAttribute *pAttribute );
+};
+
+
+//-----------------------------------------------------------------------------
+// Memory pool useds for CDmAttribute data
+// Over 8 bytes, use the small-block allocator (it aligns to 16 bytes)
+//-----------------------------------------------------------------------------
+CUtlMemoryPool g_DataAlloc4( sizeof( CDmAttribute ), 4, CUtlMemoryPool::GROW_SLOW, "4-byte data pool" );
+CUtlMemoryPool g_DataAlloc8( sizeof( CDmAttribute ), 8, CUtlMemoryPool::GROW_SLOW, "8-byte data pool" );
+
+template< class T > void* NewData()
+{
+ return new typename CDmAttributeInfo< T >::StorageType_t;
+}
+
+template< class T > void DeleteData( void *pData )
+{
+ delete reinterpret_cast< typename CDmAttributeInfo< T >::StorageType_t * >( pData );
+}
+
+#define USE_SPECIAL_ALLOCATOR( _className, _allocator ) \
+ template<> void* NewData< _className >() \
+ { \
+ void* pData = _allocator.Alloc( sizeof( CDmAttributeInfo< _className >::StorageType_t ) ); \
+ return ::new( pData ) CDmAttributeInfo< _className >::StorageType_t(); \
+ } \
+ template<> void DeleteData<_className>( void *pData ) \
+ { \
+ typedef CDmAttributeInfo< _className >::StorageType_t D; \
+ ( ( D * )pData )->~D(); \
+ _allocator.Free( pData ); \
+ }
+
+// make sure that the attribute data type sizes are what we think they are to choose the right allocator
+struct CSizeTest
+{
+ CSizeTest()
+ {
+ // test internal value attribute sizes
+ COMPILE_TIME_ASSERT( sizeof( int ) == 4 );
+ COMPILE_TIME_ASSERT( sizeof( float ) == 4 );
+ COMPILE_TIME_ASSERT( sizeof( bool ) <= 4 );
+ COMPILE_TIME_ASSERT( sizeof( Color ) == 4 );
+ COMPILE_TIME_ASSERT( sizeof( DmElementAttribute_t ) <= 8 );
+ COMPILE_TIME_ASSERT( sizeof( Vector2D ) == 8 );
+ }
+};
+static CSizeTest g_sizeTest;
+
+// turn memdbg off temporarily so we can get at placement new
+#include "tier0/memdbgoff.h"
+
+USE_SPECIAL_ALLOCATOR( bool, g_DataAlloc4 )
+USE_SPECIAL_ALLOCATOR( int, g_DataAlloc4 )
+USE_SPECIAL_ALLOCATOR( float, g_DataAlloc4 )
+USE_SPECIAL_ALLOCATOR( DmElementHandle_t, g_DataAlloc4 )
+USE_SPECIAL_ALLOCATOR( Color, g_DataAlloc4 )
+USE_SPECIAL_ALLOCATOR( Vector2D, g_DataAlloc8 )
+
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Create, destroy attribute data
+//-----------------------------------------------------------------------------
+template< class T >
+void* CDmAttributeOp<T>::CreateAttributeData()
+{
+ void *pData = NewData< T >();
+ CDmAttributeInfo< T >::SetDefaultValue( *reinterpret_cast<T*>( pData ) );
+ return pData;
+}
+
+template<> void* CDmAttributeOp< DmUnknownAttribute_t >::CreateAttributeData()
+{
+ // Fail if someone tries to create an AT_UNKNOWN attribute
+ Assert(0);
+ return NULL;
+}
+
+template< class T >
+void CDmAttributeOp<T>::DestroyAttributeData( void *pData )
+{
+ DeleteData< T >( pData );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the data to a default value, no undo (used for construction)
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmAttributeOp<T>::SetDefaultValue( void *pData )
+{
+ CDmAttributeInfo< T >::SetDefaultValue( *reinterpret_cast<T*>( pData ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Attribute type name, data size, value size
+//-----------------------------------------------------------------------------
+template< class T >
+const char *CDmAttributeOp<T>::AttributeTypeName()
+{
+ return CDmAttributeInfo<T>::AttributeTypeName();
+}
+
+template< class T >
+int CDmAttributeOp<T>::DataSize()
+{
+ return sizeof( typename CDmAttributeInfo< T >::StorageType_t );
+}
+
+template< class T >
+int CDmAttributeOp<T>::ValueSize()
+{
+ return sizeof( T );
+}
+
+
+//-----------------------------------------------------------------------------
+// Value-setting methods
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmAttributeOp<T>::SetToDefaultValue( CDmAttribute *pAttribute )
+{
+ T newValue;
+ CDmAttributeInfo< T >::SetDefaultValue( newValue );
+ pAttribute->SetValue( newValue );
+}
+
+template< class T >
+void CDmAttributeOp<T>::SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue )
+{
+ Assert(0);
+}
+
+template< class T >
+void CDmAttributeOp<T>::Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue )
+{
+ Assert(0);
+}
+
+template< class T >
+void CDmAttributeOp<T>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
+{
+ Assert( pAttribute->GetType() == valueType );
+ if ( pAttribute->GetType() == valueType )
+ {
+ pAttribute->SetValue( *reinterpret_cast< const T* >( pValue ) );
+ }
+}
+
+#define SET_VALUE_TYPE( _srcType ) \
+ case CDmAttributeInfo< _srcType >::ATTRIBUTE_TYPE: \
+ pAttribute->SetValue( *reinterpret_cast< const _srcType* >( pValue ) ); \
+ break;
+
+template<>
+void CDmAttributeOp<int>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
+{
+ switch( valueType )
+ {
+ SET_VALUE_TYPE( int );
+ SET_VALUE_TYPE( float );
+ SET_VALUE_TYPE( bool );
+
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+template<>
+void CDmAttributeOp<float>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
+{
+ switch( valueType )
+ {
+ SET_VALUE_TYPE( int );
+ SET_VALUE_TYPE( float );
+ SET_VALUE_TYPE( bool );
+
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+template<>
+void CDmAttributeOp<bool>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
+{
+ switch( valueType )
+ {
+ SET_VALUE_TYPE( int );
+ SET_VALUE_TYPE( float );
+ SET_VALUE_TYPE( bool );
+
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+
+template<>
+void CDmAttributeOp<QAngle>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
+{
+ switch( valueType )
+ {
+ SET_VALUE_TYPE( QAngle );
+ SET_VALUE_TYPE( Quaternion );
+
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+template<>
+void CDmAttributeOp<Quaternion>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
+{
+ switch( valueType )
+ {
+ SET_VALUE_TYPE( QAngle );
+ SET_VALUE_TYPE( Quaternion );
+
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods related to serialization
+//-----------------------------------------------------------------------------
+template< class T >
+bool CDmAttributeOp<T>::SerializesOnMultipleLines()
+{
+ return ::SerializesOnMultipleLines< T >();
+}
+
+template< class T >
+bool CDmAttributeOp<T>::SkipUnserialize( CUtlBuffer& buf )
+{
+ T dummy;
+ ::Unserialize( buf, dummy );
+ return buf.IsValid();
+}
+
+template< class T >
+bool CDmAttributeOp<T>::Serialize( const CDmAttribute *pAttribute, CUtlBuffer &buf )
+{
+ // NOTE: For this to work, the class must have a function defined of type
+ // bool Serialize( CUtlBuffer &buf, T &src )
+ return ::Serialize( buf, pAttribute->GetValue<T>() );
+}
+
+template< class T >
+bool CDmAttributeOp<T>::Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf )
+{
+ // NOTE: For this to work, the class must have a function defined of type
+ // bool Unserialize( CUtlBuffer &buf, T &src )
+
+ T tempVal;
+ bool bRet = ::Unserialize( buf, tempVal );
+
+ // Don't need undo hook since this goes through SetValue route
+ pAttribute->SetValue( tempVal );
+
+ return bRet;
+}
+
+template< class T >
+bool CDmAttributeOp<T>::SerializeElement( const CDmAttribute *pData, int nElement, CUtlBuffer &buf )
+{
+ Assert( 0 );
+ return false;
+}
+
+template< class T >
+bool CDmAttributeOp<T>::UnserializeElement( CDmAttribute *pData, CUtlBuffer &buf )
+{
+ Assert( 0 );
+ return false;
+}
+
+template< class T >
+bool CDmAttributeOp<T>::UnserializeElement( CDmAttribute *pData, int nElement, CUtlBuffer &buf )
+{
+ Assert( 0 );
+ return false;
+}
+
+template< class T >
+void CDmAttributeOp<T>::OnUnserializationFinished( CDmAttribute *pAttribute )
+{
+ CDmAttributeAccessor::OnChanged( pAttribute, false, true );
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// Implementation of IDmAttributeOp for array attributes
+//
+//-----------------------------------------------------------------------------
+template< class T >
+class CDmArrayAttributeOp : public CDmAttributeOp< CUtlVector< T > >
+{
+ typedef typename CDmAttributeInfo< CUtlVector< T > >::StorageType_t D;
+
+public:
+ // Inherited from IDmAttributeOp
+ virtual void SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue );
+ virtual void SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue );
+ virtual void Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue );
+ virtual bool Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf );
+ virtual bool SerializeElement( const CDmAttribute *pData, int nElement, CUtlBuffer &buf );
+ virtual bool UnserializeElement( CDmAttribute *pData, CUtlBuffer &buf );
+ virtual bool UnserializeElement( CDmAttribute *pData, int nElement, CUtlBuffer &buf );
+ virtual void OnUnserializationFinished( CDmAttribute *pAttribute );
+
+ // Other methods used by CDmaArrayBase
+ CDmArrayAttributeOp() : m_pAttribute( NULL ), m_pData( NULL ) {}
+ CDmArrayAttributeOp( CDmAttribute *pAttribute ) : m_pAttribute( pAttribute ), m_pData( (D*)m_pAttribute->GetAttributeData() ) {}
+
+ // Count
+ int Count() const;
+
+ // Insertion
+ int AddToTail( const T& src );
+ int InsertBefore( int elem, const T& src );
+ int InsertMultipleBefore( int elem, int num );
+
+ // Removal
+ void FastRemove( int elem );
+ void Remove( int elem );
+ void RemoveAll();
+ void RemoveMultiple( int elem, int num );
+ void Purge();
+
+ // Element Modification
+ void Set( int i, const T& value );
+ void SetMultiple( int i, int nCount, const T* pValue );
+ void Swap( int i, int j );
+
+ // Copy related methods
+ void CopyArray( const T *pArray, int size );
+ void SwapArray( CUtlVector< T >& src ); // Performs a pointer swap
+
+ void OnAttributeArrayElementAdded( int nFirstElem, int nLastElem, bool bUpdateElementReferences = true );
+ void OnAttributeArrayElementRemoved( int nFirstElem, int nLastElem );
+
+private:
+ bool ShouldInsertElement( const T& src );
+ bool ShouldInsert( const T& src );
+ void PerformCopyArray( const T *pArray, int nCount );
+ D& Data() { return *m_pData; }
+ const D& Data() const { return *m_pData; }
+
+ CDmAttribute *m_pAttribute;
+ D* m_pData;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+// Undo-related classes
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Undo attribute name change
+//-----------------------------------------------------------------------------
+class CUndoAttributeRenameElement : public CUndoElement
+{
+ typedef CUndoElement BaseClass;
+
+public:
+ CUndoAttributeRenameElement( CDmAttribute *pAttribute, const char *newName )
+ : BaseClass( "CUndoAttributeRenameElement" )
+ {
+ Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
+ m_hOwner = pAttribute->GetOwner()->GetHandle();
+ m_symAttributeOld = pAttribute->GetName();
+ m_symAttributeNew = newName;
+ }
+
+ virtual void Undo()
+ {
+ CDmElement *pOwner = GetOwner();
+ if ( pOwner )
+ {
+ pOwner->RenameAttribute( m_symAttributeNew.String(), m_symAttributeOld.String() );
+ }
+ }
+
+ virtual void Redo()
+ {
+ CDmElement *pOwner = GetOwner();
+ if ( pOwner )
+ {
+ pOwner->RenameAttribute( m_symAttributeOld.String(), m_symAttributeNew.String() );
+ }
+ }
+
+ virtual const char *GetDesc()
+ {
+ static char buf[ 128 ];
+
+ const char *base = BaseClass::GetDesc();
+ Q_snprintf( buf, sizeof( buf ), "%s (%s -> %s)", base, m_symAttributeOld.String(), m_symAttributeNew.String() );
+ return buf;
+ }
+
+private:
+ CDmElement *GetOwner()
+ {
+ return g_pDataModel->GetElement( m_hOwner );
+ }
+
+ CUtlSymbol m_symAttributeOld;
+ CUtlSymbol m_symAttributeNew;
+ DmElementHandle_t m_hOwner;
+};
+
+//-----------------------------------------------------------------------------
+// Undo single-valued attribute value changed
+//-----------------------------------------------------------------------------
+template< class T >
+class CUndoAttributeSetValueElement : public CUndoElement
+{
+ typedef CUndoElement BaseClass;
+public:
+ CUndoAttributeSetValueElement( CDmAttribute *pAttribute, const T &newValue )
+ : BaseClass( "CUndoAttributeSetValueElement" )
+ {
+ Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
+ m_hOwner = pAttribute->GetOwner()->GetHandle();
+ m_OldValue = pAttribute->GetValue<T>();
+ m_Value = newValue;
+ m_symAttribute = pAttribute->GetNameSymbol( );
+ }
+
+ CDmElement *GetOwner()
+ {
+ return g_pDataModel->GetElement( m_hOwner );
+ }
+
+ virtual void Undo()
+ {
+ CDmAttribute *pAttribute = GetAttribute();
+ if ( pAttribute && !pAttribute->IsFlagSet( FATTRIB_READONLY ) )
+ {
+ pAttribute->SetValue<T>( m_OldValue );
+ }
+ }
+ virtual void Redo()
+ {
+ CDmAttribute *pAttribute = GetAttribute();
+ if ( pAttribute && !pAttribute->IsFlagSet( FATTRIB_READONLY ) )
+ {
+ pAttribute->SetValue<T>( m_Value );
+ }
+ }
+
+ virtual const char *GetDesc()
+ {
+ static char buf[ 128 ];
+
+ const char *base = BaseClass::GetDesc();
+ CDmAttribute *pAtt = GetAttribute();
+ CUtlBuffer serialized( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ if ( pAtt && pAtt->GetType() != AT_ELEMENT )
+ {
+ ::Serialize( serialized, m_Value );
+ }
+ Q_snprintf( buf, sizeof( buf ), "%s(%s) = %s", base, g_pDataModel->GetString( m_symAttribute ), serialized.Base() ? (const char*)serialized.Base() : "\"\"" );
+ return buf;
+ }
+
+private:
+ CDmAttribute *GetAttribute()
+ {
+ CDmElement *pOwner = GetOwner();
+ if ( pOwner )
+ {
+ const char *pAttributeName = g_pDataModel->GetString( m_symAttribute );
+ return pOwner->GetAttribute( pAttributeName );
+ }
+ return NULL;
+ }
+
+ typedef T StorageType_t;
+
+ CUtlSymbol m_symAttribute;
+ DmElementHandle_t m_hOwner;
+ StorageType_t m_OldValue;
+ StorageType_t m_Value;
+};
+
+
+
+//-----------------------------------------------------------------------------
+// Base undo for array attributes
+//-----------------------------------------------------------------------------
+template< class T >
+class CUndoAttributeArrayBase : public CUndoElement
+{
+ typedef CUndoElement BaseClass;
+
+public:
+ CUndoAttributeArrayBase( CDmAttribute *pAttribute, const char *pUndoName ) : BaseClass( pUndoName )
+ {
+ m_hOwner = pAttribute->GetOwner()->GetHandle();
+ m_symAttribute = pAttribute->GetNameSymbol( );
+ }
+
+protected:
+ typedef typename CDmAttributeUndoStorageType< T >::UndoStorageType StorageType_t;
+
+ CDmElement *GetOwner()
+ {
+ return g_pDataModel->GetElement( m_hOwner );
+ }
+
+ const char *GetAttributeName()
+ {
+ return g_pDataModel->GetString( m_symAttribute );
+ }
+
+ CDmAttribute *GetAttribute()
+ {
+ const char *pAttributeName = GetAttributeName();
+ CDmElement *pOwner = GetOwner();
+ if ( pOwner )
+ return pOwner->GetAttribute( pAttributeName );
+ Assert( 0 );
+ return NULL;
+ }
+
+private:
+ CUtlSymbol m_symAttribute;
+ DmElementHandle_t m_hOwner;
+};
+
+
+//-----------------------------------------------------------------------------
+// Undo for setting a single element
+//-----------------------------------------------------------------------------
+template< class T >
+class CUndoArrayAttributeSetValueElement : public CUndoAttributeArrayBase<T>
+{
+ typedef CUndoAttributeArrayBase<T> BaseClass;
+
+public:
+ CUndoArrayAttributeSetValueElement( CDmAttribute *pAttribute, int slot, const T &newValue ) :
+ BaseClass( pAttribute, "CUndoArrayAttributeSetValueElement" ),
+ m_nSlot( slot )
+ {
+ Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
+
+ CDmrArray<T> array( pAttribute );
+ m_OldValue = array[ slot ];
+ m_Value = newValue;
+ }
+
+ virtual void Undo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ array.Set( m_nSlot, m_OldValue );
+ }
+ }
+
+ virtual void Redo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ array.Set( m_nSlot, m_Value );
+ }
+ }
+
+private:
+ int m_nSlot;
+ typename CUndoAttributeArrayBase<T>::StorageType_t m_OldValue;
+ typename CUndoAttributeArrayBase<T>::StorageType_t m_Value;
+};
+
+
+//-----------------------------------------------------------------------------
+// Undo for setting a multiple elements
+//-----------------------------------------------------------------------------
+template< class T >
+class CUndoArrayAttributeSetMultipleValueElement : public CUndoAttributeArrayBase<T>
+{
+ typedef CUndoAttributeArrayBase<T> BaseClass;
+
+public:
+ CUndoArrayAttributeSetMultipleValueElement( CDmAttribute *pAttribute, int nSlot, int nCount, const T *pNewValue ) :
+ BaseClass( pAttribute, "CUndoArrayAttributeSetMultipleValueElement" ),
+ m_nSlot( nSlot ), m_nCount( nCount )
+ {
+ Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
+ m_pOldValue = new typename CUndoAttributeArrayBase<T>::StorageType_t[nCount];
+ m_pValue = new typename CUndoAttributeArrayBase<T>::StorageType_t[nCount];
+
+ CDmrArray<T> array( pAttribute );
+ for ( int i = 0; i < nCount; ++i )
+ {
+ m_pOldValue[i] = array[ nSlot+i ];
+ m_pValue[i] = pNewValue[ i ];
+ }
+ }
+
+ ~CUndoArrayAttributeSetMultipleValueElement()
+ {
+ // this is a hack necessitated by MSVC's lack of partially specialized member template support
+ // (ie otherwise I'd just create a CUndoArrayAttributeSetMultipleValueElement< DmElementHandle_t,BaseClass> version with this code)
+ // anyways, the casting hackiness only happens when the value is actually a DmElementHandle_t, so it's completely safe
+ if ( CDmAttributeInfo< T >::AttributeType() == AT_ELEMENT )
+ {
+ DmElementHandle_t value = DMELEMENT_HANDLE_INVALID;
+ for ( int i = 0; i < m_nCount; ++i )
+ {
+ m_pOldValue[ i ] = m_pValue[ i ] = *( T* )&value;
+ }
+ }
+
+ delete[] m_pOldValue;
+ delete[] m_pValue;
+ }
+
+ virtual void Undo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ for ( int i = 0; i < m_nCount; ++i )
+ {
+ array.Set( m_nSlot+i, m_pOldValue[i] );
+ }
+ }
+ }
+
+ virtual void Redo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ for ( int i = 0; i < m_nCount; ++i )
+ {
+ array.Set( m_nSlot+i, m_pValue[i] );
+ }
+ }
+ }
+
+private:
+ int m_nSlot;
+ int m_nCount;
+ typename CUndoAttributeArrayBase<T>::StorageType_t *m_pOldValue;
+ typename CUndoAttributeArrayBase<T>::StorageType_t *m_pValue;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+// Implementation Undo for CDmAttributeTyped
+//
+//-----------------------------------------------------------------------------
+template< class T >
+class CUndoAttributeArrayInsertBefore : public CUndoAttributeArrayBase<T>
+{
+ typedef CUndoAttributeArrayBase<T> BaseClass;
+public:
+ CUndoAttributeArrayInsertBefore( CDmAttribute *pAttribute, int slot, int count = 1 ) :
+ BaseClass( pAttribute, "CUndoAttributeArrayInsertBefore" ),
+ m_nIndex( slot ), m_nCount( count )
+ {
+ Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
+ }
+
+ virtual void Undo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ array.RemoveMultiple( m_nIndex, m_nCount );
+ }
+ }
+
+ virtual void Redo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ T defaultVal;
+ CDmAttributeInfo<T>::SetDefaultValue( defaultVal );
+
+ array.InsertMultipleBefore( m_nIndex, m_nCount );
+ for( int i = 0; i < m_nCount; ++i )
+ {
+ array.Set( m_nIndex + i, defaultVal );
+ }
+ }
+ }
+
+private:
+ int m_nIndex;
+ int m_nCount;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+// Implementation Undo for inserting a copy
+//
+//-----------------------------------------------------------------------------
+template< class T >
+class CUndoAttributeArrayInsertCopyBefore : public CUndoAttributeArrayBase<T>
+{
+ typedef CUndoAttributeArrayBase<T> BaseClass;
+
+public:
+ CUndoAttributeArrayInsertCopyBefore( CDmAttribute *pAttribute, int slot, const T& newValue ) :
+ BaseClass( pAttribute, "CUndoAttributeArrayInsertCopyBefore" ),
+ m_nIndex( slot ),
+ m_newValue( newValue )
+ {
+ Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
+ }
+
+ virtual void Undo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ array.Remove( m_nIndex );
+ }
+ }
+
+ virtual void Redo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ array.InsertBefore( m_nIndex, m_newValue );
+ }
+ }
+
+private:
+ int m_nIndex;
+ typename CUndoAttributeArrayBase<T>::StorageType_t m_newValue;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+// Implementation Undo for remove
+//
+//-----------------------------------------------------------------------------
+template< class T >
+class CUndoAttributeArrayRemoveElement : public CUndoAttributeArrayBase<T>
+{
+ typedef CUndoAttributeArrayBase<T> BaseClass;
+
+public:
+ CUndoAttributeArrayRemoveElement( CDmAttribute *pAttribute, bool fastRemove, int elem, int count ) :
+ BaseClass( pAttribute, "CUndoAttributeArrayRemoveElement" ),
+ m_bFastRemove( fastRemove ), m_nIndex( elem ), m_nCount( count )
+ {
+ Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
+ Assert( m_nCount >= 1 );
+ // If it's fastremove, count must == 1
+ Assert( !m_bFastRemove || m_nCount == 1 );
+ CDmrArray< T > array( pAttribute );
+ Assert( array.IsValid() );
+ for ( int i = 0 ; i < m_nCount; ++i )
+ {
+ m_OldValues.AddToTail( array[ elem + i ] );
+ }
+ }
+
+ ~CUndoAttributeArrayRemoveElement()
+ {
+ // this is a hack necessitated by MSVC's lack of partially specialized member template support
+ // (ie otherwise I'd just create a CUndoArrayAttributeSetMultipleValueElement< DmElementHandle_t,BaseClass> version with this code)
+ // anyways, the casting hackiness only happens when the value is actually a DmElementHandle_t, so it's completely safe
+ if ( CDmAttributeInfo< T >::AttributeType() == AT_ELEMENT )
+ {
+ DmElementHandle_t value = DMELEMENT_HANDLE_INVALID;
+ for ( int i = 0; i < m_nCount; ++i )
+ {
+ m_OldValues[ i ] = *( T* )&value;
+ }
+ m_OldValues.RemoveAll();
+ }
+ }
+
+ virtual void Undo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ if ( m_bFastRemove )
+ {
+ Assert( m_nCount == 1 );
+ Assert( m_OldValues.Count() == 1 );
+
+ if ( array.Count() > m_nIndex )
+ {
+ // Get value at previous index (it was moved down from the "end" before
+ T m_EndValue = array.Get( m_nIndex );
+
+ // Restore previous value
+ array.Set( m_nIndex, m_OldValues[ 0 ] );
+
+ // Put old value back to end of array
+ array.AddToTail( m_EndValue );
+ }
+ else
+ {
+ Assert( array.Count() == m_nIndex );
+ array.AddToTail( m_OldValues[ 0 ] );
+ }
+ }
+ else
+ {
+ int insertPos = m_nIndex;
+ for ( int i = 0; i < m_nCount; ++i )
+ {
+ array.InsertBefore( insertPos++, m_OldValues[ i ] );
+ }
+ }
+ }
+ }
+
+ virtual void Redo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ if ( m_bFastRemove )
+ {
+ Assert( m_nCount == 1 );
+ Assert( m_OldValues.Count() == 1 );
+
+ array.FastRemove( m_nIndex );
+ }
+ else
+ {
+ array.RemoveMultiple( m_nIndex, m_nCount );
+ }
+ }
+ }
+
+ virtual const char *GetDesc()
+ {
+ static char buf[ 128 ];
+
+ const char *base = BaseClass::GetDesc();
+ Q_snprintf( buf, sizeof( buf ), "%s (%s) = remove( pos %i, count %i )", base, GetAttributeName(), m_nIndex, m_nCount );
+ return buf;
+ }
+
+private:
+ bool m_bFastRemove;
+ int m_nIndex;
+ int m_nCount;
+ CUtlVector< typename CUndoAttributeArrayBase<T>::StorageType_t > m_OldValues;
+};
+
+
+template< class T >
+class CUndoAttributeArrayCopyAllElement : public CUndoAttributeArrayBase<T>
+{
+ typedef CUndoAttributeArrayBase<T> BaseClass;
+public:
+ CUndoAttributeArrayCopyAllElement( CDmAttribute *pAttribute, const T *pNewValues, int nNewSize, bool purgeOnRemove = false )
+ : BaseClass( pAttribute, "CUndoAttributeArrayCopyAllElement" ),
+ m_bPurge( purgeOnRemove )
+ {
+ Assert( pAttribute->GetOwner() && pAttribute->GetOwner()->GetFileId() != DMFILEID_INVALID );
+ CDmrArray< T > att( pAttribute );
+ Assert( att.IsValid() );
+
+ if ( pNewValues != NULL && nNewSize > 0 )
+ {
+ m_pNewValues = new typename CUndoAttributeArrayBase<T>::StorageType_t[ nNewSize ];
+ for ( int i = 0; i < nNewSize; ++i )
+ {
+ m_pNewValues[ i ] = pNewValues[ i ];
+ }
+ m_nNewSize = nNewSize;
+ }
+ else
+ {
+ m_pNewValues = NULL;
+ m_nNewSize = 0;
+ }
+
+ int nOldSize = att.Count();
+ const T *pOldValues = att.Base();
+ if ( pOldValues != NULL && nOldSize > 0 )
+ {
+ m_pOldValues = new typename CUndoAttributeArrayBase<T>::StorageType_t[ nOldSize ];
+ for ( int i = 0; i < nOldSize; ++i )
+ {
+ m_pOldValues[ i ] = pOldValues[ i ];
+ }
+ m_nOldSize = nOldSize;
+ }
+ else
+ {
+ m_pOldValues = NULL;
+ m_nOldSize = 0;
+ }
+ }
+
+ ~CUndoAttributeArrayCopyAllElement()
+ {
+ // this is a hack necessitated by MSVC's lack of partially specialized member template support
+ // (ie otherwise I'd just create a CUndoArrayAttributeSetMultipleValueElement< DmElementHandle_t,BaseClass> version with this code)
+ // anyways, the casting hackiness only happens when the value is actually a DmElementHandle_t, so it's completely safe
+ if ( CDmAttributeInfo< T >::AttributeType() == AT_ELEMENT )
+ {
+ DmElementHandle_t value = DMELEMENT_HANDLE_INVALID;
+ for ( int i = 0; i < m_nOldSize; ++i )
+ {
+ m_pOldValues[ i ] = *( T* )&value;
+ }
+ for ( int i = 0; i < m_nNewSize; ++i )
+ {
+ m_pNewValues[ i ] = *( T* )&value;
+ }
+ }
+
+ delete[] m_pOldValues;
+ delete[] m_pNewValues;
+ }
+
+ virtual void Undo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ array.RemoveAll();
+ for ( int i = 0; i < m_nOldSize; ++i )
+ {
+ array.AddToTail( m_pOldValues[ i ] );
+ }
+ }
+ }
+
+ virtual void Redo()
+ {
+ CDmrArray<T> array( GetAttribute() );
+ if ( array.IsValid() )
+ {
+ array.RemoveAll();
+ for ( int i = 0; i < m_nNewSize; ++i )
+ {
+ array.AddToTail( m_pNewValues[ i ] );
+ }
+
+ if ( m_bPurge )
+ {
+ Assert( array.Count() == 0 );
+ array.Purge();
+ }
+ }
+ }
+
+private:
+ typename CUndoAttributeArrayBase<T>::StorageType_t *m_pOldValues;
+ int m_nOldSize;
+ typename CUndoAttributeArrayBase<T>::StorageType_t *m_pNewValues;
+ int m_nNewSize;
+ bool m_bPurge;
+};
+
+
+
+//-----------------------------------------------------------------------------
+// CDmArrayAttributeOp implementation.
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Callbacks when elements are added + removed
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmArrayAttributeOp<T>::OnAttributeArrayElementAdded( int nFirstElem, int nLastElem, bool bUpdateElementReferences )
+{
+ CDmElement *pOwner = m_pAttribute->GetOwner();
+ if ( m_pAttribute->IsFlagSet( FATTRIB_HAS_ARRAY_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( pOwner ) )
+ {
+ pOwner->OnAttributeArrayElementAdded( m_pAttribute, nFirstElem, nLastElem );
+ }
+}
+
+template< > inline void CDmArrayAttributeOp< DmElementHandle_t >::OnAttributeArrayElementAdded( int nFirstElem, int nLastElem, bool bUpdateElementReferences )
+{
+ CDmElement *pOwner = m_pAttribute->GetOwner();
+ if ( m_pAttribute->IsFlagSet( FATTRIB_HAS_ARRAY_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( pOwner ) )
+ {
+ pOwner->OnAttributeArrayElementAdded( m_pAttribute, nFirstElem, nLastElem );
+ }
+
+ if ( bUpdateElementReferences )
+ {
+ for ( int i = nFirstElem; i <= nLastElem; ++i )
+ {
+ g_pDataModelImp->OnElementReferenceAdded( Data()[ i ], m_pAttribute );
+ }
+ }
+}
+
+template< class T >
+void CDmArrayAttributeOp<T>::OnAttributeArrayElementRemoved( int nFirstElem, int nLastElem )
+{
+ CDmElement *pOwner = m_pAttribute->GetOwner();
+ if ( m_pAttribute->IsFlagSet( FATTRIB_HAS_ARRAY_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( pOwner ) )
+ {
+ pOwner->OnAttributeArrayElementRemoved( m_pAttribute, nFirstElem, nLastElem );
+ }
+}
+
+template< > void CDmArrayAttributeOp< DmElementHandle_t >::OnAttributeArrayElementRemoved( int nFirstElem, int nLastElem )
+{
+ CDmElement *pOwner = m_pAttribute->GetOwner();
+ if ( m_pAttribute->IsFlagSet( FATTRIB_HAS_ARRAY_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( pOwner ) )
+ {
+ pOwner->OnAttributeArrayElementRemoved( m_pAttribute, nFirstElem, nLastElem );
+ }
+
+ for ( int i = nFirstElem; i <= nLastElem; ++i )
+ {
+ g_pDataModelImp->OnElementReferenceRemoved( Data()[ i ], m_pAttribute );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Count
+//-----------------------------------------------------------------------------
+template< class T >
+int CDmArrayAttributeOp<T>::Count() const
+{
+ return Data().Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Should we insert this element into the list?
+//-----------------------------------------------------------------------------
+template< class T >
+inline bool CDmArrayAttributeOp<T>::ShouldInsertElement( const T& src )
+{
+ return true;
+}
+
+template<> inline bool CDmArrayAttributeOp<DmElementHandle_t>::ShouldInsertElement( const DmElementHandle_t& src )
+{
+ // For element, we need to check that the type matches
+ if ( !IsA( src, Data().m_ElementType ) )
+ return false;
+
+ if ( m_pAttribute->IsFlagSet( FATTRIB_NODUPLICATES ) )
+ {
+ // See if value exists
+ int idx = Data().Find( src );
+ if ( idx != Data().InvalidIndex() )
+ return false;
+ }
+
+ return true;
+}
+
+template< class T >
+inline bool CDmArrayAttributeOp<T>::ShouldInsert( const T& src )
+{
+ if ( !ShouldInsertElement( src ) )
+ return false;
+
+ return m_pAttribute->MarkDirty();
+}
+
+
+//-----------------------------------------------------------------------------
+// Insert Before
+//-----------------------------------------------------------------------------
+template< class T >
+int CDmArrayAttributeOp<T>::InsertBefore( int elem, const T& src )
+{
+ if ( !ShouldInsert( src ) )
+ return Data().InvalidIndex();
+
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayInsertCopyBefore<T> *pUndo = new CUndoAttributeArrayInsertCopyBefore<T>( m_pAttribute, elem, src );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ int nIndex = Data().InsertBefore( elem, src );
+ OnAttributeArrayElementAdded( nIndex, nIndex );
+ m_pAttribute->OnChanged( true );
+ return nIndex;
+}
+
+template< class T >
+inline int CDmArrayAttributeOp<T>::AddToTail( const T& src )
+{
+ return InsertBefore( Data().Count(), src );
+}
+
+
+//-----------------------------------------------------------------------------
+// Insert Multiple Before
+//-----------------------------------------------------------------------------
+template< class T >
+int CDmArrayAttributeOp<T>::InsertMultipleBefore( int elem, int num )
+{
+ if ( !m_pAttribute->MarkDirty() )
+ return Data().InvalidIndex();
+
+ // UNDO HOOK
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayInsertBefore<T> *pUndo = new CUndoAttributeArrayInsertBefore<T>( m_pAttribute, elem, num );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ int index = Data().InsertMultipleBefore( elem, num );
+ for ( int i = 0; i < num; ++i )
+ {
+ CDmAttributeInfo<T>::SetDefaultValue( Data()[ index + i ] );
+ }
+ OnAttributeArrayElementAdded( index, index + num - 1 );
+ m_pAttribute->OnChanged( true );
+ return index;
+}
+
+
+//-----------------------------------------------------------------------------
+// Removal
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmArrayAttributeOp<T>::FastRemove( int elem )
+{
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ // UNDO HOOK
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayRemoveElement<T> *pUndo = new CUndoAttributeArrayRemoveElement<T>( m_pAttribute, true, elem, 1 );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( elem, elem );
+ Data().FastRemove( elem );
+ m_pAttribute->OnChanged( true );
+}
+
+template< class T >
+void CDmArrayAttributeOp<T>::Remove( int elem )
+{
+ if ( !Data().IsValidIndex( elem ) )
+ return;
+
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ // UNDO HOOK
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayRemoveElement<T> *pUndo = new CUndoAttributeArrayRemoveElement<T>( m_pAttribute, false, elem, 1 );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( elem, elem );
+ Data().Remove( elem );
+ m_pAttribute->OnChanged( true );
+}
+
+template< class T >
+void CDmArrayAttributeOp<T>::RemoveAll()
+{
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ // UNDO HOOK
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayCopyAllElement<T> *pUndo = new CUndoAttributeArrayCopyAllElement<T>( m_pAttribute, NULL, 0 );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( 0, Data().Count() - 1 );
+ Data().RemoveAll();
+ m_pAttribute->OnChanged( true );
+}
+
+template< class T >
+void CDmArrayAttributeOp<T>::RemoveMultiple( int elem, int num )
+{
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ // UNDO HOOK
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayRemoveElement<T> *pUndo = new CUndoAttributeArrayRemoveElement<T>( m_pAttribute, false, elem, num );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( elem, elem + num - 1 );
+ Data().RemoveMultiple( elem, num );
+ m_pAttribute->OnChanged( true );
+}
+
+// Memory deallocation
+template< class T >
+void CDmArrayAttributeOp<T>::Purge()
+{
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ // UNDO HOOK
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayCopyAllElement<T> *pUndo = new CUndoAttributeArrayCopyAllElement<T>( m_pAttribute, NULL, true );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( 0, Data().Count() - 1 );
+ Data().Purge();
+ m_pAttribute->OnChanged( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Copy Array
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmArrayAttributeOp<T>::PerformCopyArray( const T *pArray, int nCount )
+{
+ Data().CopyArray( pArray, nCount );
+}
+
+template<> void CDmArrayAttributeOp<DmElementHandle_t>::PerformCopyArray( const DmElementHandle_t *pArray, int nCount )
+{
+ Data().RemoveAll();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ if ( ShouldInsertElement( pArray[ i ] ) )
+ {
+ Data().AddToTail( pArray[ i ] );
+ }
+ }
+}
+
+template< class T >
+void CDmArrayAttributeOp<T>::CopyArray( const T *pArray, int nCount )
+{
+ if ( Data().Base() == pArray )
+ {
+ int nCurrentCount = Data().Count();
+ if ( nCurrentCount > nCount )
+ {
+ RemoveMultiple( nCount, nCurrentCount - nCount );
+ }
+ else if ( nCurrentCount < nCount )
+ {
+ InsertMultipleBefore( nCurrentCount, nCount - nCurrentCount );
+ }
+ return;
+ }
+
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ // UNDO HOOK
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayCopyAllElement<T> *pUndo = new CUndoAttributeArrayCopyAllElement<T>( m_pAttribute, pArray, nCount );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( 0, Data().Count() - 1 );
+ PerformCopyArray( pArray, nCount );
+ OnAttributeArrayElementAdded( 0, Data().Count() - 1 );
+ m_pAttribute->OnChanged( true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Swap Array
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmArrayAttributeOp<T>::SwapArray( CUtlVector< T >& src )
+{
+ // this is basically just a faster version of CopyArray
+ // the end result (for purposes of undo) are the same
+ // but there's no copy - just a pointer/etc swap
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ // UNDO HOOK
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoAttributeArrayCopyAllElement<T> *pUndo = new CUndoAttributeArrayCopyAllElement<T>( m_pAttribute, src.Base(), src.Count() );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( 0, Data().Count() - 1 );
+ Data().Swap( src );
+ OnAttributeArrayElementAdded( 0, Data().Count() - 1 );
+ m_pAttribute->OnChanged( true );
+}
+
+
+template< > void CDmArrayAttributeOp<DmElementHandle_t>::SwapArray( CUtlVector< DmElementHandle_t >& src )
+{
+ // This feature doesn't work for elements..
+ // Can't do it owing to typesafety reasons as well as supporting the NODUPLICATES feature.
+ Assert( 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Set value
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmArrayAttributeOp<T>::Set( int i, const T& value )
+{
+ if ( i < 0 || i >= Data().Count() )
+ {
+ Assert( !"CDmAttributeArray<T>::Set out of range value!\n" );
+ return;
+ }
+
+ // Don't bother doing anything if the attribute is equal
+ if ( IsAttributeEqual( Data()[i], value ) )
+ return;
+
+ if ( !ShouldInsert( value ) )
+ return;
+
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoArrayAttributeSetValueElement<T> *pUndo = new CUndoArrayAttributeSetValueElement<T>( m_pAttribute, i, value );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( i, i );
+ Data()[i] = value;
+ OnAttributeArrayElementAdded( i, i );
+ m_pAttribute->OnChanged( false );
+}
+
+template< class T >
+void CDmArrayAttributeOp<T>::Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue )
+{
+ if ( valueType == ArrayTypeToValueType( pAttribute->GetType() ) )
+ {
+ // This version is in IDmAttributeOp
+ CDmArrayAttributeOp< T > array( pAttribute );
+ array.Set( i, *(const T*)pValue );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Set multiple values
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmArrayAttributeOp<T>::SetMultiple( int i, int nCount, const T* pValue )
+{
+ if ( i < 0 || ( i+nCount ) > Data().Count() )
+ {
+ AssertMsg( 0, "CDmAttributeArray<T>::SetMultiple out of range value!\n" );
+ return;
+ }
+
+ // Test for equality
+ bool bEqual = true;
+ for ( int j = 0; j < nCount; ++j )
+ {
+ if ( !IsAttributeEqual( Data()[i+j], pValue[j] ) )
+ {
+ bEqual = false;
+ break;
+ }
+ }
+ if ( bEqual )
+ return;
+
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoArrayAttributeSetMultipleValueElement<T> *pUndo = new CUndoArrayAttributeSetMultipleValueElement<T>( m_pAttribute, i, nCount, pValue );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+ OnAttributeArrayElementRemoved( i, i+nCount-1 );
+ for ( int j = 0; j < nCount; ++j )
+ {
+ if ( ShouldInsertElement( pValue[j] ) )
+ {
+ Data()[i+j] = pValue[j];
+ }
+ }
+ OnAttributeArrayElementAdded( i, i+nCount-1 );
+ m_pAttribute->OnChanged( false );
+}
+
+template< class T >
+void CDmArrayAttributeOp<T>::SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue )
+{
+ if ( valueType == ArrayTypeToValueType( pAttribute->GetType() ) )
+ {
+ // This version is in IDmAttributeOp
+ CDmArrayAttributeOp< T > array( pAttribute );
+ array.SetMultiple( i, nCount, (const T*)pValue );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Version of SetValue that's in IDmAttributeOp
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmArrayAttributeOp<T>::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue )
+{
+ Assert( pAttribute->GetType() == valueType );
+ if ( pAttribute->GetType() == valueType )
+ {
+ CDmArrayAttributeOp<T> accessor( pAttribute );
+ const CUtlVector<T>* pArray = reinterpret_cast< const CUtlVector<T>* >( pValue );
+ accessor.CopyArray( pArray->Base(), pArray->Count() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Swap
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmArrayAttributeOp<T>::Swap( int i, int j )
+{
+ if ( i == j )
+ return;
+
+ // TODO - define Swap<T> for all attribute types to make swapping strings
+ // and voids fast (via pointer swaps, rather than 3 copies!)
+ T vk = Data()[ i ];
+ if ( IsAttributeEqual( vk, Data()[j] ) )
+ return;
+
+ if ( !m_pAttribute->MarkDirty() )
+ return;
+
+ if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) )
+ {
+ CUndoArrayAttributeSetValueElement<T> *pUndo = new CUndoArrayAttributeSetValueElement<T>( m_pAttribute, i, Data()[ j ] );
+ g_pDataModel->AddUndoElement( pUndo );
+ pUndo = new CUndoArrayAttributeSetValueElement<T>( m_pAttribute, j, vk );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_pAttribute->PreChanged();
+
+ OnAttributeArrayElementRemoved( i, i );
+ Data()[i] = Data()[j];
+ OnAttributeArrayElementAdded( i, i );
+
+ OnAttributeArrayElementRemoved( j, j );
+ Data()[j] = vk;
+ OnAttributeArrayElementAdded( j, j );
+
+ m_pAttribute->OnChanged( false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods related to serialization
+//-----------------------------------------------------------------------------
+template< class T >
+bool CDmArrayAttributeOp<T>::Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf )
+{
+ if ( !pAttribute->MarkDirty() )
+ return false;
+
+ MEM_ALLOC_CREDIT_CLASS();
+
+ CUtlVector< T > tempVal;
+ bool bRet = ::Unserialize( buf, tempVal );
+
+ // Don't need undo hook since this goes through Swap route
+ CDmArrayAttributeOp<T> accessor( pAttribute );
+ accessor.SwapArray( tempVal );
+
+ return bRet;
+}
+
+template<> bool CDmArrayAttributeOp<DmElementHandle_t>::Unserialize( CDmAttribute *pAttribute, CUtlBuffer &buf )
+{
+ // Need to specialize this because element handles can't use SwapArray
+ // because it's incapable of doing type safety checks or looking for FATTRIB_NODUPLICATES
+ if ( !CDmAttributeAccessor::MarkDirty( pAttribute ) )
+ return false;
+
+ MEM_ALLOC_CREDIT_CLASS();
+
+ CUtlVector< DmElementHandle_t > tempVal;
+ bool bRet = ::Unserialize( buf, tempVal );
+
+ // Don't need undo hook since this goes through copy route
+ CDmArrayAttributeOp<DmElementHandle_t> accessor( pAttribute );
+ accessor.CopyArray( tempVal.Base(), tempVal.Count() );
+
+ return bRet;
+}
+
+// Serialization of a single element
+template< class T >
+bool CDmArrayAttributeOp<T>::SerializeElement( const CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf )
+{
+ CDmrArrayConst<T> array( pAttribute );
+ return ::Serialize( buf, array[ nElement ] );
+}
+
+template< class T >
+bool CDmArrayAttributeOp<T>::UnserializeElement( CDmAttribute *pAttribute, CUtlBuffer &buf )
+{
+ if ( !CDmAttributeAccessor::MarkDirty( pAttribute ) )
+ return false;
+
+ MEM_ALLOC_CREDIT_CLASS();
+
+ T temp;
+ bool bReadElement = ::Unserialize( buf, temp );
+ if ( bReadElement )
+ {
+ pAttribute->PreChanged();
+
+ CDmArrayAttributeOp<T> accessor( pAttribute );
+ accessor.AddToTail( temp );
+
+ pAttribute->OnChanged( true );
+ }
+ return bReadElement;
+}
+
+template< class T >
+bool CDmArrayAttributeOp<T>::UnserializeElement( CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf )
+{
+ if ( !CDmAttributeAccessor::MarkDirty( pAttribute ) )
+ return false;
+
+ CDmrArray<T> array( pAttribute );
+ if ( array.Count() <= nElement )
+ return false;
+
+ MEM_ALLOC_CREDIT_CLASS();
+
+ pAttribute->PreChanged();
+ bool bReadElement = ::Unserialize( buf, *const_cast<T*>( &array[nElement] ) );
+ if ( bReadElement )
+ {
+ pAttribute->OnChanged();
+ }
+ return bReadElement;
+}
+
+template< class T >
+void CDmArrayAttributeOp<T>::OnUnserializationFinished( CDmAttribute *pAttribute )
+{
+ CDmArrayAttributeOp<T> ref( pAttribute );
+ int nCount = ref.Count();
+ if ( nCount > 0 )
+ {
+ ref.OnAttributeArrayElementAdded( 0, nCount - 1, false );
+ }
+ CDmAttributeAccessor::OnChanged( pAttribute, true, true );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CDmAttribute begins here
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Memory pool used for CDmAttribute
+//-----------------------------------------------------------------------------
+CUtlMemoryPool g_AttrAlloc( sizeof( CDmAttribute ), 32, CUtlMemoryPool::GROW_SLOW, "CDmAttribute pool" );
+
+
+//-----------------------------------------------------------------------------
+// Class factory
+//-----------------------------------------------------------------------------
+
+// turn memdbg off temporarily so we can get at placement new
+#include "tier0/memdbgoff.h"
+
+CDmAttribute *CDmAttribute::CreateAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName )
+{
+ switch( type )
+ {
+ case AT_UNKNOWN:
+ Assert( 0 );
+ return NULL;
+
+ default:
+ {
+ void *pMem = g_AttrAlloc.Alloc( sizeof( CDmAttribute ) );
+ return ::new( pMem ) CDmAttribute( pOwner, type, pAttributeName );
+ }
+ }
+}
+
+CDmAttribute *CDmAttribute::CreateExternalAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName, void *pExternalMemory )
+{
+ switch( type )
+ {
+ case AT_UNKNOWN:
+ Assert( 0 );
+ return NULL;
+
+ default:
+ {
+ void *pMem = g_AttrAlloc.Alloc( sizeof( CDmAttribute ) );
+ return ::new( pMem ) CDmAttribute( pOwner, type, pAttributeName, pExternalMemory );
+ }
+ }
+}
+
+void CDmAttribute::DestroyAttribute( CDmAttribute *pAttribute )
+{
+ if ( !pAttribute )
+ return;
+
+ switch( pAttribute->GetType() )
+ {
+ case AT_UNKNOWN:
+ break;
+
+ default:
+ pAttribute->~CDmAttribute();
+
+#ifdef _DEBUG
+ memset( pAttribute, 0xDD, sizeof(CDmAttribute) );
+#endif
+
+ g_AttrAlloc.Free( pAttribute );
+ break;
+ }
+}
+
+// turn memdbg back on after using placement new
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CDmAttribute::CDmAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName ) :
+ m_pData( NULL )
+{
+ Init( pOwner, type, pAttributeName );
+ CreateAttributeData();
+}
+
+CDmAttribute::CDmAttribute( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName, void *pMemory ) :
+ m_pData( pMemory )
+{
+ Init( pOwner, type, pAttributeName );
+ s_pAttrInfo[ GetType() ]->SetDefaultValue( m_pData );
+ AddFlag( FATTRIB_EXTERNAL );
+}
+
+
+void CDmAttribute::Init( CDmElement *pOwner, DmAttributeType_t type, const char *pAttributeName )
+{
+ // FIXME - this is just here temporarily to catch old code trying to create type and id attributes
+ // this shouldn't actually be illegal, since users should be able to create attributes of whatever name they want
+ Assert( V_strcmp( pAttributeName, "type" ) && V_strcmp( pAttributeName, "id" ) );
+
+ m_pOwner = pOwner;
+ m_Name = g_pDataModel->GetSymbol( pAttributeName );
+ m_nFlags = type;
+ m_Handle = DMATTRIBUTE_HANDLE_INVALID;
+ m_pNext = NULL;
+ m_hMailingList = DMMAILINGLIST_INVALID;
+
+ switch ( type )
+ {
+ case AT_ELEMENT:
+ case AT_ELEMENT_ARRAY:
+ case AT_OBJECTID:
+ case AT_OBJECTID_ARRAY:
+ m_nFlags |= FATTRIB_TOPOLOGICAL;
+ break;
+ }
+}
+
+CDmAttribute::~CDmAttribute()
+{
+ switch( GetType() )
+ {
+ case AT_ELEMENT:
+ g_pDataModelImp->OnElementReferenceRemoved( GetValue<DmElementHandle_t>(), this );
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ {
+ CDmrElementArray<> array( this );
+ int nElements = array.Count();
+ for ( int i = 0; i < nElements; ++i )
+ {
+ g_pDataModelImp->OnElementReferenceRemoved( array.GetHandle( i ), this );
+ }
+ }
+ break;
+ }
+
+ CleanupMailingList();
+ InvalidateHandle();
+ DeleteAttributeData();
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates the attribute data
+//-----------------------------------------------------------------------------
+void CDmAttribute::CreateAttributeData()
+{
+ // Free the attribute memory
+ if ( !IsFlagSet( FATTRIB_EXTERNAL ) )
+ {
+ Assert( !m_pData );
+ m_pData = s_pAttrInfo[ GetType() ]->CreateAttributeData( );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Deletes the attribute data
+//-----------------------------------------------------------------------------
+void CDmAttribute::DeleteAttributeData()
+{
+ // Free the attribute memory
+ if ( m_pData && !IsFlagSet( FATTRIB_EXTERNAL ) )
+ {
+ s_pAttrInfo[ GetType() ]->DestroyAttributeData( m_pData );
+ m_pData = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Used only in attribute element arrays
+//-----------------------------------------------------------------------------
+void CDmAttribute::SetElementTypeSymbol( UtlSymId_t typeSymbol )
+{
+ switch ( GetType() )
+ {
+ case AT_ELEMENT:
+ {
+ DmElementAttribute_t *pData = GetData< DmElementHandle_t >();
+ Assert( pData->m_Handle == DMELEMENT_HANDLE_INVALID || ::IsA( pData->m_Handle, typeSymbol ) );
+ pData->m_ElementType = typeSymbol;
+ }
+ break;
+
+ case AT_ELEMENT_ARRAY:
+ {
+#ifdef _DEBUG
+ CDmrElementArray<> array( this );
+ if ( array.GetElementType() != UTL_INVAL_SYMBOL )
+ {
+ int i;
+ int c = array.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ Assert( array.GetHandle( i ) == DMELEMENT_HANDLE_INVALID || ::IsA( array.GetHandle( i ), typeSymbol ) );
+ }
+ }
+#endif
+
+ DmElementArray_t *pData = GetArrayData< DmElementHandle_t >();
+ pData->m_ElementType = typeSymbol;
+ }
+ break;
+
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+UtlSymId_t CDmAttribute::GetElementTypeSymbol() const
+{
+ switch ( GetType() )
+ {
+ case AT_ELEMENT:
+ return GetData< DmElementHandle_t >()->m_ElementType;
+
+ case AT_ELEMENT_ARRAY:
+ return GetArrayData< DmElementHandle_t >()->m_ElementType;
+
+ default:
+ Assert(0);
+ break;
+ }
+
+ return UTL_INVAL_SYMBOL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is modification allowed in this phase?
+//-----------------------------------------------------------------------------
+bool CDmAttribute::ModificationAllowed() const
+{
+ if ( IsFlagSet( FATTRIB_READONLY ) )
+ return false;
+
+ DmPhase_t phase = g_pDmElementFramework->GetPhase();
+ if ( phase == PH_EDIT )
+ return true;
+ if ( ( phase == PH_OPERATE ) && !IsFlagSet( FATTRIB_TOPOLOGICAL ) )
+ return true;
+
+ return false;
+}
+
+bool CDmAttribute::MarkDirty()
+{
+ if ( !ModificationAllowed() )
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ AddFlag( FATTRIB_DIRTY | FATTRIB_OPERATOR_DIRTY );
+ CDmeElementAccessor::MarkDirty( m_pOwner );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Called before and after the attribute has changed
+//-----------------------------------------------------------------------------
+void CDmAttribute::PreChanged()
+{
+ if ( IsFlagSet( FATTRIB_HAS_PRE_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( m_pOwner ) )
+ {
+ m_pOwner->PreAttributeChanged( this );
+ }
+
+ // FIXME: What about mailing lists?
+}
+
+void CDmAttribute::OnChanged( bool bArrayCountChanged, bool bIsTopological )
+{
+ if ( IsFlagSet( FATTRIB_HAS_CALLBACK ) && !CDmeElementAccessor::IsBeingUnserialized( m_pOwner ) )
+ {
+ m_pOwner->OnAttributeChanged( this );
+ }
+
+ if ( ( m_hMailingList != DMMAILINGLIST_INVALID ) && !CDmeElementAccessor::IsBeingUnserialized( m_pOwner ) )
+ {
+ if ( !g_pDataModelImp->PostAttributeChanged( m_hMailingList, this ) )
+ {
+ CleanupMailingList();
+ }
+ }
+
+ if ( bIsTopological || IsTopological( GetType() ) )
+ {
+ g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
+ }
+ else
+ {
+ g_pDataModelImp->NotifyState( bArrayCountChanged ? NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE : NOTIFY_CHANGE_ATTRIBUTE_VALUE );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Type conversion related methods
+//-----------------------------------------------------------------------------
+template< class T > bool CDmAttribute::IsTypeConvertable() const
+{
+ return ( CDmAttributeInfo< T >::ATTRIBUTE_TYPE == GetType() );
+}
+
+template<> bool CDmAttribute::IsTypeConvertable<bool>() const
+{
+ DmAttributeType_t type = GetType();
+ return ( type == AT_BOOL || type == AT_INT || type == AT_FLOAT );
+}
+
+template<> bool CDmAttribute::IsTypeConvertable<int>() const
+{
+ DmAttributeType_t type = GetType();
+ return ( type == AT_INT || type == AT_BOOL || type == AT_FLOAT );
+}
+
+template<> bool CDmAttribute::IsTypeConvertable<float>() const
+{
+ DmAttributeType_t type = GetType();
+ return ( type == AT_FLOAT || type == AT_INT || type == AT_BOOL );
+}
+
+template<> bool CDmAttribute::IsTypeConvertable<QAngle>() const
+{
+ DmAttributeType_t type = GetType();
+ return ( type == AT_QANGLE || type == AT_QUATERNION );
+}
+
+template<> bool CDmAttribute::IsTypeConvertable<Quaternion>() const
+{
+ DmAttributeType_t type = GetType();
+ return ( type == AT_QUATERNION || type == AT_QANGLE);
+}
+
+template< class T > void CDmAttribute::CopyData( const T& value )
+{
+ *reinterpret_cast< T* >( m_pData ) = value;
+}
+
+template< class T > void CDmAttribute::CopyDataOut( T& value ) const
+{
+ value = *reinterpret_cast< const T* >( m_pData );
+}
+
+template<> void CDmAttribute::CopyData( const bool& value )
+{
+ switch( GetType() )
+ {
+ case AT_BOOL:
+ *reinterpret_cast< bool* >( m_pData ) = value;
+ break;
+
+ case AT_INT:
+ *reinterpret_cast< int* >( m_pData ) = value ? 1 : 0;
+ break;
+
+ case AT_FLOAT:
+ *reinterpret_cast< float* >( m_pData ) = value ? 1.0f : 0.0f;
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyDataOut( bool& value ) const
+{
+ switch( GetType() )
+ {
+ case AT_BOOL:
+ value = *reinterpret_cast< bool* >( m_pData );
+ break;
+
+ case AT_INT:
+ value = *reinterpret_cast< int* >( m_pData ) != 0;
+ break;
+
+ case AT_FLOAT:
+ value = *reinterpret_cast< float* >( m_pData ) != 0.0f;
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyData( const int& value )
+{
+ switch( GetType() )
+ {
+ case AT_BOOL:
+ *reinterpret_cast< bool* >( m_pData ) = value != 0;
+ break;
+
+ case AT_INT:
+ *reinterpret_cast< int* >( m_pData ) = value;
+ break;
+
+ case AT_FLOAT:
+ *reinterpret_cast< float* >( m_pData ) = value;
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyDataOut( int& value ) const
+{
+ switch( GetType() )
+ {
+ case AT_BOOL:
+ value = *reinterpret_cast< bool* >( m_pData ) ? 1 : 0;
+ break;
+
+ case AT_INT:
+ value = *reinterpret_cast< int* >( m_pData );
+ break;
+
+ case AT_FLOAT:
+ value = *reinterpret_cast< float* >( m_pData );
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyData( const float& value )
+{
+ switch( GetType() )
+ {
+ case AT_BOOL:
+ *reinterpret_cast< bool* >( m_pData ) = value != 0.0f;
+ break;
+
+ case AT_INT:
+ *reinterpret_cast< int* >( m_pData ) = value;
+ break;
+
+ case AT_FLOAT:
+ *reinterpret_cast< float* >( m_pData ) = value;
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyDataOut( float& value ) const
+{
+ switch( GetType() )
+ {
+ case AT_BOOL:
+ value = *reinterpret_cast< bool* >( m_pData ) ? 1.0f : 0.0f;
+ break;
+
+ case AT_INT:
+ value = *reinterpret_cast< int* >( m_pData );
+ break;
+
+ case AT_FLOAT:
+ value = *reinterpret_cast< float* >( m_pData );
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyData( const QAngle& value )
+{
+ switch( GetType() )
+ {
+ case AT_QANGLE:
+ *reinterpret_cast< QAngle* >( m_pData ) = value;
+ break;
+
+ case AT_QUATERNION:
+ {
+ Quaternion qValue;
+ AngleQuaternion( value, qValue );
+ *reinterpret_cast< Quaternion* >( m_pData ) = qValue;
+ }
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyDataOut( QAngle& value ) const
+{
+ switch( GetType() )
+ {
+ case AT_QANGLE:
+ value = *reinterpret_cast< QAngle* >( m_pData );
+ break;
+
+ case AT_QUATERNION:
+ QuaternionAngles( *reinterpret_cast< Quaternion* >( m_pData ), value );
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyData( const Quaternion& value )
+{
+ switch( GetType() )
+ {
+ case AT_QANGLE:
+ {
+ QAngle aValue;
+ QuaternionAngles( value, aValue );
+ *reinterpret_cast< QAngle* >( m_pData ) = aValue;
+ }
+ break;
+
+ case AT_QUATERNION:
+ *reinterpret_cast< Quaternion* >( m_pData ) = value;
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyDataOut( Quaternion& value ) const
+{
+ switch( GetType() )
+ {
+ case AT_QANGLE:
+ AngleQuaternion( *reinterpret_cast< QAngle* >( m_pData ), value );
+ break;
+
+ case AT_QUATERNION:
+ value = *reinterpret_cast< Quaternion* >( m_pData );
+ break;
+ }
+}
+
+template<> void CDmAttribute::CopyData( const DmElementHandle_t& value )
+{
+ g_pDataModelImp->OnElementReferenceRemoved( GetValue<DmElementHandle_t>(), this );
+ *reinterpret_cast< DmElementHandle_t* >( m_pData ) = value;
+ g_pDataModelImp->OnElementReferenceAdded( value, this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Should we be allowed to modify the attribute data?
+//-----------------------------------------------------------------------------
+template< class T >
+bool CDmAttribute::ShouldModify( const T& value )
+{
+ if ( !IsTypeConvertable<T>() )
+ return false;
+
+ if ( ( GetType() == CDmAttributeInfo<T>::ATTRIBUTE_TYPE ) && IsAttributeEqual( GetValue<T>(), value ) )
+ return false;
+
+ return MarkDirty();
+}
+
+template<> bool CDmAttribute::ShouldModify( const DmElementHandle_t& value )
+{
+ if ( !IsTypeConvertable<DmElementHandle_t>() )
+ return false;
+
+ if ( IsAttributeEqual( GetValue<DmElementHandle_t>(), value ) )
+ return false;
+
+ DmElementAttribute_t *pData = GetData<DmElementHandle_t>();
+ if ( pData->m_ElementType != UTL_INVAL_SYMBOL && !::IsA( value, pData->m_ElementType ) )
+ return false;
+
+ return MarkDirty();
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point for single-valued SetValue
+//-----------------------------------------------------------------------------
+template< class T >
+void CDmAttribute::SetValue( const T &value )
+{
+ if ( !ShouldModify( value ) )
+ return;
+
+ // UNDO Hook
+ if ( g_pDataModel->UndoEnabledForElement( m_pOwner ) )
+ {
+ CUndoAttributeSetValueElement<T> *pUndo = new CUndoAttributeSetValueElement<T>( this, value );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ bool bIsBeingUnserialized = CDmeElementAccessor::IsBeingUnserialized( m_pOwner );
+ if ( IsFlagSet( FATTRIB_HAS_PRE_CALLBACK ) && !bIsBeingUnserialized )
+ {
+ m_pOwner->PreAttributeChanged( this );
+ }
+
+ CopyData< T >( value );
+
+ if ( !bIsBeingUnserialized )
+ {
+ if ( IsFlagSet( FATTRIB_HAS_CALLBACK ) )
+ {
+ m_pOwner->OnAttributeChanged( this );
+ }
+
+ if ( m_hMailingList != DMMAILINGLIST_INVALID )
+ {
+ if ( !g_pDataModelImp->PostAttributeChanged( m_hMailingList, this ) )
+ {
+ CleanupMailingList();
+ }
+ }
+ }
+
+ g_pDataModelImp->NotifyState( IsTopological( GetType() ) ? NOTIFY_CHANGE_TOPOLOGICAL : NOTIFY_CHANGE_ATTRIBUTE_VALUE );
+}
+
+
+//-----------------------------------------------------------------------------
+// Versions that work on arrays
+//-----------------------------------------------------------------------------
+#define ATTRIBUTE_SET_VALUE_ARRAY( _type ) \
+ template<> void CDmAttribute::SetValue( const CUtlVector< _type >& value ) \
+ { \
+ CDmArrayAttributeOp< _type > accessor( this ); \
+ accessor.CopyArray( value.Base(), value.Count() ); \
+ }
+
+void CDmAttribute::SetValue( const CDmAttribute *pAttribute )
+{
+ s_pAttrInfo[ GetType() ]->SetValue( this, pAttribute->GetType(), pAttribute->GetAttributeData() );
+}
+
+void CDmAttribute::SetValue( CDmAttribute *pAttribute )
+{
+ s_pAttrInfo[ GetType() ]->SetValue( this, pAttribute->GetType(), pAttribute->GetAttributeData() );
+}
+
+void CDmAttribute::SetValue( DmAttributeType_t valueType, const void *pValue )
+{
+ s_pAttrInfo[ GetType() ]->SetValue( this, valueType, pValue );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the attribute to its default value based on its type
+//-----------------------------------------------------------------------------
+void CDmAttribute::SetToDefaultValue()
+{
+ s_pAttrInfo[ GetType() ]->SetToDefaultValue( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert to and from string
+//-----------------------------------------------------------------------------
+void CDmAttribute::SetValueFromString( const char *pValue )
+{
+ switch ( GetType() )
+ {
+ case AT_STRING:
+ SetValue( pValue );
+ break;
+
+ default:
+ {
+ int nLen = pValue ? Q_strlen( pValue ) : 0;
+ if ( nLen == 0 )
+ {
+ SetToDefaultValue();
+ break;
+ }
+
+ CUtlBuffer buf( pValue, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+ if ( !Unserialize( buf ) )
+ {
+ SetToDefaultValue();
+ }
+ }
+ break;
+ }
+}
+
+const char *CDmAttribute::GetValueAsString( char *pBuffer, size_t nBufLen ) const
+{
+ Assert( pBuffer );
+ CUtlBuffer buf( pBuffer, nBufLen, CUtlBuffer::TEXT_BUFFER );
+ Serialize( buf );
+ return pBuffer;
+}
+
+
+//-----------------------------------------------------------------------------
+// Name, type
+//-----------------------------------------------------------------------------
+const char* CDmAttribute::GetTypeString() const
+{
+ return ::GetTypeString( GetType() );
+}
+
+const char *GetTypeString( DmAttributeType_t type )
+{
+ if ( ( type >= 0 ) && ( type < AT_TYPE_COUNT ) )
+ return s_pAttrInfo[ type ]->AttributeTypeName();
+ return "unknown";
+}
+
+
+void CDmAttribute::SetName( const char *pNewName )
+{
+ if ( m_pOwner->HasAttribute( pNewName ) && Q_stricmp( GetName(), pNewName ) )
+ {
+ Warning( "Tried to rename from '%s' to '%s', but '%s' already exists\n",
+ GetName(), pNewName, pNewName );
+ return;
+ }
+
+ if ( !MarkDirty() )
+ return;
+
+ // UNDO Hook
+ if ( g_pDataModel->UndoEnabledForElement( m_pOwner ) )
+ {
+ CUndoAttributeRenameElement *pUndo = new CUndoAttributeRenameElement( this, pNewName );
+ g_pDataModel->AddUndoElement( pUndo );
+ }
+
+ m_Name = g_pDataModel->GetSymbol( pNewName );
+ g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Serialization
+//-----------------------------------------------------------------------------
+bool CDmAttribute::SerializesOnMultipleLines() const
+{
+ return s_pAttrInfo[ GetType() ]->SerializesOnMultipleLines();
+}
+
+bool CDmAttribute::Serialize( CUtlBuffer &buf ) const
+{
+ return s_pAttrInfo[ GetType() ]->Serialize( this, buf );
+}
+
+bool CDmAttribute::Unserialize( CUtlBuffer &buf )
+{
+ return s_pAttrInfo[ GetType() ]->Unserialize( this, buf );
+}
+
+bool CDmAttribute::SerializeElement( int nElement, CUtlBuffer &buf ) const
+{
+ return s_pAttrInfo[ GetType() ]->SerializeElement( this, nElement, buf );
+}
+
+bool CDmAttribute::UnserializeElement( CUtlBuffer &buf )
+{
+ return s_pAttrInfo[ GetType() ]->UnserializeElement( this, buf );
+}
+
+bool CDmAttribute::UnserializeElement( int nElement, CUtlBuffer &buf )
+{
+ return s_pAttrInfo[ GetType() ]->UnserializeElement( this, nElement, buf );
+}
+
+// Called by elements after unserialization of their attributes is complete
+void CDmAttribute::OnUnserializationFinished()
+{
+ return s_pAttrInfo[ GetType() ]->OnUnserializationFinished( this );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Methods related to attribute change notification
+//-----------------------------------------------------------------------------
+void CDmAttribute::CleanupMailingList()
+{
+ if ( m_hMailingList != DMMAILINGLIST_INVALID )
+ {
+ g_pDataModelImp->DestroyMailingList( m_hMailingList );
+ m_hMailingList = DMMAILINGLIST_INVALID;
+ }
+}
+
+void CDmAttribute::NotifyWhenChanged( DmElementHandle_t h, bool bNotify )
+{
+ if ( bNotify )
+ {
+ if ( m_hMailingList == DMMAILINGLIST_INVALID )
+ {
+ m_hMailingList = g_pDataModelImp->CreateMailingList();
+ }
+ g_pDataModelImp->AddElementToMailingList( m_hMailingList, h );
+ return;
+ }
+
+ if ( m_hMailingList != DMMAILINGLIST_INVALID )
+ {
+ if ( !g_pDataModelImp->RemoveElementFromMailingList( m_hMailingList, h ) )
+ {
+ CleanupMailingList();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Get the attribute/create an attribute handle
+//-----------------------------------------------------------------------------
+DmAttributeHandle_t CDmAttribute::GetHandle( bool bCreate )
+{
+ if ( (m_Handle == DMATTRIBUTE_HANDLE_INVALID) && bCreate )
+ {
+ m_Handle = g_pDataModelImp->AcquireAttributeHandle( this );
+ }
+
+ Assert( (m_Handle == DMATTRIBUTE_HANDLE_INVALID) || g_pDataModel->IsAttributeHandleValid( m_Handle ) );
+ return m_Handle;
+}
+
+void CDmAttribute::InvalidateHandle()
+{
+ g_pDataModelImp->ReleaseAttributeHandle( m_Handle );
+ m_Handle = DMATTRIBUTE_HANDLE_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Memory usage estimations
+//-----------------------------------------------------------------------------
+bool HandleCompare( const DmElementHandle_t &a, const DmElementHandle_t &b )
+{
+ return a == b;
+}
+
+unsigned int HandleHash( const DmElementHandle_t &h )
+{
+ return (unsigned int)h;
+}
+
+int CDmAttribute::EstimateMemoryUsage( TraversalDepth_t depth ) const
+{
+ CUtlHash< DmElementHandle_t > visited( 1024, 0, 0, HandleCompare, HandleHash );
+ return EstimateMemoryUsageInternal( visited, depth, 0 ) ;
+}
+
+int CDmAttribute::EstimateMemoryUsageInternal( CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories ) const
+{
+ int nOverhead = sizeof( *this );
+ int nAttributeDataSize = s_pAttrInfo[ GetType() ]->DataSize();
+ int nTotalMemory = nOverhead + nAttributeDataSize;
+ int nAttributeExtraDataSize = 0;
+
+ if ( IsArrayType( GetType() ) )
+ {
+ CDmrGenericArrayConst array( this );
+ int nCount = array.Count();
+ nAttributeExtraDataSize = nCount * s_pAttrInfo[ GetType() ]->ValueSize(); // Data in the UtlVector
+ int nMallocOverhead = ( array.Count() == 0 ) ? 0 : 8; // malloc overhead inside the vector
+ nOverhead += nMallocOverhead;
+ nTotalMemory += nAttributeExtraDataSize + nMallocOverhead;
+ }
+
+ if ( pCategories )
+ {
+ ++pCategories[MEMORY_CATEGORY_ATTRIBUTE_COUNT];
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += nAttributeDataSize + nAttributeExtraDataSize;
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += nOverhead;
+ if ( !IsDataInline() )
+ {
+ pCategories[MEMORY_CATEGORY_OUTER] -= nAttributeDataSize;
+ Assert( pCategories[MEMORY_CATEGORY_OUTER] >= 0 );
+ nTotalMemory -= nAttributeDataSize;
+ }
+ }
+
+ switch ( GetType() )
+ {
+ case AT_STRING:
+ {
+ const CUtlString &value = GetValue<CUtlString>();
+ if ( pCategories )
+ {
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += value.Length() + 1;
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += 8;
+ }
+ return nTotalMemory + value.Length() + 1 + 8; // string's length skips trailing null
+ }
+
+ case AT_STRING_ARRAY:
+ {
+ const CUtlVector< CUtlString > &array = GetValue< CUtlVector< CUtlString > >( );
+ for ( int i = 0; i < array.Count(); ++i )
+ {
+ int nStrLen = array[ i ].Length() + 1;
+ if ( pCategories )
+ {
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += nStrLen;
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += 8;
+ }
+ nTotalMemory += nStrLen + 8; // string's length skips trailing null
+ }
+ return nTotalMemory;
+ }
+
+ case AT_VOID:
+ {
+ const CUtlBinaryBlock &value = GetValue< CUtlBinaryBlock >();
+ if ( pCategories )
+ {
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += value.Length();
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += 8;
+ }
+ return nTotalMemory + value.Length() + 8;
+ }
+
+ case AT_VOID_ARRAY:
+ {
+ const CUtlVector< CUtlBinaryBlock > &array = GetValue< CUtlVector< CUtlBinaryBlock > >();
+ for ( int i = 0; i < array.Count(); ++i )
+ {
+ if ( pCategories )
+ {
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_DATA] += array[ i ].Length();
+ pCategories[MEMORY_CATEGORY_ATTRIBUTE_OVERHEAD] += 8;
+ }
+ nTotalMemory += array[ i ].Length() + 8;
+ }
+ return nTotalMemory;
+ }
+
+ case AT_ELEMENT:
+ if ( ShouldTraverse( this, depth ) )
+ {
+ CDmElement *pElement = GetValueElement<CDmElement>();
+ if ( pElement )
+ {
+ nTotalMemory += CDmeElementAccessor::EstimateMemoryUsage( pElement, visited, depth, pCategories );
+ }
+ }
+ return nTotalMemory;
+
+ case AT_ELEMENT_ARRAY:
+ if ( ShouldTraverse( this, depth ) )
+ {
+ CDmrElementArrayConst<> array( this );
+ for ( int i = 0; i < array.Count(); ++i )
+ {
+ CDmElement *pElement = array[ i ];
+ if ( pElement )
+ {
+ nTotalMemory += CDmeElementAccessor::EstimateMemoryUsage( pElement, visited, depth, pCategories );
+ }
+ }
+ }
+ return nTotalMemory;
+ }
+
+ return nTotalMemory;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// CDmaArrayBase starts here
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+template< class T, class B >
+CDmaArrayConstBase<T,B>::CDmaArrayConstBase( )
+{
+ m_pAttribute = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Search
+//-----------------------------------------------------------------------------
+template< class T, class B >
+int CDmaArrayConstBase<T,B>::Find( const T &value ) const
+{
+ return Value().Find( value );
+}
+
+
+//-----------------------------------------------------------------------------
+// Insertion
+//-----------------------------------------------------------------------------
+template< class T, class B >
+int CDmaArrayBase<T,B>::AddToTail()
+{
+ T defaultVal;
+ CDmAttributeInfo<T>::SetDefaultValue( defaultVal );
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ return accessor.InsertBefore( Value().Count(), defaultVal );
+}
+
+template< class T, class B >
+int CDmaArrayBase<T,B>::InsertBefore( int elem )
+{
+ T defaultVal;
+ CDmAttributeInfo<T>::SetDefaultValue( defaultVal );
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ return accessor.InsertBefore( elem, defaultVal );
+}
+
+template< class T, class B >
+int CDmaArrayBase<T,B>::AddToTail( const T& src )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ return accessor.InsertBefore( Value().Count(), src );
+}
+
+template< class T, class B >
+int CDmaArrayBase<T,B>::InsertBefore( int elem, const T& src )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ return accessor.InsertBefore( elem, src );
+}
+
+template< class T, class B >
+int CDmaArrayBase<T,B>::AddMultipleToTail( int num )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ return accessor.InsertMultipleBefore( Value().Count(), num );
+}
+
+template< class T, class B >
+int CDmaArrayBase<T,B>::InsertMultipleBefore( int elem, int num )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ return accessor.InsertMultipleBefore( elem, num );
+}
+
+template< class T, class B >
+void CDmaArrayBase<T,B>::EnsureCount( int num )
+{
+ int nCurrentCount = Value().Count();
+ if ( nCurrentCount < num )
+ {
+ AddMultipleToTail( num - nCurrentCount );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Element modification
+//-----------------------------------------------------------------------------
+template< class T, class B >
+void CDmaArrayBase<T,B>::Set( int i, const T& value )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ return accessor.Set( i, value );
+}
+
+template< class T, class B >
+void CDmaArrayBase<T,B>::SetMultiple( int i, int nCount, const T* pValue )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.SetMultiple( i, nCount, pValue );
+}
+
+template< class T, class B >
+void CDmaArrayBase<T,B>::Swap( int i, int j )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.Swap( i, j );
+}
+
+template< class T, class B >
+void CDmaArrayBase<T,B>::SwapArray( CUtlVector< T > &array )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.SwapArray( array );
+}
+
+
+//-----------------------------------------------------------------------------
+// Copy
+//-----------------------------------------------------------------------------
+template< class T, class B >
+void CDmaArrayBase<T,B>::CopyArray( const T *pArray, int nCount )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.CopyArray( pArray, nCount );
+}
+
+
+//-----------------------------------------------------------------------------
+// Removal
+//-----------------------------------------------------------------------------
+template< class T, class B >
+void CDmaArrayBase<T,B>::FastRemove( int elem )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.FastRemove( elem );
+}
+
+template< class T, class B >
+void CDmaArrayBase<T,B>::Remove( int elem )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.Remove( elem );
+}
+
+template< class T, class B >
+void CDmaArrayBase<T,B>::RemoveAll()
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.RemoveAll();
+}
+
+template< class T, class B >
+void CDmaArrayBase<T,B>::RemoveMultiple( int elem, int num )
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.RemoveMultiple( elem, num );
+}
+
+
+//-----------------------------------------------------------------------------
+// Memory management
+//-----------------------------------------------------------------------------
+template< class T, class B >
+void CDmaArrayBase<T,B>::EnsureCapacity( int num )
+{
+ Value().EnsureCapacity( num );
+}
+
+template< class T, class B >
+void CDmaArrayBase<T,B>::Purge()
+{
+ CDmArrayAttributeOp<T> accessor( this->m_pAttribute );
+ accessor.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+// Attribute initialization
+//-----------------------------------------------------------------------------
+template< class T, class B >
+void CDmaDecorator<T,B>::Init( CDmElement *pOwner, const char *pAttributeName, int nFlags = 0 )
+{
+ Assert( pOwner );
+ this->m_pAttribute = pOwner->AddExternalAttribute( pAttributeName, CDmAttributeInfo<CUtlVector<T> >::AttributeType(), &Value() );
+ Assert( m_pAttribute );
+ if ( nFlags )
+ {
+ this->m_pAttribute->AddFlag( nFlags );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Attribute attribute reference
+//-----------------------------------------------------------------------------
+template< class T, class BaseClass >
+void CDmrDecoratorConst<T,BaseClass>::Init( const CDmAttribute* pAttribute )
+{
+ if ( pAttribute && pAttribute->GetType() == CDmAttributeInfo< CUtlVector< T > >::AttributeType() )
+ {
+ this->m_pAttribute = const_cast<CDmAttribute*>( pAttribute );
+ Attach( this->m_pAttribute->GetAttributeData() );
+ }
+ else
+ {
+ this->m_pAttribute = NULL;
+ Attach( NULL );
+ }
+}
+
+template< class T, class BaseClass >
+void CDmrDecoratorConst<T,BaseClass>::Init( const CDmElement *pElement, const char *pAttributeName )
+{
+ const CDmAttribute *pAttribute = NULL;
+ if ( pElement && pAttributeName && pAttributeName[0] )
+ {
+ pAttribute = pElement->GetAttribute( pAttributeName );
+ }
+ Init( pAttribute );
+}
+
+template< class T, class BaseClass >
+bool CDmrDecoratorConst<T,BaseClass>::IsValid() const
+{
+ return this->m_pAttribute != NULL;
+}
+
+
+template< class T, class BaseClass >
+void CDmrDecorator<T,BaseClass>::Init( CDmAttribute* pAttribute )
+{
+ if ( pAttribute && pAttribute->GetType() == CDmAttributeInfo< CUtlVector< T > >::AttributeType() )
+ {
+ this->m_pAttribute = pAttribute;
+ Attach( this->m_pAttribute->GetAttributeData() );
+ }
+ else
+ {
+ this->m_pAttribute = NULL;
+ Attach( NULL );
+ }
+}
+
+template< class T, class BaseClass >
+void CDmrDecorator<T,BaseClass>::Init( CDmElement *pElement, const char *pAttributeName, bool bAddAttribute )
+{
+ CDmAttribute *pAttribute = NULL;
+ if ( pElement && pAttributeName && pAttributeName[0] )
+ {
+ if ( bAddAttribute )
+ {
+ pAttribute = pElement->AddAttribute( pAttributeName, CDmAttributeInfo< CUtlVector< T > >::AttributeType() );
+ }
+ else
+ {
+ pAttribute = pElement->GetAttribute( pAttributeName );
+ }
+ }
+ Init( pAttribute );
+}
+
+template< class T, class BaseClass >
+bool CDmrDecorator<T,BaseClass>::IsValid() const
+{
+ return this->m_pAttribute != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Generic array access
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Helper macros to make switch statements based on type
+//-----------------------------------------------------------------------------
+#define ARRAY_METHOD_VOID( _type, _func ) \
+ case CDmAttributeInfo< CUtlVector< _type > >::ATTRIBUTE_TYPE: \
+ { \
+ CDmrArray< _type > &array = *reinterpret_cast< CDmrArray< _type > * >( &arrayShared ); \
+ array.Init( m_pAttribute ); \
+ array._func; \
+ } \
+ break;
+
+#define APPLY_ARRAY_METHOD_VOID( _func ) \
+ CDmrArray<int> arrayShared; \
+ switch( m_pAttribute->GetType() ) \
+ { \
+ ARRAY_METHOD_VOID( bool, _func ) \
+ ARRAY_METHOD_VOID( int, _func ) \
+ ARRAY_METHOD_VOID( float, _func ) \
+ ARRAY_METHOD_VOID( Color, _func ) \
+ ARRAY_METHOD_VOID( Vector2D, _func ) \
+ ARRAY_METHOD_VOID( Vector, _func ) \
+ ARRAY_METHOD_VOID( Vector4D, _func ) \
+ ARRAY_METHOD_VOID( QAngle, _func ) \
+ ARRAY_METHOD_VOID( Quaternion, _func ) \
+ ARRAY_METHOD_VOID( VMatrix, _func ) \
+ ARRAY_METHOD_VOID( CUtlString, _func ) \
+ ARRAY_METHOD_VOID( CUtlBinaryBlock, _func ) \
+ ARRAY_METHOD_VOID( DmObjectId_t, _func ) \
+ ARRAY_METHOD_VOID( DmElementHandle_t, _func ) \
+ default: \
+ break; \
+ }
+
+#define ARRAY_METHOD_RET( _type, _func ) \
+ case CDmAttributeInfo< CUtlVector< _type > >::ATTRIBUTE_TYPE: \
+ { \
+ CDmrArray< _type > &array = *reinterpret_cast< CDmrArray< _type > * >( &arrayShared ); \
+ array.Init( m_pAttribute ); \
+ return array._func; \
+ }
+
+#define APPLY_ARRAY_METHOD_RET( _func ) \
+ CDmrArray<int> arrayShared; \
+ switch( m_pAttribute->GetType() ) \
+ { \
+ ARRAY_METHOD_RET( bool, _func ); \
+ ARRAY_METHOD_RET( int, _func ); \
+ ARRAY_METHOD_RET( float, _func ); \
+ ARRAY_METHOD_RET( Color, _func ); \
+ ARRAY_METHOD_RET( Vector2D, _func ); \
+ ARRAY_METHOD_RET( Vector, _func ); \
+ ARRAY_METHOD_RET( Vector4D, _func ); \
+ ARRAY_METHOD_RET( QAngle, _func ); \
+ ARRAY_METHOD_RET( Quaternion, _func ); \
+ ARRAY_METHOD_RET( VMatrix, _func ); \
+ ARRAY_METHOD_RET( CUtlString, _func ); \
+ ARRAY_METHOD_RET( CUtlBinaryBlock, _func ); \
+ ARRAY_METHOD_RET( DmObjectId_t, _func ); \
+ ARRAY_METHOD_RET( DmElementHandle_t, _func ); \
+ default: \
+ break; \
+ }
+
+CDmrGenericArrayConst::CDmrGenericArrayConst() : m_pAttribute( NULL )
+{
+}
+
+CDmrGenericArrayConst::CDmrGenericArrayConst( const CDmAttribute* pAttribute )
+{
+ Init( pAttribute );
+}
+
+CDmrGenericArrayConst::CDmrGenericArrayConst( const CDmElement *pElement, const char *pAttributeName )
+{
+ Init( pElement, pAttributeName );
+}
+
+void CDmrGenericArrayConst::Init( const CDmAttribute *pAttribute )
+{
+ if ( pAttribute && IsArrayType( pAttribute->GetType() ) )
+ {
+ m_pAttribute = const_cast<CDmAttribute*>( pAttribute );
+ }
+ else
+ {
+ m_pAttribute = NULL;
+ }
+}
+
+void CDmrGenericArrayConst::Init( const CDmElement *pElement, const char *pAttributeName )
+{
+ const CDmAttribute *pAttribute = ( pElement && pAttributeName && pAttributeName[0] ) ? pElement->GetAttribute( pAttributeName ) : NULL;
+ Init( pAttribute );
+}
+
+int CDmrGenericArrayConst::Count() const
+{
+ APPLY_ARRAY_METHOD_RET( Count() );
+ return 0;
+}
+
+const void* CDmrGenericArrayConst::GetUntyped( int i ) const
+{
+ APPLY_ARRAY_METHOD_RET( GetUntyped( i ) );
+ return NULL;
+}
+
+const char* CDmrGenericArrayConst::GetAsString( int i, char *pBuffer, size_t nBufLen ) const
+{
+ if ( ( Count() > i ) && ( i >= 0 ) )
+ {
+ CUtlBuffer buf( pBuffer, nBufLen, CUtlBuffer::TEXT_BUFFER );
+ m_pAttribute->SerializeElement( i, buf );
+ }
+ else
+ {
+ pBuffer[0] = 0;
+ }
+ return pBuffer;
+}
+
+
+CDmrGenericArray::CDmrGenericArray( CDmAttribute* pAttribute )
+{
+ Init( pAttribute );
+}
+
+CDmrGenericArray::CDmrGenericArray( CDmElement *pElement, const char *pAttributeName )
+{
+ Init( pElement, pAttributeName );
+}
+
+void CDmrGenericArray::EnsureCount( int num )
+{
+ APPLY_ARRAY_METHOD_VOID( EnsureCount(num) );
+}
+
+int CDmrGenericArray::AddToTail()
+{
+ APPLY_ARRAY_METHOD_RET( AddToTail() );
+ return -1;
+}
+
+void CDmrGenericArray::Remove( int elem )
+{
+ APPLY_ARRAY_METHOD_VOID( Remove(elem) );
+}
+
+void CDmrGenericArray::RemoveAll()
+{
+ APPLY_ARRAY_METHOD_VOID( RemoveAll() );
+}
+
+void CDmrGenericArray::SetMultiple( int i, int nCount, DmAttributeType_t valueType, const void *pValue )
+{
+ s_pAttrInfo[ m_pAttribute->GetType() ]->SetMultiple( m_pAttribute, i, nCount, valueType, pValue );
+}
+
+void CDmrGenericArray::Set( int i, DmAttributeType_t valueType, const void *pValue )
+{
+ s_pAttrInfo[ m_pAttribute->GetType() ]->Set( m_pAttribute, i, valueType, pValue );
+}
+
+void CDmrGenericArray::SetFromString( int i, const char *pValue )
+{
+ if ( ( Count() > i ) && ( i >= 0 ) )
+ {
+ int nLen = pValue ? Q_strlen( pValue ) : 0;
+ CUtlBuffer buf( pValue, nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
+ m_pAttribute->UnserializeElement( i, buf );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Skip unserialization for an attribute type (unserialize into a dummy variable)
+//-----------------------------------------------------------------------------
+bool SkipUnserialize( CUtlBuffer &buf, DmAttributeType_t type )
+{
+ if ( type == AT_UNKNOWN )
+ return false;
+
+ return s_pAttrInfo[ type ]->SkipUnserialize( buf );
+}
+
+
+//-----------------------------------------------------------------------------
+// returns the number of attributes currently allocated
+//-----------------------------------------------------------------------------
+int GetAllocatedAttributeCount()
+{
+ return g_AttrAlloc.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Attribute type->name and name->attribute type
+//-----------------------------------------------------------------------------
+const char *AttributeTypeName( DmAttributeType_t type )
+{
+ if ( ( type >= 0 ) && ( type < AT_TYPE_COUNT ) )
+ return s_pAttrInfo[ type ]->AttributeTypeName();
+ return "unknown";
+}
+
+DmAttributeType_t AttributeType( const char *pName )
+{
+ for ( int i = 0; i < AT_TYPE_COUNT; ++i )
+ {
+ if ( !Q_stricmp( s_pAttrInfo[ i ]->AttributeTypeName(), pName ) )
+ return (DmAttributeType_t)i;
+ }
+
+ return AT_UNKNOWN;
+}
+
+
+//-----------------------------------------------------------------------------
+// Explicit template instantiation for the known attribute types
+//-----------------------------------------------------------------------------
+template <class T>
+class CInstantiateOp
+{
+public:
+ CInstantiateOp()
+ {
+ s_pAttrInfo[ CDmAttributeInfo<T>::ATTRIBUTE_TYPE ] = new CDmAttributeOp< T >;
+ }
+};
+static CInstantiateOp<DmUnknownAttribute_t> __s_AttrDmUnknownAttribute_t;
+
+#define INSTANTIATE_GENERIC_OPS( _className ) \
+ template< > class CInstantiateOp< CUtlVector< _className > > \
+ { \
+ public: \
+ CInstantiateOp() \
+ { \
+ s_pAttrInfo[ CDmAttributeInfo< CUtlVector< _className > >::ATTRIBUTE_TYPE ] = new CDmArrayAttributeOp< _className >; \
+ } \
+ }; \
+ static CInstantiateOp< _className > __s_Attr ## _className; \
+ static CInstantiateOp< CUtlVector< _className > > __s_AttrArray ## _className;
+
+#define DEFINE_ATTRIBUTE_TYPE( _type ) \
+ INSTANTIATE_GENERIC_OPS( _type ) \
+ ATTRIBUTE_SET_VALUE_ARRAY( _type ) \
+ template void CDmAttribute::SetValue< _type >( const _type& value ); \
+ template class CDmArrayAttributeOp< _type >; \
+ template class CDmaArrayBase< _type, CDmaDataInternal< CUtlVector< _type > > >; \
+ template class CDmaArrayBase< _type, CDmaDataExternal< CUtlVector< _type > > >; \
+ template class CDmaArrayConstBase< _type, CDmaDataInternal< CUtlVector< _type > > >; \
+ template class CDmaArrayConstBase< _type, CDmaDataExternal< CUtlVector< _type > > >; \
+ template class CDmaDecorator< _type, CDmaArrayBase< _type, CDmaDataInternal< CUtlVector< _type > > > >; \
+ template class CDmrDecorator< _type, CDmaArrayBase< _type, CDmaDataExternal< CUtlVector< _type > > > >; \
+ template class CDmrDecoratorConst< _type, CDmaArrayConstBase< _type, CDmaDataExternal< CUtlVector< _type > > > >;
+
+
+DEFINE_ATTRIBUTE_TYPE( int )
+DEFINE_ATTRIBUTE_TYPE( float )
+DEFINE_ATTRIBUTE_TYPE( bool )
+DEFINE_ATTRIBUTE_TYPE( Color )
+DEFINE_ATTRIBUTE_TYPE( Vector2D )
+DEFINE_ATTRIBUTE_TYPE( Vector )
+DEFINE_ATTRIBUTE_TYPE( Vector4D )
+DEFINE_ATTRIBUTE_TYPE( QAngle )
+DEFINE_ATTRIBUTE_TYPE( Quaternion )
+DEFINE_ATTRIBUTE_TYPE( VMatrix )
+DEFINE_ATTRIBUTE_TYPE( CUtlString )
+DEFINE_ATTRIBUTE_TYPE( CUtlBinaryBlock )
+DEFINE_ATTRIBUTE_TYPE( DmObjectId_t )
+DEFINE_ATTRIBUTE_TYPE( DmElementHandle_t )
+
+template class CDmaDecorator< CUtlString, CDmaStringArrayBase< CDmaDataInternal< CUtlVector< CUtlString > > > >;
+template class CDmrDecorator< CUtlString, CDmaStringArrayBase< CDmaDataExternal< CUtlVector< CUtlString > > > >;