//========= 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 // 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 &src1, const CUtlVector &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::CreateAttributeData() { void *pData = NewData< T >(); CDmAttributeInfo< T >::SetDefaultValue( *reinterpret_cast( 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::DestroyAttributeData( void *pData ) { DeleteData< T >( pData ); } //----------------------------------------------------------------------------- // Sets the data to a default value, no undo (used for construction) //----------------------------------------------------------------------------- template< class T > void CDmAttributeOp::SetDefaultValue( void *pData ) { CDmAttributeInfo< T >::SetDefaultValue( *reinterpret_cast( pData ) ); } //----------------------------------------------------------------------------- // Attribute type name, data size, value size //----------------------------------------------------------------------------- template< class T > const char *CDmAttributeOp::AttributeTypeName() { return CDmAttributeInfo::AttributeTypeName(); } template< class T > int CDmAttributeOp::DataSize() { return sizeof( typename CDmAttributeInfo< T >::StorageType_t ); } template< class T > int CDmAttributeOp::ValueSize() { return sizeof( T ); } //----------------------------------------------------------------------------- // Value-setting methods //----------------------------------------------------------------------------- template< class T > void CDmAttributeOp::SetToDefaultValue( CDmAttribute *pAttribute ) { T newValue; CDmAttributeInfo< T >::SetDefaultValue( newValue ); pAttribute->SetValue( newValue ); } template< class T > void CDmAttributeOp::SetMultiple( CDmAttribute *pAttribute, int i, int nCount, DmAttributeType_t valueType, const void *pValue ) { Assert(0); } template< class T > void CDmAttributeOp::Set( CDmAttribute *pAttribute, int i, DmAttributeType_t valueType, const void *pValue ) { Assert(0); } template< class T > void CDmAttributeOp::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::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::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::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::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::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::SerializesOnMultipleLines() { return ::SerializesOnMultipleLines< T >(); } template< class T > bool CDmAttributeOp::SkipUnserialize( CUtlBuffer& buf ) { T dummy; ::Unserialize( buf, dummy ); return buf.IsValid(); } template< class T > bool CDmAttributeOp::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() ); } template< class T > bool CDmAttributeOp::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::SerializeElement( const CDmAttribute *pData, int nElement, CUtlBuffer &buf ) { Assert( 0 ); return false; } template< class T > bool CDmAttributeOp::UnserializeElement( CDmAttribute *pData, CUtlBuffer &buf ) { Assert( 0 ); return false; } template< class T > bool CDmAttributeOp::UnserializeElement( CDmAttribute *pData, int nElement, CUtlBuffer &buf ) { Assert( 0 ); return false; } template< class T > void CDmAttributeOp::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(); 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( m_OldValue ); } } virtual void Redo() { CDmAttribute *pAttribute = GetAttribute(); if ( pAttribute && !pAttribute->IsFlagSet( FATTRIB_READONLY ) ) { pAttribute->SetValue( 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 { typedef CUndoAttributeArrayBase 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 array( pAttribute ); m_OldValue = array[ slot ]; m_Value = newValue; } virtual void Undo() { CDmrArray array( GetAttribute() ); if ( array.IsValid() ) { array.Set( m_nSlot, m_OldValue ); } } virtual void Redo() { CDmrArray array( GetAttribute() ); if ( array.IsValid() ) { array.Set( m_nSlot, m_Value ); } } private: int m_nSlot; typename CUndoAttributeArrayBase::StorageType_t m_OldValue; typename CUndoAttributeArrayBase::StorageType_t m_Value; }; //----------------------------------------------------------------------------- // Undo for setting a multiple elements //----------------------------------------------------------------------------- template< class T > class CUndoArrayAttributeSetMultipleValueElement : public CUndoAttributeArrayBase { typedef CUndoAttributeArrayBase 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::StorageType_t[nCount]; m_pValue = new typename CUndoAttributeArrayBase::StorageType_t[nCount]; CDmrArray 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 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 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::StorageType_t *m_pOldValue; typename CUndoAttributeArrayBase::StorageType_t *m_pValue; }; //----------------------------------------------------------------------------- // // Implementation Undo for CDmAttributeTyped // //----------------------------------------------------------------------------- template< class T > class CUndoAttributeArrayInsertBefore : public CUndoAttributeArrayBase { typedef CUndoAttributeArrayBase 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 array( GetAttribute() ); if ( array.IsValid() ) { array.RemoveMultiple( m_nIndex, m_nCount ); } } virtual void Redo() { CDmrArray array( GetAttribute() ); if ( array.IsValid() ) { T defaultVal; CDmAttributeInfo::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 { typedef CUndoAttributeArrayBase 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 array( GetAttribute() ); if ( array.IsValid() ) { array.Remove( m_nIndex ); } } virtual void Redo() { CDmrArray array( GetAttribute() ); if ( array.IsValid() ) { array.InsertBefore( m_nIndex, m_newValue ); } } private: int m_nIndex; typename CUndoAttributeArrayBase::StorageType_t m_newValue; }; //----------------------------------------------------------------------------- // // Implementation Undo for remove // //----------------------------------------------------------------------------- template< class T > class CUndoAttributeArrayRemoveElement : public CUndoAttributeArrayBase { typedef CUndoAttributeArrayBase 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 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 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::StorageType_t > m_OldValues; }; template< class T > class CUndoAttributeArrayCopyAllElement : public CUndoAttributeArrayBase { typedef CUndoAttributeArrayBase 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::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::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 array( GetAttribute() ); if ( array.IsValid() ) { array.RemoveAll(); for ( int i = 0; i < m_nOldSize; ++i ) { array.AddToTail( m_pOldValues[ i ] ); } } } virtual void Redo() { CDmrArray 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::StorageType_t *m_pOldValues; int m_nOldSize; typename CUndoAttributeArrayBase::StorageType_t *m_pNewValues; int m_nNewSize; bool m_bPurge; }; //----------------------------------------------------------------------------- // CDmArrayAttributeOp implementation. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Callbacks when elements are added + removed //----------------------------------------------------------------------------- template< class T > void CDmArrayAttributeOp::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::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::Count() const { return Data().Count(); } //----------------------------------------------------------------------------- // Should we insert this element into the list? //----------------------------------------------------------------------------- template< class T > inline bool CDmArrayAttributeOp::ShouldInsertElement( const T& src ) { return true; } template<> inline bool CDmArrayAttributeOp::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::ShouldInsert( const T& src ) { if ( !ShouldInsertElement( src ) ) return false; return m_pAttribute->MarkDirty(); } //----------------------------------------------------------------------------- // Insert Before //----------------------------------------------------------------------------- template< class T > int CDmArrayAttributeOp::InsertBefore( int elem, const T& src ) { if ( !ShouldInsert( src ) ) return Data().InvalidIndex(); if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) ) { CUndoAttributeArrayInsertCopyBefore *pUndo = new CUndoAttributeArrayInsertCopyBefore( 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::AddToTail( const T& src ) { return InsertBefore( Data().Count(), src ); } //----------------------------------------------------------------------------- // Insert Multiple Before //----------------------------------------------------------------------------- template< class T > int CDmArrayAttributeOp::InsertMultipleBefore( int elem, int num ) { if ( !m_pAttribute->MarkDirty() ) return Data().InvalidIndex(); // UNDO HOOK if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) ) { CUndoAttributeArrayInsertBefore *pUndo = new CUndoAttributeArrayInsertBefore( 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::SetDefaultValue( Data()[ index + i ] ); } OnAttributeArrayElementAdded( index, index + num - 1 ); m_pAttribute->OnChanged( true ); return index; } //----------------------------------------------------------------------------- // Removal //----------------------------------------------------------------------------- template< class T > void CDmArrayAttributeOp::FastRemove( int elem ) { if ( !m_pAttribute->MarkDirty() ) return; // UNDO HOOK if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) ) { CUndoAttributeArrayRemoveElement *pUndo = new CUndoAttributeArrayRemoveElement( 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::Remove( int elem ) { if ( !Data().IsValidIndex( elem ) ) return; if ( !m_pAttribute->MarkDirty() ) return; // UNDO HOOK if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) ) { CUndoAttributeArrayRemoveElement *pUndo = new CUndoAttributeArrayRemoveElement( 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::RemoveAll() { if ( !m_pAttribute->MarkDirty() ) return; // UNDO HOOK if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) ) { CUndoAttributeArrayCopyAllElement *pUndo = new CUndoAttributeArrayCopyAllElement( 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::RemoveMultiple( int elem, int num ) { if ( !m_pAttribute->MarkDirty() ) return; // UNDO HOOK if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) ) { CUndoAttributeArrayRemoveElement *pUndo = new CUndoAttributeArrayRemoveElement( 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::Purge() { if ( !m_pAttribute->MarkDirty() ) return; // UNDO HOOK if ( g_pDataModel->UndoEnabledForElement( m_pAttribute->GetOwner() ) ) { CUndoAttributeArrayCopyAllElement *pUndo = new CUndoAttributeArrayCopyAllElement( 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::PerformCopyArray( const T *pArray, int nCount ) { Data().CopyArray( pArray, nCount ); } template<> void CDmArrayAttributeOp::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::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 *pUndo = new CUndoAttributeArrayCopyAllElement( 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::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 *pUndo = new CUndoAttributeArrayCopyAllElement( 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::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::Set( int i, const T& value ) { if ( i < 0 || i >= Data().Count() ) { Assert( !"CDmAttributeArray::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 *pUndo = new CUndoArrayAttributeSetValueElement( 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::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::SetMultiple( int i, int nCount, const T* pValue ) { if ( i < 0 || ( i+nCount ) > Data().Count() ) { AssertMsg( 0, "CDmAttributeArray::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 *pUndo = new CUndoArrayAttributeSetMultipleValueElement( 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::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::SetValue( CDmAttribute *pAttribute, DmAttributeType_t valueType, const void *pValue ) { Assert( pAttribute->GetType() == valueType ); if ( pAttribute->GetType() == valueType ) { CDmArrayAttributeOp accessor( pAttribute ); const CUtlVector* pArray = reinterpret_cast< const CUtlVector* >( pValue ); accessor.CopyArray( pArray->Base(), pArray->Count() ); } } //----------------------------------------------------------------------------- // Swap //----------------------------------------------------------------------------- template< class T > void CDmArrayAttributeOp::Swap( int i, int j ) { if ( i == j ) return; // TODO - define Swap 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 *pUndo = new CUndoArrayAttributeSetValueElement( m_pAttribute, i, Data()[ j ] ); g_pDataModel->AddUndoElement( pUndo ); pUndo = new CUndoArrayAttributeSetValueElement( 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::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 accessor( pAttribute ); accessor.SwapArray( tempVal ); return bRet; } template<> bool CDmArrayAttributeOp::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 accessor( pAttribute ); accessor.CopyArray( tempVal.Base(), tempVal.Count() ); return bRet; } // Serialization of a single element template< class T > bool CDmArrayAttributeOp::SerializeElement( const CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf ) { CDmrArrayConst array( pAttribute ); return ::Serialize( buf, array[ nElement ] ); } template< class T > bool CDmArrayAttributeOp::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 accessor( pAttribute ); accessor.AddToTail( temp ); pAttribute->OnChanged( true ); } return bReadElement; } template< class T > bool CDmArrayAttributeOp::UnserializeElement( CDmAttribute *pAttribute, int nElement, CUtlBuffer &buf ) { if ( !CDmAttributeAccessor::MarkDirty( pAttribute ) ) return false; CDmrArray array( pAttribute ); if ( array.Count() <= nElement ) return false; MEM_ALLOC_CREDIT_CLASS(); pAttribute->PreChanged(); bool bReadElement = ::Unserialize( buf, *const_cast( &array[nElement] ) ); if ( bReadElement ) { pAttribute->OnChanged(); } return bReadElement; } template< class T > void CDmArrayAttributeOp::OnUnserializationFinished( CDmAttribute *pAttribute ) { CDmArrayAttributeOp 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(), 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() const { DmAttributeType_t type = GetType(); return ( type == AT_BOOL || type == AT_INT || type == AT_FLOAT ); } template<> bool CDmAttribute::IsTypeConvertable() const { DmAttributeType_t type = GetType(); return ( type == AT_INT || type == AT_BOOL || type == AT_FLOAT ); } template<> bool CDmAttribute::IsTypeConvertable() const { DmAttributeType_t type = GetType(); return ( type == AT_FLOAT || type == AT_INT || type == AT_BOOL ); } template<> bool CDmAttribute::IsTypeConvertable() const { DmAttributeType_t type = GetType(); return ( type == AT_QANGLE || type == AT_QUATERNION ); } template<> bool CDmAttribute::IsTypeConvertable() 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(), 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() ) return false; if ( ( GetType() == CDmAttributeInfo::ATTRIBUTE_TYPE ) && IsAttributeEqual( GetValue(), value ) ) return false; return MarkDirty(); } template<> bool CDmAttribute::ShouldModify( const DmElementHandle_t& value ) { if ( !IsTypeConvertable() ) return false; if ( IsAttributeEqual( GetValue(), value ) ) return false; DmElementAttribute_t *pData = GetData(); 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 *pUndo = new CUndoAttributeSetValueElement( 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(); 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(); 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::CDmaArrayConstBase( ) { m_pAttribute = NULL; } //----------------------------------------------------------------------------- // Search //----------------------------------------------------------------------------- template< class T, class B > int CDmaArrayConstBase::Find( const T &value ) const { return Value().Find( value ); } //----------------------------------------------------------------------------- // Insertion //----------------------------------------------------------------------------- template< class T, class B > int CDmaArrayBase::AddToTail() { T defaultVal; CDmAttributeInfo::SetDefaultValue( defaultVal ); CDmArrayAttributeOp accessor( this->m_pAttribute ); return accessor.InsertBefore( Value().Count(), defaultVal ); } template< class T, class B > int CDmaArrayBase::InsertBefore( int elem ) { T defaultVal; CDmAttributeInfo::SetDefaultValue( defaultVal ); CDmArrayAttributeOp accessor( this->m_pAttribute ); return accessor.InsertBefore( elem, defaultVal ); } template< class T, class B > int CDmaArrayBase::AddToTail( const T& src ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); return accessor.InsertBefore( Value().Count(), src ); } template< class T, class B > int CDmaArrayBase::InsertBefore( int elem, const T& src ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); return accessor.InsertBefore( elem, src ); } template< class T, class B > int CDmaArrayBase::AddMultipleToTail( int num ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); return accessor.InsertMultipleBefore( Value().Count(), num ); } template< class T, class B > int CDmaArrayBase::InsertMultipleBefore( int elem, int num ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); return accessor.InsertMultipleBefore( elem, num ); } template< class T, class B > void CDmaArrayBase::EnsureCount( int num ) { int nCurrentCount = Value().Count(); if ( nCurrentCount < num ) { AddMultipleToTail( num - nCurrentCount ); } } //----------------------------------------------------------------------------- // Element modification //----------------------------------------------------------------------------- template< class T, class B > void CDmaArrayBase::Set( int i, const T& value ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); return accessor.Set( i, value ); } template< class T, class B > void CDmaArrayBase::SetMultiple( int i, int nCount, const T* pValue ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.SetMultiple( i, nCount, pValue ); } template< class T, class B > void CDmaArrayBase::Swap( int i, int j ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.Swap( i, j ); } template< class T, class B > void CDmaArrayBase::SwapArray( CUtlVector< T > &array ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.SwapArray( array ); } //----------------------------------------------------------------------------- // Copy //----------------------------------------------------------------------------- template< class T, class B > void CDmaArrayBase::CopyArray( const T *pArray, int nCount ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.CopyArray( pArray, nCount ); } //----------------------------------------------------------------------------- // Removal //----------------------------------------------------------------------------- template< class T, class B > void CDmaArrayBase::FastRemove( int elem ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.FastRemove( elem ); } template< class T, class B > void CDmaArrayBase::Remove( int elem ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.Remove( elem ); } template< class T, class B > void CDmaArrayBase::RemoveAll() { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.RemoveAll(); } template< class T, class B > void CDmaArrayBase::RemoveMultiple( int elem, int num ) { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.RemoveMultiple( elem, num ); } //----------------------------------------------------------------------------- // Memory management //----------------------------------------------------------------------------- template< class T, class B > void CDmaArrayBase::EnsureCapacity( int num ) { Value().EnsureCapacity( num ); } template< class T, class B > void CDmaArrayBase::Purge() { CDmArrayAttributeOp accessor( this->m_pAttribute ); accessor.Purge(); } //----------------------------------------------------------------------------- // Attribute initialization //----------------------------------------------------------------------------- template< class T, class B > void CDmaDecorator::Init( CDmElement *pOwner, const char *pAttributeName, int nFlags = 0 ) { Assert( pOwner ); this->m_pAttribute = pOwner->AddExternalAttribute( pAttributeName, CDmAttributeInfo >::AttributeType(), &Value() ); Assert( m_pAttribute ); if ( nFlags ) { this->m_pAttribute->AddFlag( nFlags ); } } //----------------------------------------------------------------------------- // Attribute attribute reference //----------------------------------------------------------------------------- template< class T, class BaseClass > void CDmrDecoratorConst::Init( const CDmAttribute* pAttribute ) { if ( pAttribute && pAttribute->GetType() == CDmAttributeInfo< CUtlVector< T > >::AttributeType() ) { this->m_pAttribute = const_cast( pAttribute ); Attach( this->m_pAttribute->GetAttributeData() ); } else { this->m_pAttribute = NULL; Attach( NULL ); } } template< class T, class BaseClass > void CDmrDecoratorConst::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::IsValid() const { return this->m_pAttribute != NULL; } template< class T, class BaseClass > void CDmrDecorator::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::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::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 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 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( 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 CInstantiateOp { public: CInstantiateOp() { s_pAttrInfo[ CDmAttributeInfo::ATTRIBUTE_TYPE ] = new CDmAttributeOp< T >; } }; static CInstantiateOp __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 > > > >;