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

718 lines
25 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#ifndef DMATTRIBUTE_H
#define DMATTRIBUTE_H
#ifdef _WIN32
#pragma once
#endif
#include "datamodel/attributeflags.h"
#include "datamodel/dmattributetypes.h"
#include "datamodel/dmelement.h"
#include "datamodel/dmvar.h"
#include "datamodel/idatamodel.h"
#include "tier1/utlhash.h"
//-----------------------------------------------------------------------------
// Fast dynamic cast
//-----------------------------------------------------------------------------
template <class E>
inline E *CastElement(CDmElement *pElement) {
if (pElement && pElement->IsA(E::GetStaticTypeSymbol()))
return static_cast<E *>(pElement);
return NULL;
}
//-----------------------------------------------------------------------------
// type-safe element creation and accessor helpers - infers type name string
// from actual type
//-----------------------------------------------------------------------------
template <class E>
inline E *GetElement(DmElementHandle_t hElement) {
CDmElement *pElement = g_pDataModel->GetElement(hElement);
return CastElement<E>(pElement);
}
//-----------------------------------------------------------------------------
// Typesafe element creation + destruction
//-----------------------------------------------------------------------------
template <class E>
inline E *CreateElement(const char *pObjectName,
DmFileId_t fileid = DMFILEID_INVALID,
const DmObjectId_t *pObjectID = NULL) {
return GetElement<E>(g_pDataModel->CreateElement(
E::GetStaticTypeSymbol(), pObjectName, fileid, pObjectID));
}
template <class E>
inline E *CreateElement(const char *pElementType, const char *pObjectName,
DmFileId_t fileid = DMFILEID_INVALID,
const DmObjectId_t *pObjectID = NULL) {
return GetElement<E>(g_pDataModel->CreateElement(pElementType, pObjectName,
fileid, pObjectID));
}
//-----------------------------------------------------------------------------
// Used for attribute change callbacks
//-----------------------------------------------------------------------------
typedef unsigned short DmMailingList_t;
enum { DMMAILINGLIST_INVALID = (DmMailingList_t)~0 };
//-----------------------------------------------------------------------------
// Purpose: A general purpose pAttribute. Eventually will be extensible to
// arbitrary user types
//-----------------------------------------------------------------------------
class CDmAttribute {
public:
// Returns the type
DmAttributeType_t GetType() const;
const char *GetTypeString() const;
template <class T>
bool IsA() const;
// Returns the name. NOTE: The utlsymbol
// can be turned into a string by using g_pDataModel->String();
const char *GetName() const;
UtlSymId_t GetNameSymbol() const;
void SetName(const char *newName);
// Gets the attribute value
// NOTE: GetValueUntyped is used with GetType() for use w/ SetValue( type,
// void* )
template <class T>
const T &GetValue() const;
template <class T>
const T &GetValue(const T &defaultValue) const;
const char *GetValueString() const;
template <class E>
E *GetValueElement() const;
const void *GetValueUntyped() const;
// Sets the attribute value
template <class T>
void SetValue(const T &value);
template <class E>
void SetValue(E *pValue);
void SetValue(const void *pValue, size_t nSize);
// Copies w/ type conversion (if possible) from another attribute
void SetValue(const CDmAttribute *pAttribute);
void SetValue(CDmAttribute *pAttribute);
void SetValue(DmAttributeType_t valueType, const void *pValue);
// Sets the attribute to its default value based on its type
void SetToDefaultValue();
// Convert to and from string
void SetValueFromString(const char *pValue);
const char *GetValueAsString(char *pBuffer, size_t nBufLen) const;
// Used for element and element array attributes; it specifies which type of
// elements are valid to be referred to by this attribute
void SetElementTypeSymbol(UtlSymId_t typeSymbol);
UtlSymId_t GetElementTypeSymbol() const;
// Returns the next attribute
CDmAttribute *NextAttribute();
const CDmAttribute *NextAttribute() const;
// Returns the owner
CDmElement *GetOwner();
// Methods related to flags
void AddFlag(int flags);
void RemoveFlag(int flags);
void ClearFlags();
int GetFlags() const;
bool IsFlagSet(int flags) const;
// Serialization
bool Serialize(CUtlBuffer &buf) const;
bool Unserialize(CUtlBuffer &buf);
// Serialization of a single element.
// First version of UnserializeElement adds to tail if it worked
// Second version overwrites, but does not add, the element at the specified
// index
bool SerializeElement(int nElement, CUtlBuffer &buf) const;
bool UnserializeElement(CUtlBuffer &buf);
bool UnserializeElement(int nElement, CUtlBuffer &buf);
// Does this attribute serialize on multiple lines?
bool SerializesOnMultipleLines() const;
// Get the attribute/create an attribute handle
DmAttributeHandle_t GetHandle(bool bCreate = true);
// Notify external elements upon change ( Calls OnAttributeChanged )
// Pass false here to stop notification
void NotifyWhenChanged(DmElementHandle_t h, bool bNotify);
// estimate memory overhead
int EstimateMemoryUsage(TraversalDepth_t depth) const;
private:
// Class factory
static CDmAttribute *CreateAttribute(CDmElement *pOwner,
DmAttributeType_t type,
const char *pAttributeName);
static CDmAttribute *CreateExternalAttribute(CDmElement *pOwner,
DmAttributeType_t type,
const char *pAttributeName,
void *pExternalMemory);
static void DestroyAttribute(CDmAttribute *pAttribute);
// Constructor, destructor
CDmAttribute(CDmElement *pOwner, DmAttributeType_t type,
const char *pAttributeName);
CDmAttribute(CDmElement *pOwner, DmAttributeType_t type,
const char *pAttributeName, void *pMemory);
~CDmAttribute();
// Used when constructing CDmAttributes
void Init(CDmElement *pOwner, DmAttributeType_t type,
const char *pAttributeName);
// Used when shutting down, indicates DmAttributeHandle_t referring to this
// are invalid
void InvalidateHandle();
// Used when shutting down, indicates no more change notifications will be
// sent to listening elements
void CleanupMailingList();
// Called when the attribute changes
void PreChanged();
void OnChanged(bool bArrayCountChanged = false,
bool bIsTopological = false);
// Is modification allowed in this phase?
bool ModificationAllowed() const;
// Mark the attribute as being dirty
bool MarkDirty();
// Is the data inline in a containing element class?
bool IsDataInline() const;
// Allocates, frees internal data storage
void CreateAttributeData();
void DeleteAttributeData();
// Gets at the internal data storage
void *GetAttributeData();
const void *GetAttributeData() const;
template <class T>
typename CDmAttributeInfo<T>::StorageType_t *GetData();
template <class T>
const typename CDmAttributeInfo<T>::StorageType_t *GetData() const;
template <class T>
typename CDmAttributeInfo<CUtlVector<T> >::StorageType_t *GetArrayData();
template <class T>
const typename CDmAttributeInfo<CUtlVector<T> >::StorageType_t *
GetArrayData() const;
// Used by CDmElement to manage the list of attributes it owns
CDmAttribute **GetNextAttributeRef();
// Implementational function used for memory consumption estimation
// computation
int EstimateMemoryUsageInternal(CUtlHash<DmElementHandle_t> &visited,
TraversalDepth_t depth,
int *pCategories) const;
// Called by elements after unserialization of their attributes is complete
void OnUnserializationFinished();
template <class T>
bool IsTypeConvertable() const;
template <class T>
bool ShouldModify(const T &src);
template <class T>
void CopyData(const T &src);
template <class T>
void CopyDataOut(T &dest) const;
private:
CDmAttribute *m_pNext;
void *m_pData;
CDmElement *m_pOwner;
int m_nFlags;
DmAttributeHandle_t m_Handle;
CUtlSymbol m_Name;
DmMailingList_t m_hMailingList;
friend class CDmElement;
friend class CDmAttributeAccessor;
template <class T>
friend class CDmrElementArray;
template <class E>
friend class CDmrElementArrayConst;
template <class T>
friend class CDmaArrayAccessor;
template <class T, class B>
friend class CDmrDecorator;
template <class T, class B>
friend class CDmrDecoratorConst;
template <class T>
friend class CDmArrayAttributeOp;
};
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline DmAttributeType_t CDmAttribute::GetType() const {
return (DmAttributeType_t)(m_nFlags & FATTRIB_TYPEMASK);
}
template <class T>
inline bool CDmAttribute::IsA() const {
return GetType() == CDmAttributeInfo<T>::AttributeType();
}
inline const char *CDmAttribute::GetName() const {
return g_pDataModel->GetString(m_Name);
}
inline UtlSymId_t CDmAttribute::GetNameSymbol() const { return m_Name; }
//-----------------------------------------------------------------------------
// Iteration
//-----------------------------------------------------------------------------
inline CDmAttribute *CDmAttribute::NextAttribute() { return m_pNext; }
inline const CDmAttribute *CDmAttribute::NextAttribute() const {
return m_pNext;
}
//-----------------------------------------------------------------------------
// Returns the owner
//-----------------------------------------------------------------------------
inline CDmElement *CDmAttribute::GetOwner() { return m_pOwner; }
//-----------------------------------------------------------------------------
// Value getting methods
//-----------------------------------------------------------------------------
template <class T>
inline const T &CDmAttribute::GetValue(const T &defaultValue) const {
if (GetType() == (DmAttributeType_t)(CDmAttributeInfo<T>::ATTRIBUTE_TYPE))
return *reinterpret_cast<const T *>(m_pData);
if (IsTypeConvertable<T>()) {
static T tempVal;
CopyDataOut(tempVal);
return tempVal;
}
Assert(0);
return defaultValue;
}
template <class T>
inline const T &CDmAttribute::GetValue() const {
static CDmaVar<T> defaultVal;
return GetValue(defaultVal.Get());
}
inline const char *CDmAttribute::GetValueString() const {
Assert(GetType() == AT_STRING);
if (GetType() != AT_STRING) return NULL;
return GetValue<CUtlString>();
}
// used with GetType() for use w/ SetValue( type, void* )
inline const void *CDmAttribute::GetValueUntyped() const { return m_pData; }
template <class E>
inline E *CDmAttribute::GetValueElement() const {
Assert(GetType() == AT_ELEMENT);
if (GetType() == AT_ELEMENT)
return GetElement<E>(this->GetValue<DmElementHandle_t>());
return NULL;
}
//-----------------------------------------------------------------------------
// Value setting methods
//-----------------------------------------------------------------------------
template <class E>
inline void CDmAttribute::SetValue(E *pValue) {
Assert(GetType() == AT_ELEMENT);
if (GetType() == AT_ELEMENT) {
SetValue(pValue ? pValue->GetHandle() : DMELEMENT_HANDLE_INVALID);
}
}
template <>
inline void CDmAttribute::SetValue(const char *pValue) {
int nLen = pValue ? Q_strlen(pValue) + 1 : 0;
CUtlString str(pValue, nLen);
return SetValue(str);
}
template <>
inline void CDmAttribute::SetValue(char *pValue) {
return SetValue((const char *)pValue);
}
inline void CDmAttribute::SetValue(const void *pValue, size_t nSize) {
CUtlBinaryBlock buf(pValue, (int)nSize);
return SetValue(buf);
}
//-----------------------------------------------------------------------------
// Methods related to flags
//-----------------------------------------------------------------------------
inline void CDmAttribute::AddFlag(int nFlags) { m_nFlags |= nFlags; }
inline void CDmAttribute::RemoveFlag(int nFlags) { m_nFlags &= ~nFlags; }
inline void CDmAttribute::ClearFlags() { m_nFlags = 0; }
inline int CDmAttribute::GetFlags() const { return m_nFlags; }
inline bool CDmAttribute::IsFlagSet(int nFlags) const {
return (nFlags & m_nFlags) ? true : false;
}
inline bool CDmAttribute::IsDataInline() const {
return !IsFlagSet(FATTRIB_EXTERNAL);
}
//-----------------------------------------------------------------------------
// Gets at the internal data storage
//-----------------------------------------------------------------------------
inline void *CDmAttribute::GetAttributeData() { return m_pData; }
inline const void *CDmAttribute::GetAttributeData() const { return m_pData; }
template <class T>
inline typename CDmAttributeInfo<T>::StorageType_t *CDmAttribute::GetData() {
return (typename CDmAttributeInfo<T>::StorageType_t *)m_pData;
}
template <class T>
inline typename CDmAttributeInfo<CUtlVector<T> >::StorageType_t *
CDmAttribute::GetArrayData() {
return (typename CDmAttributeInfo<CUtlVector<T> >::StorageType_t *)m_pData;
}
template <class T>
inline const typename CDmAttributeInfo<T>::StorageType_t *
CDmAttribute::GetData() const {
return (const typename CDmAttributeInfo<T>::StorageType_t *)m_pData;
}
template <class T>
inline const typename CDmAttributeInfo<CUtlVector<T> >::StorageType_t *
CDmAttribute::GetArrayData() const {
return (const typename CDmAttributeInfo<CUtlVector<T> >::StorageType_t *)
m_pData;
}
//-----------------------------------------------------------------------------
// Used by CDmElement to manage the list of attributes it owns
//-----------------------------------------------------------------------------
inline CDmAttribute **CDmAttribute::GetNextAttributeRef() { return &m_pNext; }
//-----------------------------------------------------------------------------
// helper function for determining which attributes/elements to traverse during
// copy/find/save/etc.
//-----------------------------------------------------------------------------
inline bool ShouldTraverse(const CDmAttribute *pAttr, TraversalDepth_t depth) {
switch (depth) {
case TD_NONE:
return false;
case TD_SHALLOW:
if (!pAttr->IsFlagSet(FATTRIB_MUSTCOPY)) return false;
// fall-through intentional
case TD_DEEP:
if (pAttr->IsFlagSet(FATTRIB_NEVERCOPY)) return false;
// fall-through intentional
case TD_ALL:
return true;
}
Assert(0);
return false;
}
//-----------------------------------------------------------------------------
// Gets attributes
//-----------------------------------------------------------------------------
inline CDmAttribute *CDmElement::GetAttribute(const char *pAttributeName,
DmAttributeType_t type) {
CDmAttribute *pAttribute = FindAttribute(pAttributeName);
if ((type != AT_UNKNOWN) && pAttribute && (pAttribute->GetType() != type))
return NULL;
return pAttribute;
}
inline const CDmAttribute *CDmElement::GetAttribute(
const char *pAttributeName, DmAttributeType_t type) const {
CDmAttribute *pAttribute = FindAttribute(pAttributeName);
if ((type != AT_UNKNOWN) && pAttribute && (pAttribute->GetType() != type))
return NULL;
return pAttribute;
}
//-----------------------------------------------------------------------------
// AddAttribute calls
//-----------------------------------------------------------------------------
inline CDmAttribute *CDmElement::AddAttribute(const char *pAttributeName,
DmAttributeType_t type) {
CDmAttribute *pAttribute = FindAttribute(pAttributeName);
if (pAttribute) return (pAttribute->GetType() == type) ? pAttribute : NULL;
pAttribute = CreateAttribute(pAttributeName, type);
return pAttribute;
}
template <class E>
inline CDmAttribute *CDmElement::AddAttributeElement(
const char *pAttributeName) {
CDmAttribute *pAttribute = AddAttribute(pAttributeName, AT_ELEMENT);
if (!pAttribute) return NULL;
// FIXME: If the attribute exists but has a different element type symbol,
// should we complain?
pAttribute->SetElementTypeSymbol(E::GetStaticTypeSymbol());
return pAttribute;
}
template <class E>
inline CDmAttribute *CDmElement::AddAttributeElementArray(
const char *pAttributeName) {
CDmAttribute *pAttribute = AddAttribute(pAttributeName, AT_ELEMENT_ARRAY);
if (!pAttribute) return NULL;
// FIXME: If the attribute exists but has a different element type symbol,
// should we complain?
pAttribute->SetElementTypeSymbol(E::GetStaticTypeSymbol());
return pAttribute;
}
//-----------------------------------------------------------------------------
// GetValue methods
//-----------------------------------------------------------------------------
template <class T>
inline const T &CDmElement::GetValue(const char *pAttributeName) const {
static CDmaVar<T> defaultVal;
return GetValue(pAttributeName, defaultVal.Get());
}
inline const char *CDmElement::GetValueString(
const char *pAttributeName) const {
return GetValue<CUtlString>(pAttributeName).Get();
}
template <class E>
inline E *CDmElement::GetValueElement(const char *pAttributeName) const {
DmElementHandle_t h = GetValue<DmElementHandle_t>(pAttributeName);
return GetElement<E>(h);
}
template <class T>
inline const T &CDmElement::GetValue(const char *pAttributeName,
const T &defaultVal) const {
const CDmAttribute *pAttribute = FindAttribute(pAttributeName);
if (pAttribute != NULL) return pAttribute->GetValue<T>();
return defaultVal;
}
//-----------------------------------------------------------------------------
// SetValue methods
//-----------------------------------------------------------------------------
template <class T>
inline CDmAttribute *CDmElement::SetValue(const char *pAttributeName,
const T &value) {
CDmAttribute *pAttribute = FindAttribute(pAttributeName);
if (!pAttribute) {
pAttribute = CreateAttribute(pAttributeName,
CDmAttributeInfo<T>::AttributeType());
}
if (pAttribute) {
pAttribute->SetValue(value);
return pAttribute;
}
return NULL;
}
template <class E>
inline CDmAttribute *CDmElement::SetValue(const char *pAttributeName,
E *pElement) {
DmElementHandle_t hElement =
pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID;
return SetValue(pAttributeName, hElement);
}
template <>
inline CDmAttribute *CDmElement::SetValue(const char *pAttributeName,
const char *pValue) {
int nLen = pValue ? Q_strlen(pValue) + 1 : 0;
CUtlString str(pValue, nLen);
return SetValue(pAttributeName, str);
}
template <>
inline CDmAttribute *CDmElement::SetValue(const char *pAttributeName,
char *pValue) {
return SetValue(pAttributeName, (const char *)pValue);
}
inline CDmAttribute *CDmElement::SetValue(const char *pAttributeName,
const void *pValue, size_t nSize) {
CUtlBinaryBlock buf(pValue, (int)nSize);
return SetValue(pAttributeName, buf);
}
//-----------------------------------------------------------------------------
// AddValue methods( set value if not found )
//-----------------------------------------------------------------------------
template <class T>
inline CDmAttribute *CDmElement::InitValue(const char *pAttributeName,
const T &value) {
CDmAttribute *pAttribute = GetAttribute(pAttributeName);
if (!pAttribute) return SetValue(pAttributeName, value);
return pAttribute;
}
template <class E>
inline CDmAttribute *CDmElement::InitValue(const char *pAttributeName,
E *pElement) {
DmElementHandle_t hElement =
pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID;
return InitValue(pAttributeName, hElement);
}
inline CDmAttribute *CDmElement::InitValue(const char *pAttributeName,
const void *pValue, size_t size) {
CDmAttribute *pAttribute = GetAttribute(pAttributeName);
if (!pAttribute) return SetValue(pAttributeName, pValue, size);
return pAttribute;
}
template <class T>
T *FindReferringElement(CDmElement *pElement, UtlSymId_t symAttrName,
bool bMustBeInSameFile = true) {
DmAttributeReferenceIterator_t i =
g_pDataModel->FirstAttributeReferencingElement(pElement->GetHandle());
while (i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID) {
CDmAttribute *pAttribute = g_pDataModel->GetAttribute(i);
CDmElement *pDmeParent = pAttribute->GetOwner();
if (pDmeParent && pAttribute->GetNameSymbol() == symAttrName) {
T *pParent = CastElement<T>(pDmeParent);
if (pParent) {
if (!bMustBeInSameFile ||
(pParent->GetFileId() == pElement->GetFileId()))
return pParent;
}
}
i = g_pDataModel->NextAttributeReferencingElement(i);
}
return NULL;
}
template <class T>
T *FindAncestorReferencingElement(CDmElement *target) {
if (!target) return NULL;
for (DmAttributeReferenceIterator_t it =
g_pDataModel->FirstAttributeReferencingElement(
target->GetHandle());
it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
it = g_pDataModel->NextAttributeReferencingElement(it)) {
CDmAttribute *attr = g_pDataModel->GetAttribute(it);
Assert(attr);
CDmElement *element = attr->GetOwner();
Assert(element);
if (!element) continue;
T *t = CastElement<T>(element);
if (!t) continue;
return t;
}
return NULL;
}
template <class T>
T *FindAncestorReferencingElement_R_Impl(CUtlRBTree<CDmElement *> &visited,
CDmElement *check) {
if (visited.Find(check) != visited.InvalidIndex()) return NULL;
visited.Insert(check);
// Pass one, see if it's in this ancestor list
DmAttributeReferenceIterator_t it;
for (it =
g_pDataModel->FirstAttributeReferencingElement(check->GetHandle());
it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
it = g_pDataModel->NextAttributeReferencingElement(it)) {
CDmAttribute *attr = g_pDataModel->GetAttribute(it);
Assert(attr);
CDmElement *element = attr->GetOwner();
Assert(element);
if (!element) continue;
T *t = CastElement<T>(element);
if (!t) continue;
return t;
}
for (it =
g_pDataModel->FirstAttributeReferencingElement(check->GetHandle());
it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
it = g_pDataModel->NextAttributeReferencingElement(it)) {
CDmAttribute *attr = g_pDataModel->GetAttribute(it);
Assert(attr);
CDmElement *element = attr->GetOwner();
Assert(element);
if (!element) continue;
T *found = FindAncestorReferencingElement_R_Impl<T>(visited, element);
if (found) return found;
}
return NULL;
}
template <class T>
void FindAncestorsReferencingElement(CDmElement *target,
CUtlVector<T *> &list) {
if (!target) return;
list.RemoveAll();
for (DmAttributeReferenceIterator_t it =
g_pDataModel->FirstAttributeReferencingElement(
target->GetHandle());
it != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
it = g_pDataModel->NextAttributeReferencingElement(it)) {
CDmAttribute *attr = g_pDataModel->GetAttribute(it);
Assert(attr);
CDmElement *element = attr->GetOwner();
Assert(element);
if (!element) continue;
T *t = CastElement<T>(element);
if (!t) continue;
if (list.Find(t) != list.InvalidIndex()) continue;
list.AddToTail(t);
}
}
template <class T>
T *FindAncestorReferencingElement_R(CDmElement *target) {
if (!target) return NULL;
CUtlRBTree<CDmElement *> visited(0, 0, DefLessFunc(CDmElement *));
return FindAncestorReferencingElement_R_Impl<T>(visited, target);
}
#endif // DMATTRIBUTE_H