tamlJSONReader.cpp
Engine/source/persistence/taml/json/tamlJSONReader.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/json/tamlJSONReader.h" 25#include "persistence/taml/json/tamlJSONWriter.h" 26#include "core/stream/fileStream.h" 27#include "core/strings/stringUnit.h" 28#include "core/frameAllocator.h" 29 30// Debug Profiling. 31#include "platform/profiler.h" 32 33//----------------------------------------------------------------------------- 34 35SimObject* TamlJSONReader::read( FileStream& stream ) 36{ 37 // Debug Profiling. 38 PROFILE_SCOPE(TamlJSONReader_Read); 39 40 // Read JSON file. 41 const U32 streamSize = stream.getStreamSize(); 42 FrameTemp<char> jsonText( streamSize + 1 ); 43 if ( !stream.read( streamSize, jsonText ) ) 44 { 45 // Warn! 46 Con::warnf("TamlJSONReader::read() - Could not load Taml JSON file from stream."); 47 return NULL; 48 } 49 jsonText[streamSize] = '\0'; 50 51 // Create JSON document. 52 rapidjson::Document document; 53 document.Parse<0>( jsonText ); 54 55 // Check the document is valid. 56 if ( document.GetType() != rapidjson::kObjectType ) 57 { 58 // Warn! 59 Con::warnf("TamlJSONReader::read() - Load Taml JSON file from stream but was invalid."); 60 return NULL; 61 } 62 63 // Parse root value. 64 SimObject* pSimObject = parseType( document.MemberBegin() ); 65 66 // Reset parse. 67 resetParse(); 68 69 return pSimObject; 70} 71 72//----------------------------------------------------------------------------- 73 74void TamlJSONReader::resetParse( void ) 75{ 76 // Debug Profiling. 77 PROFILE_SCOPE(TamlJSONReader_ResetParse); 78 79 // Clear object reference map. 80 mObjectReferenceMap.clear(); 81} 82 83//----------------------------------------------------------------------------- 84 85SimObject* TamlJSONReader::parseType( const rapidjson::Value::ConstMemberIterator& memberItr ) 86{ 87 // Debug Profiling. 88 PROFILE_SCOPE(TamlJSONReader_ParseType); 89 90 // Fetch name and value. 91 const rapidjson::Value& typeName = memberItr->name; 92 const rapidjson::Value& typeValue = memberItr->value; 93 94 // Is value an object? 95 if ( !typeValue.IsObject() ) 96 { 97 // No, so warn. 98 Con::warnf( "TamlJSONReader::parseType() - Cannot process type '%s' as it is not an object.", typeName.GetString() ); 99 return NULL; 100 } 101 102 // Fetch engine type name (demangled). 103 StringTableEntry engineTypeName = getDemangledName( typeName.GetString() ); 104 105 // Fetch reference to Id. 106 const U32 tamlRefToId = getTamlRefToId( typeValue ); 107 108 // Do we have a reference to Id? 109 if ( tamlRefToId != 0 ) 110 { 111 // Yes, so fetch reference. 112 typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId ); 113 114 // Did we find the reference? 115 if ( referenceItr == mObjectReferenceMap.end() ) 116 { 117 // No, so warn. 118 Con::warnf( "TamlJSONReader::parseType() - Could not find a reference Id of '%d'", tamlRefToId ); 119 return NULL; 120 } 121 122 // Return object. 123 return referenceItr->value; 124 } 125 126 // No, so fetch reference Id. 127 const U32 tamlRefId = getTamlRefId( typeValue ); 128 129 // Create type. 130 SimObject* pSimObject = Taml::createType( engineTypeName, mpTaml ); 131 132 // Finish if we couldn't create the type. 133 if ( pSimObject == NULL ) 134 return NULL; 135 136 // Find Taml callbacks. 137 TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject ); 138 139 TamlCustomNodes customNodes; 140 141 // Are there any Taml callbacks? 142 if ( pCallbacks != NULL ) 143 { 144 // Yes, so call it. 145 mpTaml->tamlPreRead( pCallbacks ); 146 } 147 148 // Parse field members. 149 for( rapidjson::Value::ConstMemberIterator fieldMemberItr = typeValue.MemberBegin(); fieldMemberItr != typeValue.MemberEnd(); ++fieldMemberItr ) 150 { 151 // Fetch value. 152 const rapidjson::Value& fieldValue = fieldMemberItr->value; 153 154 // Skip if not a field. 155 if ( fieldValue.IsObject() ) 156 continue; 157 158 // Parse as field. 159 parseField( fieldMemberItr, pSimObject ); 160 } 161 162 // Fetch object name. 163 StringTableEntry objectName = StringTable->insert( getTamlObjectName( typeValue ) ); 164 165 // Does the object require a name? 166 if ( objectName == StringTable->EmptyString() ) 167 { 168 // No, so just register anonymously. 169 pSimObject->registerObject(); 170 } 171 else 172 { 173 // Yes, so register a named object. 174 pSimObject->registerObject( objectName ); 175 176 // Was the name assigned? 177 if ( pSimObject->getName() != objectName ) 178 { 179 // No, so warn that the name was rejected. 180 Con::warnf( "Taml::parseType() - 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.", 181 engineTypeName, objectName ); 182 } 183 } 184 185 // Do we have a reference Id? 186 if ( tamlRefId != 0 ) 187 { 188 // Yes, so insert reference. 189 mObjectReferenceMap.insertUnique( tamlRefId, pSimObject ); 190 } 191 192 // Parse children and custom node members. 193 for( rapidjson::Value::ConstMemberIterator objectMemberItr = typeValue.MemberBegin(); objectMemberItr != typeValue.MemberEnd(); ++objectMemberItr ) 194 { 195 // Fetch name and value. 196 const rapidjson::Value& objName = objectMemberItr->name; 197 const rapidjson::Value& objectValue = objectMemberItr->value; 198 199 // Skip if not an object. 200 if ( !objectValue.IsObject() ) 201 continue; 202 203 // Find the period character in the name. 204 const char* pPeriod = dStrchr( objName.GetString(), '.' ); 205 206 // Did we find the period? 207 if ( pPeriod == NULL ) 208 { 209 // No, so parse child object. 210 parseChild( objectMemberItr, pSimObject ); 211 continue; 212 } 213 214 // Yes, so parse custom object. 215 parseCustom( objectMemberItr, pSimObject, pPeriod+1, customNodes ); 216 } 217 218 // Call custom read. 219 if ( pCallbacks ) 220 { 221 mpTaml->tamlCustomRead( pCallbacks, customNodes ); 222 mpTaml->tamlPostRead( pCallbacks, customNodes ); 223 } 224 225 // Return object. 226 return pSimObject; 227} 228 229//----------------------------------------------------------------------------- 230 231inline void TamlJSONReader::parseField( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject ) 232{ 233 // Debug Profiling. 234 PROFILE_SCOPE(TamlJSONReader_ParseField); 235 236 // Fetch name and value. 237 const rapidjson::Value& name = memberItr->name; 238 const rapidjson::Value& value = memberItr->value; 239 240 // Insert the field name. 241 StringTableEntry fieldName = StringTable->insert( name.GetString() ); 242 243 // Ignore if this is a Taml attribute. 244 if ( fieldName == tamlRefIdName || 245 fieldName == tamlRefToIdName || 246 fieldName == tamlNamedObjectName ) 247 return; 248 249 // Get field value. 250 char valueBuffer[4096]; 251 if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), value, fieldName ) ) 252 { 253 // Warn. 254 Con::warnf( "Taml::parseField() Could not interpret value for field '%s'", fieldName ); 255 return; 256 } 257 258 // Set field. 259 pSimObject->setPrefixedDataField(fieldName, NULL, valueBuffer); 260} 261 262//----------------------------------------------------------------------------- 263 264inline void TamlJSONReader::parseChild( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject ) 265{ 266 // Debug Profiling. 267 PROFILE_SCOPE(TamlJSONReader_ParseChild); 268 269 // Fetch name. 270 const rapidjson::Value& name = memberItr->name; 271 272 // Fetch the Taml children. 273 TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject ); 274 275 // Is this a Taml child? 276 if ( pChildren == NULL ) 277 { 278 // No, so warn. 279 Con::warnf("Taml::parseChild() - Child member '%s' found under parent '%s' but object cannot have children.", 280 name.GetString(), 281 pSimObject->getClassName() ); 282 283 return; 284 } 285 286 // Fetch any container child class specifier. 287 AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true ); 288 289 // Parse child member. 290 SimObject* pChildSimObject = parseType( memberItr ); 291 292 // Finish if the child was not created. 293 if ( pChildSimObject == NULL ) 294 return; 295 296 // Do we have a container child class? 297 if ( pContainerChildClass != NULL ) 298 { 299 // Yes, so is the child object the correctly derived type? 300 if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) ) 301 { 302 // No, so warn. 303 Con::warnf("Taml::parseChild() - Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.", 304 pChildSimObject->getClassName(), 305 pSimObject->getClassName(), 306 pContainerChildClass->getClassName() ); 307 308 // NOTE: We can't delete the object as it may be referenced elsewhere! 309 pChildSimObject = NULL; 310 311 return; 312 } 313 } 314 315 // Add child. 316 pChildren->addTamlChild( pChildSimObject ); 317 318 // Find Taml callbacks for child. 319 TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject ); 320 321 // Do we have callbacks on the child? 322 if ( pChildCallbacks != NULL ) 323 { 324 // Yes, so perform callback. 325 mpTaml->tamlAddParent( pChildCallbacks, pSimObject ); 326 } 327} 328 329//----------------------------------------------------------------------------- 330 331inline void TamlJSONReader::parseCustom( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject, const char* pCustomNodeName, TamlCustomNodes& customNodes ) 332{ 333 // Debug Profiling. 334 PROFILE_SCOPE(TamlJSONReader_ParseCustom); 335 336 // Fetch value. 337 const rapidjson::Value& value = memberItr->value; 338 339 // Add custom node. 340 TamlCustomNode* pCustomNode = customNodes.addNode( pCustomNodeName ); 341 342 // Iterate members. 343 for( rapidjson::Value::ConstMemberIterator customMemberItr = value.MemberBegin(); customMemberItr != value.MemberEnd(); ++customMemberItr ) 344 { 345 // Fetch value. 346 const rapidjson::Value& customValue = customMemberItr->value; 347 348 // Is the member an object? 349 if ( !customValue.IsObject() && !customValue.IsArray() ) 350 { 351 // No, so warn. 352 Con::warnf( "Taml::parseCustom() - Cannot process custom node name '%s' member as child value is not an object or array.", pCustomNodeName ); 353 return; 354 } 355 356 // Parse custom node. 357 parseCustomNode( customMemberItr, pCustomNode ); 358 } 359} 360 361//----------------------------------------------------------------------------- 362 363inline void TamlJSONReader::parseCustomNode( rapidjson::Value::ConstMemberIterator& memberItr, TamlCustomNode* pCustomNode ) 364{ 365 // Debug Profiling. 366 PROFILE_SCOPE(TamlJSONReader_ParseCustomNode); 367 368 // Fetch name and value. 369 const rapidjson::Value& name = memberItr->name; 370 const rapidjson::Value& value = memberItr->value; 371 372 // Is the value an object? 373 if ( value.IsObject() ) 374 { 375 // Yes, so is the node a proxy object? 376 if ( getTamlRefId( value ) != 0 || getTamlRefToId( value ) != 0 ) 377 { 378 // Yes, so parse proxy object. 379 SimObject* pProxyObject = parseType( memberItr ); 380 381 // Add child node. 382 pCustomNode->addNode( pProxyObject ); 383 384 return; 385 } 386 } 387 388 char valueBuffer[4096]; 389 390 // Fetch the node name. 391 StringTableEntry nodeName = getDemangledName( name.GetString() ); 392 393 // Yes, so add child node. 394 TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName ); 395 396 // Is the value an array? 397 if ( value.IsArray() ) 398 { 399 // Yes, so does it have a single entry? 400 if ( value.Size() == 1 ) 401 { 402 // Yes, so parse the node text. 403 if ( parseStringValue( valueBuffer, sizeof(valueBuffer), *value.Begin(), nodeName ) ) 404 { 405 pChildNode->setNodeText( valueBuffer ); 406 } 407 else 408 { 409 // Warn. 410 Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but could not interpret the value.", nodeName ); 411 } 412 } 413 else 414 { 415 // No, so warn. 416 Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but more than a single element was found in the array.", nodeName ); 417 } 418 419 return; 420 } 421 422 // Iterate child members. 423 for( rapidjson::Value::ConstMemberIterator childMemberItr = value.MemberBegin(); childMemberItr != value.MemberEnd(); ++childMemberItr ) 424 { 425 // Fetch name and value. 426 const rapidjson::Value& childName = childMemberItr->name; 427 const rapidjson::Value& childValue = childMemberItr->value; 428 429 // Fetch the field name. 430 StringTableEntry fieldName = StringTable->insert( childName.GetString() ); 431 432 // Is the value an array? 433 if ( childValue.IsArray() ) 434 { 435 // Yes, so does it have a single entry? 436 if ( childValue.Size() == 1 ) 437 { 438 // Yes, so parse the node text. 439 if ( parseStringValue( valueBuffer, sizeof(valueBuffer), *childValue.Begin(), fieldName ) ) 440 { 441 // Yes, so add sub-child node. 442 TamlCustomNode* pSubChildNode = pChildNode->addNode( fieldName ); 443 444 // Set sub-child text. 445 pSubChildNode->setNodeText( valueBuffer ); 446 continue; 447 } 448 449 // Warn. 450 Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but could not interpret the value.", fieldName ); 451 return; 452 } 453 454 // No, so warn. 455 Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but more than a single element was found in the array.", fieldName ); 456 return; 457 } 458 459 // Is the member an object? 460 if ( childValue.IsObject() ) 461 { 462 // Yes, so parse custom node. 463 parseCustomNode( childMemberItr, pChildNode ); 464 continue; 465 } 466 467 // Ignore if this is a Taml attribute. 468 if ( fieldName == tamlRefIdName || 469 fieldName == tamlRefToIdName || 470 fieldName == tamlNamedObjectName ) 471 continue; 472 473 // Parse string value. 474 if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), childValue, childName.GetString() ) ) 475 { 476 // Warn. 477 Con::warnf( "Taml::parseCustomNode() - Could not interpret value for field '%s'", fieldName ); 478 continue; 479 } 480 481 // Add node field. 482 pChildNode->addField( fieldName, valueBuffer ); 483 } 484} 485 486//----------------------------------------------------------------------------- 487 488inline StringTableEntry TamlJSONReader::getDemangledName( const char* pMangledName ) 489{ 490 // Debug Profiling. 491 PROFILE_SCOPE(TamlJSONReader_GetDemangledName); 492 493 // Is the type name mangled? 494 if ( StringUnit::getUnitCount( pMangledName, JSON_RFC4627_NAME_MANGLING_CHARACTERS ) > 1 ) 495 { 496 // Yes, so fetch type name portion. 497 return StringTable->insert( StringUnit::getUnit( pMangledName, 0, JSON_RFC4627_NAME_MANGLING_CHARACTERS ) ); 498 } 499 500 // No, so use all the type name. 501 return StringTable->insert( pMangledName ); 502} 503 504//----------------------------------------------------------------------------- 505 506inline bool TamlJSONReader::parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName ) 507{ 508 // Debug Profiling. 509 PROFILE_SCOPE(TamlJSONReader_ParseStringValue); 510 511 // Handle field value appropriately. 512 513 if ( value.IsString() ) 514 { 515 dSprintf( pBuffer, bufferSize, "%s", value.GetString() ); 516 return true; 517 } 518 519 if ( value.IsNumber() ) 520 { 521 if ( value.IsInt() ) 522 { 523 dSprintf( pBuffer, bufferSize, "%d", value.GetInt() ); 524 return true; 525 } 526 527 if ( value.IsUint() ) 528 { 529 dSprintf( pBuffer, bufferSize, "%d", value.GetUint() ); 530 return true; 531 } 532 533 if ( value.IsInt64() ) 534 { 535 dSprintf( pBuffer, bufferSize, "%d", value.GetInt64() ); 536 return true; 537 } 538 539 if ( value.IsUint64() ) 540 { 541 dSprintf( pBuffer, bufferSize, "%d", value.GetUint64() ); 542 return true; 543 } 544 545 if ( value.IsDouble() ) 546 { 547 dSprintf( pBuffer, bufferSize, "%f", value.GetDouble() ); 548 return true; 549 } 550 } 551 552 if ( value.IsBool() ) 553 { 554 dSprintf( pBuffer, bufferSize, "%d", value.GetBool() ); 555 return true; 556 } 557 558 // Failed to get value type. 559 Con::warnf( "Taml: Encountered a field '%s' but its value is an unknown type.", pName ); 560 return false; 561} 562 563//----------------------------------------------------------------------------- 564 565inline U32 TamlJSONReader::getTamlRefId( const rapidjson::Value& value ) 566{ 567 // Debug Profiling. 568 PROFILE_SCOPE(TamlJSONReader_GetTamlRefId); 569 570 // Is the value an object? 571 if ( !value.IsObject() ) 572 { 573 // No, so warn. 574 Con::warnf( "Taml::getTamlRefId() - Cannot get '%s' member as value is not an object.", tamlRefIdName ); 575 return 0; 576 } 577 578 // Iterate members. 579 for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr ) 580 { 581 // Insert member name. 582 StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() ); 583 584 // Skip if not the correct attribute. 585 if ( attributeName != tamlRefIdName ) 586 continue; 587 588 // Is the value an integer? 589 if ( !memberItr->value.IsInt() ) 590 { 591 // No, so warn. 592 Con::warnf( "Taml::getTamlRefId() - Found '%s' member but it is not an integer.", tamlRefIdName ); 593 return 0; 594 } 595 596 // Return it. 597 return (U32)memberItr->value.GetInt(); 598 } 599 600 // Not found. 601 return 0; 602} 603 604//----------------------------------------------------------------------------- 605 606inline U32 TamlJSONReader::getTamlRefToId( const rapidjson::Value& value ) 607{ 608 // Debug Profiling. 609 PROFILE_SCOPE(TamlJSONReader_GetTamlRefToId); 610 611 // Is the value an object? 612 if ( !value.IsObject() ) 613 { 614 // No, so warn. 615 Con::warnf( "Taml::getTamlRefToId() - Cannot get '%s' member as value is not an object.", tamlRefToIdName ); 616 return 0; 617 } 618 619 // Iterate members. 620 for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr ) 621 { 622 // Insert member name. 623 StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() ); 624 625 // Skip if not the correct attribute. 626 if ( attributeName != tamlRefToIdName ) 627 continue; 628 629 // Is the value an integer? 630 if ( !memberItr->value.IsInt() ) 631 { 632 // No, so warn. 633 Con::warnf( "Taml::getTamlRefToId() - Found '%s' member but it is not an integer.", tamlRefToIdName ); 634 return 0; 635 } 636 637 // Return it. 638 return (U32)memberItr->value.GetInt(); 639 } 640 641 // Not found. 642 return 0; 643} 644 645//----------------------------------------------------------------------------- 646 647inline const char* TamlJSONReader::getTamlObjectName( const rapidjson::Value& value ) 648{ 649 // Debug Profiling. 650 PROFILE_SCOPE(TamlJSONReader_GetTamlObjectName); 651 652 // Is the value an object? 653 if ( !value.IsObject() ) 654 { 655 // No, so warn. 656 Con::warnf( "Taml::getTamlObjectName() - Cannot get '%s' member as value is not an object.", tamlNamedObjectName ); 657 return 0; 658 } 659 660 // Iterate members. 661 for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr ) 662 { 663 // Insert member name. 664 StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() ); 665 666 // Skip if not the correct attribute. 667 if ( attributeName != tamlNamedObjectName ) 668 continue; 669 670 // Is the value an integer? 671 if ( !memberItr->value.IsString() ) 672 { 673 // No, so warn. 674 Con::warnf( "Taml::getTamlObjectName() - Found '%s' member but it is not a string.", tamlNamedObjectName ); 675 return NULL; 676 } 677 678 // Return it. 679 return memberItr->value.GetString(); 680 } 681 682 // Not found. 683 return NULL; 684} 685 686 687 688