source-engine/public/datamodel/dmelement.h
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

619 lines
23 KiB
C++

//========= 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<T> - 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 <class T> friend class CDmElementFactory;
template <class T> 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 <class T> friend class CDmElementFactory; \
template <class T> friend class CDmAbstractElementFactory; \
static CUtlSymbol m_classType
#define IMPLEMENT_ELEMENT( className ) \
CUtlSymbol className::m_classType = UTL_INVAL_SYMBOL;
#endif // DMELEMENT_H