//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #ifndef DMELEMENT_H #define DMELEMENT_H #ifdef _WIN32 #pragma once #endif #include "tier1/utlmap.h" #include "tier1/utlhash.h" #include "tier1/utlvector.h" #include "tier1/utlsymbol.h" #include "datamodel/attributeflags.h" #include "datamodel/idatamodel.h" #include "datamodel/dmvar.h" //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- typedef bool (CDmElement::*pfnCommandMethod)( const char *command, const char *args ); // element/element array traversal path item - assumes the full path does NOT contain cycles struct ElementPathItem_t { ElementPathItem_t( DmElementHandle_t hElem = DMELEMENT_HANDLE_INVALID, DmAttributeHandle_t hAttr = DMATTRIBUTE_HANDLE_INVALID, int idx = -1 ) : hElement( hElem ), hAttribute( hAttr ), nIndex( idx ) { } // only uses hElement so that it can be used to search for elements bool operator==( const ElementPathItem_t &that ) const { return hElement == that.hElement; } DmElementHandle_t hElement; DmAttributeHandle_t hAttribute; int nIndex; }; //----------------------------------------------------------------------------- // singly-linked attribute list //----------------------------------------------------------------------------- struct DmAttributeList_t { DmAttributeList_t() : m_hAttribute( DMATTRIBUTE_HANDLE_INVALID ), m_pNext( NULL ) {} DmAttributeHandle_t m_hAttribute; DmAttributeList_t *m_pNext; }; //----------------------------------------------------------------------------- // helper class to allow CDmeHandle access to g_pDataModelImp //----------------------------------------------------------------------------- class CDmeElementRefHelper { protected: void Ref ( DmElementHandle_t hElement, bool bStrong ); void Unref( DmElementHandle_t hElement, bool bStrong ); }; //----------------------------------------------------------------------------- // element reference struct - containing attribute referrers and handle refcount //----------------------------------------------------------------------------- struct DmElementReference_t { explicit DmElementReference_t( DmElementHandle_t hElement = DMELEMENT_HANDLE_INVALID ) : m_hElement( hElement ), m_nWeakHandleCount( 0 ), m_nStrongHandleCount( 0 ) { } DmElementReference_t( const DmElementReference_t &that ) : m_hElement( that.m_hElement ), m_nWeakHandleCount( that.m_nWeakHandleCount ), m_nStrongHandleCount( that.m_nStrongHandleCount ), m_attributes( that.m_attributes ) { } DmElementReference_t &operator=( const DmElementReference_t &that ) { m_hElement = that.m_hElement; m_nWeakHandleCount = that.m_nWeakHandleCount; m_nStrongHandleCount = that.m_nStrongHandleCount; m_attributes.m_hAttribute = that.m_attributes.m_hAttribute; m_attributes.m_pNext = that.m_attributes.m_pNext; return *this; } ~DmElementReference_t() { // Assert( !IsStronglyReferenced() ); } void AddAttribute( CDmAttribute *pAttribute ); void RemoveAttribute( CDmAttribute *pAttribute ); bool IsStronglyReferenced() // should this element be kept around (even if it's DmElementHandle_t is invalidated) { return m_attributes.m_hAttribute != DMATTRIBUTE_HANDLE_INVALID || m_nStrongHandleCount > 0; } bool IsWeaklyReferenced() // should we keep this element's DmElementHandle_t mapped to it's id (even if the element is deleted) { return IsStronglyReferenced() || m_nWeakHandleCount > 0; } int EstimateMemoryOverhead() { int nBytes = 0; for ( DmAttributeList_t *pLink = m_attributes.m_pNext; pLink; pLink = pLink->m_pNext ) { nBytes += sizeof( DmAttributeList_t ); } return nBytes; } DmElementHandle_t m_hElement; unsigned short m_nWeakHandleCount; // CDmeHandle - for auto-hookup once the element comes back, mainly used by UI unsigned short m_nStrongHandleCount; // CDmeCountedElementRef - for preventing elements from being truly deleted, mainly used by undo and file root DmAttributeList_t m_attributes; }; //----------------------------------------------------------------------------- // Base DmElement we inherit from in higher-level classes //----------------------------------------------------------------------------- class CDmElement { public: // Can be overridden by derived classes virtual void OnAttributeChanged( CDmAttribute *pAttribute ) {} virtual void PreAttributeChanged( CDmAttribute *pAttribute ) {} virtual void OnAttributeArrayElementAdded( CDmAttribute *pAttribute, int nFirstElem, int nLastElem ) {} virtual void OnAttributeArrayElementRemoved( CDmAttribute *pAttribute, int nFirstElem, int nLastElem ) {} virtual void Resolve() {} virtual bool IsA( UtlSymId_t typeSymbol ) const; virtual int GetInheritanceDepth( UtlSymId_t typeSymbol ) const; virtual void OnElementUnserialized() {} virtual int AllocatedSize() const { return sizeof( CDmElement ); } // Returns the element handle DmElementHandle_t GetHandle() const; // Attribute iteration, finding // NOTE: Passing a type into GetAttribute will return NULL if the attribute exists but isn't that type bool HasAttribute( const char *pAttributeName, DmAttributeType_t type = AT_UNKNOWN ) const; CDmAttribute *GetAttribute( const char *pAttributeName, DmAttributeType_t type = AT_UNKNOWN ); const CDmAttribute *GetAttribute( const char *pAttributeName, DmAttributeType_t type = AT_UNKNOWN ) const; int AttributeCount() const; CDmAttribute* FirstAttribute(); const CDmAttribute* FirstAttribute() const; // Element name, type, ID // WARNING: SetType() should only be used by format conversion methods (dmxconvert) UtlSymId_t GetType() const; const char * GetTypeString() const; const char * GetName() const; const DmObjectId_t& GetId() const; void SetType( const char *pType ); void SetName( const char* pName ); // Attribute management CDmAttribute * AddAttribute( const char *pAttributeName, DmAttributeType_t type ); template< class E > CDmAttribute* AddAttributeElement( const char *pAttributeName ); template< class E > CDmAttribute* AddAttributeElementArray( const char *pAttributeName ); void RemoveAttribute( const char *pAttributeName ); void RemoveAttributeByPtr( CDmAttribute *pAttributeName ); void RenameAttribute( const char *pAttributeName, const char *pNewName ); // get attribute value template< class T > const T& GetValue( const char *pAttributeName ) const; template< class T > const T& GetValue( const char *pAttributeName, const T& defaultValue ) const; const char * GetValueString( const char *pAttributeName ) const; template< class E > E* GetValueElement( const char *pAttributeName ) const; // set attribute value CDmAttribute* SetValue( const char *pAttributeName, const void *value, size_t size ); template< class T > CDmAttribute* SetValue( const char *pAttributeName, const T& value ); template< class E > CDmAttribute* SetValue( const char *pAttributeName, E* value ); // set attribute value if the attribute doesn't already exist CDmAttribute* InitValue( const char *pAttributeName, const void *value, size_t size ); template< class T > CDmAttribute* InitValue( const char *pAttributeName, const T& value ); template< class E > CDmAttribute* InitValue( const char *pAttributeName, E* value ); // Parses an attribute from a string // Doesn't create an attribute if it doesn't exist and always preserves attribute type void SetValueFromString( const char *pAttributeName, const char *value ); const char *GetValueAsString( const char *pAttributeName, char *pBuffer, size_t buflen ) const; // Helpers for our RTTI template< class E > bool IsA() const; bool IsA( const char *pTypeName ) const; int GetInheritanceDepth( const char *pTypeName ) const; static CUtlSymbol GetStaticTypeSymbol(); // Indicates whether this element should be copied or not void SetShared( bool bShared ); bool IsShared() const; // Copies an element and all its attributes CDmElement* Copy( TraversalDepth_t depth = TD_DEEP ) const; // Copies attributes from a specified element void CopyAttributesTo( CDmElement *pCopy, TraversalDepth_t depth = TD_DEEP ) const; // recursively set fileid's, with option to only change elements in the matched file void SetFileId( DmFileId_t fileid, TraversalDepth_t depth, bool bOnlyIfMatch = false ); DmFileId_t GetFileId() const; bool IsAccessible() const; void MarkAccessible( bool bAccessible ); void MarkAccessible( TraversalDepth_t depth = TD_ALL ); // returns the first path to the element found traversing all element/element // array attributes - not necessarily the shortest. // cycle-safe (skips any references to elements in the current path) // but may re-traverse elements via different paths bool FindElement( const CDmElement *pElement, CUtlVector< ElementPathItem_t > &elementPath, TraversalDepth_t depth ) const; bool FindReferer( DmElementHandle_t hElement, CUtlVector< ElementPathItem_t > &elementPath, TraversalDepth_t depth ) const; void RemoveAllReferencesToElement( CDmElement *pElement ); bool IsStronglyReferenced() { return m_ref.IsStronglyReferenced(); } // Estimates the memory usage of the element, its attributes, and child elements int EstimateMemoryUsage( TraversalDepth_t depth = TD_DEEP ); protected: // NOTE: These are protected to ensure that the factory is the only thing that can create these CDmElement( DmElementHandle_t handle, const char *objectType, const DmObjectId_t &id, const char *objectName, DmFileId_t fileid ); virtual ~CDmElement(); // Used by derived classes to do construction and setting up CDmaVars void OnConstruction() { } void OnDestruction() { } virtual void PerformConstruction(); virtual void PerformDestruction(); // Internal methods related to RTII static void SetTypeSymbol( CUtlSymbol sym ); static bool IsA_Implementation( CUtlSymbol typeSymbol ); static int GetInheritanceDepth_Implementation( CUtlSymbol typeSymbol, int nCurrentDepth ); // Internal method for creating a copy of this element CDmElement* CopyInternal( TraversalDepth_t depth = TD_DEEP ) const; // helper for making attributevarelementarray cleanup easier template< class T > static void DeleteAttributeVarElementArray( T &array ); private: typedef CUtlMap< DmElementHandle_t, DmElementHandle_t, int > CRefMap; // Bogus constructor CDmElement(); // internal recursive copy method - builds refmap of old element's handle -> copy's handle, and uses it to fixup references void CopyAttributesTo( CDmElement *pCopy, CRefMap &refmap, TraversalDepth_t depth ) const; void CopyElementAttribute( const CDmAttribute *pAttr, CDmAttribute *pCopyAttr, CRefMap &refmap, TraversalDepth_t depth ) const; void CopyElementArrayAttribute( const CDmAttribute *pAttr, CDmAttribute *pCopyAttr, CRefMap &refmap, TraversalDepth_t depth ) const; void FixupReferences( CUtlHashFast< DmElementHandle_t > &visited, const CRefMap &refmap, TraversalDepth_t depth ); void SetFileId( DmFileId_t fileid ); void SetFileId_R( CUtlHashFast< DmElementHandle_t > &visited, DmFileId_t fileid, TraversalDepth_t depth, DmFileId_t match, bool bOnlyIfMatch ); CDmAttribute* CreateAttribute( const char *pAttributeName, DmAttributeType_t type ); void RemoveAttribute( CDmAttribute **pAttrRef ); CDmAttribute* AddExternalAttribute( const char *pAttributeName, DmAttributeType_t type, void *pMemory ); CDmAttribute *FindAttribute( const char *pAttributeName ) const; void Purge(); void SetId( const DmObjectId_t &id ); bool IsDirty() const; void MarkDirty( bool dirty = true ); void MarkAttributesClean(); void MarkBeingUnserialized( bool beingUnserialized = true ); bool IsBeingUnserialized() const; // Used by the undo system only. void AddAttributeByPtr( CDmAttribute *ptr ); void RemoveAttributeByPtrNoDelete( CDmAttribute *ptr ); // Should only be called from datamodel, who will take care of changing the fileset entry as well void ChangeHandle( DmElementHandle_t handle ); // returns element reference struct w/ list of referrers and handle count DmElementReference_t* GetReference(); void SetReference( const DmElementReference_t &ref ); // Estimates memory usage int EstimateMemoryUsage( CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories ); protected: CDmaString m_Name; private: CDmAttribute *m_pAttributes; DmElementReference_t m_ref; UtlSymId_t m_Type; bool m_bDirty : 1; bool m_bBeingUnserialized : 1; bool m_bIsAcessible : 1; unsigned char m_nReserved; // Makes Id be quad aligned DmObjectId_t m_Id; DmFileId_t m_fileId; // Stores the type symbol static CUtlSymbol m_classType; // Factories can access our constructors template friend class CDmElementFactory; template friend class CDmAbstractElementFactory; template< class T > friend class CDmaVar; template< class T > friend class CDmaArray; template< class T > friend class CDmaElementArray; template< class T, class B > friend class CDmaDecorator; template< class T > friend class CDmrElementArray; friend class CDmElementFactoryDefault; friend class CDmeElementAccessor; friend class CDmeOperator; friend void CopyElements( const CUtlVector< CDmElement* > &from, CUtlVector< CDmElement* > &to, TraversalDepth_t depth ); }; inline void DestroyElement( CDmElement *pElement ) { if ( pElement ) { g_pDataModel->DestroyElement( pElement->GetHandle() ); } } void DestroyElement( CDmElement *pElement, TraversalDepth_t depth ); //----------------------------------------------------------------------------- // copy groups of elements together so that references between them are maintained //----------------------------------------------------------------------------- void CopyElements( const CUtlVector< CDmElement* > &from, CUtlVector< CDmElement* > &to, TraversalDepth_t depth = TD_DEEP ); //----------------------------------------------------------------------------- // allows elements to chain OnAttributeChanged up to their parents (or at least, referrers) //----------------------------------------------------------------------------- void InvokeOnAttributeChangedOnReferrers( DmElementHandle_t hElement, CDmAttribute *pChangedAttr ); //----------------------------------------------------------------------------- // Returns the type, name, id, fileId //----------------------------------------------------------------------------- inline UtlSymId_t CDmElement::GetType() const { return m_Type; } inline const char *CDmElement::GetTypeString() const { return g_pDataModel->GetString( m_Type ); } inline const char *CDmElement::GetName() const { return m_Name.Get(); } inline void CDmElement::SetName( const char* pName ) { m_Name.Set( pName ); } inline const DmObjectId_t& CDmElement::GetId() const { return m_Id; } inline DmFileId_t CDmElement::GetFileId() const { return m_fileId; } //----------------------------------------------------------------------------- // Controls whether the element should be copied by default //----------------------------------------------------------------------------- inline void CDmElement::SetShared( bool bShared ) { if ( bShared ) { SetValue< bool >( "shared", true ); } else { RemoveAttribute( "shared" ); } } inline bool CDmElement::IsShared() const { return GetValue< bool >( "shared" ); // if attribute doesn't exist, returns default bool value, which is false } //----------------------------------------------------------------------------- // Copies attributes from a specified element //----------------------------------------------------------------------------- inline CDmElement* CDmElement::Copy( TraversalDepth_t depth ) const { return CopyInternal( depth ); } //----------------------------------------------------------------------------- // RTTI //----------------------------------------------------------------------------- inline bool CDmElement::IsA_Implementation( CUtlSymbol typeSymbol ) { return ( m_classType == typeSymbol ) || ( UTL_INVAL_SYMBOL == typeSymbol ); } inline int CDmElement::GetInheritanceDepth_Implementation( CUtlSymbol typeSymbol, int nCurrentDepth ) { return IsA_Implementation( typeSymbol ) ? nCurrentDepth : -1; } inline CUtlSymbol CDmElement::GetStaticTypeSymbol() { return m_classType; } inline bool CDmElement::IsA( const char *pTypeName ) const { CUtlSymbol typeSymbol = g_pDataModel->GetSymbol( pTypeName ); return IsA( typeSymbol ); } template< class E > inline bool CDmElement::IsA() const { return IsA( E::GetStaticTypeSymbol() ); } //----------------------------------------------------------------------------- // Helper for finding elements that refer to this element //----------------------------------------------------------------------------- template< class T > T *FindReferringElement( CDmElement *pElement, const char *pAttrName, bool bMustBeInSameFile = true ) { return FindReferringElement< T >( pElement, g_pDataModel->GetSymbol( pAttrName ), bMustBeInSameFile ); } void RemoveElementFromRefereringAttributes( CDmElement *pElement, bool bPreserveOrder = true ); //----------------------------------------------------------------------------- // // element-specific unique name generation methods // //----------------------------------------------------------------------------- // returns startindex if none found, 2 if only "prefix" found, and n+1 if "prefixn" found int GenerateUniqueNameIndex( const char *prefix, const CUtlVector< DmElementHandle_t > &array, int startindex = -1 ); bool GenerateUniqueName( char *name, int memsize, const char *prefix, const CUtlVector< DmElementHandle_t > &array ); void MakeElementNameUnique( CDmElement *pElement, const char *prefix, const CUtlVector< DmElementHandle_t > &array, bool forceIndex = false ); //----------------------------------------------------------------------------- // helper for making attributevarelementarray cleanup easier //----------------------------------------------------------------------------- template< class T > inline void CDmElement::DeleteAttributeVarElementArray( T &array ) { int nElements = array.Count(); for ( int i = 0; i < nElements; ++i ) { g_pDataModel->DestroyElement( array.GetHandle( i ) ); } array.RemoveAll(); } //----------------------------------------------------------------------------- // Default size computation //----------------------------------------------------------------------------- template< class T > int DmeEstimateMemorySize( T* pElement ) { return sizeof( T ); } //----------------------------------------------------------------------------- // Helper macro to create an element; this is used for elements that are helper base classes //----------------------------------------------------------------------------- #define DEFINE_UNINSTANCEABLE_ELEMENT( className, baseClassName ) \ protected: \ className( DmElementHandle_t handle, const char *pElementTypeName, const DmObjectId_t &id, const char *pElementName, DmFileId_t fileid ) : \ baseClassName( handle, pElementTypeName, id, pElementName, fileid ) \ { \ } \ virtual ~className() \ { \ } \ void OnConstruction(); \ void OnDestruction(); \ virtual void PerformConstruction() \ { \ BaseClass::PerformConstruction(); \ OnConstruction(); \ } \ virtual void PerformDestruction() \ { \ OnDestruction(); \ BaseClass::PerformDestruction(); \ } \ virtual int AllocatedSize() const { return DmeEstimateMemorySize( this ); } \ \ private: \ typedef baseClassName BaseClass; \ //----------------------------------------------------------------------------- // Helper macro to create the class factory //----------------------------------------------------------------------------- #define DEFINE_ELEMENT( className, baseClassName ) \ public: \ virtual bool IsA( UtlSymId_t typeSymbol ) const \ { \ return IsA_Implementation( typeSymbol );\ } \ \ bool IsA( const char *pTypeName ) const \ { \ CUtlSymbol typeSymbol = g_pDataModel->GetSymbol( pTypeName ); \ return IsA( typeSymbol ); \ } \ \ template< class T > bool IsA() const \ { \ return IsA( T::GetStaticTypeSymbol() ); \ } \ \ virtual int GetInheritanceDepth( UtlSymId_t typeSymbol ) const \ { \ return GetInheritanceDepth_Implementation( typeSymbol, 0 ); \ } \ \ static CUtlSymbol GetStaticTypeSymbol( ) \ { \ return m_classType; \ } \ \ className* Copy( TraversalDepth_t depth = TD_DEEP ) const \ { \ return static_cast< className* >( CopyInternal( depth ) ); \ } \ protected: \ className( DmElementHandle_t handle, const char *pElementTypeName, const DmObjectId_t &id, const char *pElementName, DmFileId_t fileid ) : \ baseClassName( handle, pElementTypeName, id, pElementName, fileid ) \ { \ } \ virtual ~className() \ { \ } \ void OnConstruction(); \ void OnDestruction(); \ virtual void PerformConstruction() \ { \ BaseClass::PerformConstruction(); \ OnConstruction(); \ } \ virtual void PerformDestruction() \ { \ OnDestruction(); \ BaseClass::PerformDestruction(); \ } \ static void SetTypeSymbol( CUtlSymbol typeSymbol ) \ { \ m_classType = typeSymbol; \ } \ \ static bool IsA_Implementation( CUtlSymbol typeSymbol ) \ { \ if ( typeSymbol == m_classType ) \ return true; \ return BaseClass::IsA_Implementation( typeSymbol ); \ } \ \ static int GetInheritanceDepth_Implementation( CUtlSymbol typeSymbol, int nCurrentDepth ) \ { \ if ( typeSymbol == m_classType ) \ return nCurrentDepth; \ return BaseClass::GetInheritanceDepth_Implementation( typeSymbol, nCurrentDepth+1 );\ } \ virtual int AllocatedSize() const { return DmeEstimateMemorySize( this ); } \ \ private: \ typedef baseClassName BaseClass; \ template friend class CDmElementFactory; \ template friend class CDmAbstractElementFactory; \ static CUtlSymbol m_classType #define IMPLEMENT_ELEMENT( className ) \ CUtlSymbol className::m_classType = UTL_INVAL_SYMBOL; #endif // DMELEMENT_H