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