StateGroupDocGenerator.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
25
26#include <filesystem>
27#include <fstream>
28
29#include <boost/format.hpp>
30
31#include <SimoxUtility/algorithm/string/string_tools.h>
32
35
36namespace armarx
37{
38
42
43 std::set<std::string>
45 {
46 std::set<std::string> usedProfiles;
47
48 for (RapidXmlReaderNode defaultNode :
49 stateNode.nodes("InputParameters", "Parameter", "DefaultValue"))
50 {
51 usedProfiles.insert(defaultNode.attribute_value_or_default("profile", "Root"));
52 }
53
54 for (RapidXmlReaderNode defaultNode :
55 stateNode.nodes("OutputParameters", "Parameter", "DefaultValue"))
56 {
57 usedProfiles.insert(defaultNode.attribute_value_or_default("profile", "Root"));
58 }
59
60 for (RapidXmlReaderNode defaultNode :
61 stateNode.nodes("LocalParameters", "Parameter", "DefaultValue"))
62 {
63 usedProfiles.insert(defaultNode.attribute_value_or_default("profile", "Root"));
64 }
65
66 return usedProfiles;
67 }
68
69 std::string
70 StatechartGroupDocGenerator::unescapeString(std::string str) const
71 {
72 std::string res;
73 std::string::const_iterator it = str.begin();
74
75 while (it != str.end())
76 {
77 char c = *it++;
78
79 if (c == '\\' && it != str.end())
80 {
81 switch (*it++)
82 {
83 case '\\':
84 c = '\\';
85 break;
86
87 case 'r':
88 c = '\r';
89 break;
90
91 case 'n':
92 c = '\n';
93 break;
94
95 case 't':
96 c = '\t';
97 break;
98
99 // all other escapes
100 default:
101 // invalid escape sequence - skip it. alternatively you can copy it as is, throw an exception...
102 continue;
103 }
104 }
105
106 res += c;
107 }
108
109 return res;
110 }
111
112 std::string
113 StatechartGroupDocGenerator::nToBr(std::string str) const
114 {
115 return simox::alg::replace_all(str, "\n", "<br />\n");
116 }
117
119 StatechartGroupDocGenerator::buildParameterTable(RapidXmlReaderNode parametersNode) const
120 {
121 std::set<std::string> usedProfiles;
122
123 for (RapidXmlReaderNode defaultNode : parametersNode.nodes("Parameter", "DefaultValue"))
124 {
125 usedProfiles.insert(defaultNode.attribute_value_or_default("profile", "Root"));
126 }
127
128 std::vector<std::string> header;
129
130 header.push_back("Name");
131 header.push_back("Type");
132 header.push_back("Description");
133 std::copy(usedProfiles.begin(), usedProfiles.end(), std::back_inserter(header));
134 DoxTablePtr table(new DoxTable(header));
135
136 for (RapidXmlReaderNode parameterNode : parametersNode.nodes("Parameter"))
137 {
138 std::vector<std::string> row(header.size());
139 row.at(0) = parameterNode.attribute_value("name");
140 row.at(1) = parameterNode.has_attribute("docType")
141 ? parameterNode.attribute_value("docType")
142 : parameterNode.attribute_value("type");
143 row.at(2) = nToBr(parameterNode.first_node_value_or_default("Description", ""));
144
145 for (RapidXmlReaderNode defaultNode : parameterNode.nodes("DefaultValue"))
146 {
147 int index = std::find(header.begin(),
148 header.end(),
149 defaultNode.attribute_value_or_default("profile", "Root")) -
150 header.begin();
151 std::string defaultValue = defaultNode.has_attribute("docValue")
152 ? defaultNode.attribute_value("docValue")
153 : defaultNode.attribute_value("value");
154 row.at(index) = nToBr(unescapeString(defaultValue));
155 }
156
157 table->addRow(row);
158 }
159
160 return table;
161 }
162
163 void
164 StatechartGroupDocGenerator::addParameterTable(const DoxDocPtr& doc,
165 const RapidXmlReaderNode& stateNode,
166 std::string name,
167 const char* nodeName) const
168 {
169 DoxTablePtr table = buildParameterTable(stateNode.first_node(nodeName));
170
171 if (table->rows() == 0)
172 {
173 doc->addLine("No " + name + ".<br />");
174 }
175 else
176 {
177 doc->addLine(name + ":");
178 doc->addEntry(table);
179 }
180 }
181
182 std::string
183 StatechartGroupDocGenerator::getNodeAttrsFromStateType(const std::string& nodeType) const
184 {
185 if (nodeType == "EndState")
186 {
187 return "shape=box,style=filled,fillcolor=\"#FFFF96\"";
188 }
189
190 if (nodeType == "RemoteState")
191 {
192 return "shape=box,style=filled,fillcolor=\"#96FFFA\"";
193 }
194
195 if (nodeType == "DynamicRemoteState")
196 {
197 return "shape=box,style=filled,fillcolor=\"#B147EA\"";
198 }
199
200 return "shape=box,style=filled,fillcolor=\"#B3D7FF\"";
201 }
202
203 std::string
205 {
206 DoxDocPtr doc(new DoxDoc);
207 std::string packageName = reader->getPackageName();
208 std::string groupName = reader->getGroupName();
209 doc->addLine(fmt("@defgroup %s-%s %s", packageName, groupName, groupName));
210 doc->addLine(fmt("@ingroup Statecharts %s-Statecharts", packageName));
211 doc->addLine(nToBr(reader->getDescription()));
212
213
214 bool anyPrivateStateFound = false;
215
216 for (std::string statefile : reader->getStateFilepaths())
217 {
218 if (reader->getStateVisibility(statefile) == StatechartGroupXmlReader::ePrivate)
219 {
220 anyPrivateStateFound = true;
221 }
222 }
223
224 if (anyPrivateStateFound)
225 {
226 doc->addLine();
227 doc->addLine(fmt("@defgroup %s-%s-PrivateStates %s Private States",
228 packageName,
229 groupName,
230 groupName));
231 doc->addLine(fmt("@ingroup %s-%s", packageName, groupName));
232 }
233
234 for (std::string statefile : reader->getStateFilepaths())
235 {
236 RapidXmlReaderPtr stateReader = RapidXmlReader::FromFile(statefile);
237 RapidXmlReaderNode stateNode = stateReader->getRoot("State");
238 std::string stateName = stateNode.attribute_value("name");
239 std::string stateUuid = stateNode.attribute_value("uuid");
240
241 doc->addLine();
242 //doc->addLine(fmt("@defgroup %s-%s-%s %s", packageName, groupName, stateName, stateName));
243 doc->addLine(fmt("@defgroup State-%s %s", stateUuid, stateName));
244 doc->addLine(
245 fmt("@ingroup %s-%s%s",
246 packageName,
247 groupName,
248 reader->getStateVisibility(statefile) == StatechartGroupXmlReader::ePublic
249 ? ""
250 : "-PrivateStates"));
251 doc->addLine("@brief " + stateNode.first_node_value_or_default(
252 "Description", "This state has no description."));
253 doc->addLine();
254
255 addParameterTable(doc, stateNode, "\\b Input \\b Parameters", "InputParameters");
256 addParameterTable(doc, stateNode, "\\b Local \\b Parameters", "LocalParameters");
257 addParameterTable(doc, stateNode, "\\b Output \\b Parameters", "OutputParameters");
258
259 doc->addLine("<br/>\\b State \\b used by:<br />");
260
261 if (usageMap.count(stateUuid) > 0)
262 {
263 std::set<std::string> users = usageMap.find(stateUuid)->second;
264
265 for (std::string uuid : users)
266 {
267 doc->addLine(fmt("@ref State-%s <br />", uuid));
268 }
269 }
270
271 doc->addLine("<br/>\\b Substates:<br />");
272
273 for (RapidXmlReaderNode substateNode : stateNode.nodes("Substates", nullptr))
274 {
275 if (substateNode.name() == "LocalState" || substateNode.name() == "RemoteState" ||
276 substateNode.name() == "DynamicRemoteState")
277 {
278 std::string name = substateNode.attribute_value("name");
279 std::string refuuid = substateNode.attribute_value("refuuid");
280 std::map<std::string, StateInfo>::const_iterator it =
281 uuidToStateInfoMap.find(refuuid);
282
283 if (it == uuidToStateInfoMap.end())
284 {
285 doc->addLine(
286 fmt("@ref State-%s \"%s (External Package)\"<br />", refuuid, name));
287 }
288 else
289 {
290 StateInfo stateInfo = it->second;
291
292 if (stateInfo.group == groupName && stateInfo.state == name)
293 {
294 doc->addLine(fmt("@ref State-%s<br />", refuuid));
295 }
296 else if (stateInfo.group == groupName)
297 {
298 doc->addLine(fmt("%s -> @ref State-%s \"%s\"<br />",
299 name,
300 refuuid,
301 stateInfo.state));
302 }
303 else
304 {
305 doc->addLine(fmt("%s -> @ref State-%s \"%s-%s\"<br />",
306 name,
307 refuuid,
308 stateInfo.group,
309 stateInfo.state));
310 }
311 }
312 }
313 }
314
315 doc->addLine("<br/>\\b Statechart \\b layout:");
317 graph->addNode("", "shape=point");
318 RapidXmlReaderNode startStateNode = stateNode.first_node("StartState");
319
320 if (startStateNode.is_valid())
321 {
322 graph->addTransition("", startStateNode.attribute_value("substateName"), "");
323 }
324
325 for (RapidXmlReaderNode substateNode : stateNode.nodes("Substates", nullptr))
326 {
327 std::string shape = getNodeAttrsFromStateType(substateNode.name());
328 graph->addNode(substateNode.attribute_value("name"), shape);
329 }
330
331 for (RapidXmlReaderNode transition : stateNode.nodes("Transitions", "Transition"))
332 {
333 if (transition.has_attribute("from") && transition.has_attribute("to") &&
334 transition.has_attribute("eventName"))
335 {
336 std::string from = transition.attribute_value_or_default("from", "");
337 std::string to = transition.attribute_value_or_default("to", "");
338 std::string eventName = transition.attribute_value_or_default("eventName", "");
339
340 graph->addTransition(from, to, eventName);
341 }
342 }
343
344 if (graph->transitionCount() > 0)
345 {
346 doc->addEntry(graph);
347 }
348
349
350 /*std::vector<std::string> header;
351
352 header.push_back("Name");
353 header.push_back("Type");
354 header.push_back("Description");
355 std::copy(usedProfiles.begin(), usedProfiles.end(), std::back_inserter(header));
356 DoxTablePtr table(new DoxTable(header));
357
358 for(RapidXmlReaderNode parameterNode : stateNode.nodes("InputParameters", "Parameter"))
359 {
360 std::vector<std::string> row(header.size());
361 row.at(0) = parameterNode.attribute_value("name");
362 row.at(1) = parameterNode.attribute_value("type");
363 row.at(2) = parameterNode.first_node_value_or_default("Description", "");
364 for(RapidXmlReaderNode defaultNode : parameterNode.nodes("DefaultValue"))
365 {
366 int index = std::find(header.begin(), header.end(), defaultNode.attribute_value_or_default("profile", "Root")) - header.begin();
367 row.at(index) = defaultNode.has_attribute("docValue") ? defaultNode.attribute_value("docValue") : defaultNode.attribute_value("value");
368 }
369 table->addRow(row);
370 }
371
372 doc->addEntry(table);*/
373 }
374
375
376 CppWriterPtr writer(new CppWriter(true));
377 doc->writeDoc(writer);
378 return writer->getString();
379 }
380
381 std::string
382 StatechartGroupDocGenerator::fmt(const std::string& fmt, const std::string& arg1) const
383 {
384 return boost::str(boost::format(fmt) % arg1);
385 }
386
387 std::string
388 StatechartGroupDocGenerator::fmt(const std::string& fmt,
389 const std::string& arg1,
390 const std::string& arg2) const
391 {
392 return boost::str(boost::format(fmt) % arg1 % arg2);
393 }
394
395 std::string
396 StatechartGroupDocGenerator::fmt(const std::string& fmt,
397 const std::string& arg1,
398 const std::string& arg2,
399 const std::string& arg3) const
400 {
401 return boost::str(boost::format(fmt) % arg1 % arg2 % arg3);
402 }
403
404 std::string
405 StatechartGroupDocGenerator::fmt(const std::string& fmt,
406 const std::string& arg1,
407 const std::string& arg2,
408 const std::string& arg3,
409 const std::string& arg4) const
410 {
411 return boost::str(boost::format(fmt) % arg1 % arg2 % arg3 % arg4);
412 }
413
414 void
415 StatechartGroupDocGenerator::generateDoxygenFile(const std::string& groupDefinitionFilePath)
416 {
418 reader->readXml(std::filesystem::path(groupDefinitionFilePath));
419 CMakePackageFinder finder = getFinder(reader);
420
421 std::filesystem::path doxyFile = finder.getBuildDir();
422
423 if (!outputPath.empty())
424 {
425 doxyFile = outputPath;
426 }
427
428 if (!doxyFile.string().empty())
429 {
430 doxyFile /= "doxygen";
431 }
432 else
433 {
434 doxyFile = ".";
435 }
436
437 doxyFile /= reader->getGroupName() + ".dox";
438
439 std::ofstream file;
440 file.open(doxyFile.string().c_str());
441
442 if (!file.is_open())
443 {
444 throw armarx::exceptions::local::FileOpenException(doxyFile.string());
445 }
446
447 std::string docstr = generateDocString(reader);
448 file << docstr;
449
450 ARMARX_INFO_S << "wrote " << docstr.size() << " bytes to " << doxyFile.string();
451 }
452
453 void
454 StatechartGroupDocGenerator::buildReferenceIndex(const std::vector<std::string>& groups)
455 {
456 for (std::string groupDefinitionFilePath : groups)
457 {
460 reader->readXml(std::filesystem::path(groupDefinitionFilePath));
461
462 for (std::string statefile : reader->getStateFilepaths())
463 {
464 RapidXmlReaderPtr stateReader = RapidXmlReader::FromFile(statefile);
465 RapidXmlReaderNode stateNode = stateReader->getRoot("State");
466 std::string stateName = stateNode.attribute_value("name");
467 std::string stateUuid = stateNode.attribute_value("uuid");
468 uuidToStateInfoMap.insert(std::make_pair(
469 stateUuid,
470 StateInfo(reader->getPackageName(), reader->getGroupName(), stateName)));
471
472 for (RapidXmlReaderNode substateNode : stateNode.nodes("Substates", nullptr))
473 {
474 if (substateNode.name() == "LocalState" ||
475 substateNode.name() == "RemoteState" ||
476 substateNode.name() == "DynamicRemoteState")
477 {
478 //std::string name = substateNode.attribute_value("name");
479 std::string refuuid = substateNode.attribute_value("refuuid");
480
481 if (usageMap.count(refuuid) == 0)
482 {
483 usageMap.insert(std::make_pair(refuuid, std::set<std::string>()));
484 }
485
486 usageMap[refuuid].insert(stateUuid);
487 }
488 }
489 }
490 }
491 }
492
493 void
494 StatechartGroupDocGenerator::generateDoxygenFiles(const std::vector<std::string>& groups)
495 {
496 for (std::string groupDefinitionFilePath : groups)
497 {
498 generateDoxygenFile(groupDefinitionFilePath);
499 }
500 }
501
502 void
504 {
505 this->outputPath = outputPath;
506 }
507
508 std::vector<std::string>
510 const std::filesystem::path& path)
511 {
512 std::vector<std::string> result;
513
514 try
515 {
516 int i = 0;
517
518 for (std::filesystem::recursive_directory_iterator end, dir(path); dir != end;
519 ++dir, i++)
520 {
521 if (dir->path().extension() == ".scgxml" &&
522 dir->path().string().find("deprecated") == std::string::npos)
523 {
524 result.push_back(dir->path().c_str());
525 }
526
527 if (i % 100 == 0)
528 {
529 ARMARX_INFO_S << "Scanning file " << i << ": " << dir->path().c_str();
530 }
531 }
532 }
533 catch (std::exception& e)
534 {
535 ARMARX_WARNING_S << "Invalid filepath: " << e.what();
536 }
537
538 return result;
539 }
540
543 {
544 auto itFinders = finders.find(reader->getPackageName());
545
546 if (itFinders == finders.end())
547 {
548 itFinders = finders
549 .insert(std::make_pair(reader->getPackageName(),
550 CMakePackageFinder(reader->getPackageName())))
551 .first;
552 }
553
554 return itFinders->second;
555 }
556
557
558} // namespace armarx
uint8_t index
constexpr T c
std::string str(const T &t)
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
std::string getBuildDir() const
RapidXmlReaderNode first_node(const char *name=nullptr) const
std::vector< RapidXmlReaderNode > nodes(const char *name=nullptr) const
std::string first_node_value_or_default(const char *name, const std::string &defaultValue) const
std::string attribute_value(const char *attrName) const
static RapidXmlReaderPtr FromFile(const std::string &path)
void setOutputpath(const std::string &outputPath)
std::set< std::string > getUsedProfiles(const RapidXmlReaderNode &stateNode) const
std::map< std::string, StateInfo > uuidToStateInfoMap
void generateDoxygenFiles(const std::vector< std::string > &groups)
CMakePackageFinder getFinder(const StatechartGroupXmlReaderPtr &reader)
static std::vector< std::string > FindAllStatechartGroupDefinitions(const std::filesystem::path &path)
std::map< std::string, std::set< std::string > > usageMap
std::string generateDocString(const StatechartGroupXmlReaderPtr &reader) const
void buildReferenceIndex(const std::vector< std::string > &groups)
void generateDoxygenFile(const std::string &groupDefinitionFilePath)
std::map< std::string, CMakePackageFinder > finders
#define ARMARX_INFO_S
Definition Logging.h:202
#define ARMARX_WARNING_S
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:213
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::shared_ptr< DoxDoc > DoxDocPtr
Definition DoxDoc.h:40
std::shared_ptr< StatechartGroupXmlReader > StatechartGroupXmlReaderPtr
std::shared_ptr< RapidXmlReader > RapidXmlReaderPtr
std::shared_ptr< CppWriter > CppWriterPtr
Definition CppWriter.h:35
std::shared_ptr< class StatechartProfile > StatechartProfilePtr
std::shared_ptr< DoxTransitionGraph > DoxTransitionGraphPtr
std::shared_ptr< DoxTable > DoxTablePtr
Definition DoxTable.h:35