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  */
24 #include "StateGroupDocGenerator.h"
25 
26 #include <filesystem>
27 #include <fstream>
28 
29 #include <boost/format.hpp>
30 
31 #include <SimoxUtility/algorithm/string/string_tools.h>
32 
35 
36 namespace armarx
37 {
38 
40  {
41  }
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
503  StatechartGroupDocGenerator::setOutputpath(const std::string& outputPath)
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
armarx::StatechartGroupDocGenerator::FindAllStatechartGroupDefinitions
static std::vector< std::string > FindAllStatechartGroupDefinitions(const std::filesystem::path &path)
Definition: StateGroupDocGenerator.cpp:509
armarx::RapidXmlReaderPtr
std::shared_ptr< RapidXmlReader > RapidXmlReaderPtr
Definition: RapidXmlReader.h:67
armarx::RapidXmlReader::FromFile
static RapidXmlReaderPtr FromFile(const std::string &path)
Definition: RapidXmlReader.h:573
StateGroupDocGenerator.h
armarx::StatechartGroupDocGenerator::usageMap
std::map< std::string, std::set< std::string > > usageMap
Definition: StateGroupDocGenerator.h:74
armarx::DoxTransitionGraphPtr
std::shared_ptr< DoxTransitionGraph > DoxTransitionGraphPtr
Definition: DoxTransitiongraph.h:66
str
std::string str(const T &t)
Definition: UserAssistedSegmenterGuiWidgetController.cpp:43
armarx::StatechartGroupDocGenerator::buildReferenceIndex
void buildReferenceIndex(const std::vector< std::string > &groups)
Definition: StateGroupDocGenerator.cpp:454
armarx::StatechartGroupXmlReader::ePublic
@ ePublic
Definition: GroupXmlReader.h:43
index
uint8_t index
Definition: EtherCATFrame.h:59
FileIOException.h
armarx::StatechartGroupDocGenerator::generateDocString
std::string generateDocString(const StatechartGroupXmlReaderPtr &reader) const
Definition: StateGroupDocGenerator.cpp:204
armarx::RapidXmlReaderNode::attribute_value
std::string attribute_value(const char *attrName) const
Definition: RapidXmlReader.h:230
DoxTransitiongraph.h
armarx::CMakePackageFinder
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
Definition: CMakePackageFinder.h:52
armarx::StatechartProfilePtr
std::shared_ptr< class StatechartProfile > StatechartProfilePtr
Definition: StatechartContext.h:51
armarx::StatechartGroupXmlReader::ePrivate
@ ePrivate
Definition: GroupXmlReader.h:42
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:46
armarx::StatechartGroupDocGenerator::StatechartGroupDocGenerator
StatechartGroupDocGenerator()
Definition: StateGroupDocGenerator.cpp:39
armarx::StatechartGroupDocGenerator::getFinder
CMakePackageFinder getFinder(const StatechartGroupXmlReaderPtr &reader)
Definition: StateGroupDocGenerator.cpp:542
armarx::DoxDocPtr
std::shared_ptr< DoxDoc > DoxDocPtr
Definition: DoxDoc.h:40
armarx::StatechartGroupDocGenerator::generateDoxygenFiles
void generateDoxygenFiles(const std::vector< std::string > &groups)
Definition: StateGroupDocGenerator.cpp:494
armarx::DoxTablePtr
std::shared_ptr< DoxTable > DoxTablePtr
Definition: DoxTable.h:35
armarx::StatechartGroupDocGenerator::setOutputpath
void setOutputpath(const std::string &outputPath)
Definition: StateGroupDocGenerator.cpp:503
armarx::StatechartGroupDocGenerator::getUsedProfiles
std::set< std::string > getUsedProfiles(const RapidXmlReaderNode &stateNode) const
Definition: StateGroupDocGenerator.cpp:44
armarx::StatechartGroupXmlReaderPtr
std::shared_ptr< StatechartGroupXmlReader > StatechartGroupXmlReaderPtr
Definition: GroupXmlReader.h:94
armarx::exceptions::local::FileOpenException
Definition: FileIOException.h:59
copy
Use of this software is granted under one of the following two to be chosen freely by the user Boost Software License Version Marcin Kalicinski Permission is hereby free of to any person or organization obtaining a copy of the software and accompanying documentation covered by this and transmit the and to prepare derivative works of the and to permit third parties to whom the Software is furnished to do all subject to the including the above license this restriction and the following must be included in all copies of the in whole or in and all derivative works of the unless such copies or derivative works are solely in the form of machine executable object code generated by a source language processor THE SOFTWARE IS PROVIDED AS WITHOUT WARRANTY OF ANY EXPRESS OR INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF FITNESS FOR A PARTICULAR TITLE AND NON INFRINGEMENT IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER WHETHER IN TORT OR ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE The MIT Marcin Kalicinski Permission is hereby free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to copy
Definition: license.txt:39
armarx::RapidXmlReaderNode
Definition: RapidXmlReader.h:69
armarx::StatechartGroupDocGenerator::finders
std::map< std::string, CMakePackageFinder > finders
Definition: StateGroupDocGenerator.h:71
ARMARX_WARNING_S
#define ARMARX_WARNING_S
Definition: Logging.h:213
armarx::StatechartGroupXmlReader
Definition: GroupXmlReader.h:37
armarx::DoxTransitionGraph
Definition: DoxTransitiongraph.h:68
armarx::StatechartGroupDocGenerator::uuidToStateInfoMap
std::map< std::string, StateInfo > uuidToStateInfoMap
Definition: StateGroupDocGenerator.h:73
armarx::CppWriter
Definition: CppWriter.h:37
armarx::RapidXmlReaderNode::first_node_value_or_default
std::string first_node_value_or_default(const char *name, const std::string &defaultValue) const
Definition: RapidXmlReader.h:436
armarx::RapidXmlReaderNode::nodes
std::vector< RapidXmlReaderNode > nodes(const char *name=nullptr) const
Definition: RapidXmlReader.h:181
armarx::CMakePackageFinder::getBuildDir
std::string getBuildDir() const
Definition: CMakePackageFinder.h:146
armarx::RapidXmlReaderNode::is_valid
bool is_valid() const
Definition: RapidXmlReader.h:457
armarx::CppWriterPtr
std::shared_ptr< CppWriter > CppWriterPtr
Definition: CppWriter.h:35
ARMARX_INFO_S
#define ARMARX_INFO_S
Definition: Logging.h:202
armarx::RapidXmlReaderNode::first_node
RapidXmlReaderNode first_node(const char *name=nullptr) const
Definition: RapidXmlReader.h:156
armarx::DoxDoc
Definition: DoxDoc.h:42
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::StatechartGroupDocGenerator::outputPath
std::string outputPath
Definition: StateGroupDocGenerator.h:72
armarx::StatechartGroupDocGenerator::generateDoxygenFile
void generateDoxygenFile(const std::string &groupDefinitionFilePath)
Definition: StateGroupDocGenerator.cpp:415