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.

468 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "dmelementdictionary.h"
  7. #include "datamodel/dmelement.h"
  8. #include "datamodel/dmattribute.h"
  9. #include "datamodel/dmattributevar.h"
  10. #include "datamodel/idatamodel.h"
  11. #include "datamodel.h"
  12. //-----------------------------------------------------------------------------
  13. // Constructor
  14. //-----------------------------------------------------------------------------
  15. CDmElementDictionary::CDmElementDictionary()
  16. : m_idmap( 1024, 0, 0, DmIdPair_t::Compare, DmIdPair_t::HashKey )
  17. {
  18. }
  19. //-----------------------------------------------------------------------------
  20. // Clears the dictionary
  21. //-----------------------------------------------------------------------------
  22. void CDmElementDictionary::Clear()
  23. {
  24. m_Dict.Purge();
  25. m_Attributes.Purge();
  26. m_ArrayAttributes.Purge();
  27. m_elementsToDelete.Purge();
  28. }
  29. //-----------------------------------------------------------------------------
  30. // Inserts an element into the table
  31. //-----------------------------------------------------------------------------
  32. DmElementDictHandle_t CDmElementDictionary::InsertElement( CDmElement *pElement )
  33. {
  34. // Insert it into the reconnection table
  35. return m_Dict.AddToTail( pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID );
  36. }
  37. //-----------------------------------------------------------------------------
  38. // Returns a particular element
  39. //-----------------------------------------------------------------------------
  40. CDmElement *CDmElementDictionary::GetElement( DmElementDictHandle_t handle )
  41. {
  42. if ( handle == ELEMENT_DICT_HANDLE_INVALID )
  43. return NULL;
  44. return g_pDataModel->GetElement( m_Dict[ handle ] );
  45. }
  46. //-----------------------------------------------------------------------------
  47. // Adds an attribute to the fixup list
  48. //-----------------------------------------------------------------------------
  49. void CDmElementDictionary::AddAttribute( CDmAttribute *pAttribute, const DmObjectId_t &objectId )
  50. {
  51. if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
  52. return; // don't add attributes if their element is being deleted
  53. int i = m_Attributes.AddToTail();
  54. m_Attributes[i].m_nType = AT_OBJECTID;
  55. m_Attributes[i].m_pAttribute = pAttribute;
  56. CopyUniqueId( objectId, &m_Attributes[i].m_ObjectId );
  57. }
  58. //-----------------------------------------------------------------------------
  59. // Adds an element of an attribute array to the fixup list
  60. //-----------------------------------------------------------------------------
  61. void CDmElementDictionary::AddArrayAttribute( CDmAttribute *pAttribute, DmElementDictHandle_t hElement )
  62. {
  63. if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
  64. return; // don't add attributes if their element is being deleted
  65. int i = m_ArrayAttributes.AddToTail();
  66. m_ArrayAttributes[i].m_nType = AT_ELEMENT;
  67. m_ArrayAttributes[i].m_pAttribute = pAttribute;
  68. m_ArrayAttributes[i].m_hElement = hElement;
  69. }
  70. void CDmElementDictionary::AddArrayAttribute( CDmAttribute *pAttribute, const DmObjectId_t &objectId )
  71. {
  72. if ( m_elementsToDelete.Find( pAttribute->GetOwner()->GetHandle() ) != m_elementsToDelete.InvalidIndex() )
  73. return; // don't add attributes if their element is being deleted
  74. int i = m_ArrayAttributes.AddToTail();
  75. m_ArrayAttributes[i].m_nType = AT_OBJECTID;
  76. m_ArrayAttributes[i].m_pAttribute = pAttribute;
  77. CopyUniqueId( objectId, &m_ArrayAttributes[i].m_ObjectId );
  78. }
  79. //-----------------------------------------------------------------------------
  80. //
  81. //-----------------------------------------------------------------------------
  82. void CDmElementDictionary::RemoveAttributeInfosOfElement( AttributeList_t &attributes, DmElementHandle_t hElement )
  83. {
  84. while ( attributes.Count() > 0 && attributes.Tail().m_pAttribute->GetOwner()->GetHandle() == hElement )
  85. {
  86. attributes.Remove( attributes.Count() - 1 );
  87. }
  88. }
  89. DmElementHandle_t CDmElementDictionary::SetElementId( DmElementDictHandle_t hDictHandle, const DmObjectId_t &newId, DmConflictResolution_t idConflictResolution )
  90. {
  91. DmElementHandle_t hElement = m_Dict[ hDictHandle ];
  92. CDmElement *pElement = g_pDataModel->GetElement( hElement );
  93. Assert( pElement );
  94. if ( !pElement )
  95. return DMELEMENT_HANDLE_INVALID;
  96. const DmObjectId_t &oldId = pElement->GetId();
  97. if ( idConflictResolution == CR_FORCE_COPY )
  98. {
  99. m_idmap.Insert( DmIdPair_t( newId, oldId ) ); // map the newId back to the old id, and keep the old id
  100. return hElement;
  101. }
  102. DmElementHandle_t newHandle = g_pDataModelImp->ChangeElementId( hElement, oldId, newId );
  103. if ( newHandle != DMELEMENT_HANDLE_INVALID )
  104. {
  105. // if ChangeElementId returns a handle, the id has been changed
  106. if ( newHandle != hElement )
  107. {
  108. int i = m_Dict.Find( hElement );
  109. if ( i != m_Dict.InvalidIndex() )
  110. {
  111. m_Dict[ i ] = newHandle;
  112. }
  113. }
  114. return newHandle; // either keeping the old handle, with the new id, or found a new handle associated with that new id
  115. }
  116. // id not changed because that id is already in use
  117. if ( idConflictResolution == CR_DELETE_NEW )
  118. {
  119. DmElementHandle_t hExistingElement = g_pDataModel->FindElement( newId );
  120. int i = m_elementsToDelete.AddToTail( );
  121. m_elementsToDelete[i].m_hDictHandle = hDictHandle;
  122. m_elementsToDelete[i].m_hElementToDelete = hElement;
  123. m_elementsToDelete[i].m_hReplacementElement = hExistingElement;
  124. // remove all element ref attributes read in before the id (typically none)
  125. RemoveAttributeInfosOfElement( m_Attributes, hElement );
  126. RemoveAttributeInfosOfElement( m_ArrayAttributes, hElement );
  127. return DMELEMENT_HANDLE_INVALID;
  128. }
  129. if ( idConflictResolution == CR_DELETE_OLD )
  130. {
  131. DmElementHandle_t hExistingElement = g_pDataModel->FindElement( newId );
  132. Assert( hExistingElement != DMELEMENT_HANDLE_INVALID );
  133. if ( hExistingElement == DMELEMENT_HANDLE_INVALID )
  134. return DMELEMENT_HANDLE_INVALID; // unexpected error in ChangeElementId (failed due to something other than a conflict)
  135. g_pDataModelImp->DeleteElement( hExistingElement, HR_NEVER ); // need to keep the handle around until ChangeElemendId
  136. newHandle = g_pDataModelImp->ChangeElementId( hElement, oldId, newId );
  137. Assert( newHandle == hExistingElement );
  138. int i = m_Dict.Find( hElement );
  139. if ( i != m_Dict.InvalidIndex() )
  140. {
  141. m_Dict[ i ] = newHandle;
  142. }
  143. return newHandle;
  144. }
  145. if ( idConflictResolution == CR_COPY_NEW )
  146. {
  147. m_idmap.Insert( DmIdPair_t( newId, oldId ) ); // map the newId back to the old id, and keep the old id
  148. return hElement;
  149. }
  150. Assert( 0 );
  151. return DMELEMENT_HANDLE_INVALID;
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Finds an element into the table
  155. //-----------------------------------------------------------------------------
  156. DmElementDictHandle_t CDmElementDictionary::FindElement( CDmElement *pElement )
  157. {
  158. return m_Dict.Find( pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID );
  159. }
  160. //-----------------------------------------------------------------------------
  161. // Hook up all element references (which were unserialized as object ids)
  162. //-----------------------------------------------------------------------------
  163. void CDmElementDictionary::HookUpElementAttributes()
  164. {
  165. int n = m_Attributes.Count();
  166. for ( int i = 0; i < n; ++i )
  167. {
  168. Assert( m_Attributes[i].m_pAttribute->GetType() == AT_ELEMENT );
  169. Assert( m_Attributes[i].m_nType == AT_OBJECTID );
  170. UtlHashHandle_t h = m_idmap.Find( DmIdPair_t( m_Attributes[i].m_ObjectId ) );
  171. DmObjectId_t &id = h == m_idmap.InvalidHandle() ? m_Attributes[i].m_ObjectId : m_idmap[ h ].m_newId;
  172. // search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
  173. DmElementHandle_t hElement = g_pDataModelImp->FindOrCreateElementHandle( id );
  174. m_Attributes[i].m_pAttribute->SetValue<DmElementHandle_t>( hElement );
  175. }
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Hook up all element array references
  179. //-----------------------------------------------------------------------------
  180. void CDmElementDictionary::HookUpElementArrayAttributes()
  181. {
  182. // Find unique array attributes; we need to clear them all before adding stuff.
  183. // This clears them of stuff added during their construction phase.
  184. int n = m_ArrayAttributes.Count();
  185. CUtlRBTree< CDmAttribute*, unsigned short > lookup( 0, n, DefLessFunc(CDmAttribute*) );
  186. for ( int i = 0; i < n; ++i )
  187. {
  188. Assert( m_ArrayAttributes[i].m_pAttribute->GetType() == AT_ELEMENT_ARRAY );
  189. CDmAttribute *pElementArray = m_ArrayAttributes[i].m_pAttribute;
  190. CDmrElementArray<> array( pElementArray );
  191. if ( lookup.Find( pElementArray ) == lookup.InvalidIndex() )
  192. {
  193. array.RemoveAll();
  194. lookup.Insert( pElementArray );
  195. }
  196. }
  197. for ( int i = 0; i < n; ++i )
  198. {
  199. Assert( m_ArrayAttributes[i].m_pAttribute->GetType() == AT_ELEMENT_ARRAY );
  200. CDmrElementArray<> array( m_ArrayAttributes[i].m_pAttribute );
  201. if ( m_ArrayAttributes[i].m_nType == AT_ELEMENT )
  202. {
  203. CDmElement *pElement = GetElement( m_ArrayAttributes[i].m_hElement );
  204. array.AddToTail( pElement );
  205. }
  206. else
  207. {
  208. UtlHashHandle_t h = m_idmap.Find( DmIdPair_t( m_ArrayAttributes[i].m_ObjectId ) );
  209. DmObjectId_t &id = ( h == m_idmap.InvalidHandle() ) ? m_ArrayAttributes[i].m_ObjectId : m_idmap[ h ].m_newId;
  210. // search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
  211. DmElementHandle_t hElement = g_pDataModelImp->FindOrCreateElementHandle( id );
  212. int nIndex = array.AddToTail();
  213. array.SetHandle( nIndex, hElement );
  214. }
  215. }
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Hook up all element references (which were unserialized as object ids)
  219. //-----------------------------------------------------------------------------
  220. void CDmElementDictionary::HookUpElementReferences()
  221. {
  222. int nElementsToDelete = m_elementsToDelete.Count();
  223. for ( int i = 0; i < nElementsToDelete; ++i )
  224. {
  225. DmElementDictHandle_t hDictIndex = m_elementsToDelete[i].m_hDictHandle;
  226. DmElementHandle_t hElement = m_Dict[ hDictIndex ];
  227. g_pDataModelImp->DeleteElement( hElement );
  228. m_Dict[ hDictIndex ] = m_elementsToDelete[i].m_hReplacementElement;
  229. }
  230. HookUpElementArrayAttributes();
  231. HookUpElementAttributes();
  232. }
  233. //-----------------------------------------------------------------------------
  234. //
  235. // Element dictionary used in serialization
  236. //
  237. //-----------------------------------------------------------------------------
  238. CDmElementSerializationDictionary::CDmElementSerializationDictionary() :
  239. m_Dict( 1024, 0, CDmElementSerializationDictionary::LessFunc )
  240. {
  241. }
  242. //-----------------------------------------------------------------------------
  243. // Used to sort the list of elements
  244. //-----------------------------------------------------------------------------
  245. bool CDmElementSerializationDictionary::LessFunc( const ElementInfo_t &lhs, const ElementInfo_t &rhs )
  246. {
  247. return lhs.m_pElement < rhs.m_pElement;
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Finds the handle of the element
  251. //-----------------------------------------------------------------------------
  252. DmElementDictHandle_t CDmElementSerializationDictionary::Find( CDmElement *pElement )
  253. {
  254. ElementInfo_t find;
  255. find.m_pElement = pElement;
  256. return m_Dict.Find( find );
  257. }
  258. //-----------------------------------------------------------------------------
  259. // Creates the list of all things to serialize
  260. //-----------------------------------------------------------------------------
  261. void CDmElementSerializationDictionary::BuildElementList_R( CDmElement *pElement, bool bFlatMode, bool bIsRoot )
  262. {
  263. if ( !pElement )
  264. return;
  265. // FIXME: Right here we should ask the element if it's an external
  266. // file reference and exit immediately if so.
  267. // This means we've already encountered this guy.
  268. // Therefore, he can never be a root element
  269. DmElementDictHandle_t h = Find( pElement );
  270. if ( h != m_Dict.InvalidIndex() )
  271. {
  272. m_Dict[h].m_bRoot = true;
  273. return;
  274. }
  275. ElementInfo_t info;
  276. info.m_bRoot = bFlatMode || bIsRoot;
  277. info.m_pElement = pElement;
  278. m_Dict.Insert( info );
  279. for ( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
  280. {
  281. if ( pAttribute->IsFlagSet( FATTRIB_DONTSAVE ) )
  282. continue;
  283. switch( pAttribute->GetType() )
  284. {
  285. case AT_ELEMENT:
  286. {
  287. CDmElement *pChild = pAttribute->GetValueElement<CDmElement>();
  288. if ( !pChild || pChild->GetFileId() != pElement->GetFileId() )
  289. break;
  290. BuildElementList_R( pChild, bFlatMode, false );
  291. }
  292. break;
  293. case AT_ELEMENT_ARRAY:
  294. {
  295. CDmrElementArray<> array( pAttribute );
  296. int nCount = array.Count();
  297. for ( int i = 0; i < nCount; ++i )
  298. {
  299. CDmElement *pChild = array[i];
  300. if ( !pChild || pChild->GetFileId() != pElement->GetFileId() )
  301. break;
  302. BuildElementList_R( pChild, bFlatMode, false );
  303. }
  304. }
  305. break;
  306. }
  307. }
  308. }
  309. void CDmElementSerializationDictionary::BuildElementList( CDmElement *pElement, bool bFlatMode )
  310. {
  311. BuildElementList_R( pElement, bFlatMode, true );
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Should I inline the serialization of this element?
  315. //-----------------------------------------------------------------------------
  316. bool CDmElementSerializationDictionary::ShouldInlineElement( CDmElement *pElement )
  317. {
  318. // This means we've already encountered this guy.
  319. // Therefore, he can never be a root element
  320. DmElementDictHandle_t h = Find( pElement );
  321. if ( h != m_Dict.InvalidIndex() )
  322. return !m_Dict[h].m_bRoot;
  323. // If we didn't find the element, it means it's a reference to an external
  324. // element (or it's NULL), so don't inline ie.
  325. return false;
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Clears the dictionary
  329. //-----------------------------------------------------------------------------
  330. void CDmElementSerializationDictionary::Clear()
  331. {
  332. m_Dict.RemoveAll();
  333. }
  334. //-----------------------------------------------------------------------------
  335. // How many root elements do we have?
  336. //-----------------------------------------------------------------------------
  337. int CDmElementSerializationDictionary::RootElementCount() const
  338. {
  339. int nCount = 0;
  340. DmElementDictHandle_t h = m_Dict.FirstInorder();
  341. while( h != m_Dict.InvalidIndex() )
  342. {
  343. if ( m_Dict[h].m_bRoot )
  344. {
  345. ++nCount;
  346. }
  347. h = m_Dict.NextInorder( h );
  348. }
  349. return nCount;
  350. }
  351. //-----------------------------------------------------------------------------
  352. // Iterates over all root elements to serialize
  353. //-----------------------------------------------------------------------------
  354. DmElementDictHandle_t CDmElementSerializationDictionary::FirstRootElement() const
  355. {
  356. // NOTE - this code only works with BlockMemory or Memory (NOT FixedMemory)
  357. // NOTE: I don't have to use First/NextInorder here because there
  358. // are guaranteed to be no removals from the dictionary.
  359. // Also, using inorder traversal won't get my actual root element to be first in the file
  360. int nCount = m_Dict.Count();
  361. for ( DmElementDictHandle_t h = 0; h < nCount; ++h )
  362. {
  363. if ( m_Dict[h].m_bRoot )
  364. return h;
  365. }
  366. return ELEMENT_DICT_HANDLE_INVALID;
  367. }
  368. DmElementDictHandle_t CDmElementSerializationDictionary::NextRootElement( DmElementDictHandle_t h ) const
  369. {
  370. // NOTE - this code only works with BlockMemory or Memory (NOT FixedMemory)
  371. // NOTE: I don't have to use First/NextInorder here because there
  372. // are guaranteed to be no removals from the dictionary.
  373. // Also, using inorder traversal won't get my actual root element to be first in the file
  374. ++h;
  375. int nCount = m_Dict.Count();
  376. for ( ; h < nCount; ++h )
  377. {
  378. if ( m_Dict[h].m_bRoot )
  379. return h;
  380. }
  381. return ELEMENT_DICT_HANDLE_INVALID;
  382. }
  383. CDmElement *CDmElementSerializationDictionary::GetRootElement( DmElementDictHandle_t h )
  384. {
  385. Assert( m_Dict[h].m_bRoot );
  386. return m_Dict[h].m_pElement;
  387. }