1 /**
2 * (C) 2016 - 2017 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
3 * (C) 2016 - 2019 Stanislav Angelovic <angelovic.s@gmail.com>
4 *
5 * @file AdaptorGenerator.cpp
6 *
7 * Created on: Feb 1, 2017
8 * Project: sdbus-c++
9 * Description: High-level D-Bus IPC C++ library based on sd-bus
10 *
11 * This file is part of sdbus-c++.
12 *
13 * sdbus-c++ is free software; you can redistribute it and/or modify it
14 * under the terms of the GNU Lesser General Public License as published by
15 * the Free Software Foundation, either version 2.1 of the License, or
16 * (at your option) any later version.
17 *
18 * sdbus-c++ is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public License
24 * along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
25 */
26
27 // Own
28 #include "generator_utils.h"
29 #include "AdaptorGenerator.h"
30
31 // STL
32 #include <iostream>
33 #include <fstream>
34 #include <cstdlib>
35 #include <algorithm>
36 #include <iterator>
37 #include <cctype>
38
39 using std::endl;
40
41 using sdbuscpp::xml::Document;
42 using sdbuscpp::xml::Node;
43 using sdbuscpp::xml::Nodes;
44
45 /**
46 * Generate adaptor code - server glue
47 */
transformXmlToFileImpl(const Document & doc,const char * filename) const48 int AdaptorGenerator::transformXmlToFileImpl(const Document& doc, const char* filename) const
49 {
50 Node &root = *(doc.root);
51 Nodes interfaces = root["interface"];
52
53 std::ostringstream code;
54 code << createHeader(filename, StubType::ADAPTOR);
55
56 for (const auto& interface : interfaces)
57 {
58 code << processInterface(*interface);
59 }
60
61 code << "#endif" << endl;
62
63 return writeToFile(filename, code.str());
64 }
65
66
processInterface(Node & interface) const67 std::string AdaptorGenerator::processInterface(Node& interface) const
68 {
69 std::string ifaceName = interface.get("name");
70 std::cout << "Generating adaptor code for interface " << ifaceName << endl;
71
72 unsigned int namespacesCount = 0;
73 std::string namespacesStr;
74 std::tie(namespacesCount, namespacesStr) = generateNamespaces(ifaceName);
75
76 std::ostringstream body;
77 body << namespacesStr;
78
79 std::string className = ifaceName.substr(ifaceName.find_last_of(".") + 1)
80 + "_adaptor";
81
82 body << "class " << className << endl
83 << "{" << endl
84 << "public:" << endl
85 << tab << "static constexpr const char* INTERFACE_NAME = \"" << ifaceName << "\";" << endl << endl
86 << "protected:" << endl
87 << tab << className << "(sdbus::IObject& object)" << endl
88 << tab << tab << ": object_(object)" << endl;
89
90 Nodes methods = interface["method"];
91 Nodes signals = interface["signal"];
92 Nodes properties = interface["property"];
93
94 auto annotations = getAnnotations(interface);
95 std::string annotationRegistration;
96 for (const auto& annotation : annotations)
97 {
98 const auto& annotationName = annotation.first;
99 const auto& annotationValue = annotation.second;
100
101 if (annotationName == "org.freedesktop.DBus.Deprecated" && annotationValue == "true")
102 annotationRegistration += ".markAsDeprecated()";
103 else if (annotationName == "org.freedesktop.systemd1.Privileged" && annotationValue == "true")
104 annotationRegistration += ".markAsPrivileged()";
105 else if (annotationName == "org.freedesktop.DBus.Property.EmitsChangedSignal")
106 annotationRegistration += ".withPropertyUpdateBehavior(" + propertyAnnotationToFlag(annotationValue) + ")";
107 else
108 std::cerr << "Node: " << ifaceName << ": "
109 << "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
110 }
111 if(!annotationRegistration.empty())
112 {
113 std::stringstream str;
114 str << tab << tab << "object_.setInterfaceFlags(INTERFACE_NAME)" << annotationRegistration << ";" << endl;
115 annotationRegistration = str.str();
116 }
117
118 std::string methodRegistration, methodDeclaration;
119 std::tie(methodRegistration, methodDeclaration) = processMethods(methods);
120
121 std::string signalRegistration, signalMethods;
122 std::tie(signalRegistration, signalMethods) = processSignals(signals);
123
124 std::string propertyRegistration, propertyAccessorDeclaration;
125 std::tie(propertyRegistration, propertyAccessorDeclaration) = processProperties(properties);
126
127 body << tab << "{" << endl
128 << annotationRegistration
129 << methodRegistration
130 << signalRegistration
131 << propertyRegistration
132 << tab << "}" << endl << endl;
133
134 body << tab << "~" << className << "() = default;" << endl << endl;
135
136 if (!signalMethods.empty())
137 {
138 body << "public:" << endl << signalMethods;
139 }
140
141 if (!methodDeclaration.empty())
142 {
143 body << "private:" << endl << methodDeclaration << endl;
144 }
145
146 if (!propertyAccessorDeclaration.empty())
147 {
148 body << "private:" << endl << propertyAccessorDeclaration << endl;
149 }
150
151 body << "private:" << endl
152 << tab << "sdbus::IObject& object_;" << endl
153 << "};" << endl << endl
154 << std::string(namespacesCount, '}') << " // namespaces" << endl << endl;
155
156 return body.str();
157 }
158
159
processMethods(const Nodes & methods) const160 std::tuple<std::string, std::string> AdaptorGenerator::processMethods(const Nodes& methods) const
161 {
162 std::ostringstream registrationSS, declarationSS;
163
164 for (const auto& method : methods)
165 {
166 auto methodName = method->get("name");
167 auto methodNameSafe = mangle_name(methodName);
168
169 auto annotations = getAnnotations(*method);
170 bool async{false};
171 std::string annotationRegistration;
172 for (const auto& annotation : annotations)
173 {
174 const auto& annotationName = annotation.first;
175 const auto& annotationValue = annotation.second;
176
177 if (annotationName == "org.freedesktop.DBus.Deprecated")
178 {
179 if (annotationValue == "true")
180 annotationRegistration += ".markAsDeprecated()";
181 }
182 else if (annotationName == "org.freedesktop.DBus.Method.NoReply")
183 {
184 if (annotationValue == "true")
185 annotationRegistration += ".withNoReply()";
186 }
187 else if (annotationName == "org.freedesktop.DBus.Method.Async")
188 {
189 if (annotationValue == "server" || annotationValue == "clientserver")
190 async = true;
191 }
192 else if (annotationName == "org.freedesktop.systemd1.Privileged")
193 {
194 if (annotationValue == "true")
195 annotationRegistration += ".markAsPrivileged()";
196 }
197 else if (annotationName != "org.freedesktop.DBus.Method.Timeout") // Whatever else...
198 {
199 std::cerr << "Node: " << methodName << ": "
200 << "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
201 }
202 }
203
204 Nodes args = (*method)["arg"];
205 Nodes inArgs = args.select("direction" , "in");
206 Nodes outArgs = args.select("direction" , "out");
207
208 std::string argStr, argTypeStr, argStringsStr, outArgStringsStr;
209 std::tie(argStr, argTypeStr, std::ignore, argStringsStr) = argsToNamesAndTypes(inArgs, async);
210 std::tie(std::ignore, std::ignore, std::ignore, outArgStringsStr) = argsToNamesAndTypes(outArgs);
211
212 using namespace std::string_literals;
213
214 registrationSS << tab << tab << "object_.registerMethod(\""
215 << methodName << "\")"
216 << ".onInterface(INTERFACE_NAME)"
217 << (!argStringsStr.empty() ? (".withInputParamNames(" + argStringsStr + ")") : "")
218 << (!outArgStringsStr.empty() ? (".withOutputParamNames(" + outArgStringsStr + ")") : "")
219 << ".implementedAs("
220 << "[this]("
221 << (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
222 << argTypeStr
223 << "){ " << (async ? "" : "return ") << "this->" << methodNameSafe << "("
224 << (async ? "std::move(result)"s + (argTypeStr.empty() ? "" : ", ") : "")
225 << argStr << "); })"
226 << annotationRegistration << ";" << endl;
227
228 declarationSS << tab
229 << "virtual "
230 << (async ? "void" : outArgsToType(outArgs))
231 << " " << methodNameSafe
232 << "("
233 << (async ? "sdbus::Result<" + outArgsToType(outArgs, true) + ">&& result" + (argTypeStr.empty() ? "" : ", ") : "")
234 << argTypeStr
235 << ") = 0;" << endl;
236 }
237
238 return std::make_tuple(registrationSS.str(), declarationSS.str());
239 }
240
241
processSignals(const Nodes & signals) const242 std::tuple<std::string, std::string> AdaptorGenerator::processSignals(const Nodes& signals) const
243 {
244 std::ostringstream signalRegistrationSS, signalMethodSS;
245
246 for (const auto& signal : signals)
247 {
248 auto name = signal->get("name");
249
250 auto annotations = getAnnotations(*signal);
251 std::string annotationRegistration;
252 for (const auto& annotation : annotations)
253 {
254 const auto& annotationName = annotation.first;
255 const auto& annotationValue = annotation.second;
256
257 if (annotationName == "org.freedesktop.DBus.Deprecated" && annotationValue == "true")
258 annotationRegistration += ".markAsDeprecated()";
259 else
260 std::cerr << "Node: " << name << ": "
261 << "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
262 }
263
264 Nodes args = (*signal)["arg"];
265
266 std::string argStr, argTypeStr, typeStr, argStringsStr;
267 std::tie(argStr, argTypeStr, typeStr, argStringsStr) = argsToNamesAndTypes(args);
268
269 signalRegistrationSS << tab << tab
270 << "object_.registerSignal(\"" << name << "\")"
271 ".onInterface(INTERFACE_NAME)";
272
273 if (args.size() > 0)
274 {
275 signalRegistrationSS << ".withParameters<" << typeStr << ">(" << argStringsStr << ")";
276 }
277
278 signalRegistrationSS << annotationRegistration;
279 signalRegistrationSS << ";" << endl;
280
281 auto nameWithCapFirstLetter = name;
282 nameWithCapFirstLetter[0] = std::toupper(nameWithCapFirstLetter[0]);
283 nameWithCapFirstLetter = mangle_name(nameWithCapFirstLetter);
284
285 signalMethodSS << tab << "void emit" << nameWithCapFirstLetter << "(" << argTypeStr << ")" << endl
286 << tab << "{" << endl
287 << tab << tab << "object_.emitSignal(\"" << name << "\")"
288 ".onInterface(INTERFACE_NAME)";
289
290 if (!argStr.empty())
291 {
292 signalMethodSS << ".withArguments(" << argStr << ")";
293 }
294
295 signalMethodSS << ";" << endl
296 << tab << "}" << endl << endl;
297 }
298
299 return std::make_tuple(signalRegistrationSS.str(), signalMethodSS.str());
300 }
301
302
processProperties(const Nodes & properties) const303 std::tuple<std::string, std::string> AdaptorGenerator::processProperties(const Nodes& properties) const
304 {
305 std::ostringstream registrationSS, declarationSS;
306
307 for (const auto& property : properties)
308 {
309 auto propertyName = property->get("name");
310 auto propertyNameSafe = mangle_name(propertyName);
311 auto propertyAccess = property->get("access");
312 auto propertySignature = property->get("type");
313
314 auto propertyType = signature_to_type(propertySignature);
315 auto propertyArg = std::string("value");
316 auto propertyTypeArg = std::string("const ") + propertyType + "& " + propertyArg;
317
318 auto annotations = getAnnotations(*property);
319 std::string annotationRegistration;
320 for (const auto& annotation : annotations)
321 {
322 const auto& annotationName = annotation.first;
323 const auto& annotationValue = annotation.second;
324
325 if (annotationName == "org.freedesktop.DBus.Deprecated" && annotationValue == "true")
326 annotationRegistration += ".markAsDeprecated()";
327 else if (annotationName == "org.freedesktop.DBus.Property.EmitsChangedSignal")
328 annotationRegistration += ".withUpdateBehavior(" + propertyAnnotationToFlag(annotationValue) + ")";
329 else if (annotationName == "org.freedesktop.systemd1.Privileged" && annotationValue == "true")
330 annotationRegistration += ".markAsPrivileged()";
331 else
332 std::cerr << "Node: " << propertyName << ": "
333 << "Option '" << annotationName << "' not allowed or supported in this context! Option ignored..." << std::endl;
334 }
335
336 registrationSS << tab << tab << "object_.registerProperty(\""
337 << propertyName << "\")"
338 << ".onInterface(INTERFACE_NAME)";
339
340 if (propertyAccess == "read" || propertyAccess == "readwrite")
341 {
342 registrationSS << ".withGetter([this](){ return this->" << propertyNameSafe << "(); })";
343 }
344
345 if (propertyAccess == "readwrite" || propertyAccess == "write")
346 {
347 registrationSS
348 << ".withSetter([this](" << propertyTypeArg << ")"
349 "{ this->" << propertyNameSafe << "(" << propertyArg << "); })";
350 }
351
352 registrationSS << annotationRegistration;
353 registrationSS << ";" << endl;
354
355 if (propertyAccess == "read" || propertyAccess == "readwrite")
356 declarationSS << tab << "virtual " << propertyType << " " << propertyNameSafe << "() = 0;" << endl;
357 if (propertyAccess == "readwrite" || propertyAccess == "write")
358 declarationSS << tab << "virtual void " << propertyNameSafe << "(" << propertyTypeArg << ") = 0;" << endl;
359 }
360
361 return std::make_tuple(registrationSS.str(), declarationSS.str());
362 }
363
getAnnotations(sdbuscpp::xml::Node & node) const364 std::map<std::string, std::string> AdaptorGenerator::getAnnotations( sdbuscpp::xml::Node& node) const
365 {
366 std::map<std::string, std::string> result;
367
368 Nodes annotations = (node)["annotation"];
369 for (const auto& annotation : annotations)
370 {
371 result[annotation->get("name")] = annotation->get("value");
372 }
373
374 return result;
375 }
376
propertyAnnotationToFlag(const std::string & annotationValue) const377 std::string AdaptorGenerator::propertyAnnotationToFlag(const std::string& annotationValue) const
378 {
379 return annotationValue == "true" ? "sdbus::Flags::EMITS_CHANGE_SIGNAL"
380 : annotationValue == "invalidates" ? "sdbus::Flags::EMITS_INVALIDATION_SIGNAL"
381 : annotationValue == "const" ? "sdbus::Flags::CONST_PROPERTY_VALUE"
382 : annotationValue == "false" ? "sdbus::Flags::EMITS_NO_SIGNAL"
383 : "EMITS_CHANGE_SIGNAL"; // Default
384 }
385