1// Copyright (C) 2017 Ford Motor Company
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include <qjsonvalue.h>
5#include <qjsonarray.h>
6#include <qjsonobject.h>
7
8#include "utils.h"
9#include "repparser.h"
10
11
12#define _(X) QLatin1String(X)
13
14QT_BEGIN_NAMESPACE
15
16namespace JSON
17{
18 enum Types {
19 Any,
20 Array,
21 Object,
22 String,
23 Bool
24 };
25
26 static QJsonValue getItem(const QJsonValue &json, const char *key, JSON::Types type = JSON::Any)
27 {
28 if (json.isUndefined())
29 qCritical() << "Invalid metadata json file. Unexpected Undefined value when looking for key:" << key;
30 if (!json.isObject())
31 qCritical() << "Invalid metadata json file. Input (" << json << ") is not an object when looking for key:" << key;
32 QJsonValue value = json.toObject()[_(key)];
33 switch (type) {
34 case JSON::Any: break;
35 case JSON::Array:
36 if (!value.isArray())
37 qCritical() << "Invalid metadata json file. Value (" << value << ") is not an array when looking for key:" << key;
38 break;
39 case JSON::Object:
40 if (!value.isObject())
41 qCritical() << "Invalid metadata json file. Value (" << value << ") is not an object when looking for key:" << key;
42 break;
43 case JSON::String:
44 if (!value.isString())
45 qCritical() << "Invalid metadata json file. Value (" << value << ") is not a string when looking for key:" << key;
46 break;
47 case JSON::Bool:
48 if (!value.isBool())
49 qCritical() << "Invalid metadata json file. Value (" << value << ") is not a bool when looking for key:" << key;
50 break;
51 }
52 return value;
53 }
54
55 static bool containsKey(const QJsonValue &json, const char *key)
56 {
57 if (json.isUndefined())
58 qCritical() << "Invalid metadata json file. Unexpected Undefined value when looking for key:" << key;
59 if (!json.isObject())
60 qCritical() << "Invalid metadata json file. Input (" << json << ") is not an object when looking for key:" << key;
61 return json.toObject().contains(_(key));
62 }
63
64 static bool isEmptyArray(const QJsonValue &json, const char *key)
65 {
66 if (!containsKey(json, key))
67 return true;
68 const auto value = getItem(json, key);
69 if (!value.isArray())
70 qCritical() << "Invalid metadata json file." << key << "is not an array.";
71 return value.toArray().count() == 0;
72 }
73
74 static QJsonArray getArray(const QJsonValue &json, const char *key)
75 {
76 return getItem(json, key, type: JSON::Array).toArray();
77 }
78 static QString getString(const QJsonValue &json, const char *key)
79 {
80 return getItem(json, key, type: JSON::String).toString();
81 }
82 static QByteArray getBytes(const QJsonValue &json, const char *key)
83 {
84 return getItem(json, key, type: JSON::String).toString().toLatin1();
85 }
86 static bool getBool(const QJsonValue &json, const char *key)
87 {
88 return getItem(json, key, type: JSON::Bool).toBool();
89 }
90 static bool getBool(const QJsonValue &json, const char *key, bool missingValue)
91 {
92 if (!containsKey(json, key))
93 return missingValue;
94 bool res = getBool(json, key);
95 return res;
96 }
97}
98
99using namespace JSON;
100
101static QByteArray join(const QByteArrayList &array, const QByteArray &separator)
102{
103 QByteArray res;
104 const auto sz = array.size();
105 if (!sz)
106 return res;
107 for (qsizetype i = 0; i < sz - 1; i++)
108 res += array.at(i) + separator;
109 res += array.at(i: sz - 1);
110 return res;
111}
112
113static QByteArrayList generateProperties(const QJsonArray &properties, bool isPod=false)
114{
115 QByteArrayList ret;
116 for (const QJsonValue prop : properties) {
117 if (!isPod && !containsKey(json: prop, key: "notify") && !getBool(json: prop, key: "constant")) {
118 qWarning() << "Skipping property" << getString(json: prop, key: "name")
119 << "because it is non-notifiable & non-constant";
120 continue; // skip non-notifiable properties
121 }
122 QByteArray output = getBytes(json: prop, key: "type") + " " + getBytes(json: prop, key: "name");
123 if (getBool(json: prop, key: "constant"))
124 output += " CONSTANT";
125 if (!containsKey(json: prop, key: "write") && containsKey(json: prop, key: "read."))
126 output += " READONLY";
127 ret << output;
128 }
129 return ret;
130}
131
132static QByteArray generateFunctions(const QByteArray &type, const QJsonArray &functions)
133{
134 QByteArray ret;
135 for (const QJsonValue func : functions) {
136 ret += type + "(" + getBytes(json: func, key: "returnType") + " " + getBytes(json: func, key: "name") + "(";
137 const auto arguments = getArray(json: func, key: "arguments");
138 for (const QJsonValue arg : arguments)
139 ret += getBytes(json: arg, key: "type") + " " + getBytes(json: arg, key: "name") + ", ";
140 if (arguments.count())
141 ret.chop(n: 2);
142 ret += "));\n";
143 }
144 return ret;
145}
146
147const auto filterNotPublic = [](const QJsonValue &value) {
148 return getString(json: value, key: "access") != QStringLiteral("public");
149};
150
151static QJsonArray cleanedSignalList(const QJsonValue &cls)
152{
153 if (isEmptyArray(json: cls, key: "signals"))
154 return QJsonArray();
155
156 auto signalList = getArray(json: cls, key: "signals");
157 if (isEmptyArray(json: cls, key: "properties"))
158 return signalList;
159
160 const auto props = getArray(json: cls, key: "properties");
161 const auto filterNotify = [&props](const QJsonValue &value) {
162 const auto filter = [&value](const QJsonValue &prop) {
163 return getItem(json: value, key: "name") == getItem(json: prop, key: "notify");
164 };
165 return std::find_if(first: props.begin(), last: props.end(), pred: filter) != props.end();
166 };
167 for (auto it = signalList.begin(); it != signalList.end(); /* blank */ ) {
168 if (filterNotify(*it))
169 it = signalList.erase(it);
170 else if (filterNotPublic(*it))
171 it = signalList.erase(it);
172 else
173 it++;
174 }
175 return signalList;
176}
177
178static QJsonArray cleanedSlotList(const QJsonValue &cls)
179{
180 if (isEmptyArray(json: cls, key: "slots"))
181 return QJsonArray();
182
183 auto slotList = getArray(json: cls, key: "slots");
184 if (!isEmptyArray(json: cls, key: "properties"))
185 return slotList;
186
187 const auto props = getArray(json: cls, key: "properties");
188 const auto filterWrite = [&props](const QJsonValue &value) {
189 const auto filter = [&value](const QJsonValue &prop) {
190 const auto args = getArray(json: prop, key: "arguments");
191 return getItem(json: value, key: "name") == getItem(json: prop, key: "write") && args.count() == 1
192 && getItem(json: args.at(i: 0), key: "type") == getItem(json: prop, key: "type");
193 };
194 return std::find_if(first: props.begin(), last: props.end(), pred: filter) != props.end();
195 };
196 for (auto it = slotList.begin(); it != slotList.end(); /* blank */ ) {
197 if (filterWrite(*it))
198 it = slotList.erase(it);
199 else if (filterNotPublic(*it))
200 it = slotList.erase(it);
201 else
202 it++;
203 }
204 return slotList;
205}
206
207QByteArray generateClass(const QJsonValue &cls, bool alwaysGenerateClass)
208{
209 if (getBool(json: cls, key: "gadget", missingValue: false) || alwaysGenerateClass
210 || (isEmptyArray(json: cls, key: "signals") && isEmptyArray(json: cls, key: "slots")))
211 return "POD " + getBytes(json: cls, key: "className") + "("
212 + join(array: generateProperties(properties: getArray(json: cls, key: "properties"), isPod: true), separator: ", ") + ")\n";
213
214 QByteArray ret("class " + getBytes(json: cls, key: "className") + "\n{\n");
215 if (!isEmptyArray(json: cls, key: "properties"))
216 ret += " PROP(" + join(array: generateProperties(properties: getArray(json: cls, key: "properties")), separator: ");\n PROP(")
217 + ");\n";
218 ret += generateFunctions(type: " SLOT", functions: cleanedSlotList(cls));
219 ret += generateFunctions(type: " SIGNAL", functions: cleanedSignalList(cls));
220 ret += "}\n";
221 return ret;
222}
223
224static QList<PODAttribute> propertyList2PODAttributes(const QJsonArray &list)
225{
226 QList<PODAttribute> ret;
227 for (const QJsonValue prop : list)
228 ret.push_back(PODAttribute(getString(json: prop, key: "type"), getString(json: prop, key: "name")));
229 return ret;
230}
231
232QList<ASTProperty> propertyList2AstProperties(const QJsonArray &list)
233{
234 QList<ASTProperty> ret;
235 for (const QJsonValue property : list) {
236 if (!containsKey(json: property, key: "notify") && !getBool(json: property, key: "constant")) {
237 qWarning() << "Skipping property" << getString(json: property, key: "name")
238 << "because it is non-notifiable & non-constant";
239 continue; // skip non-notifiable properties
240 }
241 ASTProperty prop;
242 prop.name = getString(json: property, key: "name");
243 prop.type = getString(json: property, key: "type");
244 prop.modifier = getBool(property, "constant") ? ASTProperty::Constant
245 : !containsKey(property, "write") && containsKey(property, "read")
246 ? ASTProperty::ReadOnly
247 : ASTProperty::ReadWrite;
248 ret.push_back(prop);
249 }
250 return ret;
251}
252
253QList<ASTFunction> functionList2AstFunctionList(const QJsonArray &list)
254{
255 QList<ASTFunction> ret;
256 for (const QJsonValue function : list) {
257 ASTFunction func;
258 func.name = getString(json: function, key: "name");
259 func.returnType = getString(json: function, key: "returnType");
260 const auto arguments = getArray(json: function, key: "arguments");
261 for (const QJsonValue arg : arguments)
262 func.params.push_back(ASTDeclaration(getString(json: arg, key: "type"), getString(json: arg, key: "name")));
263 ret.push_back(func);
264 }
265 return ret;
266}
267
268AST classList2AST(const QJsonArray &classes)
269{
270 AST ret;
271 for (const QJsonValue cls : classes) {
272 if (isEmptyArray(json: cls, key: "signals") && isEmptyArray(json: cls, key: "slots")) {
273 POD pod;
274 pod.name = getString(json: cls, key: "className");
275 pod.attributes = propertyList2PODAttributes(getArray(cls, "properties"));
276 ret.pods.push_back(pod);
277 } else {
278 ASTClass cl(getString(cls, "className"));
279 cl.properties = propertyList2AstProperties(getArray(cls, "properties"));
280 cl.signalsList = functionList2AstFunctionList(cleanedSignalList(cls));
281 cl.slotsList = functionList2AstFunctionList(cleanedSlotList(cls));
282 ret.classes.push_back(cl);
283 }
284 }
285 return ret;
286}
287
288QT_END_NAMESPACE
289

source code of qtremoteobjects/tools/repc/utils.cpp