Mercurial > hg > orthanc-stone
changeset 628:84af39146e76 am-dev
CodeGeneration: support default values
author | Alain Mazy <alain@mazy.be> |
---|---|
date | Wed, 08 May 2019 16:32:57 +0200 |
parents | b7fd0471281c |
children | 8d66efecd91c |
files | Resources/CodeGeneration/stonegentool.py Resources/CodeGeneration/stonegentool_test.py Resources/CodeGeneration/template.in.h.j2 Resources/CodeGeneration/template.in.ts.j2 Resources/CodeGeneration/test_data/test1.yaml |
diffstat | 5 files changed, 188 insertions(+), 59 deletions(-) [+] |
line wrap: on
line diff
--- a/Resources/CodeGeneration/stonegentool.py Wed May 08 10:51:41 2019 +0200 +++ b/Resources/CodeGeneration/stonegentool.py Wed May 08 16:32:57 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
--- a/Resources/CodeGeneration/stonegentool_test.py Wed May 08 10:51:41 2019 +0200 +++ b/Resources/CodeGeneration/stonegentool_test.py Wed May 08 16:32:57 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,18 +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', - '__meta__': {'handleInCpp': False, 'handleInTypescript': False}, - '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') @@ -268,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 { @@ -411,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'])
--- a/Resources/CodeGeneration/template.in.h.j2 Wed May 08 10:51:41 2019 +0200 +++ b/Resources/CodeGeneration/template.in.h.j2 Wed May 08 16:32:57 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<std::string, T>& 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<T>& 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<T>& 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) {
--- a/Resources/CodeGeneration/template.in.ts.j2 Wed May 08 10:51:41 2019 +0200 +++ b/Resources/CodeGeneration/template.in.ts.j2 Wed May 08 16:32:57 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 {
--- a/Resources/CodeGeneration/test_data/test1.yaml Wed May 08 10:51:41 2019 +0200 +++ b/Resources/CodeGeneration/test_data/test1.yaml Wed May 08 16:32:57 2019 +0200 @@ -24,7 +24,7 @@ d: bool struct Message2: - toto: string + toto: string = "my-default-value" tata: vector<Message1> tutu: vector<string> titi: map<string, string>