1#!/usr/bin/python
2
3# =====================================
4# Copyright 2017-2023, Andrew Lindesay
5# Distributed under the terms of the MIT License.
6# =====================================
7
8# This simple tool will read a JSON schema and will then generate
9# some model objects that can be used to hold the data-structure
10# in the C++ environment.
11
12import json
13import argparse
14import os
15import hdsjsonschemacommon
16import ustache
17
18
19HEADER_TEMPLATE = """
20/*
21 * Generated Model Object for {{cppname}}
22 */
23
24#ifndef GEN_JSON_SCHEMA_MODEL__{{cppnameupper}}_H
25#define GEN_JSON_SCHEMA_MODEL__{{cppnameupper}}_H
26
27#include <ObjectList.h>
28#include <String.h>
29
30{{#referencedclasscpptypes}}#include "{{.}}.h"
31{{/referencedclasscpptypes}}
32
33
34class {{cppname}} {
35public:
36    {{cppname}}();
37    virtual ~{{cppname}}();
38
39    void Reset();
40{{#propertyarray}}{{#property.iscppscalartype}}
41    {{property.cpptype}} {{property.cppname}}();
42    void Set{{property.cppname}}({{property.cpptype}} value);
43    void Set{{property.cppname}}Null();
44    bool {{property.cppname}}IsNull();
45{{/property.iscppscalartype}}{{#property.isarray}}
46    void AddTo{{property.cppname}}({{property.items.cpptype}}* value);
47    void Set{{property.cppname}}({{property.cpptype}}* value);
48    int32 Count{{property.cppname}}();
49    {{property.items.cpptype}}* {{property.cppname}}ItemAt(int32 index);
50    bool {{property.cppname}}IsNull();
51{{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}
52    {{property.cpptype}}* {{property.cppname}}();
53    void Set{{property.cppname}}({{property.cpptype}}* value);
54    void Set{{property.cppname}}Null();
55    bool {{property.cppname}}IsNull();
56{{/property.iscppnonscalarnoncollectiontype}}
57{{/propertyarray}}
58
59public:
60{{#propertyarray}}    static const uint16 k{{property.cppname}}Bitmask;
61{{/propertyarray}}
62
63private:
64    uint16 fHasValueBitmask;
65{{#propertyarray}}    {{property.cpptype}}{{^property.iscppscalartype}}*{{/property.iscppscalartype}} {{property.cppmembername}};
66{{/propertyarray}}
67};
68
69#endif // GEN_JSON_SCHEMA_MODEL__{{cppnameupper}}_H
70"""
71
72IMPLEMENTATION_TEMPLATE = """
73/*
74 * Generated Model Object for {{cppname}}
75 */
76
77#include "{{cppname}}.h"
78
79
80{{#propertyarray}}/*static*/ const uint16 {{cppname}}::k{{property.cppname}}Bitmask = {{cppbitmaskexpression}};
81{{/propertyarray}}
82
83
84{{cppname}}::{{cppname}}()
85    :
86    fHasValueBitmask(0),
87{{#propertyarray}}    {{property.cppmembername}}({{property.cppdefaultvalue}}){{^islast}},{{/islast}}
88{{/propertyarray}}{
89}
90
91
92{{cppname}}::~{{cppname}}()
93{
94    Reset();
95}
96
97
98void
99{{cppname}}::Reset()
100{
101{{#propertyarray}}{{#property.isstring}}    delete {{property.cppmembername}};
102{{/property.isstring}}{{#property.isobject}}    delete {{property.cppmembername}};
103{{/property.isobject}}{{#property.isarray}}
104    if ({{property.cppmembername}} != NULL) {
105        for (int i = {{property.cppmembername}}->CountItems() - 1; i >= 0; i--)
106            delete {{property.cppmembername}}->ItemAt(i);
107        delete {{property.cppmembername}};
108    }
109{{/property.isarray}}    {{property.cppmembername}} = {{property.cppdefaultvalue}};
110{{/propertyarray}}
111    fHasValueBitmask = 0;
112}
113
114{{#propertyarray}}{{#property.iscppscalartype}}{{property.cpptype}}
115{{cppobjectname}}::{{property.cppname}}()
116{
117    return {{property.cppmembername}};
118}
119
120
121void
122{{cppobjectname}}::Set{{property.cppname}}({{property.cpptype}} value)
123{
124    fHasValueBitmask |= k{{property.cppname}}Bitmask;
125    {{property.cppmembername}} = value;
126}
127
128
129void
130{{cppobjectname}}::Set{{property.cppname}}Null()
131{
132    fHasValueBitmask &= ~k{{property.cppname}}Bitmask;
133    {{property.cppmembername}} = {{property.cppdefaultvalue}};
134}
135
136
137bool
138{{cppobjectname}}::{{property.cppname}}IsNull()
139{
140    return 0 == (fHasValueBitmask & k{{property.cppname}}Bitmask);
141}
142
143{{/property.iscppscalartype}}{{#property.isarray}}void
144{{cppobjectname}}::AddTo{{property.cppname}}({{property.items.cpptype}}* value)
145{
146    if ({{property.cppmembername}} == NULL)
147        {{property.cppmembername}} = new {{property.cpptype}}();
148    {{property.cppmembername}}->AddItem(value);
149}
150
151
152void
153{{cppobjectname}}::Set{{property.cppname}}({{property.cpptype}}* value)
154{
155    if ({{property.cppmembername}} != NULL) {
156        delete {{property.cppmembername}};
157    }
158    {{property.cppmembername}} = value;
159}
160
161
162int32
163{{cppobjectname}}::Count{{property.cppname}}()
164{
165    if ({{property.cppmembername}} == NULL)
166        return 0;
167    return {{property.cppmembername}}->CountItems();
168}
169
170
171{{property.items.cpptype}}*
172{{cppobjectname}}::{{property.cppname}}ItemAt(int32 index)
173{
174    return {{property.cppmembername}}->ItemAt(index);
175}
176
177
178bool
179{{cppobjectname}}::{{property.cppname}}IsNull()
180{
181    return {{property.cppmembername}} == NULL;
182}
183
184{{/property.isarray}}{{#property.iscppnonscalarnoncollectiontype}}{{property.cpptype}}*
185{{cppobjectname}}::{{property.cppname}}()
186{
187    return {{property.cppmembername}};
188}
189
190
191void
192{{cppobjectname}}::Set{{property.cppname}}({{property.cpptype}}* value)
193{
194    {{property.cppmembername}} = value;
195}
196
197
198void
199{{cppobjectname}}::Set{{property.cppname}}Null()
200{
201    if (!{{property.cppname}}IsNull()) {
202        delete {{property.cppmembername}};
203        {{property.cppmembername}} = NULL;
204    }
205}
206
207
208bool
209{{cppobjectname}}::{{property.cppname}}IsNull()
210{
211    return {{property.cppmembername}} == NULL;
212}
213
214{{/property.iscppnonscalarnoncollectiontype}}
215{{/propertyarray}}
216"""
217
218
219def write_models_for_schema(schema: dict[str, any], output_directory: str) -> None:
220
221    def write_model_object(obj: dict[str, any]) -> None:
222        cpp_name = obj["cppname"]
223        cpp_header_filename = os.path.join(output_directory, cpp_name + '.h')
224        cpp_implementation_filename = os.path.join(output_directory, cpp_name + '.cpp')
225
226        with open(cpp_header_filename, 'w') as cpp_h_file:
227            cpp_h_file.write(ustache.render(
228                HEADER_TEMPLATE,
229                obj,
230                escape= lambda x: x))
231
232        with open(cpp_implementation_filename, 'w') as cpp_i_file:
233            cpp_i_file.write(ustache.render(
234                IMPLEMENTATION_TEMPLATE,
235                obj,
236                escape= lambda x: x))
237
238    for obj in hdsjsonschemacommon.collect_all_objects(schema):
239        write_model_object(obj)
240
241
242def main():
243    parser = argparse.ArgumentParser(
244        description='Convert JSON schema to Haiku C++ Models')
245    parser.add_argument(
246        '-i', '--inputfile',
247        required=True,
248        help='The input filename containing the JSON schema')
249    parser.add_argument(
250        '--outputdirectory',
251        help='The output directory where the C++ files should be written')
252
253    args = parser.parse_args()
254
255    output_directory = args.outputdirectory
256
257    if not output_directory:
258        output_directory = '.'
259
260    with open(args.inputfile) as inputfile:
261        schema = json.load(inputfile)
262        hdsjsonschemacommon.augment_schema(schema)
263        write_models_for_schema(schema, output_directory)
264
265
266if __name__ == "__main__":
267    main()
268
269