Torque3D Documentation / _generateds / tamlJSONWriter.cpp

tamlJSONWriter.cpp

Engine/source/persistence/taml/json/tamlJSONWriter.cpp

More...

Detailed Description

Public Variables

StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS 

RapidJson.

StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT 
  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2013 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "persistence/taml/json/tamlJSONWriter.h"
 25
 26#include "core/stringTable.h"
 27#include "core/stream/fileStream.h"
 28
 29// Debug Profiling.
 30#include "platform/profiler.h"
 31
 32//-----------------------------------------------------------------------------
 33
 34// These are the characters allowed that separate the type-name from the type.
 35// These separators can be used to ensure that each member in an object has
 36// a unique name.
 37//
 38// It is important to understand that TAML does not require entries to be unique
 39// but technically the RFC4627 spec states it as "should" be seems to be taken
 40// as MUST.  See here: http://www.ietf.org/rfc/rfc4627.txt
 41//
 42// They can be placed as a suffix to the type-name.  The very first occurance
 43// is used to split the type-name from the suffix so you can form whatever
 44// suffix you like to make entries unique.
 45// Examples are "Sprite 0", "Sprite*0", "Sprite[0]", "Sprite,0" etc.
 46//
 47// Type-names can legally consist of a-z, A-Z, 0-9 and "_" (underscore) so these
 48// characters cannot be used for mangling.  Feel free to add any characters you
 49// require to this list.
 50StringTableEntry JSON_RFC4627_NAME_MANGLING_CHARACTERS = StringTable->insert(" !$%^&*()-+{}[]@:~#|\\/?<>,.\n\r\t");
 51
 52// This is the "dSprintf" format that the JSON writer uses to encode each
 53// member if JSON_RFC4627 mode is on.  You are free to change this as long as
 54// you ensure that it starts with the "%s" character (which represents the type name)
 55// and is immediately followed by at least a single mangling character and that the
 56// "%d" is present as that represents the automatically-added member index.
 57StringTableEntry JSON_RFC4627_NAME_MANGLING_FORMAT = StringTable->insert( "%s[%d]" );
 58
 59//-----------------------------------------------------------------------------
 60
 61bool TamlJSONWriter::write( FileStream& stream, const TamlWriteNode* pTamlWriteNode )
 62{
 63    // Debug Profiling.
 64    PROFILE_SCOPE(TamlJSONWriter_Write);
 65
 66    // Create document.
 67    rapidjson::Document document;
 68    document.SetObject();
 69
 70    // Compile the root type.
 71    rapidjson::Value rootValue(rapidjson::kObjectType);
 72    compileType( document, &rootValue, NULL, pTamlWriteNode, -1 );
 73
 74    // Write document to stream.
 75    rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream );
 76    document.Accept( jsonStreamWriter );
 77
 78    return true;
 79}
 80
 81//-----------------------------------------------------------------------------
 82
 83void TamlJSONWriter::compileType( rapidjson::Document& document, rapidjson::Value* pTypeValue, rapidjson::Value* pParentValue, const TamlWriteNode* pTamlWriteNode, const S32 memberIndex )
 84{
 85    // Debug Profiling.
 86    PROFILE_SCOPE(TamlJSONWriter_CompileType);
 87
 88    // Fetch the json document allocator.
 89    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
 90
 91    // Fetch object.
 92    SimObject* pSimObject = pTamlWriteNode->mpSimObject;
 93
 94    // Fetch JSON strict flag (don't use it if member index is set to not use it).
 95    const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict();
 96   
 97    // Fetch element name (mangled or not).
 98    StringTableEntry elementName = jsonStrict ? getManagedName( pSimObject->getClassName(), memberIndex ) : pSimObject->getClassName();
 99    
100    // Is there a parent value?
101    if ( pParentValue == NULL )
102    {        
103        // No, so add as document root value member.
104        pTypeValue = &((document.AddMember(rapidjson::GenericStringRef<UTF8>(elementName), *pTypeValue, allocator ).MemberEnd()-1)->value);
105    }
106    else
107    {
108        // Yes, so add as a parent value member.
109        pTypeValue = &((pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(elementName), *pTypeValue, allocator ).MemberEnd()-1)->value);
110    }
111
112    // Fetch reference Id.
113    const U32 referenceId = pTamlWriteNode->mRefId;
114
115    // Do we have a reference Id?
116    if ( referenceId != 0 )
117    {
118        // Yes, so set reference Id.
119        rapidjson::Value value;
120        value.SetInt( referenceId );
121        pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlRefIdName), value, allocator );
122    }
123    // Do we have a reference to node?
124    else if ( pTamlWriteNode->mRefToNode != NULL )
125    {
126        // Yes, so fetch reference to Id.
127        const U32 referenceToId = pTamlWriteNode->mRefToNode->mRefId;
128
129        // Sanity!
130        AssertFatal( referenceToId != 0, "Taml: Invalid reference to Id." );
131
132        // Set reference to Id.
133        rapidjson::Value value;
134        value.SetInt( referenceToId );
135        pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlRefToIdName), value, allocator );
136
137        // Finish because we're a reference to another object.
138        return;
139    }
140
141    // Fetch object name.
142    const char* pObjectName = pTamlWriteNode->mpObjectName;
143
144    // Do we have a name?
145    if ( pObjectName != NULL )
146    {
147        // Yes, so set name.
148        rapidjson::Value value;
149        value.SetString( pObjectName, dStrlen(pObjectName), allocator );
150        pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(tamlNamedObjectName), value, allocator );
151    }
152
153    // Compile field.
154    compileFields( document, pTypeValue, pTamlWriteNode );
155
156    // Fetch children.
157    Vector<TamlWriteNode*>* pChildren = pTamlWriteNode->mChildren;
158
159    // Do we have any children?
160    if ( pChildren )
161    {
162        S32 childMemberIndex = 0;
163
164        // Yes, so iterate children.
165        for( Vector<TamlWriteNode*>::iterator itr = pChildren->begin(); itr != pChildren->end(); ++itr )
166        {
167            // Compile child type.
168            rapidjson::Value childValue(rapidjson::kObjectType);
169            compileType( document, &childValue, pTypeValue, (*itr), childMemberIndex++ );
170        }
171    }
172
173    // Compile custom.
174    compileCustom( document, pTypeValue, pTamlWriteNode );
175}
176
177//-----------------------------------------------------------------------------
178
179void TamlJSONWriter::compileFields( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode )
180{
181    // Debug Profiling.
182    PROFILE_SCOPE(TamlJSONWriter_CompileFields);
183
184    // Fetch fields.
185    const Vector<TamlWriteNode::FieldValuePair*>& fields = pTamlWriteNode->mFields;
186
187    // Ignore if no fields.
188    if ( fields.size() == 0 )
189        return;
190
191    // Fetch the json document allocator.
192    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
193
194    // Iterate fields.
195    for( Vector<TamlWriteNode::FieldValuePair*>::const_iterator itr = fields.begin(); itr != fields.end(); ++itr )
196    {
197        // Fetch field/value pair.
198        TamlWriteNode::FieldValuePair* pFieldValue = (*itr);
199
200        // Set field attribute.
201        rapidjson::Value value;
202        value.SetString( pFieldValue->mpValue, dStrlen(pFieldValue->mpValue), allocator );
203        pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(pFieldValue->mName), value, allocator );
204    }
205}
206
207//-----------------------------------------------------------------------------
208
209void TamlJSONWriter::compileCustom( rapidjson::Document& document, rapidjson::Value* pTypeValue, const TamlWriteNode* pTamlWriteNode )
210{
211    // Debug Profiling.
212    PROFILE_SCOPE(TamlJSONWriter_CompileCustom);
213
214    // Fetch custom nodes.
215    const TamlCustomNodes& customNodes = pTamlWriteNode->mCustomNodes;
216
217    // Fetch custom nodes.
218    const TamlCustomNodeVector& nodes = customNodes.getNodes();
219
220    // Finish if no custom nodes to process.
221    if ( nodes.size() == 0 )
222        return;
223
224    // Fetch the json document allocator.
225    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
226
227    // Fetch object.
228    SimObject* pSimObject = pTamlWriteNode->mpSimObject;
229
230    // Fetch element name.
231    const char* pElementName = pSimObject->getClassName();
232
233    // Iterate custom nodes.
234    for( TamlCustomNodeVector::const_iterator customNodesItr = nodes.begin(); customNodesItr != nodes.end(); ++customNodesItr )
235    {
236        // Fetch the custom node.
237        TamlCustomNode* pCustomNode = *customNodesItr;
238
239        // Format extended element name.
240        char extendedElementNameBuffer[256];
241        dSprintf( extendedElementNameBuffer, sizeof(extendedElementNameBuffer), "%s.%s", pElementName, pCustomNode->getNodeName() );
242        StringTableEntry elementNameEntry = StringTable->insert( extendedElementNameBuffer );
243
244        rapidjson::Value elementValue(rapidjson::kObjectType);
245        rapidjson::Value* pElementValue = &((pTypeValue->AddMember(rapidjson::GenericStringRef<UTF8>(elementNameEntry), elementValue, allocator ).MemberEnd()-1)->value);
246
247        // Fetch node children.
248        const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
249
250        S32 childMemberIndex = 0;
251
252        // Iterate children nodes.
253        for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
254        {
255            // Fetch child node.
256            const TamlCustomNode* pChildNode = *childNodeItr;
257
258            // Compile the custom node.
259            compileCustomNode( document, pElementValue, pChildNode, childMemberIndex++ );
260        }
261
262        // Finish if the node is set to ignore if empty and it is empty.
263        if ( pCustomNode->getIgnoreEmpty() && pTypeValue->MemberBegin() == pTypeValue->MemberEnd() )
264        {
265            // Yes, so delete the member.
266            pElementValue->SetNull();
267        }
268    }
269}
270
271//-----------------------------------------------------------------------------
272
273void TamlJSONWriter::compileCustomNode( rapidjson::Document& document, rapidjson::Value* pParentValue, const TamlCustomNode* pCustomNode, const S32 memberIndex )
274{
275    // Debug Profiling.
276    PROFILE_SCOPE(TamlJSONWriter_CompileCustomNode);
277
278    // Finish if the node is set to ignore if empty and it is empty.
279    if ( pCustomNode->getIgnoreEmpty() && pCustomNode->isEmpty() )
280        return;
281
282    // Is the node a proxy object?
283    if ( pCustomNode->isProxyObject() )
284    {
285        // Yes, so write the proxy object.
286        rapidjson::Value proxyValue(rapidjson::kObjectType);
287        compileType( document, &proxyValue, pParentValue, pCustomNode->getProxyWriteNode(), memberIndex );
288        return;
289    }
290
291    // Fetch the json document allocator.
292    rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
293
294    // Fetch fields.
295    const TamlCustomFieldVector& fields = pCustomNode->getFields();
296
297    // Fetch JSON strict flag (don't use it if member index is set to not use it).
298    const bool jsonStrict = memberIndex == -1 ? false : mpTaml->getJSONStrict();
299   
300    // Fetch element name (mangled or not).
301    StringTableEntry nodeName = jsonStrict ? getManagedName( pCustomNode->getNodeName(), memberIndex ) : pCustomNode->getNodeName();
302
303    // Is there any node text?
304    if ( !pCustomNode->getNodeTextField().isValueEmpty() )
305    {
306        // Yes, so fetch text.
307        const char* pNodeText = pCustomNode->getNodeTextField().getFieldValue();
308
309        // Create custom value.
310        rapidjson::Value customTextValue(rapidjson::kArrayType);
311        customTextValue.PushBack(rapidjson::GenericStringRef<UTF8>(pNodeText), allocator );
312        pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(nodeName), customTextValue, allocator );
313        return;
314    }
315
316    // Create custom value.
317    rapidjson::Value customValue(rapidjson::kObjectType);
318    rapidjson::Value* pCustomValue = &((pParentValue->AddMember(rapidjson::GenericStringRef<UTF8>(nodeName), customValue, allocator ).MemberEnd()-1)->value);
319
320    // Iterate fields.
321    for ( TamlCustomFieldVector::const_iterator fieldItr = fields.begin(); fieldItr != fields.end(); ++fieldItr )
322    {
323        // Fetch field.
324        const TamlCustomField* pField = *fieldItr;
325
326        // Add a field.
327        rapidjson::Value fieldValue;
328        fieldValue.SetString( pField->getFieldValue(), dStrlen(pField->getFieldValue()), allocator );
329        pCustomValue->AddMember(rapidjson::GenericStringRef<UTF8>(pField->getFieldName()), fieldValue, allocator );
330    }
331
332    // Fetch node children.
333    const TamlCustomNodeVector& nodeChildren = pCustomNode->getChildren();
334
335    S32 childMemberIndex = 0;
336
337    // Iterate children nodes.
338    for( TamlCustomNodeVector::const_iterator childNodeItr = nodeChildren.begin(); childNodeItr != nodeChildren.end(); ++childNodeItr )
339    {
340        // Fetch child node.
341        const TamlCustomNode* pChildNode = *childNodeItr;
342
343        // Compile the child node.
344        compileCustomNode( document, pCustomValue, pChildNode, childMemberIndex++ );
345    }
346
347    // Finish if the node is set to ignore if empty and it is empty (including fields).
348    if ( pCustomNode->getIgnoreEmpty() && fields.size() == 0 && pCustomValue->MemberBegin() == pCustomValue->MemberEnd() )
349    {
350        // Yes, so delete the member.
351        pCustomValue->SetNull();
352    }
353}
354
355//-----------------------------------------------------------------------------
356
357inline StringTableEntry TamlJSONWriter::getManagedName( const char* pName, const S32 memberIndex )
358{
359    // Debug Profiling.
360    PROFILE_SCOPE(TamlJSONWriter_getManagedName);
361
362    char nameBuffer[1024];
363
364    // Format mangled name.
365    dSprintf( nameBuffer, sizeof(nameBuffer), JSON_RFC4627_NAME_MANGLING_FORMAT, pName, memberIndex );
366
367    return StringTable->insert( nameBuffer );
368}
369