Team Fortress 2 Source Code as on 22/4/2020
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.

1420 lines
40 KiB

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