28#include <clang/Basic/Version.h>
29#include <clang/Frontend/CompilerInstance.h>
30#include <clang/Tooling/CompilationDatabase.h>
31#include <clang/Tooling/Tooling.h>
32#include <glob/glob.hpp>
33#include <pugixml/pugixml.hpp>
73 [[maybe_unused]] std::pair<std::string, property_type>
add(
76 std::optional<std::pair<std::string, property_type>>
get(
77 const std::string &name)
const;
95 [[maybe_unused]] std::string
add(
const std::string &name);
97 std::optional<std::string>
get(
const std::string &name)
const;
103 std::map<std::string, std::string>
map_;
112template <
typename ConfigType,
typename DiagramType>
117 DiagramType>::generator;
120 virtual std::vector<std::pair<std::string, property_type>>
123 using namespace std::string_literals;
132 virtual std::vector<std::pair<std::string, property_type>>
135 using namespace std::string_literals;
144 virtual std::vector<std::pair<std::string, property_type>>
147 using namespace std::string_literals;
164 void generate(std::ostream &ostr)
const override;
202 template <
typename T>
239 template <
typename T>
264 const std::string &name =
"",
const std::string &type =
"")
const;
266 void add_data(pugi::xml_node &node,
const std::string &key,
267 const std::string &value,
bool cdata =
false)
const;
269 void add_cdata(pugi::xml_node &node,
const std::string &key,
270 const std::string &value)
const;
273 void generate_key(pugi::xml_node &parent,
const std::string &attr_name,
274 const std::string &for_value,
const std::string &id_value,
275 const std::string &attr_type =
"string")
const;
296template <
typename DiagramModel,
typename DiagramConfig>
304template <
typename C,
typename D>
311 auto node_decl = graph.append_child(pugi::node_declaration);
312 node_decl.append_attribute(
"version") =
"1.0";
313 node_decl.append_attribute(
"encoding") =
"UTF-8";
315 generate_metadata(graph);
317 auto graphml = graph.append_child(
"graphml");
318 graphml.append_attribute(
"xmlns") =
"http://graphml.graphdrawing.org/xmlns";
319 graphml.append_attribute(
"xmlns:xsi") =
320 "http://www.w3.org/2001/XMLSchema-instance";
321 graphml.append_attribute(
"xsi:schemaLocation") =
322 "http://graphml.graphdrawing.org/xmlns "
323 "http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd";
327 .append_child(pugi::node_cdata)
328 .set_value(config.title());
333 auto graph_node = make_graph(
graphml,
"G");
335 if (config.using_namespace) {
336 add_data(graph_node,
"using_namespace",
337 config.using_namespace().to_string());
342 graph.save(ostr,
" ");
345template <
typename C,
typename D>
350 for (
const auto &el : view)
351 generate_relationships(el, parent);
355template <
typename C,
typename D>
362 auto target_element = model.get(r.destination());
363 if (!target_element.has_value()) {
364 LOG_DBG(
"Skipping {} relation from '{}' to '{}' due "
365 "to unresolved destination id",
367 r.destination().value());
371 const auto maybe_target_id =
372 node_ids_.get(target_element.value().alias());
373 const auto maybe_src_id = node_ids_.get(c.
alias());
375 if (!maybe_src_id || !maybe_target_id)
378 auto edge_node = parent.append_child(
"edge");
380 edge_node.append_attribute(
"id") = fmt::format(
"e{}", edge_id_++);
381 edge_node.append_attribute(
"source") = *maybe_src_id;
382 edge_node.append_attribute(
"target") = *maybe_target_id;
387 add_data(edge_node,
"url", *maybe_link);
390 add_data(edge_node,
"type",
to_string(r.type()));
391 if (!r.label().empty())
392 add_data(edge_node,
"label", r.label());
394 if (r.access() != access_t::kNone)
395 add_data(edge_node,
"access",
to_string(r.access()));
399template <
typename C,
typename D>
405 std::vector<std::pair< std::string,
411 for (
const auto &e : p) {
414 for (
const auto &decorator : e->decorators()) {
415 auto note = std::dynamic_pointer_cast<decorators::note>(decorator);
416 if (note && note->applies_to_diagram(config.name)) {
417 auto note_id = node_ids_.add(
418 fmt::format(
"{}-N_{}", e->alias(), note_index));
419 auto maybe_element_node_id = node_ids_.get(e->alias());
420 if (maybe_element_node_id) {
421 auto note_node = make_node(parent, note_id);
422 add_data(note_node,
"type",
"note");
423 add_data(note_node,
"name", note->text,
true);
424 note_id_map.emplace_back(*maybe_element_node_id, note_id);
431 if (config.graphml &&
432 config.graphml().notes.count(e->full_name(
false))) {
433 for (
const auto ¬e :
434 config.graphml().notes.at(e->full_name(
false))) {
435 auto note_id = node_ids_.add(
436 fmt::format(
"{}-N_{}", e->alias(), note_index));
437 auto maybe_element_node_id = node_ids_.get(e->alias());
438 if (maybe_element_node_id) {
442 auto note_node = make_node(parent, note_id);
443 add_data(note_node,
"type",
"note");
444 add_data(note_node,
"name", *rendered_note,
true);
445 note_id_map.emplace_back(
446 *maybe_element_node_id, note_id);
455 for (
const auto &[element_node_id, note_node_id] : note_id_map) {
456 auto edge_node = parent.append_child(
"edge");
458 edge_node.append_attribute(
"id") = fmt::format(
"e{}", edge_id_++);
459 edge_node.append_attribute(
"source") = note_node_id;
460 edge_node.append_attribute(
"target") = element_node_id;
462 add_data(edge_node,
"type",
"none");
466template <
typename C,
typename D>
470 auto comment = parent.append_child(pugi::node_comment);
471 comment.set_value(fmt::format(
474 comment = parent.append_child(pugi::node_comment);
476 fmt::format(
" LLVM version {} ", clang::getClangFullVersion()));
480template <
typename C,
typename D>
484 auto result = parent.append_child(
"node");
485 result.append_attribute(
"id") = id;
489template <
typename C,
typename D>
493 auto result = parent.append_child(
"graph");
494 result.append_attribute(
"id") = graph_ids_.add(
id);
495 result.append_attribute(
"edgedefault").set_value(
"directed");
496 result.append_attribute(
"parse.nodeids").set_value(
"canonical");
497 result.append_attribute(
"parse.edgeids").set_value(
"canonical");
498 result.append_attribute(
"parse.order").set_value(
"nodesfirst");
502template <
typename C,
typename D>
504 const std::string &
id,
const std::string &name,
505 const std::string &type)
const
507 auto graph_node = parent.append_child(
"node");
508 graph_node.append_attribute(
"id") = node_ids_.add(
id);
511 add_data(graph_node,
"name", name);
514 add_data(graph_node,
"type", type);
519template <
typename C,
typename D>
521 const std::string &key_name,
const std::string &value,
bool cdata)
const
523 using namespace std::string_literals;
525 std::optional<std::pair<std::string, property_type>> key_id;
526 if (node.name() ==
"node"s)
527 key_id = node_properties().get(key_name);
528 else if (node.name() ==
"graph"s)
529 key_id = graph_properties().get(key_name);
530 else if (node.name() ==
"edge"s)
531 key_id = edge_properties().get(key_name);
533 if (key_id.has_value()) {
534 auto data = node.append_child(
"data");
535 data.append_attribute(
"key") = key_id->first;
537 data.append_child(pugi::node_cdata).set_value(value);
539 data.text().set(value);
543template <
typename C,
typename D>
545 const std::string &key_name,
const std::string &value)
const
547 return add_data(node, key_name, value,
true);
550template <
typename C,
typename D>
558 add_data(node,
"url", *maybe_link);
562 add_data(node,
"tooltip", *maybe_tooltip);
566template <
typename C,
typename D>
568 const std::string &attr_name,
const std::string &for_value,
569 const std::string &id_value,
const std::string &attr_type)
const
571 auto key = parent.append_child(
"key");
572 key.append_attribute(
"attr.name") = attr_name.c_str();
573 key.append_attribute(
"attr.type") = attr_type.c_str();
574 key.append_attribute(
"for") = for_value.c_str();
575 key.append_attribute(
"id") = id_value.c_str();
578template <
typename C,
typename D>
581 for (
const auto &[name, type] : graph_property_names()) {
582 const auto [id, t] = graph_properties_.add(name);
583 generate_key(parent, name,
"graph",
id,
to_string(type));
586 for (
const auto &[name, type] : node_property_names()) {
587 const auto [id, t] = node_properties_.add(name);
588 generate_key(parent, name,
"node",
id,
to_string(type));
591 for (
const auto &[name, type] : edge_property_names()) {
592 const auto [id, t] = edge_properties_.add(name);
593 generate_key(parent, name,
"edge",
id,
to_string(type));