Initial Implementation of the Taml, Asset and Modules systems.

Only has example and shape assets currently.
This commit is contained in:
Areloch 2015-10-13 15:19:36 -05:00
parent 2044b2691e
commit 7a3b40a86d
123 changed files with 30435 additions and 181 deletions

View file

@ -0,0 +1,429 @@
//-----------------------------------------------------------------------------
// 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/binary/tamlBinaryReader.h"
#ifndef _ZIPSUBSTREAM_H_
#include "core/util/zip/zipSubStream.h"
#endif
// Debug Profiling.
#include "platform/profiler.h"
//-----------------------------------------------------------------------------
SimObject* TamlBinaryReader::read( FileStream& stream )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryReader_Read);
// Read Taml signature.
StringTableEntry tamlSignature = stream.readSTString();
// Is the signature correct?
if ( tamlSignature != StringTable->insert( TAML_SIGNATURE ) )
{
// Warn.
Con::warnf("Taml: Cannot read binary file as signature is incorrect '%s'.", tamlSignature );
return NULL;
}
// Read version Id.
U32 versionId;
stream.read( &versionId );
// Read compressed flag.
bool compressed;
stream.read( &compressed );
SimObject* pSimObject = NULL;
// Is the stream compressed?
if ( compressed )
{
// Yes, so attach zip stream.
ZipSubRStream zipStream;
zipStream.attachStream( &stream );
// Parse element.
pSimObject = parseElement( zipStream, versionId );
// Detach zip stream.
zipStream.detachStream();
}
else
{
// No, so parse element.
pSimObject = parseElement( stream, versionId );
}
return pSimObject;
}
//-----------------------------------------------------------------------------
void TamlBinaryReader::resetParse( void )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryReader_ResetParse);
// Clear object reference map.
mObjectReferenceMap.clear();
}
//-----------------------------------------------------------------------------
SimObject* TamlBinaryReader::parseElement( Stream& stream, const U32 versionId )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryReader_ParseElement);
SimObject* pSimObject = NULL;
#ifdef TORQUE_DEBUG
// Format the type location.
char typeLocationBuffer[64];
dSprintf( typeLocationBuffer, sizeof(typeLocationBuffer), "Taml [format='binary' offset=%u]", stream.getPosition() );
#endif
// Fetch element name.
StringTableEntry typeName = stream.readSTString();
// Fetch object name.
StringTableEntry objectName = stream.readSTString();
// Read references.
U32 tamlRefId;
U32 tamlRefToId;
stream.read( &tamlRefId );
stream.read( &tamlRefToId );
// 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( "Taml: Could not find a reference Id of '%d'", tamlRefToId );
return NULL;
}
// Return object.
return referenceItr->value;
}
#ifdef TORQUE_DEBUG
// Create type.
pSimObject = Taml::createType( typeName, mpTaml, typeLocationBuffer );
#else
// Create type.
pSimObject = Taml::createType( typeName, mpTaml );
#endif
// Finish if we couldn't create the type.
if ( pSimObject == NULL )
return NULL;
// Find Taml callbacks.
TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
// Are there any Taml callbacks?
if ( pCallbacks != NULL )
{
// Yes, so call it.
mpTaml->tamlPreRead( pCallbacks );
}
// Parse attributes.
parseAttributes( stream, pSimObject, versionId );
// 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.
#ifdef TORQUE_DEBUG
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 );
#else
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 );
#endif
}
}
// Do we have a reference Id?
if ( tamlRefId != 0 )
{
// Yes, so insert reference.
mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
}
// Parse custom elements.
TamlCustomNodes customProperties;
// Parse children.
parseChildren( stream, pCallbacks, pSimObject, versionId );
// Parse custom elements.
parseCustomElements( stream, pCallbacks, customProperties, versionId );
// Are there any Taml callbacks?
if ( pCallbacks != NULL )
{
// Yes, so call it.
mpTaml->tamlPostRead( pCallbacks, customProperties );
}
// Return object.
return pSimObject;
}
//-----------------------------------------------------------------------------
void TamlBinaryReader::parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryReader_ParseAttributes);
// Sanity!
AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
// Fetch attribute count.
U32 attributeCount;
stream.read( &attributeCount );
// Finish if no attributes.
if ( attributeCount == 0 )
return;
char valueBuffer[4096];
// Iterate attributes.
for ( U32 index = 0; index < attributeCount; ++index )
{
// Fetch attribute.
StringTableEntry attributeName = stream.readSTString();
stream.readLongString( 4096, valueBuffer );
// We can assume this is a field for now.
pSimObject->setPrefixedDataField(attributeName, NULL, valueBuffer);
}
}
//-----------------------------------------------------------------------------
void TamlBinaryReader::parseChildren( Stream& stream, TamlCallbacks* pCallbacks, SimObject* pSimObject, const U32 versionId )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryReader_ParseChildren);
// Sanity!
AssertFatal( pSimObject != NULL, "Taml: Cannot parse children on a NULL object." );
// Fetch children count.
U32 childrenCount;
stream.read( &childrenCount );
// Finish if no children.
if ( childrenCount == 0 )
return;
// Fetch the Taml children.
TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
// Is this a sim set?
if ( pChildren == NULL )
{
// No, so warn.
Con::warnf("Taml: Child element found under parent but object cannot have children." );
return;
}
// Fetch any container child class specifier.
AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
// Iterate children.
for ( U32 index = 0; index < childrenCount; ++ index )
{
// Parse child element.
SimObject* pChildSimObject = parseElement( stream, versionId );
// Finish if child failed.
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: 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;
// Skip.
continue;
}
}
// 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 );
}
}
}
//-----------------------------------------------------------------------------
void TamlBinaryReader::parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryReader_ParseCustomElement);
// Read custom node count.
U32 customNodeCount;
stream.read( &customNodeCount );
// Finish if no custom nodes.
if ( customNodeCount == 0 )
return;
// Iterate custom nodes.
for ( U32 nodeIndex = 0; nodeIndex < customNodeCount; ++nodeIndex )
{
//Read custom node name.
StringTableEntry nodeName = stream.readSTString();
// Add custom node.
TamlCustomNode* pCustomNode = customNodes.addNode( nodeName );
// Parse the custom node.
parseCustomNode( stream, pCustomNode, versionId );
}
// Do we have callbacks?
if ( pCallbacks == NULL )
{
// No, so warn.
Con::warnf( "Taml: Encountered custom data but object does not support custom data." );
return;
}
// Custom read callback.
mpTaml->tamlCustomRead( pCallbacks, customNodes );
}
//-----------------------------------------------------------------------------
void TamlBinaryReader::parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId )
{
// Fetch if a proxy object.
bool isProxyObject;
stream.read( &isProxyObject );
// Is this a proxy object?
if ( isProxyObject )
{
// Yes, so parse proxy object.
SimObject* pProxyObject = parseElement( stream, versionId );
// Add child node.
pCustomNode->addNode( pProxyObject );
return;
}
// No, so read custom node name.
StringTableEntry nodeName = stream.readSTString();
// Add child node.
TamlCustomNode* pChildNode = pCustomNode->addNode( nodeName );
// Read child node text.
char childNodeTextBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH];
stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, childNodeTextBuffer );
pChildNode->setNodeText( childNodeTextBuffer );
// Read child node count.
U32 childNodeCount;
stream.read( &childNodeCount );
// Do we have any children nodes?
if ( childNodeCount > 0 )
{
// Yes, so parse children nodes.
for( U32 childIndex = 0; childIndex < childNodeCount; ++childIndex )
{
// Parse child node.
parseCustomNode( stream, pChildNode, versionId );
}
}
// Read child field count.
U32 childFieldCount;
stream.read( &childFieldCount );
// Do we have any child fields?
if ( childFieldCount > 0 )
{
// Yes, so parse child fields.
for( U32 childFieldIndex = 0; childFieldIndex < childFieldCount; ++childFieldIndex )
{
// Read field name.
StringTableEntry fieldName = stream.readSTString();
// Read field value.
char valueBuffer[MAX_TAML_NODE_FIELDVALUE_LENGTH];
stream.readLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, valueBuffer );
// Add field.
pChildNode->addField( fieldName, valueBuffer );
}
}
}

View file

@ -0,0 +1,68 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_BINARYREADER_H_
#define _TAML_BINARYREADER_H_
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
#ifndef _TAML_H_
#include "persistence/taml/taml.h"
#endif
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlBinaryReader
{
public:
TamlBinaryReader( Taml* pTaml ) :
mpTaml( pTaml )
{
}
virtual ~TamlBinaryReader() {}
/// Read.
SimObject* read( FileStream& stream );
private:
Taml* mpTaml;
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
typeObjectReferenceHash mObjectReferenceMap;
private:
void resetParse( void );
SimObject* parseElement( Stream& stream, const U32 versionId );
void parseAttributes( Stream& stream, SimObject* pSimObject, const U32 versionId );
void parseChildren( Stream& stream, TamlCallbacks* pCallbacks, SimObject* pSimObject, const U32 versionId );
void parseCustomElements( Stream& stream, TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes, const U32 versionId );
void parseCustomNode( Stream& stream, TamlCustomNode* pCustomNode, const U32 versionId );
};
#endif // _TAML_BINARYREADER_H_

View file

@ -0,0 +1,297 @@
//-----------------------------------------------------------------------------
// 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/binary/tamlBinaryWriter.h"
#ifndef _ZIPSUBSTREAM_H_
#include "core/util/zip/zipSubStream.h"
#endif
// Debug Profiling.
#include "platform/profiler.h"
//-----------------------------------------------------------------------------
bool TamlBinaryWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode, const bool compressed )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryWriter_Write);
// Write Taml signature.
stream.writeString( StringTable->insert( TAML_SIGNATURE ) );
// Write version Id.
stream.write( mVersionId );
// Write compressed flag.
stream.write( compressed );
// Are we compressed?
if ( compressed )
{
// yes, so attach zip stream.
ZipSubWStream zipStream;
zipStream.attachStream( &stream );
// Write element.
writeElement( zipStream, pTamlWriteNode );
// Detach zip stream.
zipStream.detachStream();
}
else
{
// No, so write element.
writeElement( stream, pTamlWriteNode );
}
return true;
}
//-----------------------------------------------------------------------------
void TamlBinaryWriter::writeElement( Stream& stream, const TamlWriteNode* pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryWriter_WriteElement);
// Fetch object.
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
// Fetch element name.
const char* pElementName = pSimObject->getClassName();
// Write element name.
stream.writeString( pElementName );
// Fetch object name.
const char* pObjectName = pTamlWriteNode->mpObjectName;
// Write object name.
stream.writeString( pObjectName != NULL ? pObjectName : StringTable->EmptyString() );
// Fetch reference Id.
const U32 tamlRefId = pTamlWriteNode->mRefId;
// Write reference Id.
stream.write( tamlRefId );
// Do we have a reference to node?
if ( pTamlWriteNode->mRefToNode != NULL )
{
// Yes, so fetch reference to Id.
const U32 tamlRefToId = pTamlWriteNode->mRefToNode->mRefId;
// Sanity!
AssertFatal( tamlRefToId != 0, "Taml: Invalid reference to Id." );
// Write reference to Id.
stream.write( tamlRefToId );
// Finished.
return;
}
// No, so write no reference to Id.
stream.write( 0 );
// Write attributes.
writeAttributes( stream, pTamlWriteNode );
// Write children.
writeChildren( stream, pTamlWriteNode );
// Write custom elements.
writeCustomElements( stream, pTamlWriteNode );
}
//-----------------------------------------------------------------------------
void TamlBinaryWriter::writeAttributes( Stream& stream, const TamlWriteNode* pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryWriter_WriteAttributes);
// Fetch fields.
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
// Write placeholder attribute count.
stream.write( (U32)fields.size() );
// Finish if no fields.
if ( fields.size() == 0 )
return;
// Iterate fields.
for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
{
// Fetch field/value pair.
TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
// Write attribute.
stream.writeString( pFieldValue->mName );
stream.writeLongString( 4096, pFieldValue->mpValue );
}
}
void TamlBinaryWriter::writeChildren( Stream& stream, const TamlWriteNode* pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryWriter_WriteChildren);
// Fetch children.
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
// Do we have any children?
if ( pChildren == NULL )
{
// No, so write no children.
stream.write( (U32)0 );
return;
}
// Write children count.
stream.write( (U32)pChildren->size() );
// Iterate children.
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
{
// Write child.
writeElement( stream, (*itr) );
}
}
//-----------------------------------------------------------------------------
void TamlBinaryWriter::writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE(TamlBinaryWriter_WriteCustomElements);
// Fetch custom nodes.
const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
// Fetch custom nodes.
const TamlCustomNodeVector& nodes = customNodes.getNodes();
// Write custom node count.
stream.write( (U32)nodes.size() );
// Finish if there are no nodes.
if ( nodes.size() == 0 )
return;
// Iterate custom nodes.
for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
{
// Fetch the custom node.
TamlCustomNode* pCustomNode = *customNodesItr;
// Write custom node name.
stream.writeString( pCustomNode->getNodeName() );
// Fetch node children.
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
// Iterate children nodes.
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
{
// Fetch child node.
const TamlCustomNode* pChildNode = *childNodeItr;
// Write the custom node.
writeCustomNode( stream, pChildNode );
}
}
}
//-----------------------------------------------------------------------------
void TamlBinaryWriter::writeCustomNode( Stream& stream, const TamlCustomNode* pCustomNode )
{
// Is the node a proxy object?
if ( pCustomNode->isProxyObject() )
{
// Yes, so flag as proxy object.
stream.write( true );
// Write the element.
writeElement( stream, pCustomNode->getProxyWriteNode() );
return;
}
// No, so flag as custom node.
stream.write( false );
// Write custom node name.
stream.writeString( pCustomNode->getNodeName() );
// Write custom node text.
stream.writeLongString(MAX_TAML_NODE_FIELDVALUE_LENGTH, pCustomNode->getNodeTextField().getFieldValue());
// Fetch node children.
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
// Fetch child node count.
const U32 childNodeCount = (U32)nodeChildren.size();
// Write custom node count.
stream.write( childNodeCount );
// Do we have any children nodes.
if ( childNodeCount > 0 )
{
// Yes, so iterate children nodes.
for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
{
// Fetch child node.
const TamlCustomNode* pChildNode = *childNodeItr;
// Write the custom node.
writeCustomNode( stream, pChildNode );
}
}
// Fetch fields.
const TamlCustomFieldVector& fields = pCustomNode->getFields();
// Fetch child field count.
const U32 childFieldCount = (U32)fields.size();
// Write custom field count.
stream.write( childFieldCount );
// Do we have any child fields?
if ( childFieldCount > 0 )
{
// Yes, so iterate fields.
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
{
// Fetch node field.
const TamlCustomField* pField = *fieldItr;
// Write the node field.
stream.writeString( pField->getFieldName() );
stream.writeLongString( MAX_TAML_NODE_FIELDVALUE_LENGTH, pField->getFieldValue() );
}
}
}

View file

@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_BINARYWRITER_H_
#define _TAML_BINARYWRITER_H_
#ifndef _TAML_H_
#include "persistence/taml/taml.h"
#endif
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlBinaryWriter
{
public:
TamlBinaryWriter( Taml* pTaml ) :
mpTaml( pTaml ),
mVersionId(2)
{
}
virtual ~TamlBinaryWriter() {}
/// Write.
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode, const bool compressed );
private:
Taml* mpTaml;
const U32 mVersionId;
private:
void writeElement( Stream& stream, const TamlWriteNode* pTamlWriteNode );
void writeAttributes( Stream& stream, const TamlWriteNode* pTamlWriteNode );
void writeChildren( Stream& stream, const TamlWriteNode* pTamlWriteNode );
void writeCustomElements( Stream& stream, const TamlWriteNode* pTamlWriteNode );
void writeCustomNode( Stream& stream, const TamlCustomNode* pCustomNode );
};
#endif // _TAML_BINARYWRITER_H_

View file

@ -0,0 +1,747 @@
//-----------------------------------------------------------------------------
// 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 "fsTinyXml.h"
#include "console/console.h"
bool fsTiXmlDocument::LoadFile( const char * pFilename, TiXmlEncoding encoding )
{
// Expand the file-path.
char filenameBuffer[1024];
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
FileStream stream;
#ifdef TORQUE_OS_ANDROID
if (strlen(pFilename) > strlen(filenameBuffer)) {
strcpy(filenameBuffer, pFilename);
}
#endif
// File open for read?
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::Read ) )
{
// No, so warn.
Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer );
return false;
}
// Load document from stream.
if ( !LoadFile( stream ) )
{
// Warn!
Con::warnf("TamlXmlParser: Could not load Taml XML file from stream.");
return false;
}
// Close the stream.
stream.close();
return true;
}
bool fsTiXmlDocument::SaveFile( const char * pFilename ) const
{
// Expand the file-name into the file-path buffer.
char filenameBuffer[1024];
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
FileStream stream;
// File opened?
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::Write ) )
{
// No, so warn.
Con::warnf("Taml::writeFile() - Could not open filename '%s' for write.", filenameBuffer );
return false;
}
bool ret = SaveFile(stream);
stream.close();
return ret;
}
bool fsTiXmlDocument::LoadFile( FileStream &stream, TiXmlEncoding encoding )
{
// Delete the existing data:
Clear();
//TODO: Can't clear location, investigate if this gives issues.
//doc.location.Clear();
// Get the file size, so we can pre-allocate the string. HUGE speed impact.
long length = stream.getStreamSize();
// Strange case, but good to handle up front.
if ( length <= 0 )
{
SetError( TiXmlDocument::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
return false;
}
// Subtle bug here. TinyXml did use fgets. But from the XML spec:
// 2.11 End-of-Line Handling
// <snip>
// <quote>
// ...the XML processor MUST behave as if it normalized all line breaks in external
// parsed entities (including the document entity) on input, before parsing, by translating
// both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
// a single #xA character.
// </quote>
//
// It is not clear fgets does that, and certainly isn't clear it works cross platform.
// Generally, you expect fgets to translate from the convention of the OS to the c/unix
// convention, and not work generally.
/*
while( fgets( buf, sizeof(buf), file ) )
{
data += buf;
}
*/
char* buf = new char[ length+1 ];
buf[0] = 0;
if ( !stream.read( length, buf ) ) {
delete [] buf;
SetError( TiXmlDocument::TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
return false;
}
// Process the buffer in place to normalize new lines. (See comment above.)
// Copies from the 'p' to 'q' pointer, where p can advance faster if
// a newline-carriage return is hit.
//
// Wikipedia:
// Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
// CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
// * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
// * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
// * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
const char* p = buf; // the read head
char* q = buf; // the write head
const char CR = 0x0d;
const char LF = 0x0a;
buf[length] = 0;
while( *p ) {
assert( p < (buf+length) );
assert( q <= (buf+length) );
assert( q <= p );
if ( *p == CR ) {
*q++ = LF;
p++;
if ( *p == LF ) { // check for CR+LF (and skip LF)
p++;
}
}
else {
*q++ = *p++;
}
}
assert( q <= (buf+length) );
*q = 0;
Parse( buf, 0, encoding );
delete [] buf;
return !Error();
}
bool fsTiXmlDocument::SaveFile( FileStream &stream ) const
{
if ( useMicrosoftBOM )
{
const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
stream.write( TIXML_UTF_LEAD_0 );
stream.write( TIXML_UTF_LEAD_1 );
stream.write( TIXML_UTF_LEAD_2 );
}
Print( stream, 0 );
return true;
}
void fsTiXmlDocument::Print( FileStream& stream, int depth ) const
{
for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
{
//AttemptPrintTiNode(const_cast<TiXmlNode*>(node), stream, depth);
dynamic_cast<const fsTiXmlNode*>(node)->Print( stream, depth );
stream.writeText( "\n" );
}
}
void fsTiXmlAttribute::Print( FileStream& stream, int depth, TIXML_STRING* str ) const
{
TIXML_STRING n, v;
TiXmlString value = TiXmlString(Value());
EncodeString( NameTStr(), &n );
EncodeString( value, &v );
for ( int i=0; i< depth; i++ ) {
stream.writeText( " " );
}
if (value.find ('\"') == TIXML_STRING::npos) {
const char* pValue = v.c_str();
char buffer[4096];
const S32 length = dSprintf(buffer, sizeof(buffer), "%s=\"%s\"", n.c_str(), pValue);
stream.write(length, buffer);
if ( str ) {
(*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
}
}
else {
char buffer[4096];
const S32 length = dSprintf(buffer, sizeof(buffer), "%s='%s'", n.c_str(), v.c_str());
stream.write(length, buffer);
if ( str ) {
(*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
}
}
}
void fsTiXmlDeclaration::Print(FileStream& stream, int depth, TiXmlString* str) const
{
stream.writeStringBuffer( "<?xml " );
if ( str ) (*str) += "<?xml ";
if ( !version.empty() ) {
stream.writeFormattedBuffer( "version=\"%s\" ", version.c_str ());
if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; }
}
if ( !encoding.empty() ) {
stream.writeFormattedBuffer( "encoding=\"%s\" ", encoding.c_str ());
if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; }
}
if ( !standalone.empty() ) {
stream.writeFormattedBuffer( "standalone=\"%s\" ", standalone.c_str ());
if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; }
}
stream.writeStringBuffer( "?>" );
if ( str ) (*str) += "?>";
}
void fsTiXmlElement::Print(FileStream& stream, int depth) const
{
int i;
for ( i=0; i<depth; i++ ) {
stream.writeStringBuffer( " " );
}
stream.writeFormattedBuffer( "<%s", value.c_str() );
const TiXmlAttribute* attrib;
for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
{
stream.writeStringBuffer( "\n" );
dynamic_cast<const fsTiXmlAttribute*>(attrib)->Print( stream, depth+1 );
}
// There are 3 different formatting approaches:
// 1) An element without children is printed as a <foo /> node
// 2) An element with only a text child is printed as <foo> text </foo>
// 3) An element with children is printed on multiple lines.
TiXmlNode* node;
if ( !firstChild )
{
stream.writeStringBuffer( " />" );
}
else if ( firstChild == lastChild && firstChild->ToText() )
{
stream.writeStringBuffer( ">" );
dynamic_cast<const fsTiXmlNode*>(firstChild)->Print( stream, depth + 1 );
stream.writeFormattedBuffer( "</%s>", value.c_str() );
}
else
{
stream.writeStringBuffer( ">" );
for ( node = firstChild; node; node=node->NextSibling() )
{
if ( !node->ToText() )
{
stream.writeStringBuffer( "\n" );
}
dynamic_cast<const fsTiXmlNode*>(node)->Print( stream, depth+1 );
}
stream.writeStringBuffer( "\n" );
for( i=0; i<depth; ++i ) {
stream.writeStringBuffer( " " );
}
stream.writeFormattedBuffer( "</%s>", value.c_str() );
}
}
void fsTiXmlComment::Print(FileStream& stream, int depth) const
{
for ( int i=0; i<depth; i++ )
{
stream.writeStringBuffer( " " );
}
stream.writeFormattedBuffer( "<!--%s-->", value.c_str() );
}
void fsTiXmlText::Print(FileStream& stream, int depth) const
{
if ( cdata )
{
int i;
stream.writeStringBuffer( "\n" );
for ( i=0; i<depth; i++ ) {
stream.writeStringBuffer( " " );
}
stream.writeFormattedBuffer( "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output
}
else
{
TIXML_STRING buffer;
EncodeString( value, &buffer );
stream.writeFormattedBuffer( "%s", buffer.c_str() );
}
}
void fsTiXmlUnknown::Print(FileStream& stream, int depth) const
{
for ( int i=0; i<depth; i++ )
stream.writeStringBuffer( " " );
stream.writeFormattedBuffer( "<%s>", value.c_str() );
}
static TiXmlNode* TiNodeIdentify( TiXmlNode* parent, const char* p, TiXmlEncoding encoding )
{
TiXmlNode* returnNode = 0;
p = TiXmlNode::SkipWhiteSpace( p, encoding );
if( !p || !*p || *p != '<' )
{
return 0;
}
p = TiXmlNode::SkipWhiteSpace( p, encoding );
if ( !p || !*p )
{
return 0;
}
// What is this thing?
// - Elements start with a letter or underscore, but xml is reserved.
// - Comments: <!--
// - Decleration: <?xml
// - Everthing else is unknown to tinyxml.
//
const char* xmlHeader = { "<?xml" };
const char* commentHeader = { "<!--" };
const char* dtdHeader = { "<!" };
const char* cdataHeader = { "<![CDATA[" };
if ( TiXmlNode::StringEqual( p, xmlHeader, true, encoding ) )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Declaration\n" );
#endif
returnNode = new fsTiXmlDeclaration();
}
else if ( TiXmlNode::StringEqual( p, commentHeader, false, encoding ) )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Comment\n" );
#endif
returnNode = new fsTiXmlComment();
}
else if ( TiXmlNode::StringEqual( p, cdataHeader, false, encoding ) )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing CDATA\n" );
#endif
TiXmlText* text = new fsTiXmlText( "" );
text->SetCDATA( true );
returnNode = text;
}
else if ( TiXmlNode::StringEqual( p, dtdHeader, false, encoding ) )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Unknown(1)\n" );
#endif
returnNode = new fsTiXmlUnknown();
}
else if ( TiXmlNode::IsAlpha( *(p+1), encoding )
|| *(p+1) == '_' )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Element\n" );
#endif
returnNode = new fsTiXmlElement( "" );
}
else
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Unknown(2)\n" );
#endif
returnNode = new fsTiXmlUnknown();
}
if ( returnNode )
{
// Set the parent, so it can report errors
returnNode->parent = parent;
}
return returnNode;
}
TiXmlNode* fsTiXmlDocument::Identify( const char* p, TiXmlEncoding encoding )
{
return TiNodeIdentify(this, p, encoding);
}
TiXmlNode* fsTiXmlElement::Identify( const char* p, TiXmlEncoding encoding )
{
return TiNodeIdentify(this, p, encoding);
}
const char* fsTiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
{
p = SkipWhiteSpace( p, encoding );
TiXmlDocument* document = GetDocument();
if ( !p || !*p )
{
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
return 0;
}
if ( data )
{
data->Stamp( p, encoding );
location = data->Cursor();
}
if ( *p != '<' )
{
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
return 0;
}
p = SkipWhiteSpace( p+1, encoding );
// Read the name.
const char* pErr = p;
p = ReadName( p, &value, encoding );
if ( !p || !*p )
{
if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
return 0;
}
TIXML_STRING endTag ("</");
endTag += value;
// Check for and read attributes. Also look for an empty
// tag or an end tag.
while ( p && *p )
{
pErr = p;
p = SkipWhiteSpace( p, encoding );
if ( !p || !*p )
{
if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
return 0;
}
if ( *p == '/' )
{
++p;
// Empty tag.
if ( *p != '>' )
{
if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
return 0;
}
return (p+1);
}
else if ( *p == '>' )
{
// Done with attributes (if there were any.)
// Read the value -- which can include other
// elements -- read the end tag, and return.
++p;
p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens.
if ( !p || !*p ) {
// We were looking for the end tag, but found nothing.
// Fix for [ 1663758 ] Failure to report error on bad XML
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
return 0;
}
// We should find the end tag now
// note that:
// </foo > and
// </foo>
// are both valid end tags.
if ( StringEqual( p, endTag.c_str(), false, encoding ) )
{
p += endTag.length();
p = SkipWhiteSpace( p, encoding );
if ( p && *p && *p == '>' ) {
++p;
return p;
}
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
return 0;
}
else
{
if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
return 0;
}
}
else
{
// Try to read an attribute:
TiXmlAttribute* attrib = new fsTiXmlAttribute();
if ( !attrib )
{
return 0;
}
attrib->SetDocument( document );
pErr = p;
p = attrib->Parse( p, data, encoding );
if ( !p || !*p )
{
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
delete attrib;
return 0;
}
// Handle the strange case of double attributes:
#ifdef TIXML_USE_STL
TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
#else
TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
#endif
if ( node )
{
if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
delete attrib;
return 0;
}
attributeSet.Add( attrib );
}
}
return p;
}
/*
TiXmlNode* fsTiXmlNode::Identify(char const* p, TiXmlEncoding encoding)
{
TiXmlNode* returnNode = 0;
p = TiXmlBase::SkipWhiteSpace( p, encoding );
if( !p || !*p || *p != '<' )
{
return 0;
}
p = TiXmlBase::SkipWhiteSpace( p, encoding );
if ( !p || !*p )
{
return 0;
}
// What is this thing?
// - Elements start with a letter or underscore, but xml is reserved.
// - Comments: <!--
// - Decleration: <?xml
// - Everthing else is unknown to tinyxml.
//
const char* xmlHeader = { "<?xml" };
const char* commentHeader = { "<!--" };
const char* dtdHeader = { "<!" };
const char* cdataHeader = { "<![CDATA[" };
if ( TiXmlBase::StringEqual( p, xmlHeader, true, encoding ) )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Declaration\n" );
#endif
returnNode = new fsTiXmlDeclaration();
}
else if ( TiXmlBase::StringEqual( p, commentHeader, false, encoding ) )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Comment\n" );
#endif
returnNode = new fsTiXmlComment();
}
else if ( TiXmlBase::StringEqual( p, cdataHeader, false, encoding ) )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing CDATA\n" );
#endif
fsTiXmlText* text = new fsTiXmlText( "" );
text->SetCDATA( true );
returnNode = text;
}
else if ( TiXmlBase::StringEqual( p, dtdHeader, false, encoding ) )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Unknown(1)\n" );
#endif
returnNode = new fsTiXmlUnknown();
}
else if ( TiXmlBase::IsAlpha( *(p+1), encoding )
|| *(p+1) == '_' )
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Element\n" );
#endif
returnNode = new fsTiXmlElement( "" );
}
else
{
#ifdef DEBUG_PARSER
TIXML_LOG( "XML parsing Unknown(2)\n" );
#endif
returnNode = new fsTiXmlUnknown();
}
if ( returnNode )
{
// Set the parent, so it can report errors
returnNode->parent = this;
}
return returnNode;
}
const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
char const* fsTiXmlDocument::Parse(char const* p, TiXmlParsingData* prevData, TiXmlEncoding encoding)
{
ClearError();
// Parse away, at the document level. Since a document
// contains nothing but other tags, most of what happens
// here is skipping white space.
if ( !p || !*p )
{
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
return 0;
}
// Note that, for a document, this needs to come
// before the while space skip, so that parsing
// starts from the pointer we are given.
location.Clear();
if ( prevData )
{
location.row = prevData->Cursor().row;
location.col = prevData->Cursor().col;
}
else
{
location.row = 0;
location.col = 0;
}
TiXmlParsingData data( p, TabSize(), location.row, location.col );
location = data.Cursor();
if ( encoding == TIXML_ENCODING_UNKNOWN )
{
// Check for the Microsoft UTF-8 lead bytes.
const unsigned char* pU = (const unsigned char*)p;
if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
&& *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
&& *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
{
encoding = TIXML_ENCODING_UTF8;
useMicrosoftBOM = true;
}
}
p = TiXmlBase::SkipWhiteSpace( p, encoding );
if ( !p )
{
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
return 0;
}
while ( p && *p )
{
TiXmlNode* node = fsTiXmlNode::Identify( p, encoding );
if ( node )
{
p = node->Parse( p, &data, encoding );
LinkEndChild( node );
}
else
{
break;
}
// Did we get encoding info?
if ( encoding == TIXML_ENCODING_UNKNOWN
&& node->ToDeclaration() )
{
TiXmlDeclaration* dec = node->ToDeclaration();
const char* enc = dec->Encoding();
assert( enc );
if ( *enc == 0 )
encoding = TIXML_ENCODING_UTF8;
else if ( TiXmlBase::StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
encoding = TIXML_ENCODING_UTF8;
else if ( TiXmlBase::StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
else
encoding = TIXML_ENCODING_LEGACY;
}
p = TiXmlBase::SkipWhiteSpace( p, encoding );
}
// Was this empty?
if ( !firstChild ) {
SetError( TiXmlBase::TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
return 0;
}
// All is well.
return p;
}
*/

View file

@ -0,0 +1,248 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _FSTINYXML_H_
#define _FSTINYXML_H_
#ifndef TINYXML_INCLUDED
#include "tinyXML/tinyxml.h"
#endif
#include "platform/platform.h"
#ifndef _FILESTREAM_H_
#include "core/stream/fileStream.h"
#endif
class fsTiXmlNode
{
public:
virtual void Print( FileStream& stream, int depth ) const = 0;
};
class fsTiXmlDocument : public TiXmlDocument, public fsTiXmlNode
{
public:
bool LoadFile( FileStream &stream, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
bool SaveFile( FileStream &stream ) const;
/// Load a file using the given filename. Returns true if successful.
bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
/// Save a file using the given filename. Returns true if successful.
bool SaveFile( const char * filename ) const;
virtual void Print( FileStream& stream, int depth ) const;
virtual TiXmlNode* Clone() const
{
TiXmlDocument* clone = new fsTiXmlDocument();
if ( !clone )
return 0;
CopyTo( clone );
return clone;
}
TiXmlNode* Identify( const char* p, TiXmlEncoding encoding );
};
class fsTiXmlAttribute : public TiXmlAttribute, public fsTiXmlNode
{
public:
virtual void Print( FileStream& stream, int depth, TIXML_STRING* str ) const;
virtual void Print( FileStream& stream, int depth) const
{
Print(stream, depth, 0);
}
};
class fsTiXmlDeclaration : public TiXmlDeclaration, public fsTiXmlNode
{
public:
fsTiXmlDeclaration(){};
fsTiXmlDeclaration( const char* _version,
const char* _encoding,
const char* _standalone ) : TiXmlDeclaration(_version, _encoding, _standalone) { }
virtual void Print( FileStream& stream, int depth, TIXML_STRING* str ) const;
virtual void Print( FileStream& stream, int depth) const
{
Print(stream, depth, 0);
}
virtual TiXmlNode* Clone() const
{
fsTiXmlDeclaration* clone = new fsTiXmlDeclaration();
if ( !clone )
return 0;
CopyTo( clone );
return clone;
}
};
class fsTiXmlElement : public TiXmlElement, public fsTiXmlNode
{
public:
fsTiXmlElement(const char* in_value) : TiXmlElement(in_value) { }
virtual void Print( FileStream& stream, int depth ) const;
virtual TiXmlNode* Clone() const
{
TiXmlElement* clone = new fsTiXmlElement( Value() );
if ( !clone )
return 0;
CopyTo( clone );
return clone;
}
void SetAttribute( const char* name, const char * _value )
{
TiXmlAttribute* attrib = attributeSet.Find( name );
if(!attrib)
{
attrib = new fsTiXmlAttribute();
attributeSet.Add( attrib );
attrib->SetName( name );
}
if ( attrib ) {
attrib->SetValue( _value );
}
}
void SetAttribute( const char * name, int value )
{
TiXmlAttribute* attrib = attributeSet.Find( name );
if(!attrib)
{
attrib = new fsTiXmlAttribute();
attributeSet.Add( attrib );
attrib->SetName( name );
}
if ( attrib ) {
attrib->SetIntValue( value );
}
}
TiXmlNode* Identify( const char* p, TiXmlEncoding encoding );
virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
};
class fsTiXmlComment : public TiXmlComment, public fsTiXmlNode
{
public:
virtual void Print( FileStream& stream, int depth ) const;
virtual TiXmlNode* Clone() const
{
TiXmlComment* clone = new fsTiXmlComment();
if ( !clone )
return 0;
CopyTo( clone );
return clone;
}
};
class fsTiXmlText : public TiXmlText, public fsTiXmlNode
{
public:
fsTiXmlText (const char * initValue ) : TiXmlText(initValue) { }
virtual void Print( FileStream& stream, int depth ) const;
virtual TiXmlNode* Clone() const
{
TiXmlText* clone = 0;
clone = new fsTiXmlText( "" );
if ( !clone )
return 0;
CopyTo( clone );
return clone;
}
};
class fsTiXmlUnknown : public TiXmlUnknown, public fsTiXmlNode
{
public:
virtual void Print( FileStream& stream, int depth ) const;
virtual TiXmlNode* Clone() const
{
TiXmlUnknown* clone = new fsTiXmlUnknown();
if ( !clone )
return 0;
CopyTo( clone );
return clone;
}
};
static bool AttemptPrintTiNode(class fsTiXmlDocument* node, FileStream& stream, int depth)
{
fsTiXmlDocument* fsDoc = dynamic_cast<fsTiXmlDocument*>(node);
if(fsDoc != NULL)
{
fsDoc->Print(stream, depth);
return true;
}
fsTiXmlUnknown* fsUnk = dynamic_cast<fsTiXmlUnknown*>(node);
if(fsUnk != NULL)
{
fsUnk->Print(stream, depth);
return true;
}
fsTiXmlText* fsTxt = dynamic_cast<fsTiXmlText*>(node);
if(fsTxt != NULL)
{
fsTxt->Print(stream, depth);
return true;
}
fsTiXmlComment* fsCom = dynamic_cast<fsTiXmlComment*>(node);
if(fsCom != NULL)
{
fsCom->Print(stream, depth);
return true;
}
fsTiXmlElement* fsElm = dynamic_cast<fsTiXmlElement*>(node);
if(fsElm != NULL)
{
fsElm->Print(stream, depth);
return true;
}
fsTiXmlDeclaration* fsDec = dynamic_cast<fsTiXmlDeclaration*>(node);
if(fsDec != NULL)
{
fsDec->Print(stream, depth);
return true;
}
fsTiXmlAttribute* fsAtt = dynamic_cast<fsTiXmlAttribute*>(node);
if(fsAtt != NULL)
{
fsAtt->Print(stream, depth);
return true;
}
return false;
}
#endif //_FSTINYXML_H_

View file

@ -0,0 +1,258 @@
//-----------------------------------------------------------------------------
// 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/tamlJSONParser.h"
#include "persistence/taml/tamlVisitor.h"
#include "console/console.h"
#include "core/stream/fileStream.h"
#include "core/frameAllocator.h"
// Debug Profiling.
#include "platform/profiler.h"
//-----------------------------------------------------------------------------
bool TamlJSONParser::accept( const char* pFilename, TamlVisitor& visitor )
{
// Debug Profiling.
PROFILE_SCOPE(TamlJSONParser_Accept);
// Sanity!
AssertFatal( pFilename != NULL, "Cannot parse a NULL filename." );
// Expand the file-path.
char filenameBuffer[1024];
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
FileStream stream;
// File open for read?
if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) )
{
// No, so warn.
Con::warnf("TamlJSONParser::parse() - Could not open filename '%s' for parse.", filenameBuffer );
return false;
}
// Read JSON file.
const U32 streamSize = stream.getStreamSize();
FrameTemp<char> jsonText( streamSize + 1 );
if ( !stream.read( streamSize, jsonText ) )
{
// Warn!
Con::warnf("TamlJSONParser::parse() - Could not load Taml JSON file from stream.");
return false;
}
// Create JSON document.
rapidjson::Document inputDocument;
inputDocument.Parse<0>( jsonText );
// Close the stream.
stream.close();
// Check the document is valid.
if ( inputDocument.GetType() != rapidjson::kObjectType )
{
// Warn!
Con::warnf("TamlJSONParser::parse() - Load Taml JSON file from stream but was invalid.");
return false;
}
// Set parsing filename.
setParsingFilename( filenameBuffer );
// Flag document as not dirty.
mDocumentDirty = false;
// Fetch the root.
rapidjson::Value::MemberIterator rootItr = inputDocument.MemberBegin();
// Parse root value.
parseType( rootItr, visitor, true );
// Reset parsing filename.
setParsingFilename( StringTable->EmptyString() );
// Finish if the document is not dirty.
if ( !mDocumentDirty )
return true;
// Open for write?
if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) )
{
// No, so warn.
Con::warnf("TamlJSONParser::parse() - Could not open filename '%s' for write.", filenameBuffer );
return false;
}
// Create output document.
rapidjson::Document outputDocument;
outputDocument.AddMember( rootItr->name, rootItr->value, outputDocument.GetAllocator() );
// Write document to stream.
rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream );
outputDocument.Accept( jsonStreamWriter );
// Close the stream.
stream.close();
return true;
}
//-----------------------------------------------------------------------------
inline bool TamlJSONParser::parseType( rapidjson::Value::MemberIterator& memberItr, TamlVisitor& visitor, const bool isRoot )
{
// Debug Profiling.
PROFILE_SCOPE(TamlJSONParser_ParseType);
// Fetch name and value.
const rapidjson::Value& typeName = memberItr->name;
rapidjson::Value& typeValue = memberItr->value;
// Create a visitor property state.
TamlVisitor::PropertyState propertyState;
propertyState.setObjectName( typeName.GetString(), isRoot );
// Parse field members.
for( rapidjson::Value::MemberIterator fieldMemberItr = typeValue.MemberBegin(); fieldMemberItr != typeValue.MemberEnd(); ++fieldMemberItr )
{
// Fetch value.
const rapidjson::Value& fieldName = fieldMemberItr->name;
rapidjson::Value& fieldValue = fieldMemberItr->value;
// Skip if not a field.
if ( fieldValue.IsObject() )
continue;
char valueBuffer[4096];
if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), fieldValue, fieldName.GetString() ) )
{
// Warn.
Con::warnf( "TamlJSONParser::parseTyoe() - Could not interpret value for field '%s'", fieldName.GetString() );
continue;
}
// Configure property state.
propertyState.setProperty( fieldName.GetString(), valueBuffer );
// Visit this attribute.
const bool visitStatus = visitor.visit( *this, propertyState );
// Was the property value changed?
if ( propertyState.getPropertyValueDirty() )
{
// Yes, so update the attribute.
fieldValue.SetString( propertyState.getPropertyValue() );
// Flag the document as dirty.
mDocumentDirty = true;
}
// Finish if requested.
if ( !visitStatus )
return false;
}
// Finish if only the root is needed.
if ( visitor.wantsRootOnly() )
return false;
// Parse children and custom node members.
for( rapidjson::Value::MemberIterator objectMemberItr = typeValue.MemberBegin(); objectMemberItr != typeValue.MemberEnd(); ++objectMemberItr )
{
// Fetch name and value.
const rapidjson::Value& objectValue = objectMemberItr->value;
// Skip if not an object.
if ( !objectValue.IsObject() )
continue;
// Parse the type.
if ( !parseType( objectMemberItr, visitor, false ) )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
inline bool TamlJSONParser::parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName )
{
// Debug Profiling.
PROFILE_SCOPE(TamlJSONParser_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;
}

View file

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_JSONPARSER_H_
#define _TAML_JSONPARSER_H_
#ifndef _TAML_PARSER_H_
#include "persistence/taml/tamlParser.h"
#endif
/// RapidJson.
#include "persistence/rapidjson/document.h"
#include "persistence/rapidjson/prettywriter.h"
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlJSONParser : public TamlParser
{
public:
TamlJSONParser() {}
virtual ~TamlJSONParser() {}
/// Whether the parser can change a property or not.
virtual bool canChangeProperty( void ) { return true; }
/// Accept visitor.
virtual bool accept( const char* pFilename, TamlVisitor& visitor );
private:
inline bool parseType( rapidjson::Value::MemberIterator& memberItr, TamlVisitor& visitor, const bool isRoot );
inline bool parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName );
bool mDocumentDirty;
};
#endif // _TAML_JSONPARSER_H_

View file

@ -0,0 +1,686 @@
//-----------------------------------------------------------------------------
// 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& objectName = 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( objectName.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;
}

View file

@ -0,0 +1,76 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_JSONREADER_H_
#define _TAML_JSONREADER_H_
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
#ifndef _TAML_H_
#include "persistence/taml/taml.h"
#endif
/// RapidJson.
#include "persistence/rapidjson/document.h"
#include "persistence/rapidjson/prettywriter.h"
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlJSONReader
{
public:
TamlJSONReader( Taml* pTaml ) :
mpTaml( pTaml )
{}
virtual ~TamlJSONReader() {}
/// Read.
SimObject* read( FileStream& stream );
private:
Taml* mpTaml;
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
typeObjectReferenceHash mObjectReferenceMap;
private:
void resetParse( void );
SimObject* parseType( const rapidjson::Value::ConstMemberIterator& memberItr );
inline void parseField( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject );
inline void parseChild( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject );
inline void parseCustom( rapidjson::Value::ConstMemberIterator& memberItr, SimObject* pSimObject, const char* pCustomNodeName, TamlCustomNodes& customNodes );
inline void parseCustomNode( rapidjson::Value::ConstMemberIterator& memberItr, TamlCustomNode* pCustomNode );
inline StringTableEntry getDemangledName( const char* pMangledName );
inline bool parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName );
inline U32 getTamlRefId( const rapidjson::Value& value );
inline U32 getTamlRefToId( const rapidjson::Value& value );
inline const char* getTamlObjectName( const rapidjson::Value& value );
};
#endif // _TAML_JSONREADER_H_

View file

@ -0,0 +1,367 @@
//-----------------------------------------------------------------------------
// 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( elementName, *pTypeValue, allocator ).MemberEnd()-1)->value);
}
else
{
// Yes, so add as a parent value member.
pTypeValue = &((pParentValue->AddMember( 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( 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( 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( 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( 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( 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( pNodeText, allocator );
pParentValue->AddMember( nodeName, customTextValue, allocator );
return;
}
// Create custom value.
rapidjson::Value customValue(rapidjson::kObjectType);
rapidjson::Value* pCustomValue = &((pParentValue->AddMember( 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( 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 );
}

View file

@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_JSONWRITER_H_
#define _TAML_JSONWRITER_H_
#ifndef _TAML_H_
#include "persistence/taml/taml.h"
#endif
/// RapidJson.
#include "persistence/rapidjson/document.h"
#include "persistence/rapidjson/prettywriter.h"
//-----------------------------------------------------------------------------
extern StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS;
extern StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT;
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlJSONWriter
{
public:
TamlJSONWriter( Taml* pTaml ) :
mpTaml( pTaml )
{}
virtual ~TamlJSONWriter() {}
/// Write.
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode );
private:
Taml* mpTaml;
private:
void compileType( rapidjson::Document& document, rapidjson::Value* pTypeValue, rapidjson::Value* pParentValue, const TamlWriteNode* pTamlWriteNode, const S32 memberIndex );
void compileFields( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode );
void compileCustom( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode );
void compileCustomNode( rapidjson::Document& document, rapidjson::Value* pParentValue, const TamlCustomNode* pCustomNode, const S32 memberIndex );
inline StringTableEntry getManagedName(const char* pName, const S32 memberIndex );
};
#endif // _TAML_JSONWRITER_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,218 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_H_
#define _TAML_H_
#ifndef _TAML_CALLBACKS_H_
#include "persistence/taml/tamlCallbacks.h"
#endif
#ifndef _TAML_CUSTOM_H_
#include "persistence/taml/tamlCustom.h"
#endif
#ifndef _TAML_CHILDREN_H_
#include "persistence/taml/tamlChildren.h"
#endif
#ifndef _TAML_WRITE_NODE_H_
#include "persistence/taml/tamlWriteNode.h"
#endif
#ifndef _TAML_VISITOR_H_
#include "persistence/taml/tamlVisitor.h"
#endif
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
#ifndef _FILESTREAM_H_
#include "core/stream/fileStream.h"
#endif
//-----------------------------------------------------------------------------
extern StringTableEntry tamlRefIdName;
extern StringTableEntry tamlRefToIdName;
extern StringTableEntry tamlNamedObjectName;
//-----------------------------------------------------------------------------
#define TAML_SIGNATURE "Taml"
#define TAML_SCHEMA_VARIABLE "$pref::T2D::TAMLSchema"
#define TAML_JSON_STRICT_VARIBLE "$pref::T2D::JSONStrict"
class TiXmlElement;
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class Taml : public SimObject
{
public:
enum TamlFormatMode
{
InvalidFormat = 0,
XmlFormat,
BinaryFormat,
JSONFormat,
};
private:
typedef SimObject Parent;
typedef Vector<TamlWriteNode*> typeNodeVector;
typedef HashTable<SimObjectId, TamlWriteNode*> typeCompiledHash;
typeNodeVector mCompiledNodes;
typeCompiledHash mCompiledObjects;
U32 mMasterNodeId;
TamlFormatMode mFormatMode;
StringTableEntry mAutoFormatXmlExtension;
StringTableEntry mAutoFormatBinaryExtension;
StringTableEntry mAutoFormatJSONExtension;
bool mJSONStrict;
bool mBinaryCompression;
bool mAutoFormat;
bool mWriteDefaults;
bool mProgenitorUpdate;
char mFilePathBuffer[1024];
private:
void resetCompilation( void );
TamlWriteNode* compileObject( SimObject* pSimObject, const bool forceId = false );
void compileStaticFields( TamlWriteNode* pTamlWriteNode );
void compileDynamicFields( TamlWriteNode* pTamlWriteNode );
void compileChildren( TamlWriteNode* pTamlWriteNode );
void compileCustomState( TamlWriteNode* pTamlWriteNode );
void compileCustomNodeState( TamlCustomNode* pCustomNode );
bool write( FileStream& stream, SimObject* pSimObject, const TamlFormatMode formatMode );
SimObject* read( FileStream& stream, const TamlFormatMode formatMode );
template<typename T> inline T* read( FileStream& stream, const TamlFormatMode formatMode )
{
SimObject* pSimObject = read( stream, formatMode );
if ( pSimObject == NULL )
return NULL;
T* pObj = dynamic_cast<T*>( pSimObject );
if ( pObj != NULL )
return pObj;
pSimObject->deleteObject();
return NULL;
}
public:
Taml();
virtual ~Taml() {}
virtual bool onAdd();
virtual void onRemove();
static void initPersistFields();
/// Format mode.
inline void setFormatMode( const TamlFormatMode formatMode ) { mFormatMode = formatMode != Taml::InvalidFormat ? formatMode : Taml::XmlFormat; }
inline TamlFormatMode getFormatMode( void ) const { return mFormatMode; }
/// Auto-Format mode.
inline void setAutoFormat( const bool autoFormat ) { mAutoFormat = autoFormat; }
inline bool getAutoFormat( void ) const { return mAutoFormat; }
/// Write defaults.
inline void setWriteDefaults( const bool writeDefaults ) { mWriteDefaults = writeDefaults; }
inline bool getWriteDefaults( void ) const { return mWriteDefaults; }
/// Progenitor.
inline void setProgenitorUpdate( const bool progenitorUpdate ) { mProgenitorUpdate = progenitorUpdate; }
inline bool getProgenitorUpdate( void ) const { return mProgenitorUpdate; }
/// Auto-format extensions.
inline void setAutoFormatXmlExtension( const char* pExtension ) { mAutoFormatXmlExtension = StringTable->insert( pExtension ); }
inline StringTableEntry getAutoFormatXmlExtension( void ) const { return mAutoFormatXmlExtension; }
inline void setAutoFormatBinaryExtension( const char* pExtension ) { mAutoFormatBinaryExtension = StringTable->insert( pExtension ); }
inline StringTableEntry getAutoFormatBinaryExtension( void ) const { return mAutoFormatBinaryExtension; }
/// Compression.
inline void setBinaryCompression( const bool compressed ) { mBinaryCompression = compressed; }
inline bool getBinaryCompression( void ) const { return mBinaryCompression; }
/// JSON Strict RFC4627 mode.
inline void setJSONStrict( const bool jsonStrict ) { mJSONStrict = jsonStrict; }
inline bool getJSONStrict( void ) const { return mJSONStrict; }
TamlFormatMode getFileAutoFormatMode( const char* pFilename );
const char* getFilePathBuffer( void ) const { return mFilePathBuffer; }
/// Write.
bool write( SimObject* pSimObject, const char* pFilename );
/// Read.
template<typename T> inline T* read( const char* pFilename )
{
SimObject* pSimObject = read( pFilename );
if ( pSimObject == NULL )
return NULL;
T* pObj = dynamic_cast<T*>( pSimObject );
if ( pObj != NULL )
return pObj;
pSimObject->deleteObject();
return NULL;
}
SimObject* read( const char* pFilename );
/// Parse.
bool parse( const char* pFilename, TamlVisitor& visitor );
/// Create type.
static SimObject* createType( StringTableEntry typeName, const Taml* pTaml, const char* pProgenitorSuffix = NULL );
/// Schema generation.
static bool generateTamlSchema();
/// Write a unrestricted custom Taml schema.
static void WriteUnrestrictedCustomTamlSchema( const char* pCustomNodeName, const AbstractClassRep* pClassRep, TiXmlElement* pParentElement );
/// Get format mode info.
static TamlFormatMode getFormatModeEnum( const char* label );
static const char* getFormatModeDescription( const TamlFormatMode formatMode );
/// Taml callbacks.
inline void tamlPreWrite( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPreWrite(); }
inline void tamlPostWrite( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPostWrite(); }
inline void tamlPreRead( TamlCallbacks* pCallbacks ) { pCallbacks->onTamlPreRead(); }
inline void tamlPostRead( TamlCallbacks* pCallbacks, const TamlCustomNodes& customNodes ) { pCallbacks->onTamlPostRead( customNodes ); }
inline void tamlAddParent( TamlCallbacks* pCallbacks, SimObject* pParentObject ) { pCallbacks->onTamlAddParent( pParentObject ); }
inline void tamlCustomWrite( TamlCallbacks* pCallbacks, TamlCustomNodes& customNodes ) { pCallbacks->onTamlCustomWrite( customNodes ); }
inline void tamlCustomRead( TamlCallbacks* pCallbacks, const TamlCustomNodes& customNodes ) { pCallbacks->onTamlCustomRead( customNodes ); }
/// Declare Console Object.
DECLARE_CONOBJECT( Taml );
};
#endif // _TAML_H_

View file

@ -0,0 +1,61 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_CALLBACKS_H_
#define _TAML_CALLBACKS_H_
//-----------------------------------------------------------------------------
class TamlCustomNodes;
class SimObject;
//-----------------------------------------------------------------------------
class TamlCallbacks
{
friend class Taml;
private:
/// Called prior to Taml writing the object.
virtual void onTamlPreWrite( void ) = 0;
/// Called after Taml has finished writing the object.
virtual void onTamlPostWrite( void ) = 0;
/// Called prior to Taml reading the object.
virtual void onTamlPreRead( void ) = 0;
/// Called after Taml has finished reading the object.
/// The custom properties is additionally passed here for object who want to process it at the end of reading.
virtual void onTamlPostRead( const TamlCustomNodes& customNodes ) = 0;
/// Called after Taml has finished reading the object and has added the object to any parent.
virtual void onTamlAddParent( SimObject* pParentObject ) = 0;
/// Called during the writing of the object to allow custom properties to be written.
virtual void onTamlCustomWrite( TamlCustomNodes& customNodes ) = 0;
/// Called during the reading of the object to allow custom properties to be read.
virtual void onTamlCustomRead( const TamlCustomNodes& customNodes ) = 0;
};
#endif // _TAML_CALLBACKS_H_

View file

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_CHILDREN_H_
#define _TAML_CHILDREN_H_
#ifndef _TORQUE_TYPES_H_
#include "platform/types.h"
#endif
//-----------------------------------------------------------------------------
class SimObject;
//-----------------------------------------------------------------------------
class TamlChildren
{
public:
/// Called when Taml attempts to compile a list of children.
virtual U32 getTamlChildCount( void ) const = 0;
/// Called when Taml attempts to compile a list of children.
virtual SimObject* getTamlChild( const U32 childIndex ) const = 0;
/// Called when Taml attempts to populate an objects children during a read.
virtual void addTamlChild( SimObject* pSimObject ) = 0;
};
#endif // _TAML_CHILDREN_H_

View file

@ -0,0 +1,72 @@
//-----------------------------------------------------------------------------
// 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/tamlCustom.h"
#ifndef _TAML_WRITE_NODE_H_
#include "persistence/taml/tamlWriteNode.h"
#endif
//-----------------------------------------------------------------------------
FactoryCache<TamlCustomField> TamlCustomFieldFactory;
FactoryCache<TamlCustomNode> TamlCustomNodeFactory;
//-----------------------------------------------------------------------------
void TamlCustomField::set( const char* pFieldName, const char* pFieldValue )
{
// Sanity!
AssertFatal( pFieldName != NULL, "Field name cannot be NULL." );
AssertFatal( pFieldValue != NULL, "Field value cannot be NULL." );
// Set field name.
mFieldName = StringTable->insert( pFieldName );
#if TORQUE_DEBUG
// Is the field value too big?
if ( dStrlen(pFieldValue) >= sizeof(mFieldValue) )
{
// Yes, so warn!
Con::warnf( "Taml property field '%s' has a value that exceeds then maximum length: '%s'", pFieldName, pFieldValue );
AssertFatal( false, "Field value is too big!" );
return;
}
#endif
// Copy field value.
dStrcpy( mFieldValue, pFieldValue );
}
//-----------------------------------------------------------------------------
void TamlCustomNode::setWriteNode( TamlWriteNode* pWriteNode )
{
// Sanity!
AssertFatal( mNodeName != StringTable->EmptyString(), "Cannot set write node with an empty node name." );
AssertFatal( pWriteNode != NULL, "Write node cannot be NULL." );
AssertFatal( pWriteNode->mpSimObject == mpProxyObject, "Write node does not match existing proxy object." );
AssertFatal( mpProxyWriteNode == NULL, "Field write node must be NULL." );
// Set proxy write node.
mpProxyWriteNode = pWriteNode;
}

View file

@ -0,0 +1,785 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_CUSTOM_H_
#define _TAML_CUSTOM_H_
#ifndef _FACTORY_CACHE_H_
#include "core/factoryCache.h"
#endif
#ifndef _STRINGTABLE_H_
#include "core/stringTable.h"
#endif
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
#ifndef _CONSOLETYPES_H_
#include "console/consoleTypes.h"
#endif
#ifndef B2_MATH_H
//TODO: Look at this
//#include "box2d/Common/b2Math.h"
#endif
#ifndef _COLOR_H_
#include "core/color.h"
#endif
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#include "core/util/safeDelete.h"
#include "math/mMath.h"
//-----------------------------------------------------------------------------
#define MAX_TAML_NODE_FIELDVALUE_LENGTH 2048
//-----------------------------------------------------------------------------
class TamlWriteNode;
class TamlCustomNode;
class TamlCustomField;
extern FactoryCache<TamlCustomNode> TamlCustomNodeFactory;
extern FactoryCache<TamlCustomField> TamlCustomFieldFactory;
typedef Vector<TamlCustomNode*> TamlCustomNodeVector;
typedef Vector<TamlCustomField*> TamlCustomFieldVector;
//-----------------------------------------------------------------------------
class TamlCustomField : public IFactoryObjectReset
{
public:
TamlCustomField()
{
resetState();
}
virtual ~TamlCustomField()
{
// Everything should already be cleared in a state reset.
// Touching any memory here is dangerous as this type is typically
// held in a static factory cache until shutdown at which point
// pretty much anything or everything could be invalid!
}
virtual void resetState( void )
{
mFieldName = StringTable->EmptyString();
*mFieldValue = 0;
}
void set( const char* pFieldName, const char* pFieldValue );
inline void setFieldValue( const char* pFieldName, const ColorI& fieldValue )
{
// Fetch the field value.
const char* pFieldValue = Con::getData( TypeColorI, &const_cast<ColorI&>(fieldValue), 0 );
// Did we get a field value?
if ( pFieldValue == NULL )
{
// No, so warn.
Con::warnf( "Taml: Failed to add node field name '%s' with ColorI value.", pFieldName );
pFieldValue = StringTable->EmptyString();
}
set( pFieldName, pFieldValue );
}
inline void setFieldValue( const char* pFieldName, const ColorF& fieldValue )
{
// Fetch the field value.
const char* pFieldValue = Con::getData( TypeColorF, &const_cast<ColorF&>(fieldValue), 0 );
// Did we get a field value?
if ( pFieldValue == NULL )
{
// No, so warn.
Con::warnf( "Taml: Failed to add node field name '%s' with ColorF value.", pFieldName );
pFieldValue = StringTable->EmptyString();
}
set( pFieldName, pFieldValue );
}
inline void setFieldValue( const char* pFieldName, const Point2I& fieldValue )
{
char fieldValueBuffer[32];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d %d", fieldValue.x, fieldValue.y );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const Point2F& fieldValue )
{
char fieldValueBuffer[32];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g", fieldValue.x, fieldValue.y );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const Point3I& fieldValue )
{
char fieldValueBuffer[32];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d %d %d", fieldValue.x, fieldValue.y, fieldValue.z );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const Point3F& fieldValue )
{
char fieldValueBuffer[32];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g", fieldValue.x, fieldValue.y, fieldValue.z );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const RectF& fieldValue )
{
char fieldValueBuffer[32];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g",
fieldValue.point.x, fieldValue.point.y, fieldValue.extent.x, fieldValue.extent.y);
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const QuatF& fieldValue )
{
char fieldValueBuffer[32];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g", fieldValue.x, fieldValue.y, fieldValue.z, fieldValue.w );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const AngAxisF& fieldValue )
{
char fieldValueBuffer[32];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g %0.5g %.5g %.5g", fieldValue.axis.x, fieldValue.axis.y, fieldValue.axis.z, fieldValue.angle );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const U32 fieldValue )
{
char fieldValueBuffer[16];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const bool fieldValue )
{
char fieldValueBuffer[16];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const S32 fieldValue )
{
char fieldValueBuffer[16];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%d", fieldValue );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const float fieldValue )
{
char fieldValueBuffer[16];
dSprintf( fieldValueBuffer, sizeof(fieldValueBuffer), "%.5g", fieldValue );
set( pFieldName, fieldValueBuffer );
}
inline void setFieldValue( const char* pFieldName, const char* fieldValue )
{
set( pFieldName, fieldValue );
}
inline void getFieldValue( ColorF& fieldValue ) const
{
fieldValue.set( 1.0f, 1.0f, 1.0f, 1.0f );
// Set color.
const char* argv = (char*)mFieldValue;
Con::setData( TypeColorF, &fieldValue, 0, 1, &argv );
}
inline void getFieldValue( ColorI& fieldValue ) const
{
fieldValue.set( 255, 255, 255, 255 );
// Set color.
const char* argv = (char*)mFieldValue;
Con::setData( TypeColorI, &fieldValue, 0, 1, &argv );
}
inline void getFieldValue( Point2I& fieldValue ) const
{
if ( dSscanf( mFieldValue, "%d %d", &fieldValue.x, &fieldValue.y ) != 2 )
{
// Warn.
Con::warnf( "TamlCustomField - Reading point2I but it has an incorrect format: '%s'.", mFieldValue );
}
}
inline void getFieldValue( Point2F& fieldValue ) const
{
if ( dSscanf( mFieldValue, "%g %g", &fieldValue.x, &fieldValue.y ) != 2 )
{
// Warn.
Con::warnf( "TamlCustomField - Reading point2F but it has an incorrect format: '%s'.", mFieldValue );
}
}
inline void getFieldValue( Point3I& fieldValue ) const
{
if ( dSscanf( mFieldValue, "%d %d %d", &fieldValue.x, &fieldValue.y, &fieldValue.z ) != 3 )
{
// Warn.
Con::warnf( "TamlCustomField - Reading point3I but it has an incorrect format: '%s'.", mFieldValue );
}
}
inline void getFieldValue( Point3F& fieldValue ) const
{
if ( dSscanf( mFieldValue, "%g %g %g", &fieldValue.x, &fieldValue.y, &fieldValue.z ) != 3 )
{
// Warn.
Con::warnf( "TamlCustomField - Reading point3F but it has an incorrect format: '%s'.", mFieldValue );
}
}
inline void getFieldValue( RectF& fieldValue ) const
{
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.point.x, &fieldValue.point.y, &fieldValue.extent.x, &fieldValue.extent.y ) != 3 )
{
// Warn.
Con::warnf( "TamlCustomField - Reading RectF but it has an incorrect format: '%s'.", mFieldValue );
}
}
inline void getFieldValue( QuatF& fieldValue ) const
{
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.x, &fieldValue.y, &fieldValue.z, &fieldValue.w ) != 4 )
{
// Warn.
Con::warnf( "TamlCustomField - Reading QuatF but it has an incorrect format: '%s'.", mFieldValue );
}
}
inline void getFieldValue( AngAxisF& fieldValue ) const
{
if ( dSscanf( mFieldValue, "%g %g %g %g", &fieldValue.axis.x, &fieldValue.axis.y, &fieldValue.axis.z, &fieldValue.angle ) != 4 )
{
// Warn.
Con::warnf( "TamlCustomField - Reading AngAxisF but it has an incorrect format: '%s'.", mFieldValue );
}
}
inline void getFieldValue( bool& fieldValue ) const
{
fieldValue = dAtob( mFieldValue );
}
inline void getFieldValue( S32& fieldValue ) const
{
fieldValue = dAtoi( mFieldValue );
}
inline void getFieldValue( U32& fieldValue ) const
{
fieldValue = (U32)dAtoi( mFieldValue );
}
inline void getFieldValue( F32& fieldValue ) const
{
fieldValue = dAtof( mFieldValue );
}
inline const char* getFieldValue( void ) const
{
return mFieldValue;
}
inline StringTableEntry getFieldName( void ) const { return mFieldName; }
bool fieldNameBeginsWith( const char* pComparison ) const
{
const U32 comparisonLength = dStrlen( pComparison );
const U32 fieldNameLength = dStrlen( mFieldName );
if ( comparisonLength == 0 || fieldNameLength == 0 || comparisonLength > fieldNameLength )
return false;
StringTableEntry comparison = StringTable->insert( pComparison );
char fieldNameBuffer[1024];
// Sanity!
AssertFatal( fieldNameLength < sizeof(fieldNameBuffer), "TamlCustomField: Field name is too long." );
dStrcpy( fieldNameBuffer, mFieldName );
fieldNameBuffer[fieldNameLength-1] = 0;
StringTableEntry fieldName = StringTable->insert( fieldNameBuffer );
return ( fieldName == comparison );
}
inline bool isValueEmpty( void ) const { return *mFieldValue == 0; }
private:
StringTableEntry mFieldName;
char mFieldValue[MAX_TAML_NODE_FIELDVALUE_LENGTH];
};
//-----------------------------------------------------------------------------
class TamlCustomNode : public IFactoryObjectReset
{
public:
TamlCustomNode()
{
// Reset proxy object.
// NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
mpProxyWriteNode = NULL;
mpProxyObject = NULL;
resetState();
}
virtual ~TamlCustomNode()
{
// Everything should already be cleared in a state reset.
// Touching any memory here is dangerous as this type is typically
// held in a static factory cache until shutdown at which point
// pretty much anything or everything could be invalid!
}
virtual void resetState( void )
{
// We don't need to delete the write node as it'll get destroyed when the compilation is reset!
mpProxyWriteNode = NULL;
mpProxyObject = NULL;
// Cache the children.
while ( mChildren.size() > 0 )
{
TamlCustomNodeFactory.cacheObject( mChildren.back() );
mChildren.pop_back();
}
// Cache the fields.
while( mFields.size() > 0 )
{
TamlCustomFieldFactory.cacheObject( mFields.back() );
mFields.pop_back();
}
// Reset the node name.
mNodeName = StringTable->EmptyString();
// Reset node text.
mNodeText.resetState();
// Reset the ignore empty flag.
mIgnoreEmpty = false;
}
inline TamlCustomNode* addNode( SimObject* pProxyObject )
{
// Sanity!
AssertFatal( pProxyObject != NULL, "Field object cannot be NULL." );
AssertFatal( mpProxyWriteNode == NULL, "Field write node must be NULL." );
// Create a custom node.
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
// Set node name.
pCustomNode->setNodeName( pProxyObject->getClassName() );
// Set proxy object.
pCustomNode->mpProxyObject = pProxyObject;
// Store node.
mChildren.push_back( pCustomNode );
return pCustomNode;
}
inline TamlCustomNode* addNode( const char* pNodeName, const bool ignoreEmpty = true )
{
// Create a custom node.
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
// Fetch node name.
pCustomNode->setNodeName( pNodeName );
// Set ignore-empty flag.
pCustomNode->setIgnoreEmpty( ignoreEmpty );
// Store node.
mChildren.push_back( pCustomNode );
return pCustomNode;
}
inline void removeNode( const U32 index )
{
// Sanity!
AssertFatal( index < (U32)mChildren.size(), "tamlCustomNode::removeNode() - Index is out of bounds." );
// Cache the custom node.
TamlCustomNodeFactory.cacheObject( mChildren[index] );
// Remove it.
mChildren.erase( index );
}
inline const TamlCustomNode* findNode( const char* pNodeName ) const
{
// Sanity!
AssertFatal( pNodeName != NULL, "Cannot find Taml node name that is NULL." );
// Fetch node name.
StringTableEntry nodeName = StringTable->insert( pNodeName );
// Find node.
for( Vector<TamlCustomNode*>::const_iterator nodeItr = mChildren.begin(); nodeItr != mChildren.end(); ++nodeItr )
{
if ( (*nodeItr)->getNodeName() == nodeName )
return (*nodeItr);
}
return NULL;
}
inline TamlCustomField* addField( const char* pFieldName, const ColorI& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const ColorF& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const Point2I& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const Point2F& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const Point3I& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const Point3F& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const RectF& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const QuatF& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const AngAxisF& fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const U32 fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const bool fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const S32 fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const float fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline TamlCustomField* addField( const char* pFieldName, const char* fieldValue )
{
TamlCustomField* pNodeField = TamlCustomFieldFactory.createObject();
pNodeField->setFieldValue( pFieldName, fieldValue );
return registerField( pNodeField );
}
inline const TamlCustomField* findField( const char* pFieldName ) const
{
// Sanity!
AssertFatal( pFieldName != NULL, "Cannot find Taml field name that is NULL." );
// Fetch field name.
StringTableEntry fieldName = StringTable->insert( pFieldName );
// Find node field.
for( TamlCustomFieldVector::const_iterator fieldItr = mFields.begin(); fieldItr != mFields.end(); ++fieldItr )
{
if ( (*fieldItr)->getFieldName() == fieldName )
return (*fieldItr);
}
return NULL;
}
inline void setNodeName( const char* pNodeName )
{
// Sanity!
AssertFatal( pNodeName != NULL, "Cannot add a NULL node name." );
mNodeName = StringTable->insert( pNodeName );
}
inline StringTableEntry getNodeName( void ) const { return mNodeName; }
void setWriteNode( TamlWriteNode* pWriteNode );
inline void setNodeText( const char* pNodeText )
{
AssertFatal( dStrlen( pNodeText ) < MAX_TAML_NODE_FIELDVALUE_LENGTH, "Custom node text is too long." );
mNodeText.set( StringTable->EmptyString(), pNodeText );
}
inline const TamlCustomField& getNodeTextField( void ) const { return mNodeText; }
inline TamlCustomField& getNodeTextField( void ) { return mNodeText; }
inline const Vector<TamlCustomNode*>& getChildren( void ) const { return mChildren; }
inline const TamlCustomFieldVector& getFields( void ) const { return mFields; }
inline bool isProxyObject( void ) const { return mpProxyObject != NULL; }
template<typename T> T* getProxyObject( const bool deleteIfNotType ) const
{
// Return nothing if no proxy object.
if ( mpProxyObject == NULL )
return NULL;
// Cast object to specified type.
T* pTypeCast = dynamic_cast<T*>( mpProxyObject );
// Destroy the object if not the specified type and requested to do so.
if ( deleteIfNotType && pTypeCast == NULL )
{
mpProxyObject->deleteObject();
return NULL;
}
return pTypeCast;
}
inline const TamlWriteNode* getProxyWriteNode( void ) const { return mpProxyWriteNode; }
inline bool isEmpty( void ) const { return mNodeText.isValueEmpty() && mFields.size() == 0 && mChildren.size() == 0; }
inline void setIgnoreEmpty( const bool ignoreEmpty ) { mIgnoreEmpty = ignoreEmpty; }
inline bool getIgnoreEmpty( void ) const { return mIgnoreEmpty; }
private:
inline TamlCustomField* registerField( TamlCustomField* pCustomField )
{
#if TORQUE_DEBUG
// Ensure a field name conflict does not exist.
for( Vector<TamlCustomField*>::iterator nodeFieldItr = mFields.begin(); nodeFieldItr != mFields.end(); ++nodeFieldItr )
{
// Skip if field name is not the same.
if ( pCustomField->getFieldName() != (*nodeFieldItr)->getFieldName() )
continue;
// Warn!
Con::warnf("Conflicting Taml node field name of '%s' in node '%s'.", pCustomField->getFieldName(), mNodeName );
// Cache node field.
TamlCustomFieldFactory.cacheObject( pCustomField );
return NULL;
}
// Ensure the field value is not too long.
if ( dStrlen( pCustomField->getFieldValue() ) >= MAX_TAML_NODE_FIELDVALUE_LENGTH )
{
// Warn.
Con::warnf("Taml field name '%s' has a field value that is too long (Max:%d): '%s'.",
pCustomField->getFieldName(),
MAX_TAML_NODE_FIELDVALUE_LENGTH,
pCustomField->getFieldValue() );
// Cache node field.
TamlCustomFieldFactory.cacheObject( pCustomField );
return NULL;
}
#endif
// Store node field.
mFields.push_back( pCustomField );
return pCustomField;
}
inline TamlCustomField* createField( void ) const { return TamlCustomFieldFactory.createObject(); }
private:
StringTableEntry mNodeName;
TamlCustomField mNodeText;
Vector<TamlCustomNode*> mChildren;
TamlCustomFieldVector mFields;
bool mIgnoreEmpty;
SimObject* mpProxyObject;
TamlWriteNode* mpProxyWriteNode;
};
//-----------------------------------------------------------------------------
class TamlCustomNodes : public IFactoryObjectReset
{
public:
TamlCustomNodes()
{
}
virtual ~TamlCustomNodes()
{
resetState();
}
virtual void resetState( void )
{
// Cache the nodes.
while ( mNodes.size() > 0 )
{
TamlCustomNodeFactory.cacheObject( mNodes.back() );
mNodes.pop_back();
}
}
inline TamlCustomNode* addNode( const char* pNodeName, const bool ignoreEmpty = true )
{
// Create a custom node.
TamlCustomNode* pCustomNode = TamlCustomNodeFactory.createObject();
// Set node name.
pCustomNode->setNodeName( pNodeName );
// Set ignore-empty flag.
pCustomNode->setIgnoreEmpty( ignoreEmpty );
#if TORQUE_DEBUG
// Ensure a node name conflict does not exist.
for( TamlCustomNodeVector::iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
{
// Skip if node name is not the same.
if ( pCustomNode->getNodeName() != (*nodeItr)->getNodeName() )
continue;
// Warn!
Con::warnf("Conflicting Taml custom node name of '%s'.", pNodeName );
// Cache node.
TamlCustomNodeFactory.cacheObject( pCustomNode );
return NULL;
}
#endif
// Store node.
mNodes.push_back( pCustomNode );
return pCustomNode;
}
inline void removeNode( const U32 index )
{
// Sanity!
AssertFatal( index < (U32)mNodes.size(), "tamlCustomNode::removeNode() - Index is out of bounds." );
// Cache the custom node.
TamlCustomNodeFactory.cacheObject( mNodes[index] );
// Remove it.
mNodes.erase( index );
}
inline const TamlCustomNode* findNode( const char* pNodeName ) const
{
// Sanity!
AssertFatal( pNodeName != NULL, "Cannot find Taml node name that is NULL." );
// Fetch node name.
StringTableEntry nodeName = StringTable->insert( pNodeName );
// Find node.
for( Vector<TamlCustomNode*>::const_iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
{
if ( (*nodeItr)->getNodeName() == nodeName )
return (*nodeItr);
}
return NULL;
}
inline const TamlCustomNodeVector& getNodes( void ) const { return mNodes; }
private:
TamlCustomNodeVector mNodes;
};
#endif // _TAML_CUSTOM_H_

View file

@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_PARSER_H_
#define _TAML_PARSER_H_
#include "core/stringTable.h"
//-----------------------------------------------------------------------------
class TamlVisitor;
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlParser
{
public:
TamlParser() :
mParsingFilename(StringTable->EmptyString())
{}
virtual ~TamlParser() {}
/// Whether the parser can change a property or not.
virtual bool canChangeProperty( void ) = 0;
/// Accept visitor.
virtual bool accept( const char* pFilename, TamlVisitor& visitor ) = 0;
/// Filename.
inline void setParsingFilename( const char* pFilename ) { mParsingFilename = StringTable->insert(pFilename); }
inline StringTableEntry getParsingFilename( void ) const { return mParsingFilename; }
private:
StringTableEntry mParsingFilename;
};
#endif // _TAML_PARSER_H_

View file

@ -0,0 +1,111 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_VISITOR_H_
#define _TAML_VISITOR_H_
#include "core/stringTable.h"
#include "core/strings/stringFunctions.h"
//-----------------------------------------------------------------------------
class TamlParser;
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlVisitor
{
public:
/// Visitor property state.
struct PropertyState
{
private:
StringTableEntry mObjectName;
StringTableEntry mPropertyName;
char mPropertyValue[4096];
bool mPropertyValueDirty;
bool mIsRootObject;
public:
PropertyState()
{
mObjectName = StringTable->EmptyString();
mPropertyName = StringTable->EmptyString();
*mPropertyValue = 0;
mPropertyValueDirty = false;
mIsRootObject = false;
}
inline void setObjectName( const char* pObjectName, const bool rootObject )
{
mObjectName = StringTable->insert( pObjectName );
mIsRootObject = rootObject;
}
inline StringTableEntry getObjectName( void ) const { return mObjectName; }
inline bool isRootObject( void ) const { return mIsRootObject; }
inline void setProperty( const char* pPropertyName, const char* pPropertyValue )
{
// Set property name.
mPropertyName = StringTable->insert( pPropertyName );
// Format property value.
dSprintf( mPropertyValue, sizeof(mPropertyValue), "%s", pPropertyValue );
// Flag as not dirty.
mPropertyValueDirty = false;
}
inline void updatePropertyValue( const char* pPropertyValue )
{
// Update property value.
dSprintf( mPropertyValue, sizeof(mPropertyValue), "%s", pPropertyValue );
// Flag as dirty.
mPropertyValueDirty = true;
}
inline StringTableEntry getPropertyName( void ) const { return mPropertyName; }
inline const char* getPropertyValue( void ) const { return mPropertyValue; }
inline void resetPropertyValueDirty( void ) { mPropertyValueDirty = false; }
inline bool getPropertyValueDirty( void ) const { return mPropertyValueDirty; }
};
public:
TamlVisitor() {}
virtual ~TamlVisitor() {}
/// Whether the visitor wants to perform property changes.
/// This allows a check against the parser which may not allow changes.
virtual bool wantsPropertyChanges( void ) = 0;
/// Whether the visitor wants to visit the root node only.
virtual bool wantsRootOnly( void ) = 0;
/// The state of the visited property.
virtual bool visit( const TamlParser& parser, PropertyState& propertyState ) = 0;
};
#endif // _TAML_VISITOR_H_

View file

@ -0,0 +1,66 @@
//-----------------------------------------------------------------------------
// 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/tamlWriteNode.h"
// Debug Profiling.
#include "platform/profiler.h"
//-----------------------------------------------------------------------------
void TamlWriteNode::resetNode( void )
{
// Debug Profiling.
PROFILE_SCOPE(TamlWriteNode_ResetNode);
// Clear fields.
for( Vector<TamlWriteNode::FieldValuePair*>::iterator itr = mFields.begin(); itr != mFields.end(); ++itr )
{
delete (*itr)->mpValue;
}
mFields.clear();
// Clear children.
if ( mChildren != NULL )
{
for( Vector<TamlWriteNode*>::iterator itr = mChildren->begin(); itr != mChildren->end(); ++itr )
{
(*itr)->resetNode();
}
mChildren->clear();
delete mChildren;
mChildren = NULL;
}
mRefId = 0;
mRefToNode = NULL;
mChildren = NULL;
mpObjectName = NULL;
mpSimObject = NULL;
// Reset callbacks.
mpTamlCallbacks = NULL;
// Reset custom nodes.
mCustomNodes.resetState();
}

View file

@ -0,0 +1,116 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_WRITE_NODE_H_
#define _TAML_WRITE_NODE_H_
#ifndef _TAML_CUSTOM_H_
#include "persistence/taml/tamlCustom.h"
#endif
#ifndef _SIMBASE_H_
#include "console/simBase.h"
#endif
#ifndef _VECTOR_H_
#include "core/util/tVector.h"
#endif
//-----------------------------------------------------------------------------
class TamlCallbacks;
//-----------------------------------------------------------------------------
class TamlWriteNode
{
public:
class FieldValuePair
{
public:
FieldValuePair( StringTableEntry name, const char* pValue )
{
// Set the field name.
mName = name;
// Allocate and copy the value.
mpValue = new char[ dStrlen(pValue)+1 ];
dStrcpy( (char *)mpValue, pValue );
}
StringTableEntry mName;
const char* mpValue;
};
public:
TamlWriteNode()
{
// NOTE: This MUST be done before the state is reset otherwise we'll be touching uninitialized stuff.
mRefToNode = NULL;
mpSimObject = NULL;
mpTamlCallbacks = NULL;
mpObjectName = NULL;
mChildren = NULL;
resetNode();
}
void set( SimObject* pSimObject )
{
// Sanity!
AssertFatal( pSimObject != NULL, "Cannot set a write node with a NULL sim object." );
// Reset the node.
resetNode();
// Set sim object.
mpSimObject = pSimObject;
// Fetch name.
const char* pObjectName = pSimObject->getName();
// Assign name usage.
if ( pObjectName != NULL &&
pObjectName != StringTable->EmptyString() &&
dStrlen( pObjectName ) > 0 )
{
mpObjectName = pObjectName;
}
// Find Taml callbacks.
mpTamlCallbacks = dynamic_cast<TamlCallbacks*>( mpSimObject );
}
void resetNode( void );
U32 mRefId;
TamlWriteNode* mRefToNode;
SimObject* mpSimObject;
TamlCallbacks* mpTamlCallbacks;
const char* mpObjectName;
Vector<TamlWriteNode::FieldValuePair*> mFields;
Vector<TamlWriteNode*>* mChildren;
TamlCustomNodes mCustomNodes;
};
#endif // _TAML_WRITE_NODE_H_

View file

@ -0,0 +1,311 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_SCRIPTBINDING_H
#define _TAML_SCRIPTBINDING_H
#include "console/engineAPI.h"
#include "persistence/taml/taml.h"
DefineEngineMethod(Taml, setFormat, void, (const char* formatName), , "(format) - Sets the format that Taml should use to read/write.\n"
"@param format The format to use: 'xml' or 'binary'.\n"
"@return No return value.")
{
// Fetch format mode.
const Taml::TamlFormatMode formatMode = Taml::getFormatModeEnum(formatName);
// Was the format valid?
if ( formatMode == Taml::InvalidFormat )
{
// No, so warn.
Con::warnf( "Taml::setFormat() - Invalid format mode used: '%s'.", formatName );
return;
}
// Set format mode.
object->setFormatMode( formatMode );
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, getFormat, const char*, (), , "() - Gets the format that Taml should use to read/write.\n"
"@return The format that Taml should use to read/write.")
{
// Fetch format mode.
return Taml::getFormatModeDescription(object->getFormatMode());
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, setAutoFormat, void, (bool autoFormat), , "(autoFormat) Sets whether the format type is automatically determined by the filename extension or not.\n"
"@param autoFormat Whether the format type is automatically determined by the filename extension or not.\n"
"@return No return value." )
{
object->setAutoFormat( autoFormat );
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, getAutoFormat, bool, (), , "() Gets whether the format type is automatically determined by the filename extension or not.\n"
"@return Whether the format type is automatically determined by the filename extension or not." )
{
return object->getAutoFormat();
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, setWriteDefaults, void, (bool writeDefaults), , "(writeDefaults) Sets whether to write static fields that are at their default or not.\n"
"@param writeDefaults Whether to write static fields that are at their default or not.\n"
"@return No return value." )
{
object->setWriteDefaults( writeDefaults );
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, getWriteDefaults, bool, (), , "() Gets whether to write static fields that are at their default or not.\n"
"@return Whether to write static fields that are at their default or not." )
{
return object->getWriteDefaults();
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, setProgenitorUpdate, void, (bool progenitorUpdate), , "(progenitorUpdate) Sets whether to update each type instances file-progenitor or not.\n"
"If not updating then the progenitor stay as the script that executed the call to Taml.\n"
"@param progenitorUpdate Whether to update each type instances file-progenitor or not.\n"
"@return No return value." )
{
object->setProgenitorUpdate( progenitorUpdate );
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, getProgenitorUpdate, bool, (), , "() Gets whether to update each type instances file-progenitor or not.\n"
"@return Whether to update each type instances file-progenitor or not." )
{
return object->getProgenitorUpdate();
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, setAutoFormatXmlExtension, void, (const char* extension), , "(extension) Sets the extension (end of filename) used to detect the XML format.\n"
"@param extension The extension (end of filename) used to detect the XML format.\n"
"@return No return value." )
{
object->setAutoFormatXmlExtension( extension );
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, getAutoFormatXmlExtension, const char*, (), , "() Gets the extension (end of filename) used to detect the XML format.\n"
"@return The extension (end of filename) used to detect the XML format." )
{
return object->getAutoFormatXmlExtension();
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, setAutoFormatBinaryExtension, void, (const char* extension), , "(extension) Sets the extension (end of filename) used to detect the Binary format.\n"
"@param extension The extension (end of filename) used to detect the Binary format.\n"
"@return No return value." )
{
object->setAutoFormatBinaryExtension( extension );
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, getAutoFormatBinaryExtension, const char*, (), , "() Gets the extension (end of filename) used to detect the Binary format.\n"
"@return The extension (end of filename) used to detect the Binary format." )
{
return object->getAutoFormatBinaryExtension();
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, setBinaryCompression, void, (bool compressed), , "(compressed) - Sets whether ZIP compression is used on binary formatting or not.\n"
"@param compressed Whether compression is on or off.\n"
"@return No return value.")
{
// Set compression.
object->setBinaryCompression( compressed );
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, getBinaryCompression, bool, (), , "() - Gets whether ZIP compression is used on binary formatting or not.\n"
"@return Whether ZIP compression is used on binary formatting or not.")
{
// Fetch compression.
return object->getBinaryCompression();
}
//-----------------------------------------------------------------------------
/*! Sets whether to write JSON that is strictly compatible with RFC4627 or not.
@param jsonStrict Whether to write JSON that is strictly compatible with RFC4627 or not.
@return No return value.
*/
DefineEngineMethod(Taml, setJSONStrict, void, (bool strict), , "(jsonStrict) - Sets whether to write JSON that is strictly compatible with RFC4627 or not."
"@param jsonStrict Whether to write JSON that is strictly compatible with RFC4627 or not."
"@return No return value.")
{
// Set JSON Strict.
object->setJSONStrict( strict );
}
//-----------------------------------------------------------------------------
/*! Gets whether to write JSON that is strictly compatible with RFC4627 or not.
@return whether to write JSON that is strictly compatible with RFC4627 or not.
*/
DefineEngineMethod(Taml, getJSONStrict, bool, (), , "() - Gets whether to write JSON that is strictly compatible with RFC4627 or not."
"@return whether to write JSON that is strictly compatible with RFC4627 or not.")
{
// Get RFC strict.
return object->getJSONStrict();
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, write, bool, (SimObject* obj, const char* filename), , "(object, filename) - Writes an object to a file using Taml.\n"
"@param object The object to write.\n"
"@param filename The filename to write to.\n"
"@return Whether the write was successful or not.")
{
// Did we find the object?
if ( obj == NULL )
{
// No, so warn.
Con::warnf( "Taml::write() - Tried to write a NULL object to file '%s'.", filename );
return false;
}
return object->write( obj, filename );
}
//-----------------------------------------------------------------------------
DefineEngineMethod(Taml, read, SimObject*, (const char* filename), , "(filename) - Read an object from a file using Taml.\n"
"@param filename The filename to read from.\n"
"@return (Object) The object read from the file or an empty string if read failed.")
{
// Read object.
SimObject* pSimObject = object->read( filename );
// Did we find the object?
if ( pSimObject == NULL )
{
// No, so warn.
Con::warnf( "Taml::read() - Could not read object from file '%s'.", filename );
return NULL;
}
return pSimObject;
}
//-----------------------------------------------------------------------------
DefineEngineFunction(TamlWrite, bool, (SimObject* simObject, const char* filename, const char* format, bool compressed),
("xml", true),
"(object, filename, [format], [compressed]) - Writes an object to a file using Taml.\n"
"@param object The object to write.\n"
"@param filename The filename to write to.\n"
"@param format The file format to use. Optional: Defaults to 'xml'. Can be set to 'binary'.\n"
"@param compressed Whether ZIP compression is used on binary formatting or not. Optional: Defaults to 'true'.\n"
"@return Whether the write was successful or not.")
{
// Did we find the object?
if ( simObject == NULL )
{
// No, so warn.
//Con::warnf( "TamlWrite() - Could not find object '%s' to write to file '%s'.", simObject->getIdString(), filename );
Con::warnf( "TamlWrite() - Could not find object to write to file '%s'.", filename );
return false;
}
Taml taml;
taml.setFormatMode( Taml::getFormatModeEnum(format) );
// Yes, so is the format mode binary?
if ( taml.getFormatMode() == Taml::BinaryFormat )
{
// Yes, so set binary compression.
taml.setBinaryCompression( compressed );
}
else
{
// No, so warn.
Con::warnf( "TamlWrite() - Setting binary compression is only valid for XML formatting." );
}
// Turn-off auto-formatting.
taml.setAutoFormat( false );
// Write.
return taml.write( simObject, filename );
}
//-----------------------------------------------------------------------------
DefineEngineFunction(TamlRead, const char*, (const char* filename, const char* format), ("xml"), "(filename, [format]) - Read an object from a file using Taml.\n"
"@param filename The filename to read from.\n"
"@param format The file format to use. Optional: Defaults to 'xml'. Can be set to 'binary'.\n"
"@return (Object) The object read from the file or an empty string if read failed.")
{
// Set the format mode.
Taml taml;
// Yes, so set it.
taml.setFormatMode( Taml::getFormatModeEnum(format) );
// Turn-off auto-formatting.
taml.setAutoFormat( false );
// Read object.
SimObject* pSimObject = taml.read( filename );
// Did we find the object?
if ( pSimObject == NULL )
{
// No, so warn.
Con::warnf( "TamlRead() - Could not read object from file '%s'.", filename );
return StringTable->EmptyString();
}
return pSimObject->getIdString();
}
//-----------------------------------------------------------------------------
DefineEngineFunction(GenerateTamlSchema, bool, (), , "() - Generate a TAML schema file of all engine types.\n"
"The schema file is specified using the console variable '" TAML_SCHEMA_VARIABLE "'.\n"
"@return Whether the schema file was writtent or not." )
{
// Generate the schema.
return Taml::generateTamlSchema();
}
#endif _TAML_SCRIPTBINDING_H

View file

@ -0,0 +1,193 @@
//-----------------------------------------------------------------------------
// 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/xml/tamlXmlParser.h"
#include "persistence/taml/tamlVisitor.h"
#include "console/console.h"
// Debug Profiling.
#include "platform/profiler.h"
#ifndef _FILESTREAM_H_
#include "core/stream/fileStream.h"
#endif
//-----------------------------------------------------------------------------
bool TamlXmlParser::accept( const char* pFilename, TamlVisitor& visitor )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlParser_Accept);
// Sanity!
AssertFatal( pFilename != NULL, "Cannot parse a NULL filename." );
// Expand the file-path.
char filenameBuffer[1024];
// TODO: Make sure this is a proper substitute for
// Con::expandPath (T2D)
Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename );
/** T2D uses a custom version of TinyXML that supports FileStream.
* We don't so we can't do this
*
FileStream stream;
#ifdef TORQUE_OS_ANDROID
if (strlen(pFilename) > strlen(filenameBuffer)) {
strcpy(filenameBuffer, pFilename);
}
#endif
// File open for read?
if ( !stream.open( filenameBuffer, Torque::FS::File::AccessMode::Read ) )
{
// No, so warn.
Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for parse.", filenameBuffer );
return false;
}
*/
TiXmlDocument xmlDocument;
// Load document from stream.
if ( !xmlDocument.LoadFile( filenameBuffer ) )
{
// Warn!
Con::warnf("TamlXmlParser: Could not load Taml XML file from stream.");
return false;
}
// Close the stream.
// stream.close();
// Set parsing filename.
setParsingFilename( filenameBuffer );
// Flag document as not dirty.
mDocumentDirty = false;
// Parse root element.
parseElement( xmlDocument.RootElement(), visitor );
// Reset parsing filename.
setParsingFilename( StringTable->EmptyString() );
// Finish if the document is not dirty.
if ( !mDocumentDirty )
return true;
// Open for write?
/*if ( !stream.open( filenameBuffer, FileStream::StreamWrite ) )
{
// No, so warn.
Con::warnf("TamlXmlParser::parse() - Could not open filename '%s' for write.", filenameBuffer );
return false;
}*/
// Yes, so save the document.
if ( !xmlDocument.SaveFile( filenameBuffer ) )
{
// Warn!
Con::warnf("TamlXmlParser: Could not save Taml XML document.");
return false;
}
// Close the stream.
//stream.close();
return true;
}
//-----------------------------------------------------------------------------
inline bool TamlXmlParser::parseElement( TiXmlElement* pXmlElement, TamlVisitor& visitor )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlParser_ParseElement);
// Parse attributes (stop processing if instructed).
if ( !parseAttributes( pXmlElement, visitor ) )
return false;
// Finish if only the root is needed.
if ( visitor.wantsRootOnly() )
return false;
// Fetch any children.
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
// Do we have any element children?
if ( pChildXmlNode != NULL && pChildXmlNode->Type() == TiXmlNode::TINYXML_ELEMENT )
{
// Iterate children.
for ( TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode ); pChildXmlElement; pChildXmlElement = pChildXmlElement->NextSiblingElement() )
{
// Parse element (stop processing if instructed).
if ( !parseElement( pChildXmlElement, visitor ) )
return false;
}
}
return true;
}
//-----------------------------------------------------------------------------
inline bool TamlXmlParser::parseAttributes( TiXmlElement* pXmlElement, TamlVisitor& visitor )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlParser_ParseAttribute);
// Calculate if element is at the root or not.
const bool isRoot = pXmlElement->GetDocument()->RootElement() == pXmlElement;
// Create a visitor property state.
TamlVisitor::PropertyState propertyState;
propertyState.setObjectName( pXmlElement->Value(), isRoot );
// Iterate attributes.
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
{
// Configure property state.
propertyState.setProperty( pAttribute->Name(), pAttribute->Value() );
// Visit this attribute.
const bool visitStatus = visitor.visit( *this, propertyState );
// Was the property value changed?
if ( propertyState.getPropertyValueDirty() )
{
// Yes, so update the attribute.
pAttribute->SetValue( propertyState.getPropertyValue() );
// Flag the document as dirty.
mDocumentDirty = true;
}
// Finish if requested.
if ( !visitStatus )
return false;
}
return true;
}

View file

@ -0,0 +1,57 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_XMLPARSER_H_
#define _TAML_XMLPARSER_H_
#ifndef _TAML_PARSER_H_
#include "persistence/taml/tamlParser.h"
#endif
#ifndef TINYXML_INCLUDED
#include "tinyXML/tinyxml.h"
#endif
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlXmlParser : public TamlParser
{
public:
TamlXmlParser() {}
virtual ~TamlXmlParser() {}
/// Whether the parser can change a property or not.
virtual bool canChangeProperty( void ) { return true; }
/// Accept visitor.
virtual bool accept( const char* pFilename, TamlVisitor& visitor );
private:
inline bool parseElement( TiXmlElement* pXmlElement, TamlVisitor& visitor );
inline bool parseAttributes( TiXmlElement* pXmlElement, TamlVisitor& visitor );
bool mDocumentDirty;
};
#endif // _TAML_XMLPARSER_H_

View file

@ -0,0 +1,484 @@
//-----------------------------------------------------------------------------
// 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/xml/tamlXmlReader.h"
// Debug Profiling.
#include "platform/profiler.h"
#include "persistence/taml/fsTinyxml.h"
//-----------------------------------------------------------------------------
SimObject* TamlXmlReader::read( FileStream& stream )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlReader_Read);
// Create document.
fsTiXmlDocument xmlDocument;
// Load document from stream.
if ( !xmlDocument.LoadFile( stream ) )
{
// Warn!
Con::warnf("Taml: Could not load Taml XML file from stream.");
return NULL;
}
// Parse root element.
SimObject* pSimObject = parseElement( xmlDocument.RootElement() );
// Reset parse.
resetParse();
return pSimObject;
}
//-----------------------------------------------------------------------------
void TamlXmlReader::resetParse( void )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlReader_ResetParse);
// Clear object reference map.
mObjectReferenceMap.clear();
}
//-----------------------------------------------------------------------------
SimObject* TamlXmlReader::parseElement( TiXmlElement* pXmlElement )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlReader_ParseElement);
SimObject* pSimObject = NULL;
// Fetch element name.
StringTableEntry typeName = StringTable->insert( pXmlElement->Value() );
// Fetch reference to Id.
const U32 tamlRefToId = getTamlRefToId( pXmlElement );
// 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( "Taml: 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( pXmlElement );
#ifdef TORQUE_DEBUG
// Format the type location.
char typeLocationBuffer[64];
dSprintf( typeLocationBuffer, sizeof(typeLocationBuffer), "Taml [format='xml' row=%d column=%d]", pXmlElement->Row(), pXmlElement->Column() );
// Create type.
pSimObject = Taml::createType( typeName, mpTaml, typeLocationBuffer );
#else
// Create type.
pSimObject = Taml::createType( typeName, mpTaml );
#endif
// Finish if we couldn't create the type.
if ( pSimObject == NULL )
return NULL;
// Find Taml callbacks.
TamlCallbacks* pCallbacks = dynamic_cast<TamlCallbacks*>( pSimObject );
// Are there any Taml callbacks?
if ( pCallbacks != NULL )
{
// Yes, so call it.
mpTaml->tamlPreRead( pCallbacks );
}
// Parse attributes.
parseAttributes( pXmlElement, pSimObject );
// Fetch object name.
StringTableEntry objectName = StringTable->insert( getTamlObjectName( pXmlElement ) );
// 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.
#ifdef TORQUE_DEBUG
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 );
#else
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 );
#endif
}
}
// Do we have a reference Id?
if ( tamlRefId != 0 )
{
// Yes, so insert reference.
mObjectReferenceMap.insertUnique( tamlRefId, pSimObject );
}
// Fetch any children.
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
TamlCustomNodes customProperties;
// Do we have any element children?
if ( pChildXmlNode != NULL )
{
// Fetch the Taml children.
TamlChildren* pChildren = dynamic_cast<TamlChildren*>( pSimObject );
// Fetch any container child class specifier.
AbstractClassRep* pContainerChildClass = pSimObject->getClassRep()->getContainerChildClass( true );
// Iterate siblings.
do
{
// Fetch element.
TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
// Move to next sibling.
pChildXmlNode = pChildXmlNode->NextSibling();
// Skip if this is not an element?
if ( pChildXmlElement == NULL )
continue;
// Is this a standard child element?
if ( dStrchr( pChildXmlElement->Value(), '.' ) == NULL )
{
// Is this a Taml child?
if ( pChildren == NULL )
{
// No, so warn.
Con::warnf("Taml: Child element '%s' found under parent '%s' but object cannot have children.",
pChildXmlElement->Value(),
pXmlElement->Value() );
// Skip.
continue;
}
// Yes, so parse child element.
SimObject* pChildSimObject = parseElement( pChildXmlElement );
// Skip if the child was not created.
if ( pChildSimObject == NULL )
continue;
// 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: 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;
// Skip.
continue;
}
}
// 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 );
}
}
else
{
// No, so parse custom element.
parseCustomElement( pChildXmlElement, customProperties );
}
}
while( pChildXmlNode != NULL );
// Call custom read.
mpTaml->tamlCustomRead( pCallbacks, customProperties );
}
// Are there any Taml callbacks?
if ( pCallbacks != NULL )
{
// Yes, so call it.
mpTaml->tamlPostRead( pCallbacks, customProperties );
}
// Return object.
return pSimObject;
}
//-----------------------------------------------------------------------------
void TamlXmlReader::parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlReader_ParseAttributes);
// Sanity!
AssertFatal( pSimObject != NULL, "Taml: Cannot parse attributes on a NULL object." );
// Iterate attributes.
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
{
// Insert attribute name.
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
// Ignore if this is a Taml attribute.
if ( attributeName == tamlRefIdName ||
attributeName == tamlRefToIdName ||
attributeName == tamlNamedObjectName )
continue;
// Set the field.
pSimObject->setPrefixedDataField(attributeName, NULL, pAttribute->Value());
}
}
//-----------------------------------------------------------------------------
void TamlXmlReader::parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& customNodes )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlReader_ParseCustomElement);
// Is this a standard child element?
const char* pPeriod = dStrchr( pXmlElement->Value(), '.' );
// Sanity!
AssertFatal( pPeriod != NULL, "Parsing extended element but no period character found." );
// Fetch any custom XML node.
TiXmlNode* pCustomXmlNode = pXmlElement->FirstChild();
// Finish is no XML node exists.
if ( pCustomXmlNode == NULL )
return;
// Yes, so add custom node.
TamlCustomNode* pCustomNode = customNodes.addNode( pPeriod+1 );
do
{
// Fetch element.
TiXmlElement* pCustomXmlElement = dynamic_cast<TiXmlElement*>( pCustomXmlNode );
// Move to next sibling.
pCustomXmlNode = pCustomXmlNode->NextSibling();
// Skip if this is not an element.
if ( pCustomXmlElement == NULL )
continue;
// Parse custom node.
parseCustomNode( pCustomXmlElement, pCustomNode );
}
while ( pCustomXmlNode != NULL );
}
//-----------------------------------------------------------------------------
void TamlXmlReader::parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode )
{
// Is the node a proxy object?
if ( getTamlRefId( pXmlElement ) != 0 || getTamlRefToId( pXmlElement ) != 0 )
{
// Yes, so parse proxy object.
SimObject* pProxyObject = parseElement( pXmlElement );
// Add child node.
pCustomNode->addNode( pProxyObject );
return;
}
// Yes, so add child node.
TamlCustomNode* pChildNode = pCustomNode->addNode( pXmlElement->Value() );
// Iterate attributes.
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
{
// Insert attribute name.
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
// Skip if a Taml reference attribute.
if ( attributeName == tamlRefIdName || attributeName == tamlRefToIdName )
continue;
// Add node field.
pChildNode->addField( attributeName, pAttribute->Value() );
}
// Fetch any element text.
const char* pElementText = pXmlElement->GetText();
// Do we have any element text?
if ( pElementText != NULL )
{
// Yes, so store it.
pChildNode->setNodeText( pElementText );
}
// Fetch any children.
TiXmlNode* pChildXmlNode = pXmlElement->FirstChild();
// Do we have any element children?
if ( pChildXmlNode != NULL )
{
do
{
// Yes, so fetch child element.
TiXmlElement* pChildXmlElement = dynamic_cast<TiXmlElement*>( pChildXmlNode );
// Move to next sibling.
pChildXmlNode = pChildXmlNode->NextSibling();
// Skip if this is not an element.
if ( pChildXmlElement == NULL )
continue;
// Parse custom node.
parseCustomNode( pChildXmlElement, pChildNode );
}
while( pChildXmlNode != NULL );
}
}
//-----------------------------------------------------------------------------
U32 TamlXmlReader::getTamlRefId( TiXmlElement* pXmlElement )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlReader_GetTamlRefId);
// Iterate attributes.
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
{
// Insert attribute name.
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
// Skip if not the correct attribute.
if ( attributeName != tamlRefIdName )
continue;
// Return it.
return dAtoi( pAttribute->Value() );
}
// Not found.
return 0;
}
//-----------------------------------------------------------------------------
U32 TamlXmlReader::getTamlRefToId( TiXmlElement* pXmlElement )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlReader_GetTamlRefToId);
// Iterate attributes.
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
{
// Insert attribute name.
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
// Skip if not the correct attribute.
if ( attributeName != tamlRefToIdName )
continue;
// Return it.
return dAtoi( pAttribute->Value() );
}
// Not found.
return 0;
}
//-----------------------------------------------------------------------------
const char* TamlXmlReader::getTamlObjectName( TiXmlElement* pXmlElement )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlReader_GetTamlObjectName);
// Iterate attributes.
for ( TiXmlAttribute* pAttribute = pXmlElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->Next() )
{
// Insert attribute name.
StringTableEntry attributeName = StringTable->insert( pAttribute->Name() );
// Skip if not the correct attribute.
if ( attributeName != tamlNamedObjectName )
continue;
// Return it.
return pAttribute->Value();
}
// Not found.
return NULL;
}

View file

@ -0,0 +1,73 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_XMLREADER_H_
#define _TAML_XMLREADER_H_
#ifndef _TDICTIONARY_H_
#include "core/util/tDictionary.h"
#endif
#ifndef _TAML_H_
#include "persistence/taml/taml.h"
#endif
#ifndef TINYXML_INCLUDED
#include "tinyXML/tinyxml.h"
#endif
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlXmlReader
{
public:
TamlXmlReader( Taml* pTaml ) :
mpTaml( pTaml )
{}
virtual ~TamlXmlReader() {}
/// Read.
SimObject* read( FileStream& stream );
private:
Taml* mpTaml;
typedef HashTable<SimObjectId, SimObject*> typeObjectReferenceHash;
typeObjectReferenceHash mObjectReferenceMap;
private:
void resetParse( void );
SimObject* parseElement( TiXmlElement* pXmlElement );
void parseAttributes( TiXmlElement* pXmlElement, SimObject* pSimObject );
void parseCustomElement( TiXmlElement* pXmlElement, TamlCustomNodes& pCustomNode );
void parseCustomNode( TiXmlElement* pXmlElement, TamlCustomNode* pCustomNode );
U32 getTamlRefId( TiXmlElement* pXmlElement );
U32 getTamlRefToId( TiXmlElement* pXmlElement );
const char* getTamlObjectName( TiXmlElement* pXmlElement );
};
#endif // _TAML_XMLREADER_H_

View file

@ -0,0 +1,301 @@
//-----------------------------------------------------------------------------
// 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/xml/tamlXmlWriter.h"
// Debug Profiling.
#include "platform/profiler.h"
#include "persistence/taml/fsTinyxml.h"
//-----------------------------------------------------------------------------
bool TamlXmlWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlWriter_Write);
// Create document.
fsTiXmlDocument xmlDocument;
// Compile the root element.
TiXmlElement* pRootElement = compileElement( pTamlWriteNode );
// Fetch any TAML Schema file reference.
const char* pTamlSchemaFile = Con::getVariable( TAML_SCHEMA_VARIABLE );
// Do we have a schema file reference?
if ( pTamlSchemaFile != NULL && *pTamlSchemaFile != 0 )
{
// Yes, so add namespace attribute to root.
pRootElement->SetAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
// Expand the file-path reference.
char schemaFilePathBuffer[1024];
Con::expandToolScriptFilename( schemaFilePathBuffer, sizeof(schemaFilePathBuffer), pTamlSchemaFile );
// Fetch the output path for the Taml file.
char outputFileBuffer[1024];
dSprintf( outputFileBuffer, sizeof(outputFileBuffer), "%s", mpTaml->getFilePathBuffer() );
char* pFileStart = dStrrchr( outputFileBuffer, '/' );
if ( pFileStart == NULL )
*outputFileBuffer = 0;
else
*pFileStart = 0;
// Fetch the schema file-path relative to the output file.
StringTableEntry relativeSchemaFilePath = Platform::makeRelativePathName( schemaFilePathBuffer, outputFileBuffer );
// Add schema location attribute to root.
pRootElement->SetAttribute( "xsi:noNamespaceSchemaLocation", relativeSchemaFilePath );
}
// Link the root element.
xmlDocument.LinkEndChild( pRootElement );
// Save document to stream.
return xmlDocument.SaveFile( stream );
}
//-----------------------------------------------------------------------------
TiXmlElement* TamlXmlWriter::compileElement( const TamlWriteNode* pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlWriter_CompileElement);
// Fetch object.
SimObject* pSimObject = pTamlWriteNode->mpSimObject;
// Fetch element name.
const char* pElementName = pSimObject->getClassName();
// Create element.
TiXmlElement* pElement = new fsTiXmlElement( pElementName );
// Fetch reference Id.
const U32 referenceId = pTamlWriteNode->mRefId;
// Do we have a reference Id?
if ( referenceId != 0 )
{
// Yes, so set reference Id attribute.
pElement->SetAttribute( tamlRefIdName, referenceId );
}
// 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 attribute.
pElement->SetAttribute( tamlRefToIdName, referenceToId );
// Finish because we're a reference to another object.
return pElement;
}
// Fetch object name.
const char* pObjectName = pTamlWriteNode->mpObjectName;
// Do we have a name?
if ( pObjectName != NULL )
{
// Yes, so set name attribute.
pElement->SetAttribute( tamlNamedObjectName, pObjectName );
}
// Compile attributes.
compileAttributes( pElement, pTamlWriteNode );
// Fetch children.
Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
// Do we have any children?
if ( pChildren )
{
// Yes, so iterate children.
for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
{
// Write child element.
pElement->LinkEndChild( compileElement( (*itr) ) );
}
}
// Compile custom elements.
compileCustomElements( pElement, pTamlWriteNode );
return pElement;
}
//-----------------------------------------------------------------------------
void TamlXmlWriter::compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlWriter_CompileAttributes);
// Fetch fields.
const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
// Ignore if no fields.
if ( fields.size() == 0 )
return;
// 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.
pXmlElement->SetAttribute( pFieldValue->mName, pFieldValue->mpValue );
}
}
//-----------------------------------------------------------------------------
void TamlXmlWriter::compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode )
{
// Debug Profiling.
PROFILE_SCOPE(TamlXmlWriter_CompileCustomElements);
// 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;
// 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", pXmlElement->Value(), pCustomNode->getNodeName() );
StringTableEntry extendedElementName = StringTable->insert( extendedElementNameBuffer );
// Create element.
TiXmlElement* pExtendedPropertyElement = new fsTiXmlElement( extendedElementName );
// Fetch node children.
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
// 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( pExtendedPropertyElement, pChildNode );
}
// Finish if the node is set to ignore if empty and it is empty.
if ( pCustomNode->getIgnoreEmpty() && pExtendedPropertyElement->NoChildren() )
{
// Yes, so delete the extended element.
delete pExtendedPropertyElement;
pExtendedPropertyElement = NULL;
}
else
{
// No, so add elementt as child.
pXmlElement->LinkEndChild( pExtendedPropertyElement );
}
}
}
//-----------------------------------------------------------------------------
void TamlXmlWriter::compileCustomNode( TiXmlElement* pXmlElement, const TamlCustomNode* pCustomNode )
{
// 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.
pXmlElement->LinkEndChild( compileElement( pCustomNode->getProxyWriteNode() ) );
return;
}
// Create element.
TiXmlElement* pNodeElement = new fsTiXmlElement( pCustomNode->getNodeName() );
// Is there any node text?
if ( !pCustomNode->getNodeTextField().isValueEmpty() )
{
// Yes, so add a text node.
pNodeElement->LinkEndChild( new TiXmlText( pCustomNode->getNodeTextField().getFieldValue() ) );
}
// Fetch fields.
const TamlCustomFieldVector& fields = pCustomNode->getFields();
// Iterate fields.
for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
{
// Fetch field.
const TamlCustomField* pField = *fieldItr;
// Set field.
pNodeElement->SetAttribute( pField->getFieldName(), pField->getFieldValue() );
}
// Fetch node children.
const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
// 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( pNodeElement, pChildNode );
}
// Finish if the node is set to ignore if empty and it is empty (including fields).
if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pNodeElement->NoChildren() )
{
// Yes, so delete the extended element.
delete pNodeElement;
pNodeElement = NULL;
}
else
{
// Add node element as child.
pXmlElement->LinkEndChild( pNodeElement );
}
}

View file

@ -0,0 +1,59 @@
//-----------------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------------
#ifndef _TAML_XMLWRITER_H_
#define _TAML_XMLWRITER_H_
#ifndef _TAML_H_
#include "persistence/taml/taml.h"
#endif
#ifndef TINYXML_INCLUDED
#include "tinyXML/tinyxml.h"
#endif
//-----------------------------------------------------------------------------
/// @ingroup tamlGroup
/// @see tamlGroup
class TamlXmlWriter
{
public:
TamlXmlWriter( Taml* pTaml ) :
mpTaml( pTaml )
{}
virtual ~TamlXmlWriter() {}
/// Write.
bool write( FileStream& stream, const TamlWriteNode* pTamlWriteNode );
private:
Taml* mpTaml;
private:
TiXmlElement* compileElement( const TamlWriteNode* pTamlWriteNode );
void compileAttributes( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
void compileCustomElements( TiXmlElement* pXmlElement, const TamlWriteNode* pTamlWriteNode );
void compileCustomNode( TiXmlElement* pXmlElement, const TamlCustomNode* pCustomNode );
};
#endif // _TAML_XMLWRITER_H_