mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 04:34:48 +00:00
367 lines
14 KiB
C++
367 lines
14 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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/tamlJSONWriter.h"
|
|
|
|
#include "core/stringTable.h"
|
|
#include "core/stream/fileStream.h"
|
|
|
|
// Debug Profiling.
|
|
#include "platform/profiler.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// These are the characters allowed that separate the type-name from the type.
|
|
// These separators can be used to ensure that each member in an object has
|
|
// a unique name.
|
|
//
|
|
// It is important to understand that TAML does not require entries to be unique
|
|
// but technically the RFC4627 spec states it as "should" be seems to be taken
|
|
// as MUST. See here: http://www.ietf.org/rfc/rfc4627.txt
|
|
//
|
|
// They can be placed as a suffix to the type-name. The very first occurance
|
|
// is used to split the type-name from the suffix so you can form whatever
|
|
// suffix you like to make entries unique.
|
|
// Examples are "Sprite 0", "Sprite*0", "Sprite[0]", "Sprite,0" etc.
|
|
//
|
|
// Type-names can legally consist of a-z, A-Z, 0-9 and "_" (underscore) so these
|
|
// characters cannot be used for mangling. Feel free to add any characters you
|
|
// require to this list.
|
|
StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS = StringTable->insert(" !$%^&*()-+{}[]@:~#|\\/?<>,.\n\r\t");
|
|
|
|
// This is the "dSprintf" format that the JSON writer uses to encode each
|
|
// member if JSON_RFC4627 mode is on. You are free to change this as long as
|
|
// you ensure that it starts with the "%s" character (which represents the type name)
|
|
// and is immediately followed by at least a single mangling character and that the
|
|
// "%d" is present as that represents the automatically-added member index.
|
|
StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT = StringTable->insert( "%s[%d]" );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
bool TamlJSONWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode )
|
|
{
|
|
// Debug Profiling.
|
|
PROFILE_SCOPE(TamlJSONWriter_Write);
|
|
|
|
// Create document.
|
|
rapidjson::Document document;
|
|
document.SetObject();
|
|
|
|
// Compile the root type.
|
|
rapidjson::Value rootValue(rapidjson::kObjectType);
|
|
compileType( document, &rootValue, NULL, pTamlWriteNode, -1 );
|
|
|
|
// Write document to stream.
|
|
rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream );
|
|
document.Accept( jsonStreamWriter );
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TamlJSONWriter::compileType( rapidjson::Document& document, rapidjson::Value* pTypeValue, rapidjson::Value* pParentValue, const TamlWriteNode* pTamlWriteNode, const S32 memberIndex )
|
|
{
|
|
// Debug Profiling.
|
|
PROFILE_SCOPE(TamlJSONWriter_CompileType);
|
|
|
|
// Fetch the json document allocator.
|
|
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
|
|
|
// Fetch object.
|
|
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
|
|
|
// Fetch JSON strict flag (don't use it if member index is set to not use it).
|
|
const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict();
|
|
|
|
// Fetch element name (mangled or not).
|
|
StringTableEntry elementName = jsonStrict ? getManagedName( pSimObject->getClassName(), memberIndex ) : pSimObject->getClassName();
|
|
|
|
// Is there a parent value?
|
|
if ( pParentValue == NULL )
|
|
{
|
|
// No, so add as document root value member.
|
|
pTypeValue = &((document.AddMember(rapidjson::GenericStringRef<UTF8>(elementName), *pTypeValue, allocator ).MemberEnd()-1)->value);
|
|
}
|
|
else
|
|
{
|
|
// Yes, so add as a parent value member.
|
|
pTypeValue = &((pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(elementName), *pTypeValue, allocator ).MemberEnd()-1)->value);
|
|
}
|
|
|
|
// Fetch reference Id.
|
|
const U32 referenceId = pTamlWriteNode->mRefId;
|
|
|
|
// Do we have a reference Id?
|
|
if ( referenceId != 0 )
|
|
{
|
|
// Yes, so set reference Id.
|
|
rapidjson::Value value;
|
|
value.SetInt( referenceId );
|
|
pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlRefIdName), value, allocator );
|
|
}
|
|
// Do we have a reference to node?
|
|
else if ( pTamlWriteNode->mRefToNode != NULL )
|
|
{
|
|
// Yes, so fetch reference to Id.
|
|
const U32 referenceToId = pTamlWriteNode->mRefToNode->mRefId;
|
|
|
|
// Sanity!
|
|
AssertFatal( referenceToId != 0, "Taml: Invalid reference to Id." );
|
|
|
|
// Set reference to Id.
|
|
rapidjson::Value value;
|
|
value.SetInt( referenceToId );
|
|
pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlRefToIdName), value, allocator );
|
|
|
|
// Finish because we're a reference to another object.
|
|
return;
|
|
}
|
|
|
|
// Fetch object name.
|
|
const char* pObjectName = pTamlWriteNode->mpObjectName;
|
|
|
|
// Do we have a name?
|
|
if ( pObjectName != NULL )
|
|
{
|
|
// Yes, so set name.
|
|
rapidjson::Value value;
|
|
value.SetString( pObjectName, dStrlen(pObjectName), allocator );
|
|
pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlNamedObjectName), value, allocator );
|
|
}
|
|
|
|
// Compile field.
|
|
compileFields( document, pTypeValue, pTamlWriteNode );
|
|
|
|
// Fetch children.
|
|
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
|
|
|
|
// Do we have any children?
|
|
if ( pChildren )
|
|
{
|
|
S32 childMemberIndex = 0;
|
|
|
|
// Yes, so iterate children.
|
|
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
|
|
{
|
|
// Compile child type.
|
|
rapidjson::Value childValue(rapidjson::kObjectType);
|
|
compileType( document, &childValue, pTypeValue, (*itr), childMemberIndex++ );
|
|
}
|
|
}
|
|
|
|
// Compile custom.
|
|
compileCustom( document, pTypeValue, pTamlWriteNode );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TamlJSONWriter::compileFields( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode )
|
|
{
|
|
// Debug Profiling.
|
|
PROFILE_SCOPE(TamlJSONWriter_CompileFields);
|
|
|
|
// Fetch fields.
|
|
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
|
|
|
|
// Ignore if no fields.
|
|
if ( fields.size() == 0 )
|
|
return;
|
|
|
|
// Fetch the json document allocator.
|
|
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
|
|
|
// Iterate fields.
|
|
for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
|
|
{
|
|
// Fetch field/value pair.
|
|
TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
|
|
|
|
// Set field attribute.
|
|
rapidjson::Value value;
|
|
value.SetString( pFieldValue->mpValue, dStrlen(pFieldValue->mpValue), allocator );
|
|
pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(pFieldValue->mName), value, allocator );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TamlJSONWriter::compileCustom( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode )
|
|
{
|
|
// Debug Profiling.
|
|
PROFILE_SCOPE(TamlJSONWriter_CompileCustom);
|
|
|
|
// Fetch custom nodes.
|
|
const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
|
|
|
|
// Fetch custom nodes.
|
|
const TamlCustomNodeVector& nodes = customNodes.getNodes();
|
|
|
|
// Finish if no custom nodes to process.
|
|
if ( nodes.size() == 0 )
|
|
return;
|
|
|
|
// Fetch the json document allocator.
|
|
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
|
|
|
// Fetch object.
|
|
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
|
|
|
|
// Fetch element name.
|
|
const char* pElementName = pSimObject->getClassName();
|
|
|
|
// Iterate custom nodes.
|
|
for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
|
|
{
|
|
// Fetch the custom node.
|
|
TamlCustomNode* pCustomNode = *customNodesItr;
|
|
|
|
// Format extended element name.
|
|
char extendedElementNameBuffer[256];
|
|
dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pElementName, pCustomNode->getNodeName() );
|
|
StringTableEntry elementNameEntry = StringTable->insert( extendedElementNameBuffer );
|
|
|
|
rapidjson::Value elementValue(rapidjson::kObjectType);
|
|
rapidjson::Value* pElementValue = &((pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(elementNameEntry), elementValue, allocator ).MemberEnd()-1)->value);
|
|
|
|
// Fetch node children.
|
|
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
|
|
|
S32 childMemberIndex = 0;
|
|
|
|
// Iterate children nodes.
|
|
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
|
{
|
|
// Fetch child node.
|
|
const TamlCustomNode* pChildNode = *childNodeItr;
|
|
|
|
// Compile the custom node.
|
|
compileCustomNode( document, pElementValue, pChildNode, childMemberIndex++ );
|
|
}
|
|
|
|
// Finish if the node is set to ignore if empty and it is empty.
|
|
if ( pCustomNode->getIgnoreEmpty() && pTypeValue->MemberBegin() == pTypeValue->MemberEnd() )
|
|
{
|
|
// Yes, so delete the member.
|
|
pElementValue->SetNull();
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void TamlJSONWriter::compileCustomNode( rapidjson::Document& document, rapidjson::Value* pParentValue, const TamlCustomNode* pCustomNode, const S32 memberIndex )
|
|
{
|
|
// Debug Profiling.
|
|
PROFILE_SCOPE(TamlJSONWriter_CompileCustomNode);
|
|
|
|
// Finish if the node is set to ignore if empty and it is empty.
|
|
if ( pCustomNode->getIgnoreEmpty() && pCustomNode->isEmpty() )
|
|
return;
|
|
|
|
// Is the node a proxy object?
|
|
if ( pCustomNode->isProxyObject() )
|
|
{
|
|
// Yes, so write the proxy object.
|
|
rapidjson::Value proxyValue(rapidjson::kObjectType);
|
|
compileType( document, &proxyValue, pParentValue, pCustomNode->getProxyWriteNode(), memberIndex );
|
|
return;
|
|
}
|
|
|
|
// Fetch the json document allocator.
|
|
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
|
|
|
|
// Fetch fields.
|
|
const TamlCustomFieldVector& fields = pCustomNode->getFields();
|
|
|
|
// Fetch JSON strict flag (don't use it if member index is set to not use it).
|
|
const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict();
|
|
|
|
// Fetch element name (mangled or not).
|
|
StringTableEntry nodeName = jsonStrict ? getManagedName( pCustomNode->getNodeName(), memberIndex ) : pCustomNode->getNodeName();
|
|
|
|
// Is there any node text?
|
|
if ( !pCustomNode->getNodeTextField().isValueEmpty() )
|
|
{
|
|
// Yes, so fetch text.
|
|
const char* pNodeText = pCustomNode->getNodeTextField().getFieldValue();
|
|
|
|
// Create custom value.
|
|
rapidjson::Value customTextValue(rapidjson::kArrayType);
|
|
customTextValue.PushBack(rapidjson::GenericStringRef<UTF8>(pNodeText), allocator );
|
|
pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(nodeName), customTextValue, allocator );
|
|
return;
|
|
}
|
|
|
|
// Create custom value.
|
|
rapidjson::Value customValue(rapidjson::kObjectType);
|
|
rapidjson::Value* pCustomValue = &((pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(nodeName), customValue, allocator ).MemberEnd()-1)->value);
|
|
|
|
// Iterate fields.
|
|
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
|
|
{
|
|
// Fetch field.
|
|
const TamlCustomField* pField = *fieldItr;
|
|
|
|
// Add a field.
|
|
rapidjson::Value fieldValue;
|
|
fieldValue.SetString( pField->getFieldValue(), dStrlen(pField->getFieldValue()), allocator );
|
|
pCustomValue->AddMember(rapidjson::GenericStringRef<UTF8>(pField->getFieldName()), fieldValue, allocator );
|
|
}
|
|
|
|
// Fetch node children.
|
|
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
|
|
|
|
S32 childMemberIndex = 0;
|
|
|
|
// Iterate children nodes.
|
|
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
|
|
{
|
|
// Fetch child node.
|
|
const TamlCustomNode* pChildNode = *childNodeItr;
|
|
|
|
// Compile the child node.
|
|
compileCustomNode( document, pCustomValue, pChildNode, childMemberIndex++ );
|
|
}
|
|
|
|
// Finish if the node is set to ignore if empty and it is empty (including fields).
|
|
if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pCustomValue->MemberBegin() == pCustomValue->MemberEnd() )
|
|
{
|
|
// Yes, so delete the member.
|
|
pCustomValue->SetNull();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline StringTableEntry TamlJSONWriter::getManagedName( const char* pName, const S32 memberIndex )
|
|
{
|
|
// Debug Profiling.
|
|
PROFILE_SCOPE(TamlJSONWriter_getManagedName);
|
|
|
|
char nameBuffer[1024];
|
|
|
|
// Format mangled name.
|
|
dSprintf( nameBuffer, sizeof(nameBuffer), JSON_RFC4627_NAME_MANGLING_FORMAT, pName, memberIndex );
|
|
|
|
return StringTable->insert( nameBuffer );
|
|
} |