# HG changeset patch # User Alain Mazy # Date 1557851990 -7200 # Node ID 63982c8c294af1f10a30c957368201c58d301865 # Parent 002d9562c8f5411da218ad0470b5b7a5a31f37b6# Parent 9e3bb8b4f72662489a369fe9ce4abb94741e6c01 merge am-dev -> default diff -r 002d9562c8f5 -r 63982c8c294a .hgignore --- a/.hgignore Tue May 14 16:54:13 2019 +0200 +++ b/.hgignore Tue May 14 18:39:50 2019 +0200 @@ -17,6 +17,8 @@ Applications/build-* CMakeLists.txt.user Platforms/Generic/ThirdPartyDownloads/ +Resources/CodeGeneration/.env +Resources/CodeGeneration/.idea Resources/CodeGeneration/__pycache__ Resources/CodeGeneration/build/ Resources/CodeGeneration/build_browser/ diff -r 002d9562c8f5 -r 63982c8c294a Applications/Generic/NativeStoneApplicationRunner.cpp --- a/Applications/Generic/NativeStoneApplicationRunner.cpp Tue May 14 16:54:13 2019 +0200 +++ b/Applications/Generic/NativeStoneApplicationRunner.cpp Tue May 14 18:39:50 2019 +0200 @@ -106,7 +106,7 @@ { boost::program_options::store( boost::program_options::command_line_parser(argc, argv). - options(options).run(), parameters); + options(options).allow_unregistered().run(), parameters); boost::program_options::notify(parameters); } catch (boost::program_options::error& e) @@ -124,11 +124,6 @@ if (error || parameters.count("help")) { std::cout << std::endl - << "Usage: " << argv[0] << " [OPTION]..." << std::endl - << "Orthanc, lightweight, RESTful DICOM server for healthcare " - << "and medical research." << std::endl << std::endl - << "Demonstration application of Orthanc Stone in native " - << "environment." << std::endl; std::cout << options << "\n"; return error ? -1 : 0; diff -r 002d9562c8f5 -r 63982c8c294a Applications/Wasm/StartupParametersBuilder.cpp --- a/Applications/Wasm/StartupParametersBuilder.cpp Tue May 14 16:54:13 2019 +0200 +++ b/Applications/Wasm/StartupParametersBuilder.cpp Tue May 14 18:39:50 2019 +0200 @@ -3,7 +3,7 @@ namespace OrthancStone { - void StartupParametersBuilder::Clear() + void StartupParametersBuilder::Clear() { startupParameters_.clear(); } @@ -43,11 +43,6 @@ if(std::get<1>(*it).length() > 0) argSs << "=" << std::get<1>(*it); - argvStrings[argCounter] = argSs.str(); - cmdLine = cmdLine + " " + argvStrings[argCounter]; - argv[argCounter] = argvStrings[argCounter].c_str(); - argCounter++; - } std::cout << "simulated cmdLine = \"" << cmdLine.c_str() << "\"\n"; @@ -55,7 +50,7 @@ { boost::program_options::store( boost::program_options::command_line_parser(argCounter, argv.data()). - options(options).run(), parameters); + options(options).allow_unregistered().run(), parameters); boost::program_options::notify(parameters); } catch (boost::program_options::error& e) diff -r 002d9562c8f5 -r 63982c8c294a Framework/Radiography/RadiographyLayer.cpp --- a/Framework/Radiography/RadiographyLayer.cpp Tue May 14 16:54:13 2019 +0200 +++ b/Framework/Radiography/RadiographyLayer.cpp Tue May 14 18:39:50 2019 +0200 @@ -34,6 +34,8 @@ RadiographyLayer::Geometry::Geometry() : hasCrop_(false), + flipVertical_(false), + flipHorizontal_(false), panX_(0), panY_(0), angle_(0), @@ -57,7 +59,7 @@ void RadiographyLayer::UpdateTransform() { - transform_ = AffineTransform2D::CreateScaling(geometry_.GetPixelSpacingX(), geometry_.GetPixelSpacingY()); + transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY()); double centerX, centerY; GetCenter(centerX, centerY); @@ -204,6 +206,21 @@ BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); } + void RadiographyLayer::SetFlipVertical(bool flip) + { + geometry_.SetFlipVertical(flip); + UpdateTransform(); + + EmitMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetFlipHorizontal(bool flip) + { + geometry_.SetFlipHorizontal(flip); + UpdateTransform(); + + EmitMessage(RadiographyLayer::LayerEditedMessage(*this)); + } void RadiographyLayer::SetSize(unsigned int width, unsigned int height) diff -r 002d9562c8f5 -r 63982c8c294a Framework/Radiography/RadiographyLayer.h --- a/Framework/Radiography/RadiographyLayer.h Tue May 14 16:54:13 2019 +0200 +++ b/Framework/Radiography/RadiographyLayer.h Tue May 14 18:39:50 2019 +0200 @@ -64,6 +64,8 @@ unsigned int cropY_; unsigned int cropWidth_; unsigned int cropHeight_; + bool flipVertical_; + bool flipHorizontal_; double panX_; double panY_; double angle_; @@ -155,6 +157,35 @@ return pixelSpacingY_; } + void SetFlipVertical(bool flip) // mirrors image around an horizontal axis (note: flip is applied before the rotation !) + { + flipVertical_ = flip; + } + + void SetFlipHorizontal(bool flip) // mirrors image around a vertical axis (note: flip is applied before the rotation !) + { + flipHorizontal_ = flip; + } + + bool GetFlipVertical() const + { + return flipVertical_; + } + + bool GetFlipHorizontal() const + { + return flipHorizontal_; + } + + double GetScalingX() const + { + return (flipHorizontal_ ? - pixelSpacingX_: pixelSpacingX_); + } + + double GetScalingY() const + { + return (flipVertical_ ? - pixelSpacingY_: pixelSpacingY_); + } }; private: @@ -240,6 +271,10 @@ void SetPan(double x, double y); + void SetFlipVertical(bool flip); // mirrors image around an horizontal axis (note: flip is applied before the rotation !) + + void SetFlipHorizontal(bool flip); // mirrors image around a vertical axis (note: flip is applied before the rotation !) + void SetResizeable(bool resizeable) { geometry_.SetResizeable(resizeable); diff -r 002d9562c8f5 -r 63982c8c294a Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Tue May 14 16:54:13 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.h Tue May 14 18:39:50 2019 +0200 @@ -151,7 +151,7 @@ void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message); - void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message); + virtual void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message); void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message); diff -r 002d9562c8f5 -r 63982c8c294a Framework/Radiography/RadiographySceneReader.cpp --- a/Framework/Radiography/RadiographySceneReader.cpp Tue May 14 16:54:13 2019 +0200 +++ b/Framework/Radiography/RadiographySceneReader.cpp Tue May 14 18:39:50 2019 +0200 @@ -236,5 +236,16 @@ geometry.SetResizeable(jsonLayer["isResizable"].asBool()); geometry.SetPan(jsonLayer["pan"]["x"].asDouble(), jsonLayer["pan"]["y"].asDouble()); geometry.SetPixelSpacing(jsonLayer["pixelSpacing"]["x"].asDouble(), jsonLayer["pixelSpacing"]["y"].asDouble()); + + // these fields were introduced later -> they might not exist + if (jsonLayer.isMember("flipVertical")) + { + geometry.SetFlipVertical(jsonLayer["flipVertical"].asBool()); + } + if (jsonLayer.isMember("flipHorizontal")) + { + geometry.SetFlipHorizontal(jsonLayer["flipHorizontal"].asBool()); + } + } } diff -r 002d9562c8f5 -r 63982c8c294a Framework/Radiography/RadiographySceneWriter.cpp --- a/Framework/Radiography/RadiographySceneWriter.cpp Tue May 14 16:54:13 2019 +0200 +++ b/Framework/Radiography/RadiographySceneWriter.cpp Tue May 14 18:39:50 2019 +0200 @@ -132,6 +132,9 @@ output["pixelSpacing"] = pan; } + output["flipVertical"] = geometry.GetFlipVertical(); + output["flipHorizontal"] = geometry.GetFlipHorizontal(); + if (dynamic_cast(&layer) != NULL) { WriteLayer(output, dynamic_cast(layer)); diff -r 002d9562c8f5 -r 63982c8c294a Resources/CodeGeneration/README.md --- a/Resources/CodeGeneration/README.md Tue May 14 16:54:13 2019 +0200 +++ b/Resources/CodeGeneration/README.md Tue May 14 18:39:50 2019 +0200 @@ -14,4 +14,7 @@ 'testWasmIntegrated` contains a small Web app demonstrating the interaction between TypeScript and C++ in WASM. -source ~/apps/emsdk/emsdk_env.sh \ No newline at end of file +source ~/apps/emsdk/emsdk_env.sh + + +Install Python and the following packages `pip install pyyaml jinja2` diff -r 002d9562c8f5 -r 63982c8c294a Resources/CodeGeneration/stonegentool.py --- a/Resources/CodeGeneration/stonegentool.py Tue May 14 16:54:13 2019 +0200 +++ b/Resources/CodeGeneration/stonegentool.py Tue May 14 18:39:50 2019 +0200 @@ -69,6 +69,26 @@ fileContent = JsonHelpers.removeCommentsFromJsonContent(fileContent) return json.loads(fileContent) +class FieldDefinition: + + def __init__(self, name: str, type: str, defaultValue: str): + self.name = name + self.type = type + self.defaultValue = defaultValue + + @staticmethod + def fromKeyValue(key: str, value: str): + + if "=" in value: + splitValue = value.split(sep="=") + type = splitValue[0].strip(" ") + defaultValue = splitValue[1].strip(" ") + else: + type = value + defaultValue = None + + return FieldDefinition(name = key, type = type, defaultValue = defaultValue) + def LoadSchemaFromJson(filePath): return JsonHelpers.loadJsonWithComments(filePath) @@ -129,6 +149,30 @@ def NeedsCppConstruction(canonTypename): return False +def DefaultValueToTs(enums, field:FieldDefinition): + tsType = CanonToTs(field.type) + + enumNames = [] + for enum in enums: + enumNames.append(enum['name']) + + if tsType in enumNames: + return tsType + "." + field.defaultValue + else: + return field.defaultValue + +def DefaultValueToCpp(root, enums, field:FieldDefinition): + cppType = CanonToCpp(field.type) + + enumNames = [] + for enum in enums: + enumNames.append(enum['name']) + + if cppType in enumNames: + return root + "::" + cppType + "_" + field.defaultValue + else: + return field.defaultValue + def RegisterTemplateFunction(template,func): """Makes a function callable by a jinja2 template""" template.globals[func.__name__] = func @@ -140,13 +184,16 @@ RegisterTemplateFunction(template,CanonToTs) RegisterTemplateFunction(template,NeedsTsConstruction) RegisterTemplateFunction(template,NeedsCppConstruction) + RegisterTemplateFunction(template, DefaultValueToTs) + RegisterTemplateFunction(template, DefaultValueToCpp) return template def MakeTemplateFromFile(templateFileName): - templateFile = open(templateFileName, "r") - templateFileContents = templateFile.read() - return MakeTemplate(templateFileContents) - templateFile.close() + + with open(templateFileName, "r") as templateFile: + templateFileContents = templateFile.read() + return MakeTemplate(templateFileContents) + def EatToken(sentence): """splits "A,B,C" into "A" and "B,C" where A, B and C are type names @@ -363,7 +410,7 @@ ret = {} for k,v in fieldDict.items(): if k != "__handler": - ret[k] = v + ret[k] = FieldDefinition.fromKeyValue(k, v) if k.startswith("__") and k != "__handler": raise RuntimeError("Fields starting with __ (double underscore) are reserved names!") return ret @@ -473,6 +520,9 @@ with open(fn, 'r', encoding='latin-1') as f: schemaText = f.read() assert(type(schemaText) == str) + return LoadSchemaFromString(schemaText = schemaText) + +def LoadSchemaFromString(schemaText:str): # ensure there is a space after each colon. Otherwise, dicts could be # erroneously recognized as an array of strings containing ':' for i in range(len(schemaText)-1): @@ -483,12 +533,14 @@ lineNumber = schemaText.count("\n",0,i) + 1 raise RuntimeError("Error at line " + str(lineNumber) + " in the schema: colons must be followed by a space or a newline!") schema = yaml.load(schemaText) - return schema + return schema def GetTemplatingDictFromSchemaFilename(fn): - obj = LoadSchema(fn) - genOrder = ComputeRequiredDeclarationOrder(obj) - templatingDict = ProcessSchema(obj, genOrder) + return GetTemplatingDictFromSchema(LoadSchema(fn)) + +def GetTemplatingDictFromSchema(schema): + genOrder = ComputeRequiredDeclarationOrder(schema) + templatingDict = ProcessSchema(schema, genOrder) currentDT = datetime.datetime.now() templatingDict['currentDatetime'] = str(currentDT) return templatingDict diff -r 002d9562c8f5 -r 63982c8c294a Resources/CodeGeneration/stonegentool_test.py --- a/Resources/CodeGeneration/stonegentool_test.py Tue May 14 16:54:13 2019 +0200 +++ b/Resources/CodeGeneration/stonegentool_test.py Tue May 14 18:39:50 2019 +0200 @@ -6,7 +6,7 @@ from stonegentool import \ EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \ CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \ -GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile +GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile,LoadSchemaFromString,GetTemplatingDictFromSchema import unittest import os import re @@ -127,17 +127,10 @@ self.assertTrue('someBs' in structs['C']['fields']) self.assertTrue('CrispType' in enums) self.assertTrue('Message1' in structs) - message1Struct = structs['Message1'] - self.assertDictEqual(message1Struct, - { - 'name':'Message1', - 'fields': { - 'a': 'int32', - 'b': 'string', - 'c': 'EnumMonth0', - 'd': 'bool' - } - }) + self.assertEqual('int32', structs['Message1']['fields']['a'].type) + self.assertEqual('string', structs['Message1']['fields']['b'].type) + self.assertEqual('EnumMonth0', structs['Message1']['fields']['c'].type) + self.assertEqual('bool', structs['Message1']['fields']['d'].type) def test_GenerateTypeScriptEnums(self): fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml') @@ -164,6 +157,12 @@ Barbecue, }; + export enum EnumMonth0 { + January, + February, + March, + }; + """ self.assertEqual(renderedCodeRef,renderedCode) @@ -192,6 +191,12 @@ Barbecue, }; + enum EnumMonth0 { + January, + February, + March, + }; + """ self.assertEqual(renderedCodeRef,renderedCode) @@ -255,10 +260,10 @@ # } };""") template = MakeTemplate(""" // end of generic methods {% for struct in structs%} export class {{struct['name']}} { -{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key])}}; +{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key]['type'])}}; {% endfor %} constructor() { -{% for key in struct['fields']%} this.{{key}} = new {{CanonToTs(struct['fields'][key])}}(); +{% for key in struct['fields']%} this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}(); {% endfor %} } public StoneSerialize(): string { @@ -278,9 +283,9 @@ movies:Array; constructor() { - someStrings = new Array(); - someInts2 = new Array(); - movies = new Array(); + this.someStrings = new Array(); + this.someInts2 = new Array(); + this.movies = new Array(); } public StoneSerialize(): string { @@ -296,8 +301,8 @@ someInts:Array; constructor() { - someAs = new Array(); - someInts = new Array(); + this.someAs = new Array(); + this.someInts = new Array(); } public StoneSerialize(): string { @@ -313,8 +318,8 @@ ddd:Array; constructor() { - someBs = new Array(); - ddd = new Array(); + this.someBs = new Array(); + this.ddd = new Array(); } public StoneSerialize(): string { @@ -332,10 +337,10 @@ d:boolean; constructor() { - a = new number(); - b = new string(); - c = new EnumMonth0(); - d = new boolean(); + this.a = new number(); + this.b = new string(); + this.c = new EnumMonth0(); + this.d = new boolean(); } public StoneSerialize(): string { @@ -352,13 +357,15 @@ tutu:Array; titi:Map; lulu:Map; + movieType:MovieType; constructor() { - toto = new string(); - tata = new Array(); - tutu = new Array(); - titi = new Map(); - lulu = new Map(); + this.toto = new string(); + this.tata = new Array(); + this.tutu = new Array(); + this.titi = new Map(); + this.lulu = new Map(); + this.movieType = new MovieType(); } public StoneSerialize(): string { @@ -396,6 +403,53 @@ def test_GenerateCppDispatcher(self): pass + def test_StringDefaultValueInTs(self): + schema = LoadSchemaFromString(""" + rootName: MyTest + struct Toto: + withoutDefault: string + withDefault: string = \"tutu\" + """) + tdico = GetTemplatingDictFromSchema(schema) + + tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') + template = MakeTemplateFromFile(tsTemplateFile) + renderedCode = template.render(**tdico) + self.assertIn("withDefault = \"tutu\"", renderedCode) + # print(renderedCode) + + cppTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2') + template = MakeTemplateFromFile(cppTemplateFile) + renderedCode = template.render(**tdico) + print(renderedCode) + self.assertIn("withDefault = \"tutu\"", renderedCode) + + + def test_EnumDefaultValue(self): + schema = LoadSchemaFromString(""" + rootName: MyTest + enum MyEnum: + - Toto + - Tutu + struct Toto: + withoutDefault: MyEnum + withDefault: MyEnum = Toto + """) + tdico = GetTemplatingDictFromSchema(schema) + + tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2') + template = MakeTemplateFromFile(tsTemplateFile) + renderedCode = template.render(**tdico) + # print(renderedCode) + self.assertIn("withDefault = MyEnum.Toto", renderedCode) + + tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2') + template = MakeTemplateFromFile(tsTemplateFile) + renderedCode = template.render(**tdico) + self.assertIn("withDefault = MyTest::MyEnum_Toto", renderedCode) + # print(renderedCode) + + # def test(self): # s = 'hello world' # self.assertEqual(s.split(), ['hello', 'world']) diff -r 002d9562c8f5 -r 63982c8c294a Resources/CodeGeneration/template.in.h.j2 --- a/Resources/CodeGeneration/template.in.h.j2 Tue May 14 16:54:13 2019 +0200 +++ b/Resources/CodeGeneration/template.in.h.j2 Tue May 14 18:39:50 2019 +0200 @@ -29,7 +29,10 @@ /** Throws in case of problem */ inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue) { - destValue = jsonValue.asInt(); + if (!jsonValue.isNull()) + { + destValue = jsonValue.asInt(); + } } inline Json::Value _StoneSerializeValue(int32_t value) @@ -40,7 +43,10 @@ inline void _StoneDeserializeValue(int64_t& destValue, const Json::Value& jsonValue) { - destValue = jsonValue.asInt64(); + if (!jsonValue.isNull()) + { + destValue = jsonValue.asInt64(); + } } inline Json::Value _StoneSerializeValue(int64_t value) @@ -51,7 +57,10 @@ inline void _StoneDeserializeValue(uint32_t& destValue, const Json::Value& jsonValue) { - destValue = jsonValue.asUInt(); + if (!jsonValue.isNull()) + { + destValue = jsonValue.asUInt(); + } } inline Json::Value _StoneSerializeValue(uint32_t value) @@ -62,7 +71,10 @@ inline void _StoneDeserializeValue(uint64_t& destValue, const Json::Value& jsonValue) { - destValue = jsonValue.asUInt64(); + if (!jsonValue.isNull()) + { + destValue = jsonValue.asUInt64(); + } } inline Json::Value _StoneSerializeValue(uint64_t value) @@ -84,7 +96,10 @@ /** Throws in case of problem */ inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue) { - destValue = jsonValue.asDouble(); + if (!jsonValue.isNull()) + { + destValue = jsonValue.asDouble(); + } } inline Json::Value _StoneSerializeValue(double value) @@ -96,7 +111,10 @@ /** Throws in case of problem */ inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue) { - destValue = jsonValue.asBool(); + if (!jsonValue.isNull()) + { + destValue = jsonValue.asBool(); + } } inline Json::Value _StoneSerializeValue(bool value) @@ -110,7 +128,10 @@ std::string& destValue , const Json::Value& jsonValue) { - destValue = jsonValue.asString(); + if (!jsonValue.isNull()) + { + destValue = jsonValue.asString(); + } } inline Json::Value _StoneSerializeValue(const std::string& value) @@ -151,19 +172,22 @@ void _StoneDeserializeValue( std::map& destValue, const Json::Value& jsonValue) { - destValue.clear(); - for ( - Json::Value::const_iterator itr = jsonValue.begin(); - itr != jsonValue.end(); - itr++) + if (!jsonValue.isNull()) { - std::string key; - _StoneDeserializeValue(key, itr.key()); + destValue.clear(); + for ( + Json::Value::const_iterator itr = jsonValue.begin(); + itr != jsonValue.end(); + itr++) + { + std::string key; + _StoneDeserializeValue(key, itr.key()); - T innerDestValue; - _StoneDeserializeValue(innerDestValue, *itr); + T innerDestValue; + _StoneDeserializeValue(innerDestValue, *itr); - destValue[key] = innerDestValue; + destValue[key] = innerDestValue; + } } } @@ -200,13 +224,16 @@ void _StoneDeserializeValue( std::vector& destValue, const Json::Value& jsonValue) { - destValue.clear(); - destValue.reserve(jsonValue.size()); - for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) + if (!jsonValue.isNull()) { - T innerDestValue; - _StoneDeserializeValue(innerDestValue, jsonValue[i]); - destValue.push_back(innerDestValue); + destValue.clear(); + destValue.reserve(jsonValue.size()); + for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) + { + T innerDestValue; + _StoneDeserializeValue(innerDestValue, jsonValue[i]); + destValue.push_back(innerDestValue); + } } } @@ -238,12 +265,15 @@ void _StoneDeserializeValue( std::set& destValue, const Json::Value& jsonValue) { - destValue.clear(); - for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) + if (!jsonValue.isNull()) { - T innerDestValue; - _StoneDeserializeValue(innerDestValue, jsonValue[i]); - destValue.insert(innerDestValue); + destValue.clear(); + for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++) + { + T innerDestValue; + _StoneDeserializeValue(innerDestValue, jsonValue[i]); + destValue.insert(innerDestValue); + } } } @@ -335,7 +365,10 @@ inline void _StoneDeserializeValue( {{enum['name']}}& destValue, const Json::Value& jsonValue) { - FromString(destValue, jsonValue.asString()); + if (!jsonValue.isNull()) + { + FromString(destValue, jsonValue.asString()); + } } inline Json::Value _StoneSerializeValue(const {{enum['name']}}& value) @@ -361,9 +394,9 @@ struct {{struct['name']}} { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} {{CanonToCpp(struct['fields'][key])}} {{key}}; +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} {{CanonToCpp(struct['fields'][key]['type'])}} {{key}}; {% endfor %}{% endif %}{% endif %} - {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{{CanonToCpp(struct['fields'][key])}} {{key}} = {{CanonToCpp(struct['fields'][key])}}(){{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %}) + {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %}) { {% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} this->{{key}} = {{key}}; {% endfor %}{% endif %}{% endif %} } @@ -371,8 +404,11 @@ inline void _StoneDeserializeValue({{struct['name']}}& destValue, const Json::Value& value) { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]); + if (!value.isNull()) + { +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]); {% endfor %}{% endif %}{% endif %} } + } inline Json::Value _StoneSerializeValue(const {{struct['name']}}& value) { diff -r 002d9562c8f5 -r 63982c8c294a Resources/CodeGeneration/template.in.ts.j2 --- a/Resources/CodeGeneration/template.in.ts.j2 Tue May 14 16:54:13 2019 +0200 +++ b/Resources/CodeGeneration/template.in.ts.j2 Tue May 14 18:39:50 2019 +0200 @@ -72,10 +72,12 @@ {% for struct in structs%}export class {{struct['name']}} { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key])}}; +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%} {{key}}:{{CanonToTs(struct['fields'][key]['type'])}}; {% endfor %}{% endif %}{% endif %} constructor() { -{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key])) %} this.{{key}} = new {{CanonToTs(struct['fields'][key])}}(); +{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key]['type'])) %} this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}(); +{% endif %} +{% if struct['fields'][key]['defaultValue'] %} this.{{key}} = {{DefaultValueToTs(enums,struct['fields'][key])}}; {% endif %}{% endfor %}{% endif %}{% endif %} } public StoneSerialize(): string { diff -r 002d9562c8f5 -r 63982c8c294a Resources/CodeGeneration/test_data/test1.yaml --- a/Resources/CodeGeneration/test_data/test1.yaml Tue May 14 16:54:13 2019 +0200 +++ b/Resources/CodeGeneration/test_data/test1.yaml Tue May 14 18:39:50 2019 +0200 @@ -24,7 +24,7 @@ d: bool struct Message2: - toto: string + toto: string = "my-default-value" tata: vector tutu: vector titi: map