Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1465 lines
42 KiB

  1. //====== Copyright � 1996-2004, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "datamodel/dmelement.h"
  7. #include "tier0/dbg.h"
  8. #include "datamodel.h"
  9. #include "tier1/utllinkedlist.h"
  10. #include "tier1/utlbuffer.h"
  11. #include "datamodel/dmattribute.h"
  12. #include "Color.h"
  13. #include "mathlib/mathlib.h"
  14. #include "mathlib/vmatrix.h"
  15. #include "datamodel/dmelementfactoryhelper.h"
  16. #include "datamodel/dmattributevar.h"
  17. #include "dmattributeinternal.h"
  18. #include "DmElementFramework.h"
  19. #include <ctype.h>
  20. // memdbgon must be the last include file in a .cpp file!!!
  21. #include "tier0/memdbgon.h"
  22. //-----------------------------------------------------------------------------
  23. // helper class to allow CDmeHandle access to g_pDataModelImp
  24. //-----------------------------------------------------------------------------
  25. void CDmeElementRefHelper::Ref( DmElementHandle_t hElement, HandleType_t handleType )
  26. {
  27. g_pDataModelImp->OnElementReferenceAdded( hElement, handleType );
  28. }
  29. void CDmeElementRefHelper::Unref( DmElementHandle_t hElement, HandleType_t handleType )
  30. {
  31. g_pDataModelImp->OnElementReferenceRemoved( hElement, handleType );
  32. }
  33. // turn memdbg off temporarily so we can get at placement new
  34. #include "tier0/memdbgoff.h"
  35. DEFINE_FIXEDSIZE_ALLOCATOR( DmAttributeList_t, 1024, CUtlMemoryPool::GROW_SLOW );
  36. #include "tier0/memdbgon.h"
  37. //-----------------------------------------------------------------------------
  38. // element reference struct - containing attribute referrers and handle refcount
  39. //-----------------------------------------------------------------------------
  40. void DmElementReference_t::AddAttribute( CDmAttribute *pAttribute )
  41. {
  42. if ( m_attributes.m_hAttribute != DMATTRIBUTE_HANDLE_INVALID )
  43. {
  44. DmAttributeList_t *pLink = new DmAttributeList_t; // TODO - create a fixed size allocator for these
  45. pLink->m_hAttribute = m_attributes.m_hAttribute;
  46. pLink->m_pNext = m_attributes.m_pNext;
  47. m_attributes.m_pNext = pLink;
  48. }
  49. m_attributes.m_hAttribute = pAttribute->GetHandle();
  50. }
  51. void DmElementReference_t::RemoveAttribute( CDmAttribute *pAttribute )
  52. {
  53. DmAttributeHandle_t hAttribute = pAttribute->GetHandle();
  54. if ( m_attributes.m_hAttribute == hAttribute )
  55. {
  56. DmAttributeList_t *pNext = m_attributes.m_pNext;
  57. if ( pNext )
  58. {
  59. m_attributes.m_hAttribute = pNext->m_hAttribute;
  60. m_attributes.m_pNext = pNext->m_pNext;
  61. delete pNext;
  62. }
  63. else
  64. {
  65. m_attributes.m_hAttribute = DMATTRIBUTE_HANDLE_INVALID;
  66. }
  67. return;
  68. }
  69. for ( DmAttributeList_t *pLink = &m_attributes; pLink->m_pNext; pLink = pLink->m_pNext )
  70. {
  71. DmAttributeList_t *pNext = pLink->m_pNext;
  72. if ( pNext->m_hAttribute == hAttribute )
  73. {
  74. pLink->m_pNext = pNext->m_pNext;
  75. delete pNext; // TODO - create a fixed size allocator for these
  76. return;
  77. }
  78. }
  79. Assert( 0 );
  80. }
  81. bool DmElementReference_t::FindAttribute( CDmAttribute *pAttribute )
  82. {
  83. DmAttributeHandle_t hAttribute = pAttribute->GetHandle();
  84. for ( DmAttributeList_t *pLink = &m_attributes; pLink; pLink = pLink->m_pNext )
  85. {
  86. if ( pLink->m_hAttribute == hAttribute )
  87. return true;
  88. }
  89. return false;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Class factory
  93. //-----------------------------------------------------------------------------
  94. IMPLEMENT_ELEMENT_FACTORY( DmElement, CDmElement );
  95. //-----------------------------------------------------------------------------
  96. // For backward compat: DmeElement -> creates a CDmElement class
  97. //-----------------------------------------------------------------------------
  98. CDmElementFactory< CDmElement > g_CDmeElement_Factory( "DmElement" );
  99. CDmElementFactoryHelper g_CDmeElement_Helper( "DmeElement", &g_CDmeElement_Factory, true );
  100. //-----------------------------------------------------------------------------
  101. // Constructor, destructor
  102. //-----------------------------------------------------------------------------
  103. CDmElement::CDmElement( DmElementHandle_t handle, const char *pElementType, const DmObjectId_t &id, const char *pElementName, DmFileId_t fileid ) :
  104. m_ref( handle ), m_Type( g_pDataModel->GetSymbol( pElementType ) ), m_fileId( fileid ),
  105. m_pAttributes( NULL ), m_bDirty( false ), m_bOnChangedCallbacksEnabled( false ), m_nParityBits( 0 ), m_bOnlyInUndo( false )
  106. {
  107. MEM_ALLOC_CREDIT();
  108. g_pDataModelImp->AddElementToFile( m_ref.m_hElement, m_fileId );
  109. {
  110. DMX_PROFILE_SCOPE( CDmElement_m_Name_InitAndSet );
  111. m_Name.InitAndSet( this, "name", pElementName, FATTRIB_TOPOLOGICAL );
  112. }
  113. CopyUniqueId( id, &m_Id );
  114. }
  115. CDmElement::~CDmElement()
  116. {
  117. g_pDataModelImp->RemoveElementFromFile( m_ref.m_hElement, m_fileId );
  118. }
  119. void CDmElement::PerformConstruction()
  120. {
  121. OnConstruction();
  122. }
  123. void CDmElement::PerformDestruction()
  124. {
  125. OnDestruction();
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Purpose: Deletes all attributes
  129. //-----------------------------------------------------------------------------
  130. void CDmElement::Purge()
  131. {
  132. // Don't create "undo" records for attribute changes here, since
  133. // the entire element is getting deleted...
  134. CDisableUndoScopeGuard guard;
  135. while ( m_pAttributes )
  136. {
  137. #if defined( _DEBUG )
  138. // So you can see what attribute is being destroyed
  139. const char *pName = m_pAttributes->GetName();
  140. NOTE_UNUSED( pName );
  141. #endif
  142. CDmAttribute *pNext = m_pAttributes->NextAttribute();
  143. CDmAttribute::DestroyAttribute( m_pAttributes );
  144. m_pAttributes = pNext;
  145. }
  146. g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  147. }
  148. void CDmElement::SetId( const DmObjectId_t &id )
  149. {
  150. CopyUniqueId( id, &m_Id );
  151. }
  152. //-----------------------------------------------------------------------------
  153. // RTTI implementation
  154. //-----------------------------------------------------------------------------
  155. void CDmElement::SetTypeSymbol( CUtlSymbolLarge sym )
  156. {
  157. m_classType = sym;
  158. }
  159. bool CDmElement::IsA( CUtlSymbolLarge typeSymbol ) const
  160. {
  161. // NOTE: This pattern here is used to avoid a zillion virtual function
  162. // calls in the implementation of IsA. The IsA_Implementation is
  163. // all static function calls.
  164. return IsA_Implementation( typeSymbol );
  165. }
  166. int CDmElement::GetInheritanceDepth( CUtlSymbolLarge typeSymbol ) const
  167. {
  168. // NOTE: This pattern here is used to avoid a zillion virtual function
  169. // calls in the implementation of IsA. The IsA_Implementation is
  170. // all static function calls.
  171. return GetInheritanceDepth_Implementation( typeSymbol, 0 );
  172. }
  173. // Helper for GetInheritanceDepth
  174. int CDmElement::GetInheritanceDepth( const char *pTypeName ) const
  175. {
  176. CUtlSymbolLarge typeSymbol = g_pDataModel->GetSymbol( pTypeName );
  177. return GetInheritanceDepth( typeSymbol );
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Is the element dirty?
  181. //-----------------------------------------------------------------------------
  182. bool CDmElement::IsDirty() const
  183. {
  184. return m_bDirty;
  185. }
  186. void CDmElement::MarkDirty( bool bDirty )
  187. {
  188. if ( bDirty && !m_bDirty )
  189. {
  190. g_pDmElementFrameworkImp->AddElementToDirtyList( m_ref.m_hElement );
  191. }
  192. m_bDirty = bDirty;
  193. }
  194. void CDmElement::MarkAttributesClean()
  195. {
  196. for ( CDmAttribute *pAttr = m_pAttributes; pAttr; pAttr = pAttr->NextAttribute() )
  197. {
  198. // No Undo for flag changes
  199. pAttr->RemoveFlag( FATTRIB_DIRTY );
  200. }
  201. }
  202. void CDmElement::DisableOnChangedCallbacks()
  203. {
  204. m_bOnChangedCallbacksEnabled = false;
  205. }
  206. void CDmElement::EnableOnChangedCallbacks()
  207. {
  208. m_bOnChangedCallbacksEnabled = true;
  209. }
  210. bool CDmElement::AreOnChangedCallbacksEnabled()
  211. {
  212. return m_bOnChangedCallbacksEnabled;
  213. }
  214. void CDmElement::FinishUnserialization()
  215. {
  216. for( CDmAttribute *pAttribute = m_pAttributes; pAttribute; pAttribute = pAttribute->NextAttribute() )
  217. {
  218. pAttribute->OnUnserializationFinished();
  219. }
  220. // loop referencing attributes, and call OnAttributeChanged on them as well
  221. if ( m_ref.m_attributes.m_hAttribute != DMATTRIBUTE_HANDLE_INVALID )
  222. {
  223. for ( DmAttributeList_t *pAttrLink = &m_ref.m_attributes; pAttrLink; pAttrLink = pAttrLink->m_pNext )
  224. {
  225. CDmAttribute *pAttr = g_pDataModel->GetAttribute( pAttrLink->m_hAttribute );
  226. if ( !pAttr || pAttr->GetOwner()->GetFileId() == m_fileId )
  227. continue; // attributes in this file will already have OnAttributeChanged called on them
  228. pAttr->OnUnserializationFinished();
  229. }
  230. }
  231. // Mostly used for backward compatibility reasons
  232. CDmElement *pElement = g_pDataModel->GetElement( m_ref.m_hElement );
  233. pElement->OnElementUnserialized();
  234. // Force a resolve also, and set it up to remove it from the dirty list
  235. // after unserialization is complete
  236. pElement->Resolve();
  237. MarkDirty( false );
  238. MarkAttributesClean();
  239. g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  240. }
  241. // Should only be called from datamodel, who will take care of changing the fileset entry as well
  242. void CDmElement::ChangeHandle( DmElementHandle_t handle )
  243. {
  244. m_ref.m_hElement = handle;
  245. }
  246. // returns element reference struct w/ list of referrers and handle count
  247. DmElementReference_t *CDmElement::GetReference()
  248. {
  249. return &m_ref;
  250. }
  251. void CDmElement::SetReference( const DmElementReference_t &ref )
  252. {
  253. Assert( !m_ref.IsWeaklyReferenced() );
  254. m_ref = ref;
  255. }
  256. int CDmElement::EstimateMemoryUsage( CUtlHash< DmElementHandle_t > &visited, TraversalDepth_t depth, int *pCategories )
  257. {
  258. if ( visited.Find( m_ref.m_hElement ) != visited.InvalidHandle() )
  259. return 0;
  260. visited.Insert( m_ref.m_hElement );
  261. int nDataModelUsage = g_pDataModelImp->EstimateMemoryOverhead( );
  262. int nReferenceUsage = m_ref.EstimateMemoryOverhead();
  263. CDmElement *pElement = g_pDataModel->GetElement( m_ref.m_hElement );
  264. int nInternalUsage = sizeof( *this ) - sizeof( CUtlSymbolLarge ); // NOTE: The utlSymbolLarge is the 'name' attribute var
  265. int nOuterUsage = pElement->AllocatedSize() - nInternalUsage;
  266. Assert( nOuterUsage >= 0 );
  267. if ( pCategories )
  268. {
  269. pCategories[MEMORY_CATEGORY_OUTER] += nOuterUsage;
  270. pCategories[MEMORY_CATEGORY_DATAMODEL] += nDataModelUsage;
  271. pCategories[MEMORY_CATEGORY_REFERENCES] += nReferenceUsage;
  272. pCategories[MEMORY_CATEGORY_ELEMENT_INTERNAL] += nInternalUsage;
  273. }
  274. int nAttributeDataUsage = 0;
  275. for ( CDmAttribute *pAttr = m_pAttributes; pAttr; pAttr = pAttr->NextAttribute() )
  276. {
  277. nAttributeDataUsage += pAttr->EstimateMemoryUsageInternal( visited, depth, pCategories );
  278. }
  279. return nInternalUsage + nDataModelUsage + nReferenceUsage + nOuterUsage + nAttributeDataUsage;
  280. }
  281. //-----------------------------------------------------------------------------
  282. // parity methods for marking elements during traversal
  283. //-----------------------------------------------------------------------------
  284. bool CDmElement::GetParity( int bit /*=0*/ ) const
  285. {
  286. return ( m_nParityBits & ( 1 << bit ) ) != 0;
  287. }
  288. void CDmElement::SetParity( bool bParity, int bit /*=0*/ )
  289. {
  290. m_nParityBits = bParity ? m_nParityBits | ( 1 << bit ) : m_nParityBits & ~( 1 << bit );
  291. }
  292. void CDmElement::SetParity( bool bParity, TraversalDepth_t depth, int bit /*=0*/ )
  293. {
  294. if ( GetParity( bit ) == bParity )
  295. return;
  296. SetParity( bParity, bit );
  297. for ( const CDmAttribute *pAttr = FirstAttribute(); pAttr != NULL; pAttr = pAttr->NextAttribute() )
  298. {
  299. if ( !ShouldTraverse( pAttr, depth ) )
  300. continue;
  301. if ( pAttr->GetType() == AT_ELEMENT )
  302. {
  303. CDmElement *pChild = pAttr->GetValueElement<CDmElement>();
  304. if ( !pChild )
  305. continue;
  306. pChild->SetParity( bParity, depth, bit );
  307. }
  308. else if ( pAttr->GetType() == AT_ELEMENT_ARRAY )
  309. {
  310. const CDmrElementArrayConst<> elementArrayAttr( pAttr );
  311. int nChildren = elementArrayAttr.Count();
  312. for ( int i = 0; i < nChildren; ++i )
  313. {
  314. CDmElement *pChild = elementArrayAttr[ i ];
  315. if ( !pChild )
  316. continue;
  317. pChild->SetParity( bParity, depth, bit );
  318. }
  319. }
  320. }
  321. }
  322. bool CDmElement::IsOnlyInUndo() const
  323. {
  324. return m_bOnlyInUndo;
  325. }
  326. void CDmElement::SetOnlyInUndo( bool bOnlyInUndo )
  327. {
  328. m_bOnlyInUndo = bOnlyInUndo;
  329. }
  330. //-----------------------------------------------------------------------------
  331. // returns the first path to the element found traversing all element/element array attributes - not necessarily the shortest
  332. //-----------------------------------------------------------------------------
  333. // do we want a true visited set to avoid retraversing the same subtree over and over again?
  334. // for most dag trees, it's probably a perf loss, since multiple instances are rare, (and searching the visited set costs log(n))
  335. // for trees that include channels, it's probably a perf win, since many channels link into the same element most of the time
  336. bool CDmElement::FindElement( const CDmElement *pElement, CUtlVector< ElementPathItem_t > &elementPath, TraversalDepth_t depth ) const
  337. {
  338. if ( this == pElement )
  339. return true;
  340. ElementPathItem_t search( GetHandle() );
  341. if ( elementPath.Find( search ) != elementPath.InvalidIndex() )
  342. return false;
  343. int idx = elementPath.AddToTail( search );
  344. ElementPathItem_t &pathItem = elementPath[ idx ];
  345. for ( const CDmAttribute *pAttr = FirstAttribute(); pAttr != NULL; pAttr = pAttr->NextAttribute() )
  346. {
  347. if ( !ShouldTraverse( pAttr, depth ) )
  348. continue;
  349. if ( pAttr->GetType() == AT_ELEMENT )
  350. {
  351. pathItem.hAttribute = const_cast< CDmAttribute* >( pAttr )->GetHandle();
  352. pathItem.nIndex = -1;
  353. CDmElement *pChild = pAttr->GetValueElement<CDmElement>();
  354. if ( pChild && pChild->FindElement( pElement, elementPath, depth ) )
  355. return true;
  356. }
  357. else if ( pAttr->GetType() == AT_ELEMENT_ARRAY )
  358. {
  359. pathItem.hAttribute = const_cast< CDmAttribute* >( pAttr )->GetHandle();
  360. CDmrElementArrayConst<> elementArrayAttr( pAttr );
  361. int nChildren = elementArrayAttr.Count();
  362. for ( int i = 0; i < nChildren; ++i )
  363. {
  364. pathItem.nIndex = i;
  365. CDmElement *pChild = elementArrayAttr[ i ];
  366. if ( pChild && pChild->FindElement( pElement, elementPath, depth ) )
  367. return true;
  368. }
  369. }
  370. }
  371. elementPath.Remove( idx );
  372. return false;
  373. }
  374. bool CDmElement::FindReferer( DmElementHandle_t hElement, CUtlVector< ElementPathItem_t > &elementPath, TraversalDepth_t depth /* = TD_SHALLOW */ ) const
  375. {
  376. DmElementHandle_t hThis = GetHandle();
  377. DmAttributeReferenceIterator_t hAttr = g_pDataModel->FirstAttributeReferencingElement( hThis );
  378. for ( ; hAttr != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; hAttr = g_pDataModel->NextAttributeReferencingElement( hAttr ) )
  379. {
  380. CDmAttribute *pAttr = g_pDataModel->GetAttribute( hAttr );
  381. if ( !pAttr )
  382. continue;
  383. if ( !ShouldTraverse( pAttr, depth ) )
  384. continue;
  385. DmElementHandle_t hOwner = pAttr->GetOwner()->GetHandle();
  386. if ( elementPath.Find( ElementPathItem_t( hOwner ) ) != elementPath.InvalidIndex() )
  387. return false;
  388. int i = elementPath.AddToTail();
  389. ElementPathItem_t &item = elementPath[ i ];
  390. item.hElement = hOwner;
  391. item.hAttribute = pAttr->GetHandle();
  392. item.nIndex = -1;
  393. if ( pAttr->GetType() == AT_ELEMENT_ARRAY )
  394. {
  395. CDmrElementArray<> array( pAttr );
  396. item.nIndex = array.Find( hThis );
  397. }
  398. if ( hOwner == hElement )
  399. return true;
  400. CDmElement *pOwner = GetElement< CDmElement >( hOwner );
  401. if ( pOwner->FindReferer( hElement, elementPath, depth ) )
  402. return true;
  403. elementPath.Remove( i );
  404. }
  405. return false;
  406. }
  407. void CDmElement::RemoveAllReferencesToElement( CDmElement *pElement )
  408. {
  409. for ( CDmAttribute *pAttr = FirstAttribute(); pAttr != NULL; pAttr = pAttr->NextAttribute() )
  410. {
  411. if ( pAttr->GetType() == AT_ELEMENT )
  412. {
  413. CDmElement *pChild = pAttr->GetValueElement<CDmElement>();
  414. if ( pChild == pElement )
  415. {
  416. pAttr->SetValue( DMELEMENT_HANDLE_INVALID );
  417. }
  418. }
  419. else if ( pAttr->GetType() == AT_ELEMENT_ARRAY )
  420. {
  421. CDmrElementArray<> elementArrayAttr( pAttr );
  422. int nChildren = elementArrayAttr.Count();
  423. for ( int i = nChildren - 1; i >= 0; --i )
  424. {
  425. CDmElement *pChild = elementArrayAttr[ i ];
  426. if ( pChild == pElement )
  427. {
  428. elementArrayAttr.Remove( i );
  429. }
  430. }
  431. }
  432. }
  433. }
  434. int CDmElement::EstimateMemoryUsage( TraversalDepth_t depth /* = TD_DEEP */ )
  435. {
  436. return g_pDataModel->EstimateMemoryUsage( GetHandle(), depth );
  437. }
  438. //-----------------------------------------------------------------------------
  439. //
  440. // Implementation Undo for copied objects
  441. //
  442. //-----------------------------------------------------------------------------
  443. CDmElement* CDmElement::CopyInternal( TraversalDepth_t depth /* = TD_DEEP */ ) const
  444. {
  445. CDmElement *pCopy = GetElement< CDmElement >( g_pDataModel->CreateElement( GetType(), GetName(), GetFileId() ) );
  446. if ( pCopy )
  447. {
  448. CopyAttributesTo( pCopy, depth );
  449. }
  450. return pCopy;
  451. }
  452. //-----------------------------------------------------------------------------
  453. // Copy - implementation of shallow and deep element copying
  454. // - allows attributes to be marked to always (or never) copy
  455. //-----------------------------------------------------------------------------
  456. void CDmElement::CopyAttributesTo( CDmElement *pCopy, TraversalDepth_t depth ) const
  457. {
  458. CDisableUndoScopeGuard sg;
  459. CUtlMap< DmElementHandle_t, DmElementHandle_t, int > refmap( DefLessFunc( DmElementHandle_t ) );
  460. CopyAttributesTo( pCopy, refmap, depth );
  461. CUtlHashFast< DmElementHandle_t > visited;
  462. uint nPow2Size = 1;
  463. while( nPow2Size < refmap.Count() )
  464. {
  465. nPow2Size <<= 1;
  466. }
  467. visited.Init( nPow2Size );
  468. pCopy->FixupReferences( visited, refmap, depth );
  469. }
  470. //-----------------------------------------------------------------------------
  471. // Copy an element-type attribute
  472. //-----------------------------------------------------------------------------
  473. void CDmElement::CopyElementAttribute( const CDmAttribute *pSrcAttr, CDmAttribute *pDestAttr, CRefMap &refmap, TraversalDepth_t depth ) const
  474. {
  475. DmElementHandle_t hSrc = pSrcAttr->GetValue<DmElementHandle_t>();
  476. CDmElement *pSrc = GetElement< CDmElement >( hSrc );
  477. if ( pSrc == NULL )
  478. {
  479. pDestAttr->SetValue( DMELEMENT_HANDLE_INVALID );
  480. return;
  481. }
  482. if ( pSrc->IsShared() )
  483. {
  484. pDestAttr->SetValue( pSrcAttr );
  485. return;
  486. }
  487. int idx = refmap.Find( hSrc );
  488. if ( idx != refmap.InvalidIndex() )
  489. {
  490. pDestAttr->SetValue( refmap[ idx ] );
  491. return;
  492. }
  493. if ( ShouldTraverse( pSrcAttr, depth ) )
  494. {
  495. DmElementHandle_t hDest = pDestAttr->GetValue<DmElementHandle_t>();
  496. if ( hDest == DMELEMENT_HANDLE_INVALID )
  497. {
  498. hDest = g_pDataModel->CreateElement( pSrc->GetType(), pSrc->GetName(), pSrc->GetFileId() );
  499. pDestAttr->SetValue( hDest );
  500. }
  501. CDmElement *pDest = GetElement< CDmElement >( hDest );
  502. pSrc->CopyAttributesTo( pDest, refmap, depth );
  503. return;
  504. }
  505. pDestAttr->SetValue( pSrcAttr );
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Copy an element array-type attribute
  509. //-----------------------------------------------------------------------------
  510. void CDmElement::CopyElementArrayAttribute( const CDmAttribute *pAttr, CDmAttribute *pCopyAttr, CRefMap &refmap, TraversalDepth_t depth ) const
  511. {
  512. CDmrElementArrayConst<> srcAttr( pAttr );
  513. CDmrElementArray<> destAttr( pCopyAttr );
  514. destAttr.RemoveAll(); // automatically releases each handle
  515. bool bCopy = ShouldTraverse( pAttr, depth );
  516. int n = srcAttr.Count();
  517. destAttr.EnsureCapacity( n );
  518. for ( int i = 0; i < n; ++i )
  519. {
  520. DmElementHandle_t hSrc = srcAttr.GetHandle( i );
  521. CDmElement *pSrc = srcAttr[i];
  522. if ( pSrc == NULL )
  523. {
  524. destAttr.AddToTail( DMELEMENT_HANDLE_INVALID );
  525. continue;
  526. }
  527. if ( pSrc->IsShared() )
  528. {
  529. destAttr.AddToTail( srcAttr[ i ] );
  530. continue;
  531. }
  532. int idx = refmap.Find( hSrc );
  533. if ( idx != refmap.InvalidIndex() )
  534. {
  535. destAttr.AddToTail( refmap[ idx ] );
  536. continue;
  537. }
  538. if ( bCopy )
  539. {
  540. DmElementHandle_t hDest = g_pDataModel->CreateElement( pSrc->GetType(), pSrc->GetName(), pSrc->GetFileId() );
  541. destAttr.AddToTail( hDest );
  542. CDmElement *pDest = GetElement< CDmElement >( hDest );
  543. pSrc->CopyAttributesTo( pDest, refmap, depth );
  544. continue;
  545. }
  546. destAttr.AddToTail( srcAttr[ i ] );
  547. }
  548. }
  549. //-----------------------------------------------------------------------------
  550. // internal recursive copy method
  551. // builds refmap of old element's handle -> copy's handle, and uses it to fixup references
  552. //-----------------------------------------------------------------------------
  553. void CDmElement::CopyAttributesTo( CDmElement *pCopy, CRefMap &refmap, TraversalDepth_t depth ) const
  554. {
  555. refmap.Insert( this->GetHandle(), pCopy->GetHandle() );
  556. // loop attrs, copying - element (and element array) attrs can be marked to always copy deep(er)
  557. for ( const CDmAttribute *pAttr = FirstAttribute(); pAttr != NULL; pAttr = pAttr->NextAttribute() )
  558. {
  559. DmAttributeType_t type = pAttr->GetType();
  560. const char *pAttrName = pAttr->GetName();
  561. CDmAttribute *pCopyAttr = pCopy->GetAttribute( pAttrName );
  562. if ( pCopyAttr == NULL )
  563. {
  564. pCopyAttr = pCopy->AddAttribute( pAttrName, type );
  565. int flags = pAttr->GetFlags();
  566. Assert( ( flags & FATTRIB_EXTERNAL ) == 0 );
  567. flags &= ~FATTRIB_EXTERNAL;
  568. pCopyAttr->ClearFlags();
  569. pCopyAttr->AddFlag( flags );
  570. }
  571. // Temporarily remove the read-only flag from the copy while we copy into it
  572. bool bReadOnly = pCopyAttr->IsFlagSet( FATTRIB_READONLY );
  573. if ( bReadOnly )
  574. {
  575. pCopyAttr->RemoveFlag( FATTRIB_READONLY );
  576. }
  577. if ( type == AT_ELEMENT )
  578. {
  579. CopyElementAttribute( pAttr, pCopyAttr, refmap, depth );
  580. }
  581. else if ( type == AT_ELEMENT_ARRAY )
  582. {
  583. CopyElementArrayAttribute( pAttr, pCopyAttr, refmap, depth );
  584. }
  585. else
  586. {
  587. pCopyAttr->SetValue( pAttr );
  588. }
  589. if ( bReadOnly )
  590. {
  591. pCopyAttr->AddFlag( FATTRIB_READONLY );
  592. }
  593. }
  594. }
  595. //-----------------------------------------------------------------------------
  596. // FixupReferences
  597. // fixes up any references that Copy wasn't able to figure out
  598. // example:
  599. // during a shallow copy, a channel doesn't copy its target element,
  600. // but the targets parent might decide to copy it, (later on in the travesal)
  601. // so the channel needs to change to refer to the copy
  602. //-----------------------------------------------------------------------------
  603. void CDmElement::FixupReferences( CUtlHashFast< DmElementHandle_t > &visited, const CRefMap &refmap, TraversalDepth_t depth )
  604. {
  605. if ( visited.Find( GetHandle() ) != visited.InvalidHandle() )
  606. return;
  607. visited.Insert( GetHandle(), DMELEMENT_HANDLE_INVALID ); // ignore data arguement - we're just using it as a set
  608. // loop attrs, copying - element (and element array) attrs can be marked to always copy deep(er)
  609. for ( CDmAttribute *pAttr = FirstAttribute(); pAttr != NULL; pAttr = pAttr->NextAttribute() )
  610. {
  611. DmAttributeType_t type = pAttr->GetType();
  612. bool bCopy = ShouldTraverse( pAttr, depth );
  613. if ( type == AT_ELEMENT )
  614. {
  615. DmElementHandle_t handle = pAttr->GetValue<DmElementHandle_t>();
  616. int idx = refmap.Find( handle );
  617. if ( idx == refmap.InvalidIndex() )
  618. {
  619. CDmElement *pElement = GetElement< CDmElement >( handle );
  620. if ( pElement == NULL || !bCopy )
  621. continue;
  622. pElement->FixupReferences( visited, refmap, depth );
  623. }
  624. else
  625. {
  626. pAttr->SetValue( refmap[ idx ] );
  627. }
  628. }
  629. else if ( type == AT_ELEMENT_ARRAY )
  630. {
  631. CDmrElementArray<> attrArray( pAttr );
  632. int nElements = attrArray.Count();
  633. for ( int i = 0; i < nElements; ++i )
  634. {
  635. DmElementHandle_t handle = attrArray.GetHandle( i );
  636. int idx = refmap.Find( handle );
  637. if ( idx == refmap.InvalidIndex() )
  638. {
  639. CDmElement *pElement = GetElement< CDmElement >( handle );
  640. if ( pElement == NULL || !bCopy )
  641. continue;
  642. pElement->FixupReferences( visited, refmap, depth );
  643. }
  644. else
  645. {
  646. attrArray.SetHandle( i, refmap[ idx ] );
  647. }
  648. }
  649. }
  650. }
  651. }
  652. //-----------------------------------------------------------------------------
  653. // Change type (only possible when versioning file formats)
  654. //-----------------------------------------------------------------------------
  655. void CDmElement::SetType( const char *pType )
  656. {
  657. if ( !g_pDataModelImp->IsCreatingUntypedElements() )
  658. {
  659. Warning( "Unable to set type unless you're creating untyped elements!\n" );
  660. return;
  661. }
  662. m_Type = g_pDataModel->GetSymbol( pType );
  663. }
  664. //-----------------------------------------------------------------------------
  665. // owning file
  666. //-----------------------------------------------------------------------------
  667. void CDmElement::SetFileId( DmFileId_t fileid )
  668. {
  669. g_pDataModelImp->RemoveElementFromFile( m_ref.m_hElement, m_fileId );
  670. m_fileId = fileid;
  671. g_pDataModelImp->AddElementToFile( m_ref.m_hElement, fileid );
  672. }
  673. //-----------------------------------------------------------------------------
  674. // recursively set fileid's, with option to only change elements in the matched file
  675. //-----------------------------------------------------------------------------
  676. void CDmElement::SetFileId( DmFileId_t fileid, TraversalDepth_t depth, bool bOnlyIfMatch /* = false */ )
  677. {
  678. if ( depth != TD_NONE )
  679. {
  680. CUtlHashFast< DmElementHandle_t > visited;
  681. visited.Init( 4096 ); // this will make visited behave reasonably (perf-wise) for trees w/ around 4k elements in them
  682. SetFileId_R( visited, fileid, depth, GetFileId(), bOnlyIfMatch );
  683. }
  684. else
  685. {
  686. SetFileId( fileid );
  687. }
  688. }
  689. void CDmElement::SetFileId_R( CUtlHashFast< DmElementHandle_t > &visited, DmFileId_t fileid, TraversalDepth_t depth, DmFileId_t match, bool bOnlyIfMatch )
  690. {
  691. if ( bOnlyIfMatch && match != GetFileId() )
  692. return;
  693. if ( visited.Find( GetHandle() ) != visited.InvalidHandle() )
  694. return;
  695. visited.Insert( GetHandle(), DMELEMENT_HANDLE_INVALID ); // ignore data arguement - we're just using it as a set
  696. SetFileId( fileid );
  697. for ( CDmAttribute *pAttr = FirstAttribute(); pAttr != NULL; pAttr = pAttr->NextAttribute() )
  698. {
  699. DmAttributeType_t type = pAttr->GetType();
  700. if ( !ShouldTraverse( pAttr, depth ) )
  701. continue;
  702. if ( type == AT_ELEMENT )
  703. {
  704. CDmElement *pElement = pAttr->GetValueElement<CDmElement>();
  705. if ( pElement )
  706. {
  707. pElement->SetFileId_R( visited, fileid, depth, match, bOnlyIfMatch );
  708. }
  709. }
  710. else if ( type == AT_ELEMENT_ARRAY )
  711. {
  712. CDmrElementArray<> attrArray( pAttr );
  713. int nElements = attrArray.Count();
  714. for ( int i = 0; i < nElements; ++i )
  715. {
  716. CDmElement *pElement = attrArray[ i ];
  717. if ( pElement )
  718. {
  719. pElement->SetFileId_R( visited, fileid, depth, match, bOnlyIfMatch );
  720. }
  721. }
  722. }
  723. }
  724. }
  725. //-----------------------------------------------------------------------------
  726. //
  727. //-----------------------------------------------------------------------------
  728. DmElementHandle_t CDmElement::GetHandle() const
  729. {
  730. Assert( m_ref.m_hElement != DMELEMENT_HANDLE_INVALID );
  731. return m_ref.m_hElement;
  732. }
  733. //-----------------------------------------------------------------------------
  734. // Iteration
  735. //-----------------------------------------------------------------------------
  736. int CDmElement::AttributeCount() const
  737. {
  738. int nAttrs = 0;
  739. for ( CDmAttribute *pAttr = m_pAttributes; pAttr; pAttr = pAttr->NextAttribute() )
  740. {
  741. ++nAttrs;
  742. }
  743. return nAttrs;
  744. }
  745. CDmAttribute* CDmElement::FirstAttribute()
  746. {
  747. return m_pAttributes;
  748. }
  749. const CDmAttribute* CDmElement::FirstAttribute() const
  750. {
  751. return m_pAttributes;
  752. }
  753. bool CDmElement::HasAttribute( const char *pAttributeName, DmAttributeType_t type ) const
  754. {
  755. CDmAttribute *pAttribute = FindAttribute( pAttributeName );
  756. if ( !pAttribute )
  757. return false;
  758. return ( type == AT_UNKNOWN || ( pAttribute->GetType() == type ) );
  759. }
  760. //-----------------------------------------------------------------------------
  761. //
  762. // Implementation of Undo for adding or removing an attribute to or from and
  763. // element.
  764. //
  765. //-----------------------------------------------------------------------------
  766. class CUndoAttributeAddRemove : public CUndoElement
  767. {
  768. typedef CUndoElement BaseClass;
  769. public:
  770. CUndoAttributeAddRemove( CDmElement *pElement, CDmAttribute *pAttribute, bool bRemove )
  771. : BaseClass( "CUndoAttributeAddRemove" ),
  772. m_bRemove( bRemove ),
  773. m_bHoldingPtr( bRemove ),
  774. m_hElement( pElement->GetHandle() ),
  775. m_pAttribute( pAttribute )
  776. {
  777. m_symAttribute = g_pDataModel->GetSymbol( pAttribute->GetName() );
  778. Assert( pElement && pElement->GetFileId() != DMFILEID_INVALID );
  779. }
  780. ~CUndoAttributeAddRemove()
  781. {
  782. if ( m_bHoldingPtr && m_pAttribute )
  783. {
  784. CDmAttributeAccessor::DestroyAttribute( m_pAttribute );
  785. }
  786. }
  787. virtual void Undo()
  788. {
  789. // Remove the attribute if this is an add undo, or add the attribute if a remove undo.
  790. AddOrRemoveAttributeFromElement( m_bRemove );
  791. }
  792. virtual void Redo()
  793. {
  794. // Add the attribute if this an add undo, or remove the attribute if this a remove undo.
  795. AddOrRemoveAttributeFromElement( !m_bRemove );
  796. }
  797. virtual const char *GetDesc()
  798. {
  799. static char buf[ 128 ];
  800. const char *base = BaseClass::GetDesc();
  801. Q_snprintf( buf, sizeof( buf ), "%s(%s)", base, m_symAttribute.String() );
  802. return buf;
  803. }
  804. private:
  805. void AddOrRemoveAttributeFromElement( bool bAdd )
  806. {
  807. CDmElement *pElement = GetElement();
  808. if ( pElement == NULL )
  809. return;
  810. if ( bAdd )
  811. {
  812. CDmeElementAccessor::AddAttributeByPtr( pElement, m_pAttribute );
  813. m_bHoldingPtr = false;
  814. }
  815. else
  816. {
  817. CDmeElementAccessor::RemoveAttributeByPtrNoDelete( pElement, m_pAttribute );
  818. m_bHoldingPtr = true;
  819. }
  820. }
  821. CDmElement *GetElement() const
  822. {
  823. return g_pDataModel->GetElement( m_hElement );
  824. }
  825. const bool m_bRemove;
  826. bool m_bHoldingPtr;
  827. const DmElementHandle_t m_hElement;
  828. CUtlSymbolLarge m_symAttribute;
  829. CDmAttribute *const m_pAttribute;
  830. };
  831. //-----------------------------------------------------------------------------
  832. // Containing object
  833. //-----------------------------------------------------------------------------
  834. void CDmElement::RemoveAttributeByPtrNoDelete( CDmAttribute *ptr )
  835. {
  836. for ( CDmAttribute **ppAttr = &m_pAttributes; *ppAttr; ppAttr = ( *ppAttr )->GetNextAttributeRef() )
  837. {
  838. if ( ptr == *ppAttr )
  839. {
  840. MarkDirty();
  841. ptr->InvalidateHandle();
  842. *ppAttr = ( *ppAttr )->NextAttribute();
  843. g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  844. return;
  845. }
  846. }
  847. }
  848. //-----------------------------------------------------------------------------
  849. // Attribute removal
  850. //-----------------------------------------------------------------------------
  851. void CDmElement::RemoveAttribute( CDmAttribute **pAttrRef )
  852. {
  853. CDmAttribute *pAttrToDelete = *pAttrRef;
  854. // Removal of external attributes is verboten
  855. Assert( !pAttrToDelete->IsFlagSet( FATTRIB_EXTERNAL ) );
  856. if( pAttrToDelete->IsFlagSet( FATTRIB_EXTERNAL ) )
  857. return;
  858. // This will cause element attributes to be properly removed from the attribute
  859. // reference list of the element they refer to, resolving issues where dead
  860. // attributes would be encountered when walking the of referring elements.
  861. pAttrToDelete->SetToDefaultValue();
  862. MarkDirty();
  863. // UNDO Hook
  864. bool storedbyundo = false;
  865. if ( g_pDataModel->UndoEnabledForElement( this ) )
  866. {
  867. MEM_ALLOC_CREDIT_CLASS();
  868. CUndoAttributeAddRemove *pUndo = new CUndoAttributeAddRemove( this, pAttrToDelete, true );
  869. g_pDataModel->AddUndoElement( pUndo );
  870. storedbyundo = true;
  871. }
  872. *pAttrRef = ( *pAttrRef )->NextAttribute();
  873. if ( !storedbyundo )
  874. {
  875. CDmAttribute::DestroyAttribute( pAttrToDelete );
  876. }
  877. g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  878. }
  879. void CDmElement::RemoveAttribute( const char *pAttributeName )
  880. {
  881. CUtlSymbolLarge find = g_pDataModel->GetSymbol( pAttributeName );
  882. for ( CDmAttribute **ppAttr = &m_pAttributes; *ppAttr; ppAttr = ( *ppAttr )->GetNextAttributeRef() )
  883. {
  884. if ( find == ( *ppAttr )->GetNameSymbol() )
  885. {
  886. RemoveAttribute( ppAttr );
  887. return;
  888. }
  889. }
  890. }
  891. void CDmElement::RemoveAttributeByPtr( CDmAttribute *pAttribute )
  892. {
  893. Assert( pAttribute );
  894. for ( CDmAttribute **ppAttr = &m_pAttributes; *ppAttr; ppAttr = ( *ppAttr )->GetNextAttributeRef() )
  895. {
  896. if ( pAttribute == *ppAttr )
  897. {
  898. RemoveAttribute( ppAttr );
  899. return;
  900. }
  901. }
  902. }
  903. //-----------------------------------------------------------------------------
  904. // Sets an attribute from a string
  905. //-----------------------------------------------------------------------------
  906. void CDmElement::SetValueFromString( const char *pAttributeName, const char *pValue )
  907. {
  908. CDmAttribute *pAttribute = FindAttribute( pAttributeName );
  909. if ( pAttribute )
  910. {
  911. pAttribute->SetValueFromString( pValue );
  912. }
  913. }
  914. //-----------------------------------------------------------------------------
  915. // Writes an attribute as a string
  916. //-----------------------------------------------------------------------------
  917. const char *CDmElement::GetValueAsString( const char *pAttributeName, char *pBuffer, size_t nBufLen ) const
  918. {
  919. Assert( pBuffer );
  920. const CDmAttribute *pAttribute = FindAttribute( pAttributeName );
  921. if ( pAttribute )
  922. return pAttribute->GetValueAsString( pBuffer, nBufLen );
  923. pBuffer[ 0 ] = 0;
  924. return pBuffer;
  925. }
  926. //-----------------------------------------------------------------------------
  927. // Adds, removes attributes
  928. //-----------------------------------------------------------------------------
  929. void CDmElement::AddAttributeByPtr( CDmAttribute *ptr )
  930. {
  931. MarkDirty();
  932. for ( CDmAttribute *pAttr = m_pAttributes; pAttr; pAttr = pAttr->NextAttribute() )
  933. {
  934. if ( pAttr == ptr )
  935. {
  936. Assert( 0 );
  937. return;
  938. }
  939. }
  940. *( ptr->GetNextAttributeRef() ) = m_pAttributes;
  941. m_pAttributes = ptr;
  942. g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  943. }
  944. CDmAttribute *CDmElement::CreateAttribute( const char *pAttributeName, DmAttributeType_t type )
  945. {
  946. Assert( !HasAttribute( pAttributeName ) );
  947. MarkDirty( );
  948. CDmAttribute *pAttribute = NULL;
  949. {
  950. CDisableUndoScopeGuard guard;
  951. pAttribute = CDmAttribute::CreateAttribute( this, type, pAttributeName );
  952. *( pAttribute->GetNextAttributeRef() ) = m_pAttributes;
  953. m_pAttributes = pAttribute;
  954. }
  955. if ( g_pDataModel->UndoEnabledForElement( this ) )
  956. {
  957. MEM_ALLOC_CREDIT_CLASS();
  958. CUndoAttributeAddRemove *pUndo = new CUndoAttributeAddRemove( this, pAttribute, false );
  959. g_pDataModel->AddUndoElement( pUndo );
  960. }
  961. g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  962. return pAttribute;
  963. }
  964. CDmAttribute* CDmElement::AddExternalAttribute( const char *pAttributeName, DmAttributeType_t type, void *pMemory )
  965. {
  966. DMX_PROFILE_SCOPE( AddExternalAttribute );
  967. {
  968. DMX_PROFILE_SCOPE( AddExternalAttribute_MarkDirty );
  969. MarkDirty( );
  970. }
  971. // Add will only add the attribute doesn't already exist
  972. {
  973. DMX_PROFILE_SCOPE( AddExternalAttribute_HasAttribute );
  974. if ( HasAttribute( pAttributeName ) )
  975. {
  976. Assert( 0 );
  977. return NULL;
  978. }
  979. }
  980. CDmAttribute *pAttribute = NULL;
  981. {
  982. CDisableUndoScopeGuard guard;
  983. {
  984. DMX_PROFILE_SCOPE( AddExternalAttribute_CreateExternalAttribute );
  985. pAttribute = CDmAttribute::CreateExternalAttribute( this, type, pAttributeName, pMemory );
  986. }
  987. *( pAttribute->GetNextAttributeRef() ) = m_pAttributes;
  988. m_pAttributes = pAttribute;
  989. }
  990. {
  991. DMX_PROFILE_SCOPE( AddExternalAttribute_CheckUndo );
  992. if ( g_pDataModel->UndoEnabledForElement( this ) )
  993. {
  994. MEM_ALLOC_CREDIT_CLASS();
  995. CUndoAttributeAddRemove *pUndo = new CUndoAttributeAddRemove( this, pAttribute, false );
  996. g_pDataModel->AddUndoElement( pUndo );
  997. }
  998. }
  999. g_pDataModelImp->NotifyState( NOTIFY_CHANGE_TOPOLOGICAL );
  1000. return pAttribute;
  1001. }
  1002. //-----------------------------------------------------------------------------
  1003. // Find an attribute in the list
  1004. //-----------------------------------------------------------------------------
  1005. CDmAttribute *CDmElement::FindAttribute( const char *pAttributeName ) const
  1006. {
  1007. CUtlSymbolLarge find = g_pDataModel->GetSymbol( pAttributeName );
  1008. for ( CDmAttribute *pAttr = m_pAttributes; pAttr; pAttr = pAttr->NextAttribute() )
  1009. {
  1010. if ( find == pAttr->GetNameSymbol() )
  1011. return pAttr;
  1012. }
  1013. return NULL;
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // attribute renaming
  1017. //-----------------------------------------------------------------------------
  1018. void CDmElement::RenameAttribute( const char *pAttributeName, const char *pNewName )
  1019. {
  1020. CDmAttribute *pAttr = FindAttribute( pAttributeName );
  1021. if ( pAttr )
  1022. {
  1023. pAttr->SetName( pNewName );
  1024. }
  1025. }
  1026. //-----------------------------------------------------------------------------
  1027. // allows elements to chain OnAttributeChanged up to their parents (or at least, referrers)
  1028. //-----------------------------------------------------------------------------
  1029. void InvokeOnAttributeChangedOnReferrers( DmElementHandle_t hElement, CDmAttribute *pChangedAttr )
  1030. {
  1031. DmAttributeReferenceIterator_t ai = g_pDataModel->FirstAttributeReferencingElement( hElement );
  1032. for ( ; ai != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID; ai = g_pDataModel->NextAttributeReferencingElement( ai ) )
  1033. {
  1034. CDmAttribute *pAttr = g_pDataModel->GetAttribute( ai );
  1035. Assert( pAttr );
  1036. if ( !pAttr )
  1037. continue;
  1038. if ( pAttr->IsFlagSet( FATTRIB_NEVERCOPY ) )
  1039. continue;
  1040. CDmElement *pOwner = pAttr->GetOwner();
  1041. Assert( pOwner );
  1042. if ( !pOwner )
  1043. continue;
  1044. pOwner->OnAttributeChanged( pChangedAttr );
  1045. }
  1046. }
  1047. //-----------------------------------------------------------------------------
  1048. // Destroys an element and all elements it refers to via attributes
  1049. //-----------------------------------------------------------------------------
  1050. void DestroyElement( CDmElement *pElement, TraversalDepth_t depth )
  1051. {
  1052. if ( !pElement )
  1053. return;
  1054. DmElementHandle_t hElement = pElement->GetHandle();
  1055. CDmAttribute* pAttribute;
  1056. for ( pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
  1057. {
  1058. if ( !ShouldTraverse( pAttribute, depth ) )
  1059. continue;
  1060. g_pDataModelImp->MarkHandleInvalid( hElement ); // prevents recursing through this element again
  1061. switch( pAttribute->GetType() )
  1062. {
  1063. case AT_ELEMENT:
  1064. {
  1065. CDmElement *pChild = pAttribute->GetValueElement<CDmElement>();
  1066. DestroyElement( pChild, depth );
  1067. }
  1068. break;
  1069. case AT_ELEMENT_ARRAY:
  1070. {
  1071. CDmrElementArray<> array( pAttribute );
  1072. int nElements = array.Count();
  1073. for ( int i = 0; i < nElements; ++i )
  1074. {
  1075. CDmElement *pChild = array[ i ];
  1076. DestroyElement( pChild, depth );
  1077. }
  1078. }
  1079. break;
  1080. }
  1081. g_pDataModelImp->MarkHandleValid( hElement );
  1082. }
  1083. g_pDataModel->DestroyElement( hElement );
  1084. }
  1085. //-----------------------------------------------------------------------------
  1086. //
  1087. // generic element tree traversal helper class
  1088. //
  1089. //-----------------------------------------------------------------------------
  1090. CElementTreeTraversal::CElementTreeTraversal( CDmElement *pRoot, const char *pAttrName )
  1091. {
  1092. Reset( pRoot, pAttrName );
  1093. }
  1094. void CElementTreeTraversal::Reset( CDmElement *pRoot, const char *pAttrName )
  1095. {
  1096. m_state.RemoveAll();
  1097. m_state.AddToTail( State_t( pRoot, NOT_VISITED ) );
  1098. m_pAttrName = pAttrName;
  1099. }
  1100. CDmElement *CElementTreeTraversal::Next( bool bSkipChildren /*= false*/ )
  1101. {
  1102. if ( m_state.Count() == 1 && m_state[ 0 ].nIndex == NOT_VISITED )
  1103. {
  1104. m_state[ 0 ].nIndex = VISITING;
  1105. return m_state[ 0 ].pElement;
  1106. }
  1107. if ( bSkipChildren )
  1108. {
  1109. m_state.Remove( m_state.Count() - 1 );
  1110. }
  1111. while ( int nCount = m_state.Count() )
  1112. {
  1113. State_t &state = m_state[ nCount - 1 ];
  1114. Assert( state.pElement );
  1115. if ( !state.pElement )
  1116. return NULL;
  1117. CDmrElementArray<> children( state.pElement, m_pAttrName );
  1118. if ( children.IsValid() )
  1119. {
  1120. int nChildren = children.Count();
  1121. while ( ++state.nIndex < nChildren )
  1122. {
  1123. if ( CDmElement *pElement = children[ state.nIndex ] )
  1124. {
  1125. m_state.AddToTail( State_t( pElement, VISITING ) );
  1126. return pElement;
  1127. }
  1128. }
  1129. }
  1130. m_state.Remove( nCount - 1 );
  1131. }
  1132. return NULL;
  1133. }
  1134. CDmElement *CElementTreeTraversal::GetElement()
  1135. {
  1136. int nCount = m_state.Count();
  1137. if ( nCount < 1 )
  1138. return NULL;
  1139. State_t &state = m_state[ nCount - 1 ];
  1140. Assert( state.nIndex == NOT_VISITED || state.nIndex == VISITING );
  1141. return state.nIndex == NOT_VISITED ? NULL : state.pElement;
  1142. }
  1143. CDmElement *CElementTreeTraversal::GetParent( int i = 0 )
  1144. {
  1145. return i + 1 < m_state.Count() ? m_state[ m_state.Count() - i - 1 ].pElement : NULL;
  1146. }
  1147. int CElementTreeTraversal::GetChildIndex( int i )
  1148. {
  1149. return i + 1 < m_state.Count() ? m_state[ m_state.Count() - i - 1 ].nIndex : NOT_VISITED;
  1150. }
  1151. //-----------------------------------------------------------------------------
  1152. //
  1153. // element-specific unique name generation methods
  1154. //
  1155. //-----------------------------------------------------------------------------
  1156. // returns startindex if none found, 1 if only "prefix" found, and n+1 if "prefixn" found
  1157. int GenerateUniqueNameIndex( const char *prefix, const CUtlVector< DmElementHandle_t > &array, int startindex /*= 0*/ )
  1158. {
  1159. return V_GenerateUniqueNameIndex( prefix, ElementArrayNameAccessor< DmElementHandle_t >( array ), startindex );
  1160. }
  1161. bool GenerateUniqueName( char *name, int memsize, const char *prefix, const CUtlVector< DmElementHandle_t > &array )
  1162. {
  1163. return V_GenerateUniqueName( name, memsize, prefix, ElementArrayNameAccessor< DmElementHandle_t >( array ) );
  1164. }
  1165. int SplitStringIntoBaseAndIntegerSuffix( const char *pName, int len, char *pBaseName )
  1166. {
  1167. int baselen = len;
  1168. while ( baselen > 0 && V_isdigit( pName[ baselen - 1 ] ) )
  1169. {
  1170. --baselen;
  1171. }
  1172. V_strncpy( pBaseName, pName, baselen + 1 );
  1173. return ( baselen < len ) ? V_atoi( pName + baselen ) : 0;
  1174. }
  1175. void MakeElementNameUnique( CDmElement *pElement, const CUtlVector< DmElementHandle_t > &array )
  1176. {
  1177. if ( !pElement )
  1178. return;
  1179. const char *pName = pElement->GetName();
  1180. int len = V_strlen( pName );
  1181. char *pBaseName = ( char* )stackalloc( len + 1 );
  1182. int nStartingIndex = SplitStringIntoBaseAndIntegerSuffix( pName, len, pBaseName );
  1183. int i = GenerateUniqueNameIndex( pBaseName, array, nStartingIndex );
  1184. if ( i <= 0 )
  1185. {
  1186. pElement->SetName( pBaseName );
  1187. return;
  1188. }
  1189. int newlen = len + 11; // reserve at least enough space for 10 digits and a terminating '\0'
  1190. char *pNewName = ( char* )stackalloc( newlen );
  1191. Q_snprintf( pNewName, newlen, "%s%d", pBaseName, i );
  1192. pElement->SetName( pNewName );
  1193. }
  1194. void RemoveElementFromRefereringAttributes( CDmElement *pElement, bool bPreserveOrder /*= true*/ )
  1195. {
  1196. for ( DmAttributeReferenceIterator_t i = g_pDataModel->FirstAttributeReferencingElement( pElement->GetHandle() );
  1197. i != DMATTRIBUTE_REFERENCE_ITERATOR_INVALID;
  1198. i = g_pDataModel->FirstAttributeReferencingElement( pElement->GetHandle() ) ) // always re-get the FIRST attribute, since we're removing from this list
  1199. {
  1200. CDmAttribute *pAttribute = g_pDataModel->GetAttribute( i );
  1201. Assert( pAttribute );
  1202. if ( !pAttribute )
  1203. continue;
  1204. if ( IsArrayType( pAttribute->GetType() ) )
  1205. {
  1206. CDmrElementArray<> array( pAttribute );
  1207. int i = array.Find( pElement );
  1208. Assert( i != array.InvalidIndex() );
  1209. if ( bPreserveOrder )
  1210. {
  1211. array.Remove( i );
  1212. }
  1213. else
  1214. {
  1215. array.FastRemove( i );
  1216. }
  1217. }
  1218. else
  1219. {
  1220. pAttribute->SetValue( DMELEMENT_HANDLE_INVALID );
  1221. }
  1222. }
  1223. }