tamlJSONWriter.cpp 14 KB

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