123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2013 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "persistence/taml/json/tamlJSONReader.h"
- #include "persistence/taml/json/tamlJSONWriter.h"
- #include "core/stream/fileStream.h"
- #include "core/strings/stringUnit.h"
- #include "core/frameAllocator.h"
- // Debug Profiling.
- #include "platform/profiler.h"
- //-----------------------------------------------------------------------------
- SimObject* TamlJSONReader::read( FileStream& stream )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_Read);
-
- // Read JSON file.
- const U32 streamSize = stream.getStreamSize();
- FrameTemp<char> jsonText( streamSize + 1 );
- if ( !stream.read( streamSize, jsonText ) )
- {
- // Warn!
- Con::warnf("TamlJSONReader::read() - Could not load Taml JSON file from stream.");
- return NULL;
- }
- jsonText[streamSize] = '\0';
- // Create JSON document.
- rapidjson::Document document;
- document.Parse<0>( jsonText );
- // Check the document is valid.
- if ( document.GetType() != rapidjson::kObjectType )
- {
- // Warn!
- Con::warnf("TamlJSONReader::read() - Load Taml JSON file from stream but was invalid.");
- return NULL;
- }
-
- // Parse root value.
- SimObject* pSimObject = parseType( document.MemberBegin() );
- // Reset parse.
- resetParse();
- return pSimObject;
- }
- //-----------------------------------------------------------------------------
- void TamlJSONReader::resetParse( void )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_ResetParse);
- // Clear object reference map.
- mObjectReferenceMap.clear();
- }
- //-----------------------------------------------------------------------------
- SimObject* TamlJSONReader::parseType( const rapidjson::Value::ConstMemberIterator& memberItr )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_ParseType);
- // Fetch name and value.
- const rapidjson::Value& typeName = memberItr->name;
- const rapidjson::Value& typeValue = memberItr->value;
- // Is value an object?
- if ( !typeValue.IsObject() )
- {
- // No, so warn.
- Con::warnf( "TamlJSONReader::parseType() - Cannot process type '%s' as it is not an object.", typeName.GetString() );
- return NULL;
- }
- // Fetch engine type name (demangled).
- StringTableEntry engineTypeName = getDemangledName( typeName.GetString() );
- // Fetch reference to Id.
- const U32 tamlRefToId = getTamlRefToId( typeValue );
- // Do we have a reference to Id?
- if ( tamlRefToId != 0 )
- {
- // Yes, so fetch reference.
- typeObjectReferenceHash::Iterator referenceItr = mObjectReferenceMap.find( tamlRefToId );
- // Did we find the reference?
- if ( referenceItr == mObjectReferenceMap.end() )
- {
- // No, so warn.
- Con::warnf( "TamlJSONReader::parseType() - Could not find a reference Id of '%d'", tamlRefToId );
- return NULL;
- }
- // Return object.
- return referenceItr->value;
- }
- // No, so fetch reference Id.
- const U32 tamlRefId = getTamlRefId( typeValue );
- // Create type.
- SimObject* pSimObject = Taml::createType( engineTypeName, mpTaml );
- // Finish if we couldn't create the type.
- if ( pSimObject == NULL )
- return NULL;
- // Find Taml callbacks.
- TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
- TamlCustomNodes customNodes;
- // Are there any Taml callbacks?
- if ( pCallbacks != NULL )
- {
- // Yes, so call it.
- mpTaml->tamlPreRead( pCallbacks );
- }
- // Parse field members.
- for( rapidjson::Value::ConstMemberIterator fieldMemberItr = typeValue.MemberBegin(); fieldMemberItr != typeValue.MemberEnd(); ++fieldMemberItr )
- {
- // Fetch value.
- const rapidjson::Value& fieldValue = fieldMemberItr->value;
-
- // Skip if not a field.
- if ( fieldValue.IsObject() )
- continue;
- // Parse as field.
- parseField( fieldMemberItr, pSimObject );
- }
- // Fetch object name.
- StringTableEntry objectName = StringTable->insert( getTamlObjectName( typeValue ) );
- // Does the object require a name?
- if ( objectName == StringTable->EmptyString() )
- {
- // No, so just register anonymously.
- pSimObject->registerObject();
- }
- else
- {
- // Yes, so register a named object.
- pSimObject->registerObject( objectName );
- // Was the name assigned?
- if ( pSimObject->getName() != objectName )
- {
- // No, so warn that the name was rejected.
- 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.",
- engineTypeName, objectName );
- }
- }
- // Do we have a reference Id?
- if ( tamlRefId != 0 )
- {
- // Yes, so insert reference.
- mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
- }
- // Parse children and custom node members.
- for( rapidjson::Value::ConstMemberIterator objectMemberItr = typeValue.MemberBegin(); objectMemberItr != typeValue.MemberEnd(); ++objectMemberItr )
- {
- // Fetch name and value.
- const rapidjson::Value& objName = objectMemberItr->name;
- const rapidjson::Value& objectValue = objectMemberItr->value;
-
- // Skip if not an object.
- if ( !objectValue.IsObject() )
- continue;
- // Find the period character in the name.
- const char* pPeriod = dStrchr( objName.GetString(), '.' );
- // Did we find the period?
- if ( pPeriod == NULL )
- {
- // No, so parse child object.
- parseChild( objectMemberItr, pSimObject );
- continue;
- }
- // Yes, so parse custom object.
- parseCustom( objectMemberItr, pSimObject, pPeriod+1, customNodes );
- }
- // Call custom read.
- if ( pCallbacks )
- {
- mpTaml->tamlCustomRead( pCallbacks, customNodes );
- mpTaml->tamlPostRead( pCallbacks, customNodes );
- }
- // Return object.
- return pSimObject;
- }
- //-----------------------------------------------------------------------------
- inline void TamlJSONReader::parseField( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_ParseField);
- // Fetch name and value.
- const rapidjson::Value& name = memberItr->name;
- const rapidjson::Value& value = memberItr->value;
- // Insert the field name.
- StringTableEntry fieldName = StringTable->insert( name.GetString() );
- // Ignore if this is a Taml attribute.
- if ( fieldName == tamlRefIdName ||
- fieldName == tamlRefToIdName ||
- fieldName == tamlNamedObjectName )
- return;
- // Get field value.
- char valueBuffer[4096];
- if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), value, fieldName ) )
- {
- // Warn.
- Con::warnf( "Taml::parseField() Could not interpret value for field '%s'", fieldName );
- return;
- }
- // Set field.
- pSimObject->setPrefixedDataField(fieldName, NULL, valueBuffer);
- }
- //-----------------------------------------------------------------------------
- inline void TamlJSONReader::parseChild( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_ParseChild);
- // Fetch name.
- const rapidjson::Value& name = memberItr->name;
- // Fetch the Taml children.
- TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
- // Is this a Taml child?
- if ( pChildren == NULL )
- {
- // No, so warn.
- Con::warnf("Taml::parseChild() - Child member '%s' found under parent '%s' but object cannot have children.",
- name.GetString(),
- pSimObject->getClassName() );
- return;
- }
- // Fetch any container child class specifier.
- AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
- // Parse child member.
- SimObject* pChildSimObject = parseType( memberItr );
- // Finish if the child was not created.
- if ( pChildSimObject == NULL )
- return;
- // Do we have a container child class?
- if ( pContainerChildClass != NULL )
- {
- // Yes, so is the child object the correctly derived type?
- if ( !pChildSimObject->getClassRep()->isClass( pContainerChildClass ) )
- {
- // No, so warn.
- Con::warnf("Taml::parseChild() - Child element '%s' found under parent '%s' but object is restricted to children of type '%s'.",
- pChildSimObject->getClassName(),
- pSimObject->getClassName(),
- pContainerChildClass->getClassName() );
- // NOTE: We can't delete the object as it may be referenced elsewhere!
- pChildSimObject = NULL;
- return;
- }
- }
- // Add child.
- pChildren->addTamlChild( pChildSimObject );
- // Find Taml callbacks for child.
- TamlCallbacks* pChildCallbacks = dynamic_cast<TamlCallbacks*>( pChildSimObject );
- // Do we have callbacks on the child?
- if ( pChildCallbacks != NULL )
- {
- // Yes, so perform callback.
- mpTaml->tamlAddParent( pChildCallbacks, pSimObject );
- }
- }
- //-----------------------------------------------------------------------------
- inline void TamlJSONReader::parseCustom( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject, const char* pCustomNodeName, TamlCustomNodes& customNodes )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_ParseCustom);
- // Fetch value.
- const rapidjson::Value& value = memberItr->value;
- // Add custom node.
- TamlCustomNode* pCustomNode = customNodes.addNode( pCustomNodeName );
- // Iterate members.
- for( rapidjson::Value::ConstMemberIterator customMemberItr = value.MemberBegin(); customMemberItr != value.MemberEnd(); ++customMemberItr )
- {
- // Fetch value.
- const rapidjson::Value& customValue = customMemberItr->value;
- // Is the member an object?
- if ( !customValue.IsObject() && !customValue.IsArray() )
- {
- // No, so warn.
- Con::warnf( "Taml::parseCustom() - Cannot process custom node name '%s' member as child value is not an object or array.", pCustomNodeName );
- return;
- }
- // Parse custom node.
- parseCustomNode( customMemberItr, pCustomNode );
- }
- }
- //-----------------------------------------------------------------------------
- inline void TamlJSONReader::parseCustomNode( rapidjson::Value::ConstMemberIterator& memberItr, TamlCustomNode* pCustomNode )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_ParseCustomNode);
- // Fetch name and value.
- const rapidjson::Value& name = memberItr->name;
- const rapidjson::Value& value = memberItr->value;
- // Is the value an object?
- if ( value.IsObject() )
- {
- // Yes, so is the node a proxy object?
- if ( getTamlRefId( value ) != 0 || getTamlRefToId( value ) != 0 )
- {
- // Yes, so parse proxy object.
- SimObject* pProxyObject = parseType( memberItr );
- // Add child node.
- pCustomNode->addNode( pProxyObject );
- return;
- }
- }
- char valueBuffer[4096];
- // Fetch the node name.
- StringTableEntry nodeName = getDemangledName( name.GetString() );
- // Yes, so add child node.
- TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName );
- // Is the value an array?
- if ( value.IsArray() )
- {
- // Yes, so does it have a single entry?
- if ( value.Size() == 1 )
- {
- // Yes, so parse the node text.
- if ( parseStringValue( valueBuffer, sizeof(valueBuffer), value.Begin(), nodeName ) )
- {
- pChildNode->setNodeText( valueBuffer );
- }
- else
- {
- // Warn.
- Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but could not interpret the value.", nodeName );
- }
- }
- else
- {
- // No, so warn.
- Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but more than a single element was found in the array.", nodeName );
- }
- return;
- }
- // Iterate child members.
- for( rapidjson::Value::ConstMemberIterator childMemberItr = value.MemberBegin(); childMemberItr != value.MemberEnd(); ++childMemberItr )
- {
- // Fetch name and value.
- const rapidjson::Value& childName = childMemberItr->name;
- const rapidjson::Value& childValue = childMemberItr->value;
- // Fetch the field name.
- StringTableEntry fieldName = StringTable->insert( childName.GetString() );
- // Is the value an array?
- if ( childValue.IsArray() )
- {
- // Yes, so does it have a single entry?
- if ( childValue.Size() == 1 )
- {
- // Yes, so parse the node text.
- if ( parseStringValue( valueBuffer, sizeof(valueBuffer), *childValue.Begin(), fieldName ) )
- {
- // Yes, so add sub-child node.
- TamlCustomNode* pSubChildNode = pChildNode->addNode( fieldName );
- // Set sub-child text.
- pSubChildNode->setNodeText( valueBuffer );
- continue;
- }
- // Warn.
- Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but could not interpret the value.", fieldName );
- return;
- }
- // No, so warn.
- Con::warnf( "Taml::parseCustomNode() - Encountered text in the custom node '%s' but more than a single element was found in the array.", fieldName );
- return;
- }
- // Is the member an object?
- if ( childValue.IsObject() )
- {
- // Yes, so parse custom node.
- parseCustomNode( childMemberItr, pChildNode );
- continue;
- }
- // Ignore if this is a Taml attribute.
- if ( fieldName == tamlRefIdName ||
- fieldName == tamlRefToIdName ||
- fieldName == tamlNamedObjectName )
- continue;
- // Parse string value.
- if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), childValue, childName.GetString() ) )
- {
- // Warn.
- Con::warnf( "Taml::parseCustomNode() - Could not interpret value for field '%s'", fieldName );
- continue;
- }
-
- // Add node field.
- pChildNode->addField( fieldName, valueBuffer );
- }
- }
- //-----------------------------------------------------------------------------
- inline StringTableEntry TamlJSONReader::getDemangledName( const char* pMangledName )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_GetDemangledName);
- // Is the type name mangled?
- if ( StringUnit::getUnitCount( pMangledName, JSON_RFC4627_NAME_MANGLING_CHARACTERS ) > 1 )
- {
- // Yes, so fetch type name portion.
- return StringTable->insert( StringUnit::getUnit( pMangledName, 0, JSON_RFC4627_NAME_MANGLING_CHARACTERS ) );
- }
- // No, so use all the type name.
- return StringTable->insert( pMangledName );
- }
- //-----------------------------------------------------------------------------
- inline bool TamlJSONReader::parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_ParseStringValue);
- // Handle field value appropriately.
- if ( value.IsString() )
- {
- dSprintf( pBuffer, bufferSize, "%s", value.GetString() );
- return true;
- }
- if ( value.IsNumber() )
- {
- if ( value.IsInt() )
- {
- dSprintf( pBuffer, bufferSize, "%d", value.GetInt() );
- return true;
- }
- if ( value.IsUint() )
- {
- dSprintf( pBuffer, bufferSize, "%d", value.GetUint() );
- return true;
- }
- if ( value.IsInt64() )
- {
- dSprintf( pBuffer, bufferSize, "%d", value.GetInt64() );
- return true;
- }
- if ( value.IsUint64() )
- {
- dSprintf( pBuffer, bufferSize, "%d", value.GetUint64() );
- return true;
- }
- if ( value.IsDouble() )
- {
- dSprintf( pBuffer, bufferSize, "%f", value.GetDouble() );
- return true;
- }
- }
- if ( value.IsBool() )
- {
- dSprintf( pBuffer, bufferSize, "%d", value.GetBool() );
- return true;
- }
- // Failed to get value type.
- Con::warnf( "Taml: Encountered a field '%s' but its value is an unknown type.", pName );
- return false;
- }
- //-----------------------------------------------------------------------------
- inline U32 TamlJSONReader::getTamlRefId( const rapidjson::Value& value )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_GetTamlRefId);
- // Is the value an object?
- if ( !value.IsObject() )
- {
- // No, so warn.
- Con::warnf( "Taml::getTamlRefId() - Cannot get '%s' member as value is not an object.", tamlRefIdName );
- return 0;
- }
- // Iterate members.
- for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
- {
- // Insert member name.
- StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
- // Skip if not the correct attribute.
- if ( attributeName != tamlRefIdName )
- continue;
- // Is the value an integer?
- if ( !memberItr->value.IsInt() )
- {
- // No, so warn.
- Con::warnf( "Taml::getTamlRefId() - Found '%s' member but it is not an integer.", tamlRefIdName );
- return 0;
- }
- // Return it.
- return (U32)memberItr->value.GetInt();
- }
- // Not found.
- return 0;
- }
- //-----------------------------------------------------------------------------
- inline U32 TamlJSONReader::getTamlRefToId( const rapidjson::Value& value )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_GetTamlRefToId);
- // Is the value an object?
- if ( !value.IsObject() )
- {
- // No, so warn.
- Con::warnf( "Taml::getTamlRefToId() - Cannot get '%s' member as value is not an object.", tamlRefToIdName );
- return 0;
- }
- // Iterate members.
- for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
- {
- // Insert member name.
- StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
- // Skip if not the correct attribute.
- if ( attributeName != tamlRefToIdName )
- continue;
- // Is the value an integer?
- if ( !memberItr->value.IsInt() )
- {
- // No, so warn.
- Con::warnf( "Taml::getTamlRefToId() - Found '%s' member but it is not an integer.", tamlRefToIdName );
- return 0;
- }
- // Return it.
- return (U32)memberItr->value.GetInt();
- }
- // Not found.
- return 0;
- }
- //-----------------------------------------------------------------------------
- inline const char* TamlJSONReader::getTamlObjectName( const rapidjson::Value& value )
- {
- // Debug Profiling.
- PROFILE_SCOPE(TamlJSONReader_GetTamlObjectName);
- // Is the value an object?
- if ( !value.IsObject() )
- {
- // No, so warn.
- Con::warnf( "Taml::getTamlObjectName() - Cannot get '%s' member as value is not an object.", tamlNamedObjectName );
- return 0;
- }
- // Iterate members.
- for( rapidjson::Value::ConstMemberIterator memberItr = value.MemberBegin(); memberItr != value.MemberEnd(); ++memberItr )
- {
- // Insert member name.
- StringTableEntry attributeName = StringTable->insert( memberItr->name.GetString() );
- // Skip if not the correct attribute.
- if ( attributeName != tamlNamedObjectName )
- continue;
- // Is the value an integer?
- if ( !memberItr->value.IsString() )
- {
- // No, so warn.
- Con::warnf( "Taml::getTamlObjectName() - Found '%s' member but it is not a string.", tamlNamedObjectName );
- return NULL;
- }
- // Return it.
- return memberItr->value.GetString();
- }
- // Not found.
- return NULL;
- }
|