preCICE v3.2.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Printer.cpp
Go to the documentation of this file.
1#include "xml/Printer.hpp"
2#include <algorithm>
3#include <cctype>
4#include <fmt/format.h>
5#include <map>
6#include <regex>
7#include <set>
8#include <sstream>
9#include <string>
10#include <utility>
11#include <vector>
12#include "utils/String.hpp"
13#include "utils/TypeNames.hpp"
14#include "xml/XMLAttribute.hpp"
15#include "xml/XMLTag.hpp"
16
17namespace precice::xml {
18
19namespace {
20
22
24std::string toGHLink(const std::string &heading)
25{
26 try {
27 std::regex sanitizer{"[^a-zA-Z0-9-]"};
28 std::regex spaces{"\\s"};
29
30 // sanitize the heading
31 std::string sanitized = std::regex_replace(std::regex_replace(heading, sanitizer, ""), spaces, "-");
32
33 // convert to lowercase
34 std::transform(sanitized.begin(), sanitized.end(), sanitized.begin(),
35 [](unsigned char c) { return std::tolower(c); });
36 return "#" + sanitized;
37
38 } catch (const std::regex_error &e) {
39 std::cerr << "Error sanitizing link: " << e.what() << '\n';
40 std::exit(-1);
41 }
42}
43
45
47template <typename ATTRIBUTE_T>
48std::ostream &printDTD(std::ostream &out, const XMLAttribute<ATTRIBUTE_T> &attr, const std::string &ElementName)
49{
50 out << "<!ATTLIST " << ElementName << " " << attr.getName() << " CDATA ";
51
52 if (attr.hasDefaultValue()) {
53 out << "\"" << attr.getDefaultValue() << "\"";
54 } else {
55 out << "#REQUIRED";
56 }
57
58 out << ">\n";
59 return out;
60}
61
63template <typename ATTRIBUTE_T>
64std::ostream &printMD(std::ostream &out, const XMLAttribute<ATTRIBUTE_T> &attr)
65{
66 fmt::print(out,
67 "| {} | {} | {} |",
68 attr.getName(),
69 utils::getTypeName(attr.getDefaultValue()),
70 attr.getUserDocumentation());
71
72 if (attr.hasDefaultValue()) {
73 fmt::print(out, " `{}` |", attr.getDefaultValue());
74 } else {
75 out << " _none_ |";
76 }
77
78 const auto &options = attr.getOptions();
79 if (options.empty()) {
80 out << " none |";
81 } else {
82 fmt::print(out, " `{}` |", fmt::join(options, "`, `"));
83 }
84 return out;
85}
86
88template <typename ATTRIBUTE_T>
89std::ostream &printExample(std::ostream &out, const XMLAttribute<ATTRIBUTE_T> &attr)
90{
91 out << attr.getName() << "=\"";
92 if (attr.hasDefaultValue()) {
93 fmt::print(out, "{}", attr.getDefaultValue());
94 } else {
95 out << '{' << utils::getTypeName(attr.getDefaultValue()) << '}';
96 }
97 out << '"';
98 return out;
99}
100
102template <typename ATTRIBUTE_T>
103std::ostream &printDocumentation(std::ostream &out, const XMLAttribute<ATTRIBUTE_T> &attr)
104{
105 out << attr.getName() << "=\"{" << utils::getTypeName(attr.getDefaultValue());
106 if (attr.hasValidation()) {
107 out << ":";
108 // print the first item
109 auto first = attr.getOptions().begin();
110 out << '\'' << *first << '\'';
111 ++first;
112 // print the remaining items with separator
113 for (; first != attr.getOptions().end(); ++first) {
114 out << " or '" << *first << '\'';
115 }
116 }
117 out << "}";
118 if (attr.hasDefaultValue()) {
119 out << "(default:'" << attr.getDefaultValue() << "')";
120 }
121 out << "\"";
122 return out;
123}
124
126
130std::ostream &printDTD(std::ostream &out, const XMLTag &tag, bool start = false)
131{
132 if (start)
133 out << "<!DOCTYPE " << tag.getFullName() << " [\n";
134
135 out << "<!ELEMENT " << tag.getFullName() << " ";
136
137 if (not tag.getSubtags().empty()) {
138
139 out << "(";
140
141 bool first = true;
142 for (auto const &subtag : tag.getSubtags()) {
143
144 std::string occurrenceChar;
145
146 XMLTag::Occurrence occ = subtag->getOccurrence();
147
148 if (occ == XMLTag::OCCUR_ARBITRARY)
149 occurrenceChar = "*";
150 else if (occ == XMLTag::OCCUR_NOT_OR_ONCE)
151 occurrenceChar = "?";
152 else if (occ == XMLTag::OCCUR_ONCE_OR_MORE)
153 occurrenceChar = "+";
154
155 out << (first ? "" : ", ") << subtag->getFullName() << occurrenceChar;
156 first = false;
157 }
158
159 out << ")>\n";
160 } else {
161 out << "EMPTY>\n";
162 }
163
164 for (const auto &attribute : tag.getAttributes()) {
165 std::visit([&out, &tag](const auto &attribute) { printDTD(out, attribute, tag.getFullName()); }, attribute);
166 }
167
168 if (not tag.getSubtags().empty()) {
169 for (const auto &subtag : tag.getSubtags()) {
170 printDTD(out, *subtag);
171 }
172 }
173
174 out << '\n';
175
176 if (start)
177 out << "]>\n";
178 return out;
179}
180
186std::ostream &printExample(std::ostream &out, const XMLTag &tag, int level)
187{
188 std::string prefix(level * 2, ' ');
189 out << prefix << '<' << tag.getFullName();
190 for (const auto &attribute : tag.getAttributes()) {
191 out << ' ';
192 std::visit([&out](const auto &attribute) { printExample(out, attribute); }, attribute);
193 }
194 if (tag.getSubtags().empty()) {
195 out << "/>";
196 return out;
197 }
198 out << ">\n";
199
200 constexpr int threshold{1};
201 if (level >= threshold) {
202 out << std::string((level + 1) * 2, ' ') << "...\n";
203 } else {
204 std::set<std::string> namespaces;
205 for (const auto &subtag : tag.getSubtags()) {
206 const auto ns = subtag->getNamespace();
207 if (!ns.empty()) {
208 if (namespaces.count(subtag->getNamespace()) > 0) {
209 continue;
210 }
211 namespaces.emplace(ns);
212 }
213 printExample(out, *subtag, level + 1) << '\n';
214 }
215 }
216
217 out << prefix << "</" << tag.getFullName() << '>';
218 return out;
219}
220
227std::ostream &printMD(std::ostream &out, const XMLTag &tag, int level, std::map<std::string, int> &occurrences)
228{
229 out << std::string(level, '#') << ' ' << tag.getFullName() << "\n\n";
230
231 out << tag.getDocumentation() << "\n\n";
232
233 out << "**Example:** \n```xml\n";
234 printExample(out, tag, 0) << "\n```\n\n";
235
236 if (const auto &attributes = tag.getAttributes();
237 !attributes.empty()) {
238 out << "| Attribute | Type | Description | Default | Options |\n";
239 out << "| --- | --- | --- | --- | --- |\n";
240
241 for (const auto &attribute : attributes) {
242 std::visit([&out](const auto &attribute) { printMD(out, attribute) << '\n'; }, attribute);
243 }
244 out << '\n';
245 }
246
247 if (not tag.getSubtags().empty()) {
248 out << "**Valid Subtags:**\n\n";
249
250 std::map<std::string, std::vector<std::string>> groupedTags;
251
252 for (const auto &subtag : tag.getSubtags()) {
253 const auto heading = subtag->getFullName();
254 auto link = toGHLink(heading);
255 auto iter = occurrences.find(heading);
256 if (iter != occurrences.end()) {
257 link.append("-").append(std::to_string(iter->second));
258 iter->second *= 1;
259 } else {
260 occurrences.emplace(heading, 1);
261 }
262
263 const auto ns = subtag->getNamespace();
264 if (ns.empty()) {
265 fmt::print(out, "* [{}]({}) `{}`\n",
266 heading,
267 link,
268 subtag->getOccurrenceString(subtag->getOccurrence()));
269 } else {
270 auto &tags = groupedTags[ns];
271 tags.emplace_back(fmt::format("[{}]({}) `{}`",
272 subtag->getName(),
273 link,
274 subtag->getOccurrenceString(subtag->getOccurrence())));
275 }
276 }
277 for (const auto &kv : groupedTags) {
278 out << "* " << kv.first << "\n";
279 for (const auto &link : kv.second) {
280 out << " * " << link << "\n";
281 }
282 }
283
284 out << "\n\n";
285
286 for (const auto &subtag : tag.getSubtags()) {
287 printMD(out, *subtag, level + 1, occurrences) << '\n';
288 }
289 }
290
291 out << '\n';
292 return out;
293}
294
298std::ostream &printMD(std::ostream &out, const XMLTag &tag, int level = 1)
299{
300 std::map<std::string, int> occurrences;
301 printMD(out, tag, level, occurrences);
302 return out;
303}
304
306std::ostream &printDocumentation(std::ostream &out, const XMLTag &tag, int indentation)
307{
308 const int linewidth = 1000;
309 std::string indent;
310 for (int i = 0; i < indentation; i++) {
311 indent += " ";
312 }
313
314 out << indent << "<!-- TAG " << tag.getFullName() << '\n';
315 if (not tag.getDocumentation().empty()) {
316 std::string indentedDoc = indent + " " + tag.getDocumentation();
317 out << utils::wrapText(indentedDoc, linewidth, indentation + 9);
318 out << '\n';
319 }
320 out << indent << " (can occur " << XMLTag::getOccurrenceString(tag.getOccurrence()) << " times)";
321
322 for (const auto &attribute : tag.getAttributes()) {
323 out << '\n';
324 std::ostringstream attrDoc;
325 attrDoc << indent << " ATTR " << getName(attribute) << ": "
326 << std::visit([](const auto &attribute) { return attribute.getUserDocumentation(); }, attribute);
327 out << utils::wrapText(attrDoc.str(), linewidth, indentation + 10);
328 }
329
330 out << " -->\n";
331 std::ostringstream tagHead;
332 tagHead << indent << "<" << tag.getFullName();
333
334 // Print XML namespaces, necessary for correct XML format and display in browser
335 for (const std::string &namespaceName : tag.getNamespaces()) {
336 tagHead << " xmlns:" << namespaceName << "=\"precice." << namespaceName << "\"";
337 }
338
339 for (const auto &attribute : tag.getAttributes()) {
340 tagHead << indent << " ";
341 std::visit([&tagHead](const auto &attribute) { printDocumentation(tagHead, attribute); }, attribute);
342 }
343
344 out << utils::wrapText(tagHead.str(), linewidth, indentation + 3);
345
346 if (not tag.getSubtags().empty()) {
347 out << ">\n\n";
348 for (const auto &subtag : tag.getSubtags()) {
349 printDocumentation(out, *subtag, indentation + 3);
350 }
351 out << indent << "</" << tag.getFullName() << ">\n\n";
352 } else {
353 out << "/>\n\n";
354 }
355 return out;
356}
357
358} // namespace
359
360void toMarkdown(std::ostream &out, const XMLTag &tag)
361{
362 printMD(out, tag);
363}
364
365void toDTD(std::ostream &out, const XMLTag &tag)
366{
367 printDTD(out, tag);
368}
369
370void toDocumentation(std::ostream &out, const XMLTag &tag)
371{
372 printDocumentation(out, tag, 0);
373}
374
375} // namespace precice::xml
T begin(T... args)
Represents an XML tag to be configured automatically.
Definition XMLTag.hpp:28
static std::string_view getOccurrenceString(Occurrence occurrence)
Definition XMLTag.cpp:302
Occurrence
Types of occurrences of an XML tag.
Definition XMLTag.hpp:70
T count(T... args)
T emplace(T... args)
T end(T... args)
T exit(T... args)
T find(T... args)
std::string wrapText(const std::string &text, int linewidth, int indentation)
Definition String.cpp:13
std::string getTypeName(const double &var)
Definition TypeNames.hpp:14
contains the XML configuration parser.
void toMarkdown(std::ostream &out, const XMLTag &tag)
Prints the Markdown reference for the given tag.
Definition Printer.cpp:360
void toDTD(std::ostream &out, const XMLTag &tag)
Prints the DTD reference for the given tag.
Definition Printer.cpp:365
void toDocumentation(std::ostream &out, const XMLTag &tag)
Prints the XML reference for the given tag.
Definition Printer.cpp:370
std::string getName(const XMLTag::Attribute &attribute)
Returns the name of an Attribute.
Definition XMLTag.cpp:273
T regex_replace(T... args)
T str(T... args)
T to_string(T... args)
T transform(T... args)
T visit(T... args)
T what(T... args)