diff options
Diffstat (limited to 'datamodel/dmattribute.cpp')
| -rw-r--r-- | datamodel/dmattribute.cpp | 3265 |
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 > > > >; |