tamlXmlReader.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "persistence/taml/xml/tamlXmlReader.h"
  23. // Debug Profiling.
  24. #include "debug/profiler.h"
  25. //-----------------------------------------------------------------------------
  26. SimObject* TamlXmlReader::read( FileStream& stream )
  27. {
  28. // Debug Profiling.
  29. PROFILE_SCOPE(TamlXmlReader_Read);
  30. // Create document.
  31. TiXmlDocument xmlDocument;
  32. // Load document from stream.
  33. if ( !xmlDocument.LoadFile( stream ) )
  34. {
  35. // Warn!
  36. Con::warnf("Taml: Could not load Taml XML file from stream.");
  37. return NULL;
  38. }
  39. // Parse root element.
  40. SimObject* pSimObject = parseElement( xmlDocument.RootElement() );
  41. // Reset parse.
  42. resetParse();
  43. return pSimObject;
  44. }
  45. //-----------------------------------------------------------------------------
  46. void TamlXmlReader::resetParse( void )
  47. {
  48. // Debug Profiling.
  49. PROFILE_SCOPE(TamlXmlReader_ResetParse);
  50. // Clear object reference map.
  51. mObjectReferenceMap.clear();
  52. }
  53. //-----------------------------------------------------------------------------
  54. SimObject* TamlXmlReader::parseElement( TiXmlElement* pXmlElement )
  55. {
  56. // Debug Profiling.
  57. PROFILE_SCOPE(TamlXmlReader_ParseElement);
  58. SimObject* pSimObject = NULL;
  59. // Fetch element name.
  60. StringTableEntry typeName = StringTable->insert( pXmlElement->Value() );
  61. // Fetch reference to Id.
  62. const U32 tamlRefToId = getTamlRefToId( pXmlElement );
  63. // Do we have a reference to Id?
  64. if ( tamlRefToId != 0 )
  65. {
  66. // Yes, so fetch reference.
  67. typeObjectReferenceHash::iterator referenceItr = mObjectReferenceMap.find( tamlRefToId );
  68. // Did we find the reference?
  69. if ( referenceItr == mObjectReferenceMap.end() )
  70. {
  71. // No, so warn.
  72. Con::warnf( "Taml: Could not find a reference Id of '%d'", tamlRefToId );
  73. return NULL;
  74. }
  75. // Return object.
  76. return referenceItr->value;
  77. }
  78. // No, so fetch reference Id.
  79. const U32 tamlRefId = getTamlRefId( pXmlElement );
  80. #ifdef TORQUE_DEBUG
  81. // Format the type location.
  82. char typeLocationBuffer[64];
  83. dSprintf( typeLocationBuffer, sizeof(typeLocationBuffer), "Taml [format='xml' row=%d column=%d]", pXmlElement->Row(), pXmlElement->Column() );
  84. // Create type.
  85. pSimObject = Taml::createType( typeName, mpTaml, typeLocationBuffer );
  86. #else
  87. // Create type.
  88. pSimObject = Taml::createType( typeName, mpTaml );
  89. #endif
  90. // Finish if we couldn't create the type.
  91. if ( pSimObject == NULL )
  92. return NULL;
  93. // Find Taml callbacks.
  94. TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
  95. // Are there any Taml callbacks?
  96. if ( pCallbacks != NULL )
  97. {
  98. // Yes, so call it.
  99. mpTaml->tamlPreRead( pCallbacks );
  100. }
  101. // Parse attributes.
  102. parseAttributes( pXmlElement, pSimObject );
  103. // Fetch object name.
  104. StringTableEntry objectName = StringTable->insert( getTamlObjectName( pXmlElement ) );
  105. // Does the object require a name?
  106. if ( objectName == StringTable->EmptyString )
  107. {
  108. // No, so just register anonymously.
  109. pSimObject->registerObject();
  110. }
  111. else
  112. {
  113. // Yes, so register a named object.
  114. pSimObject->registerObject( objectName );
  115. // Was the name assigned?
  116. if ( pSimObject->getName() != objectName )
  117. {
  118. // No, so warn that the name was rejected.
  119. #ifdef TORQUE_DEBUG
  120. Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists. '%s'", typeName, objectName, typeLocationBuffer );
  121. #else
  122. Con::warnf( "Taml::parseElement() - Registered an instance of type '%s' but a request to name it '%s' was rejected. This is typically because an object of that name already exists.", typeName, objectName );
  123. #endif
  124. }
  125. }
  126. // Do we have a reference Id?
  127. if ( tamlRefId != 0 )
  128. {
  129. // Yes, so insert reference.
  130. mObjectReferenceMap.insert( tamlRefId, pSimObject );
  131. }
  132. // Fetch any children.
  133. TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
  134. TamlCustomNodes customProperties;
  135. // Do we have any element children?
  136. if ( pChildXmlNode != NULL )
  137. {
  138. // Fetch the Taml children.
  139. TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
  140. // Fetch any container child class specifier.
  141. AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
  142. // Iterate siblings.
  143. do
  144. {
  145. // Fetch element.
  146. TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
  147. // Move to next sibling.
  148. pChildXmlNode = pChildXmlNode->NextSibling();
  149. // Skip if this is not an element?
  150. if ( pChildXmlElement == NULL )
  151. continue;
  152. // Is this a standard child element?
  153. if ( dStrchr( pChildXmlElement->Value(), '.' ) == NULL )
  154. {
  155. // Is this a Taml child?
  156. if ( pChildren == NULL )
  157. {
  158. // No, so warn.
  159. Con::warnf("Taml: Child element '%s' found under parent '%s' but object cannot have children.",
  160. pChildXmlElement->Value(),
  161. pXmlElement->Value() );
  162. // Skip.
  163. continue;
  164. }
  165. // Yes, so parse child element.
  166. SimObject* pChildSimObject = parseElement( pChildXmlElement );
  167. // Skip if the child was not created.
  168. if ( pChildSimObject == NULL )
  169. continue;
  170. // Do we have a container child class?
  171. if ( pContainerChildClass != NULL )
  172. {
  173. // Yes, so is the child object the correctly derived type?
  174. if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) )
  175. {
  176. // No, so warn.
  177. Con::warnf("Taml: Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.",
  178. pChildSimObject->getClassName(),
  179. pSimObject->getClassName(),
  180. pContainerChildClass->getClassName() );
  181. // NOTE: We can't delete the object as it may be referenced elsewhere!
  182. pChildSimObject = NULL;
  183. // Skip.
  184. continue;
  185. }
  186. }
  187. // Add child.
  188. pChildren->addTamlChild( pChildSimObject );
  189. // Find Taml callbacks for child.
  190. TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject );
  191. // Do we have callbacks on the child?
  192. if ( pChildCallbacks != NULL )
  193. {
  194. // Yes, so perform callback.
  195. mpTaml->tamlAddParent( pChildCallbacks, pSimObject );
  196. }
  197. }
  198. else
  199. {
  200. // No, so parse custom element.
  201. parseCustomElement( pChildXmlElement, customProperties );
  202. }
  203. }
  204. while( pChildXmlNode != NULL );
  205. // Call custom read.
  206. mpTaml->tamlCustomRead( pCallbacks, customProperties );
  207. }
  208. // Are there any Taml callbacks?
  209. if ( pCallbacks != NULL )
  210. {
  211. // Yes, so call it.
  212. mpTaml->tamlPostRead( pCallbacks, customProperties );
  213. }
  214. // Return object.
  215. return pSimObject;
  216. }
  217. //-----------------------------------------------------------------------------
  218. void TamlXmlReader::parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject )
  219. {
  220. // Debug Profiling.
  221. PROFILE_SCOPE(TamlXmlReader_ParseAttributes);
  222. // Sanity!
  223. AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
  224. // Iterate attributes.
  225. for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
  226. {
  227. // Insert attribute name.
  228. StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
  229. // Ignore if this is a Taml attribute.
  230. if ( attributeName == tamlRefIdName ||
  231. attributeName == tamlRefToIdName ||
  232. attributeName == tamlNamedObjectName )
  233. continue;
  234. // Set the field.
  235. pSimObject->setPrefixedDataField( attributeName, NULL, pAttribute->Value() );
  236. }
  237. }
  238. //-----------------------------------------------------------------------------
  239. void TamlXmlReader::parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& customNodes )
  240. {
  241. // Debug Profiling.
  242. PROFILE_SCOPE(TamlXmlReader_ParseCustomElement);
  243. // Is this a standard child element?
  244. const char* pPeriod = dStrchr( pXmlElement->Value(), '.' );
  245. // Sanity!
  246. AssertFatal( pPeriod != NULL, "Parsing extended element but no period character found." );
  247. // Fetch any custom XML node.
  248. TiXmlNode* pCustomXmlNode = pXmlElement->FirstChild();
  249. // Finish is no XML node exists.
  250. if ( pCustomXmlNode == NULL )
  251. return;
  252. // Yes, so add custom node.
  253. TamlCustomNode* pCustomNode = customNodes.addNode( pPeriod+1 );
  254. do
  255. {
  256. // Fetch element.
  257. TiXmlElement* pCustomXmlElement = dynamic_cast<TiXmlElement*>( pCustomXmlNode );
  258. // Move to next sibling.
  259. pCustomXmlNode = pCustomXmlNode->NextSibling();
  260. // Skip if this is not an element.
  261. if ( pCustomXmlElement == NULL )
  262. continue;
  263. // Parse custom node.
  264. parseCustomNode( pCustomXmlElement, pCustomNode );
  265. }
  266. while ( pCustomXmlNode != NULL );
  267. }
  268. //-----------------------------------------------------------------------------
  269. void TamlXmlReader::parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode )
  270. {
  271. // Is the node a proxy object?
  272. if ( getTamlRefId( pXmlElement ) != 0 || getTamlRefToId( pXmlElement ) != 0 )
  273. {
  274. // Yes, so parse proxy object.
  275. SimObject* pProxyObject = parseElement( pXmlElement );
  276. // Add child node.
  277. pCustomNode->addNode( pProxyObject );
  278. return;
  279. }
  280. // Yes, so add child node.
  281. TamlCustomNode* pChildNode = pCustomNode->addNode( pXmlElement->Value() );
  282. // Iterate attributes.
  283. for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
  284. {
  285. // Insert attribute name.
  286. StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
  287. // Skip if a Taml reference attribute.
  288. if ( attributeName == tamlRefIdName || attributeName == tamlRefToIdName )
  289. continue;
  290. // Add node field.
  291. pChildNode->addField( attributeName, pAttribute->Value() );
  292. }
  293. // Fetch any element text.
  294. const char* pElementText = pXmlElement->GetText();
  295. // Do we have any element text?
  296. if ( pElementText != NULL )
  297. {
  298. // Yes, so store it.
  299. pChildNode->setNodeText( pElementText );
  300. }
  301. // Fetch any children.
  302. TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
  303. // Do we have any element children?
  304. if ( pChildXmlNode != NULL )
  305. {
  306. do
  307. {
  308. // Yes, so fetch child element.
  309. TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
  310. // Move to next sibling.
  311. pChildXmlNode = pChildXmlNode->NextSibling();
  312. // Skip if this is not an element.
  313. if ( pChildXmlElement == NULL )
  314. continue;
  315. // Parse custom node.
  316. parseCustomNode( pChildXmlElement, pChildNode );
  317. }
  318. while( pChildXmlNode != NULL );
  319. }
  320. }
  321. //-----------------------------------------------------------------------------
  322. U32 TamlXmlReader::getTamlRefId( TiXmlElement* pXmlElement )
  323. {
  324. // Debug Profiling.
  325. PROFILE_SCOPE(TamlXmlReader_GetTamlRefId);
  326. // Iterate attributes.
  327. for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
  328. {
  329. // Insert attribute name.
  330. StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
  331. // Skip if not the correct attribute.
  332. if ( attributeName != tamlRefIdName )
  333. continue;
  334. // Return it.
  335. return dAtoi( pAttribute->Value() );
  336. }
  337. // Not found.
  338. return 0;
  339. }
  340. //-----------------------------------------------------------------------------
  341. U32 TamlXmlReader::getTamlRefToId( TiXmlElement* pXmlElement )
  342. {
  343. // Debug Profiling.
  344. PROFILE_SCOPE(TamlXmlReader_GetTamlRefToId);
  345. // Iterate attributes.
  346. for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
  347. {
  348. // Insert attribute name.
  349. StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
  350. // Skip if not the correct attribute.
  351. if ( attributeName != tamlRefToIdName )
  352. continue;
  353. // Return it.
  354. return dAtoi( pAttribute->Value() );
  355. }
  356. // Not found.
  357. return 0;
  358. }
  359. //-----------------------------------------------------------------------------
  360. const char* TamlXmlReader::getTamlObjectName( TiXmlElement* pXmlElement )
  361. {
  362. // Debug Profiling.
  363. PROFILE_SCOPE(TamlXmlReader_GetTamlObjectName);
  364. // Iterate attributes.
  365. for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
  366. {
  367. // Insert attribute name.
  368. StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
  369. // Skip if not the correct attribute.
  370. if ( attributeName != tamlNamedObjectName )
  371. continue;
  372. // Return it.
  373. return pAttribute->Value();
  374. }
  375. // Not found.
  376. return NULL;
  377. }