//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #ifndef DMELEMENT_H #define DMELEMENT_H #ifdef _WIN32 #pragma once #endif #include "datamodel/attributeflags.h" #include "datamodel/dmvar.h" #include "datamodel/idatamodel.h" #include "tier1/utlhash.h" #include "tier1/utlmap.h" #include "tier1/utlsymbol.h" #include "tier1/utlvector.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 CDmAttribute *AddAttributeElement(const char *pAttributeName); template 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 const T &GetValue(const char *pAttributeName) const; template const T &GetValue(const char *pAttributeName, const T &defaultValue) const; const char *GetValueString(const char *pAttributeName) const; template E *GetValueElement(const char *pAttributeName) const; // set attribute value CDmAttribute *SetValue(const char *pAttributeName, const void *value, size_t size); template CDmAttribute *SetValue(const char *pAttributeName, const T &value); template 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 CDmAttribute *InitValue(const char *pAttributeName, const T &value); template 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 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 &elementPath, TraversalDepth_t depth) const; bool FindReferer(DmElementHandle_t hElement, CUtlVector &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 static void DeleteAttributeVarElementArray(T &array); private: typedef CUtlMap 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 &visited, const CRefMap &refmap, TraversalDepth_t depth); void SetFileId(DmFileId_t fileid); void SetFileId_R(CUtlHashFast &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 &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 friend class CDmaVar; template friend class CDmaArray; template friend class CDmaElementArray; template friend class CDmaDecorator; template friend class CDmrElementArray; friend class CDmElementFactoryDefault; friend class CDmeElementAccessor; friend class CDmeOperator; friend void CopyElements(const CUtlVector &from, CUtlVector &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 &from, CUtlVector &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("shared", true); } else { RemoveAttribute("shared"); } } inline bool CDmElement::IsShared() const { return GetValue("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 inline bool CDmElement::IsA() const { return IsA(E::GetStaticTypeSymbol()); } //----------------------------------------------------------------------------- // Helper for finding elements that refer to this element //----------------------------------------------------------------------------- template T *FindReferringElement(CDmElement *pElement, const char *pAttrName, bool bMustBeInSameFile = true) { return FindReferringElement(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 &array, int startindex = -1); bool GenerateUniqueName(char *name, int memsize, const char *prefix, const CUtlVector &array); void MakeElementNameUnique(CDmElement *pElement, const char *prefix, const CUtlVector &array, bool forceIndex = false); //----------------------------------------------------------------------------- // helper for making attributevarelementarray cleanup easier //----------------------------------------------------------------------------- template 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 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 \ 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(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