This repository has been archived on 2024-06-13. You can view files and clone it, but cannot push or open issues or pull requests.
2020-08-04 13:13:01 -04:00

624 lines
28 KiB
C++

//========= 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<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