tamlXmlReader.cpp 15 KB

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