tamlJSONParser.cpp
Engine/source/persistence/taml/json/tamlJSONParser.cpp
Detailed Description
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/tamlJSONParser.h" 25#include "persistence/taml/tamlVisitor.h" 26#include "console/console.h" 27#include "core/stream/fileStream.h" 28#include "core/frameAllocator.h" 29 30// Debug Profiling. 31#include "platform/profiler.h" 32 33//----------------------------------------------------------------------------- 34 35bool TamlJSONParser::accept( const char* pFilename, TamlVisitor& visitor ) 36{ 37 // Debug Profiling. 38 PROFILE_SCOPE(TamlJSONParser_Accept); 39 40 // Sanity! 41 AssertFatal( pFilename != NULL, "Cannot parse a NULL filename." ); 42 43 // Expand the file-path. 44 char filenameBuffer[1024]; 45 Con::expandToolScriptFilename( filenameBuffer, sizeof(filenameBuffer), pFilename ); 46 47 FileStream stream; 48 49 // File open for read? 50 if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) ) 51 { 52 // No, so warn. 53 Con::warnf("TamlJSONParser::parse() - Could not open filename '%s' for parse.", filenameBuffer ); 54 return false; 55 } 56 57 // Read JSON file. 58 const U32 streamSize = stream.getStreamSize(); 59 FrameTemp<char> jsonText( streamSize + 1 ); 60 if ( !stream.read( streamSize, jsonText ) ) 61 { 62 // Warn! 63 Con::warnf("TamlJSONParser::parse() - Could not load Taml JSON file from stream."); 64 return false; 65 } 66 67 // Create JSON document. 68 rapidjson::Document inputDocument; 69 inputDocument.Parse<0>( jsonText ); 70 71 // Close the stream. 72 stream.close(); 73 74 // Check the document is valid. 75 if ( inputDocument.GetType() != rapidjson::kObjectType ) 76 { 77 // Warn! 78 Con::warnf("TamlJSONParser::parse() - Load Taml JSON file from stream but was invalid."); 79 return false; 80 } 81 82 // Set parsing filename. 83 setParsingFilename( filenameBuffer ); 84 85 // Flag document as not dirty. 86 mDocumentDirty = false; 87 88 // Fetch the root. 89 rapidjson::Value::MemberIterator rootItr = inputDocument.MemberBegin(); 90 91 // Parse root value. 92 parseType( rootItr, visitor, true ); 93 94 // Reset parsing filename. 95 setParsingFilename( StringTable->EmptyString() ); 96 97 // Finish if the document is not dirty. 98 if ( !mDocumentDirty ) 99 return true; 100 101 // Open for write? 102 103 if ( !stream.open( filenameBuffer, Torque::FS::File::Write ) ) 104 { 105 // No, so warn. 106 Con::warnf("TamlJSONParser::parse() - Could not open filename '%s' for write.", filenameBuffer ); 107 return false; 108 } 109 110 // Create output document. 111 rapidjson::Document outputDocument; 112 outputDocument.AddMember( rootItr->name, rootItr->value, outputDocument.GetAllocator() ); 113 114 // Write document to stream. 115 rapidjson::PrettyWriter<FileStream> jsonStreamWriter( stream ); 116 outputDocument.Accept( jsonStreamWriter ); 117 118 // Close the stream. 119 stream.close(); 120 121 return true; 122} 123 124//----------------------------------------------------------------------------- 125 126inline bool TamlJSONParser::parseType( rapidjson::Value::MemberIterator& memberItr, TamlVisitor& visitor, const bool isRoot ) 127{ 128 // Debug Profiling. 129 PROFILE_SCOPE(TamlJSONParser_ParseType); 130 131 // Fetch name and value. 132 const rapidjson::Value& typeName = memberItr->name; 133 rapidjson::Value& typeValue = memberItr->value; 134 135 // Create a visitor property state. 136 TamlVisitor::PropertyState propertyState; 137 propertyState.setObjectName( typeName.GetString(), isRoot ); 138 139 // Parse field members. 140 for( rapidjson::Value::MemberIterator fieldMemberItr = typeValue.MemberBegin(); fieldMemberItr != typeValue.MemberEnd(); ++fieldMemberItr ) 141 { 142 // Fetch value. 143 const rapidjson::Value& fieldName = fieldMemberItr->name; 144 rapidjson::Value& fieldValue = fieldMemberItr->value; 145 146 // Skip if not a field. 147 if ( fieldValue.IsObject() ) 148 continue; 149 150 char valueBuffer[4096]; 151 if ( !parseStringValue( valueBuffer, sizeof(valueBuffer), fieldValue, fieldName.GetString() ) ) 152 { 153 // Warn. 154 Con::warnf( "TamlJSONParser::parseTyoe() - Could not interpret value for field '%s'", fieldName.GetString() ); 155 continue; 156 } 157 158 // Configure property state. 159 propertyState.setProperty( fieldName.GetString(), valueBuffer ); 160 161 // Visit this attribute. 162 const bool visitStatus = visitor.visit( *this, propertyState ); 163 164 // Was the property value changed? 165 if ( propertyState.getPropertyValueDirty() ) 166 { 167 // Yes, so update the attribute. 168 fieldValue.SetString(rapidjson::GenericStringRef<UTF8>(propertyState.getPropertyValue()) ); 169 170 // Flag the document as dirty. 171 mDocumentDirty = true; 172 } 173 174 // Finish if requested. 175 if ( !visitStatus ) 176 return false; 177 } 178 179 // Finish if only the root is needed. 180 if ( visitor.wantsRootOnly() ) 181 return false; 182 183 // Parse children and custom node members. 184 for( rapidjson::Value::MemberIterator objectMemberItr = typeValue.MemberBegin(); objectMemberItr != typeValue.MemberEnd(); ++objectMemberItr ) 185 { 186 // Fetch name and value. 187 const rapidjson::Value& objectValue = objectMemberItr->value; 188 189 // Skip if not an object. 190 if ( !objectValue.IsObject() ) 191 continue; 192 193 // Parse the type. 194 if ( !parseType( objectMemberItr, visitor, false ) ) 195 return false; 196 } 197 198 return true; 199} 200 201//----------------------------------------------------------------------------- 202 203inline bool TamlJSONParser::parseStringValue( char* pBuffer, const S32 bufferSize, const rapidjson::Value& value, const char* pName ) 204{ 205 // Debug Profiling. 206 PROFILE_SCOPE(TamlJSONParser_ParseStringValue); 207 208 // Handle field value appropriately. 209 210 if ( value.IsString() ) 211 { 212 dSprintf( pBuffer, bufferSize, "%s", value.GetString() ); 213 return true; 214 } 215 216 if ( value.IsNumber() ) 217 { 218 if ( value.IsInt() ) 219 { 220 dSprintf( pBuffer, bufferSize, "%d", value.GetInt() ); 221 return true; 222 } 223 224 if ( value.IsUint() ) 225 { 226 dSprintf( pBuffer, bufferSize, "%d", value.GetUint() ); 227 return true; 228 } 229 230 if ( value.IsInt64() ) 231 { 232 dSprintf( pBuffer, bufferSize, "%d", value.GetInt64() ); 233 return true; 234 } 235 236 if ( value.IsUint64() ) 237 { 238 dSprintf( pBuffer, bufferSize, "%d", value.GetUint64() ); 239 return true; 240 } 241 242 if ( value.IsDouble() ) 243 { 244 dSprintf( pBuffer, bufferSize, "%f", value.GetDouble() ); 245 return true; 246 } 247 } 248 249 if ( value.IsBool() ) 250 { 251 dSprintf( pBuffer, bufferSize, "%d", value.GetBool() ); 252 return true; 253 } 254 255 // Failed to get value type. 256 Con::warnf( "Taml: Encountered a field '%s' but its value is an unknown type.", pName ); 257 return false; 258} 259 260