Added initial Doxygen config

This commit is contained in:
Bartek Kryza
2023-06-18 01:18:14 +02:00
parent 031235bf49
commit da2cb63ab3
51 changed files with 4330 additions and 158 deletions

View File

@@ -1,4 +1,4 @@
/**
/*
* src/options/cli_handler.h
*
* Copyright (c) 2021-2023 Bartek Kryza <bkryza@gmail.com>
@@ -25,9 +25,21 @@
#include <optional>
namespace clanguml::cli {
/**
* This enum represents possible exit states of the command line parser.
*/
enum class cli_flow_t {
kExit, /*!< The application should exit (e.g. `-h`) */
kError, /*!< The options or configuration file were invalid */
kContinue /*!< Continue with processing diagrams */
};
enum class cli_flow_t { kExit, kError, kContinue };
/**
* @brief Command line options handler
*
* This class is responsible for handling the command line options
* and executing required actions.
*/
class cli_handler {
public:
cli_handler(std::ostream &ostr = std::cout,
@@ -37,45 +49,55 @@ public:
/**
* Main CLI handling method.
*
* @embed{cli_handle_options_sequence.svg}
*
* @param argc
* @param argv
* @return
* @return Command line handler state
*/
cli_flow_t handle_options(int argc, const char **argv);
/**
* Print the program version and basic information
*
* @return Command line handler state
*/
cli_flow_t print_version();
/**
* Print list of diagrams available in the configuration file
*
* @return Command line handler state
*/
cli_flow_t print_diagrams_list();
/**
* Print list of available diagram templates, including their names
* and types.
*
* @return Command line handler state
*/
cli_flow_t print_diagram_templates();
/**
* Print definition of a specific diagram template.
*
* @param template_name
* @return
* @param template_name Name of the diagram template
* @return Command line handler state
*/
cli_flow_t print_diagram_template(const std::string &template_name);
/**
* Print effective config after loading and setting default values.
*
* @return Command line handler state
*/
cli_flow_t print_config();
/**
* Generate sample configuration file and exit.
*
* @return 0 on success or error code
* @return Command line handler state
*/
cli_flow_t create_config_file();
@@ -85,7 +107,7 @@ public:
* @param type Type of the sample diagram to add
* @param config_file_path Path to the config file
* @param name Name of the new diagram
* @return 0 on success or error code
* @return Command line handler state
*/
cli_flow_t add_config_diagram(clanguml::common::model::diagram_t type,
const std::string &config_file_path, const std::string &name);
@@ -96,7 +118,7 @@ public:
* @param config_file_path
* @param template_name
* @param template_variables
* @return
* @return Command line handler state
*/
cli_flow_t add_config_diagram_from_template(
const std::string &config_file_path, const std::string &template_name,
@@ -144,14 +166,39 @@ public:
clanguml::config::config config;
private:
/**
* This method parses the command line options using CLI11 library.
*
* @param argc
* @param argv
* @return Command line handler state
*/
cli_flow_t parse(int argc, const char **argv);
/**
* Handle command line options before parsing the configuration file
*
* @return Command line handler state
*/
cli_flow_t handle_pre_config_options();
/**
* Load configuration file from file or stdin
*
* @return Command line handler state
*/
cli_flow_t load_config();
/**
* Handle command line options before parsing the configuration file
*
* @return Command line handler state
*/
cli_flow_t handle_post_config_options();
/**
* Setup spdlog library depending on provided command line options
*/
void setup_logging();
std::ostream &ostr_;

View File

@@ -35,23 +35,35 @@ class NamespaceDecl;
namespace clanguml::common {
/**
* @brief Convert @link clang::AccessSpecifier to @link model::access_t
* @brief Convert `clang::AccessSpecifier` to @see clanguml::model::access_t
*
* @param access_specifier Clang member access specifier
* @return Enum value of @link model::access_t
* @return Enum value of @see clanguml::model::access_t
*/
model::access_t access_specifier_to_access_t(
clang::AccessSpecifier access_specifier);
/**
* @brief Generate full qualified name for @link clang::TagDecl instance
* @brief Generate full qualified name for
* [clang::TagDecl](https://clang.llvm.org/doxygen/classclang_1_1TagDecl.html)
* instance
*
* @param declaration Input declaration
* @return String representation including any templates, parameters and
* attribtues
* attribtues
*/
std::string get_tag_name(const clang::TagDecl &declaration);
/**
* @brief Get qualified name of some Clang declaration
*
* This template is convenient for getting qualified name of various types of
* clang declarations.
*
* @tparam T Type of Clang's declaration, e.g. `clang::TagDecl`
* @param declaration Reference to a clang declaration
* @return Fully qualified name
*/
template <typename T> std::string get_qualified_name(const T &declaration)
{
auto qualified_name = declaration.getQualifiedNameAsString();
@@ -70,8 +82,20 @@ template <typename T> std::string get_qualified_name(const T &declaration)
return qualified_name;
}
/**
* Get namespace of a specific `clang::TagDecl`
*
* @param declaration Reference to clang::TagDecl
* @return Namespace instance
*/
model::namespace_ get_tag_namespace(const clang::TagDecl &declaration);
/**
* Get namespace of a specific `clang::TemplateDecl`
*
* @param declaration Reference to clang::TemplateDecl
* @return Namespace instance
*/
model::namespace_ get_template_namespace(
const clang::TemplateDecl &declaration);
@@ -94,12 +118,35 @@ std::string to_string(const clang::TypeConstraint *tc);
std::string to_string(const clang::TemplateName &templ);
/**
* @brief Get raw text of specific source range
*
* @param range Source range
* @param sm Source manager reference
* @return Raw source text
*/
std::string get_source_text_raw(
clang::SourceRange range, const clang::SourceManager &sm);
/**
* @brief Get printable range of text of specific source range
*
* @param range Source range
* @param sm Source manager reference
* @return Printable source text
*/
std::string get_source_text(
clang::SourceRange range, const clang::SourceManager &sm);
/**
* @brief Extract template depth and index
*
* This function extracts template depth and index values from Clang's
* `type-parameter-` names.
*
* @param type_parameter Clang's type parameter string
* @return (depth, index, qualifier)
*/
std::tuple<unsigned int, unsigned int, std::string>
extract_template_parameter_index(const std::string &type_parameter);
@@ -115,17 +162,14 @@ extract_template_parameter_index(const std::string &type_parameter);
*/
bool is_subexpr_of(const clang::Stmt *parent_stmt, const clang::Stmt *sub_stmt);
/**
* @brief Forward template for convertions to ID from various entities
/** @defgroup to_id Forward template for convertions to ID from various entities
*
* These methods provide the main mechanism for generating globally unique
* identifiers for all elements in the diagrams. The identifiers must be unique
* between different translation units in order for element relationships to
* be properly rendered in diagrams.
*
* @tparam T Type of entity for which ID should be computed
* @param declaration Element (e.g. declaration) for which the ID is needed
* @return Unique ID
* @{
*/
template <typename T> id_t to_id(const T &declaration);
@@ -148,10 +192,25 @@ template <> id_t to_id(const clang::EnumType &type);
template <> id_t to_id(const clang::TemplateSpecializationType &type);
template <> id_t to_id(const std::filesystem::path &type);
/** @} */ // end of to_id
/**
* @brief Split qualified name to namespace and name
*
* @param full_name Fully qualified element name
* @return (namespace, name)
*/
std::pair<common::model::namespace_, std::string> split_ns(
const std::string &full_name);
/**
* @brief Parse unexposed (available as string) template params
*
* @param params String parameters as provided by Clang
* @param ns_resolve Namespace resolver function
* @param depth Current depth in the template specification
* @return Parsed template parameter
*/
std::vector<common::model::template_parameter> parse_unexposed_template_params(
const std::string &params,
const std::function<std::string(const std::string &)> &ns_resolve,
@@ -191,6 +250,17 @@ bool is_type_token(const std::string &t);
clang::QualType dereference(clang::QualType type);
/**
* @brief Extract type context and return raw type
*
* This function removes the context for a type, for example for:
* `std::string const&`
* it will return
* `(std::string, [const&])`
*
* @param type Type to process
* @return (type, [qualifiers])
*/
std::pair<clang::QualType, std::deque<common::model::context>>
consume_type_context(clang::QualType type);

View File

@@ -38,6 +38,16 @@ class compilation_database_error : public std::runtime_error {
using std::runtime_error::runtime_error;
};
/**
* @brief Custom compilation database class
*
* This class provides custom specialization of Clang's
* [CompilationDatabase](https://clang.llvm.org/doxygen/classclang_1_1tooling_1_1CompilationDatabase.html),
* which provides the possibility of adjusting the compilation flags after
* they have been loaded from the `compile_commands.json` file.
*
* @embed{compilation_database_context_class.svg}
*/
class compilation_database : public clang::tooling::CompilationDatabase {
public:
compilation_database(
@@ -46,19 +56,52 @@ public:
~compilation_database() override = default;
/**
* Loads the compilation database from directory specified on command
* line or in the configuration file.
*
* @param cfg Reference to config instance
* @return Instance of compilation_database.
*/
static std::unique_ptr<compilation_database> auto_detect_from_directory(
const clanguml::config::config &cfg);
/**
* Retrieves and adjusts compilation commands from the database, for
* a given translation unit.
*
* @return List of adjusted compile commands.
*/
std::vector<clang::tooling::CompileCommand> getCompileCommands(
clang::StringRef FilePath) const override;
/**
* Returns all files in the database.
*
* @return List of all files in compilation database.
*/
std::vector<std::string> getAllFiles() const override;
/**
* Retrieves and adjusts all compilation commands from the database.
*
* @return List of adjusted compile commands.
*/
std::vector<clang::tooling::CompileCommand>
getAllCompileCommands() const override;
/**
* Returns reference to clanguml's config instance.
*
* @return Reference to config instance.
*/
const clanguml::config::config &config() const;
/**
* Returns reference to CompilationDatabase as was loaded from file.
*
* @return Reference to CompilationDatabase.
*/
const clang::tooling::CompilationDatabase &base() const;
std::string guess_language_from_filename(const std::string &filename) const;
@@ -67,11 +110,17 @@ private:
void adjust_compilation_database(
std::vector<clang::tooling::CompileCommand> &commands) const;
// Actual instance of the compilation database is stored in here
// The inheritance is just to keep the interface
/*!
* Pointer to the Clang's original compilation database.
*
* Actual instance of the compilation database is stored in here.
* The inheritance is just to keep the interface.
*/
std::unique_ptr<clang::tooling::CompilationDatabase> base_;
// Reference to the clang-uml config
/*!
* Reference to the instance of clanguml config.
*/
const clanguml::config::config &config_;
};

View File

@@ -184,32 +184,33 @@ void generate_diagrams(const std::vector<std::string> &diagram_names,
continue;
}
futs.emplace_back(generator_executor.add(
[&od, &generators, &name = name, &diagram = diagram, &indicator,
db = std::ref(*db), translation_units = valid_translation_units,
verbose]() mutable {
try {
if (indicator)
indicator->add_progress_bar(name,
translation_units.size(),
diagram_type_to_color(diagram->type()));
auto generator = [&od, &generators, &name = name, &diagram = diagram,
&indicator, db = std::ref(*db),
translation_units = valid_translation_units,
verbose]() mutable {
try {
if (indicator)
indicator->add_progress_bar(name, translation_units.size(),
diagram_type_to_color(diagram->type()));
generate_diagram(od, name, diagram, db, translation_units,
generators, verbose != 0, [&indicator, &name]() {
if (indicator)
indicator->increment(name);
});
generate_diagram(od, name, diagram, db, translation_units,
generators, verbose != 0, [&indicator, &name]() {
if (indicator)
indicator->increment(name);
});
if (indicator)
indicator->complete(name);
}
catch (std::runtime_error &e) {
if (indicator)
indicator->fail(name);
if (indicator)
indicator->complete(name);
}
catch (std::runtime_error &e) {
if (indicator)
indicator->fail(name);
LOG_ERROR(e.what());
}
}));
LOG_ERROR(e.what());
}
};
futs.emplace_back(generator_executor.add(std::move(generator)));
}
for (auto &fut : futs) {

View File

@@ -49,8 +49,13 @@
namespace clanguml::common::generators {
// template trait for selecting diagram model type based on diagram config
// type
/** @defgroup diagram_model_t Diagram model selector
*
* Template traits for selecting diagram model type based on diagram config
* type
*
* @{
*/
template <typename DiagramConfig> struct diagram_model_t;
template <> struct diagram_model_t<clanguml::config::class_diagram> {
using type = clanguml::class_diagram::model::diagram;
@@ -64,9 +69,15 @@ template <> struct diagram_model_t<clanguml::config::package_diagram> {
template <> struct diagram_model_t<clanguml::config::include_diagram> {
using type = clanguml::include_diagram::model::diagram;
};
/** @} */
// template trait for selecting diagram visitor type based on diagram config
// type
/** @defgroup diagram_visitor_t Diagram model selector
*
* Template traits for selecting diagram visitor type based on diagram config
* type
*
* @{
*/
template <typename DiagramConfig> struct diagram_visitor_t;
template <> struct diagram_visitor_t<clanguml::config::class_diagram> {
using type = clanguml::class_diagram::visitor::translation_unit_visitor;
@@ -80,16 +91,29 @@ template <> struct diagram_visitor_t<clanguml::config::package_diagram> {
template <> struct diagram_visitor_t<clanguml::config::include_diagram> {
using type = clanguml::include_diagram::visitor::translation_unit_visitor;
};
/** @} */
// template trait for selecting diagram generator type based on diagram config
// type
/** @defgroup diagram_generator_tag Diagram model tags
*
* Tags to determine the generator output file extension
*
* @{
*/
struct plantuml_generator_tag {
inline static const std::string extension = "puml";
};
struct json_generator_tag {
inline static const std::string extension = "json";
};
/** @} */
/** @defgroup diagram_generator_t Diagram generator selector
*
* Tags to determine the generator type based on diagram config type
* and output format
*
* @{
*/
template <typename DiagramConfig, typename GeneratorType>
struct diagram_generator_t;
template <>
@@ -132,14 +156,40 @@ struct diagram_generator_t<clanguml::config::include_diagram,
json_generator_tag> {
using type = clanguml::include_diagram::generators::json::generator;
};
/** @} */
template <typename DiagramConfig> struct diagram_visitor_t;
/**
* @brief Assign translation units to diagrams
*
* This function assigns for each diagram to be generated the list of
* translation units based on it's `glob` pattern if any.
*
* If `diagram_names` is empty, this function processes all diagrams in
* `config`.
*
* @param diagram_names List of diagram names, applies to all if empty
* @param config Reference to config instance
* @param compilation_database_files List of files found in compilation database
* @param translation_units_map Resulting translation units map is stored here
*/
void find_translation_units_for_diagrams(
const std::vector<std::string> &diagram_names,
clanguml::config::config &config,
const std::vector<std::string> &compilation_database_files,
std::map<std::string, std::vector<std::string>> &translation_units_map);
/**
* @brief Specialization of
* [clang::ASTConsumer](https://clang.llvm.org/doxygen/classclang_1_1ASTConsumer.html)
*
* This class provides overriden HandleTranslationUnit() method, which
* calls a translation_unit_visitor for a specific diagram type on
* each translation unit assigned to the diagram.
*
* @tparam DiagramModel Type of diagram_model
* @tparam DiagramConfig Type of diagram_config
* @tparam TranslationUnitVisitor Type of translation_unit_visitor
*/
template <typename DiagramModel, typename DiagramConfig,
typename TranslationUnitVisitor>
class diagram_ast_consumer : public clang::ASTConsumer {
@@ -161,6 +211,17 @@ public:
}
};
/**
* @brief Specialization of
* [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1ASTFrontendAction.html)
*
* This class overrides the BeginSourceFileAction() and CreateASTConsumer()
* methods to create and setup an appropriate diagram_ast_consumer instance.
*
* @tparam DiagramModel Type of diagram_model
* @tparam DiagramConfig Type of diagram_config
* @tparam TranslationUnitVisitor Type of translation_unit_visitor
*/
template <typename DiagramModel, typename DiagramConfig,
typename DiagramVisitor>
class diagram_fronted_action : public clang::ASTFrontendAction {
@@ -193,6 +254,8 @@ protected:
{
LOG_DBG("Visiting source file: {}", getCurrentFile().str());
// Update progress indicators, if enabled, on each translation
// unit
if (progress_)
progress_();
@@ -216,6 +279,17 @@ private:
std::function<void()> progress_;
};
/**
* @brief Specialization of
* [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1tooling_1_1FrontendActionFactory.html)
*
* This class overrides the create() method in order to create an instance
* of diagram_frontend_action of appropriate type.
*
* @tparam DiagramModel Type of diagram_model
* @tparam DiagramConfig Type of diagram_config
* @tparam TranslationUnitVisitor Type of translation_unit_visitor
*/
template <typename DiagramModel, typename DiagramConfig,
typename DiagramVisitor>
class diagram_action_visitor_factory
@@ -241,6 +315,19 @@ private:
std::function<void()> progress_;
};
/**
* @brief Specialization of
* [clang::ASTFrontendAction](https://clang.llvm.org/doxygen/classclang_1_1tooling_1_1FrontendActionFactory.html)
*
* This is the entry point function to initiate AST frontend action for a
* specific diagram.
*
* @embed{diagram_generate_generic_sequence.svg}
*
* @tparam DiagramModel Type of diagram_model
* @tparam DiagramConfig Type of diagram_config
* @tparam TranslationUnitVisitor Type of translation_unit_visitor
*/
template <typename DiagramModel, typename DiagramConfig,
typename DiagramVisitor>
std::unique_ptr<DiagramModel> generate(const common::compilation_database &db,
@@ -275,6 +362,17 @@ std::unique_ptr<DiagramModel> generate(const common::compilation_database &db,
return diagram;
}
/**
* @brief Generate a single diagram
*
* @param od Output directory path
* @param name Name of the diagram
* @param diagram Effective diagram configuration
* @param db Reference to compilation database
* @param translation_units List of translation units for the diagram
* @param generators List of generator types to be used for the diagram
* @param verbose Log level
*/
void generate_diagram(const std::string &od, const std::string &name,
std::shared_ptr<clanguml::config::diagram> diagram,
const common::compilation_database &db,
@@ -282,6 +380,19 @@ void generate_diagram(const std::string &od, const std::string &name,
const std::vector<clanguml::common::generator_type_t> &generators,
bool verbose);
/**
* @brief Generate diagrams
*
* @param diagram_names List of diagram names to generate
* @param config Reference to config instance
* @param od Path to output directory
* @param db Reference to compilation database
* @param verbose Log level
* @param thread_count Number of diagrams to be generated in parallel
* @param progress Whether progress indicators should be displayed
* @param generators List of generator types to use for each diagram
* @param translation_units_map Map of translation units for each file
*/
void generate_diagrams(const std::vector<std::string> &diagram_names,
clanguml::config::config &config, const std::string &od,
const common::compilation_database_ptr &db, int verbose,
@@ -290,6 +401,12 @@ void generate_diagrams(const std::vector<std::string> &diagram_names,
const std::map<std::string, std::vector<std::string>>
&translation_units_map);
/**
* @brief Return indicators progress bar color for diagram type
*
* @param diagram_type Diagram type
* @return Progress bar color
*/
indicators::Color diagram_type_to_color(model::diagram_t diagram_type);
} // namespace clanguml::common::generators

View File

@@ -334,7 +334,7 @@ void generator<C, D>::generate_plantuml_directives(
// Render the directive with template engine first
std::string directive{env().render(std::string_view{d}, context())};
// Now search for alias @A() directives in the text
// Now search for alias `@A()` directives in the text
// (this is deprecated)
std::tuple<std::string, size_t, size_t> alias_match;
while (util::find_element_alias(directive, alias_match)) {

View File

@@ -34,26 +34,95 @@ namespace clanguml::common::model {
using comment_t = inja::json;
/**
* @brief Base class for decorated diagram elements
*
* Decorators in `clang-uml` mean that custom `@uml{}` directives can be
* applied to them in the code comments.
*
* @embed{decorated_element_hierarchy_class.svg}
*
* @see clanguml::decorators::decorator
*
*/
class decorated_element {
public:
/**
* Whether this element should be skipped from the diagram.
*
* @return
*/
bool skip() const;
/**
* Whether this relationship should be skipped from the diagram.
*
* @return
*/
bool skip_relationship() const;
/**
* If this element is a member or a method, get relationship decorator
* if any.
*
* @code
* /// @uml{aggregation[0..1:1..5]}
* std::vector<C> ccc;
* @endcode
*
* @return Relationship specified as a decorator on class member.
*/
std::pair<relationship_t, std::string> get_relationship() const;
/**
* Get stype specification for this element, if any.
*
* @code
* /// @uml{style[#back:lightgreen|yellow;header:blue/red]}
* class A { };
* @endcode
*
* @return
*/
std::string style_spec() const;
/**
* Get all decorators for this element.
*
* @return List of decorator pointers.
*/
const std::vector<std::shared_ptr<decorators::decorator>> &
decorators() const;
/**
* Add decorators to the element.
*
* @param decorators List of decorator pointers.
*/
void add_decorators(
const std::vector<std::shared_ptr<decorators::decorator>> &decorators);
/**
* Append decorators from another element.
*
* @param de Source element to copy decorators from.
*/
void append(const decorated_element &de);
/**
* Get entire comment model for this element.
*
* @return Comment model.
*/
std::optional<comment_t> comment() const;
/**
* Set comment model for this element.
*
* Comment model is currently a JSON object.
*
* @param c Comment model.
*/
void set_comment(const comment_t &c);
private:

View File

@@ -31,22 +31,49 @@ class diagram_filter;
class element;
class relationship;
/**
* @brief Base class for all diagram models
*
* @embed{diagram_hierarchy_class.svg}
*/
class diagram {
public:
diagram();
virtual ~diagram();
/**
* @brief Return type of the diagram.
*
* @return Type of diagram
*/
virtual diagram_t type() const = 0;
/**
* Return optional reference to a diagram_element by name.
*
* @param full_name Fully qualified name of a diagram element.
* @return Optional reference to a diagram element.
*/
virtual opt_ref<clanguml::common::model::diagram_element> get(
const std::string &full_name) const = 0;
/**
* Return optional reference to a diagram_element by id.
*
* @param id Id of a diagram element.
* @return Optional reference to a diagram element.
*/
virtual common::optional_ref<clanguml::common::model::diagram_element> get(
diagram_element::id_t id) const = 0;
/// \brief Find element in diagram which can have full name or be
/// relative to ns
/**
* Return optional reference to a diagram_element by name and namespace.
*
* @param name Name of the diagram element (e.g. a class name)
* @param ns Namespace of the element.
* @return Optional reference to a diagram element.
*/
virtual common::optional_ref<clanguml::common::model::diagram_element>
get_with_namespace(const std::string &name, const namespace_ &ns) const;
@@ -55,13 +82,52 @@ public:
diagram &operator=(const diagram &) = delete;
diagram &operator=(diagram && /*unused*/) noexcept;
/**
* Set diagram name.
*
* @param name Name of the diagram.
*/
void set_name(const std::string &name);
/**
* Return the name of the diagram.
*
* @return Name of the diagram.
*/
std::string name() const;
/**
* Set diagram filter for this diagram.
*
* @param filter diagram_filter instance
*
* @see clanguml::common::model::diagram_filter
*/
void set_filter(std::unique_ptr<diagram_filter> filter);
/**
* Get diagram filter
*
* @return Reference to the diagrams element filter
*/
const diagram_filter &filter() const { return *filter_; }
/**
* @brief Set diagram in a complete state.
*
* This must be called after the diagram's 'translation_unit_visitor' has
* completed for all translation units, in order to apply filters which can
* only work after the diagram is complete.
*
* @param complete Status of diagram visitor completion.
*/
void set_complete(bool complete);
/**
* Whether the diagram is complete.
*
* @return Diagram completion status.
*/
bool complete() const;
// TODO: refactor to a template method
@@ -80,6 +146,11 @@ public:
virtual bool should_include(
const namespace_ &ns, const std::string &name) const;
/**
* Return diagrams JSON context for inja templates.
*
* @return JSON context.
*/
virtual inja::json context() const = 0;
private:

View File

@@ -31,6 +31,12 @@
namespace clanguml::common::model {
/**
* @brief Base class for standalone diagram elements.
*
* This is a base cass of any standalone elements such as classes, structs,
* concepts, packages and so on participants and so on.
*/
class diagram_element : public decorated_element, public source_location {
public:
using id_t = int64_t;
@@ -39,26 +45,91 @@ public:
virtual ~diagram_element() = default;
/**
* @brief Returns diagram element id.
*
* Each element in the diagram is uniquely identified by id. The id
* is currently calculated from the full string representation of the
* element, in order to be uniquely identifiable among multiple translation
* units.
*
* @return Elements id.
*/
id_t id() const;
/**
* Set elements id.
*
* @param id Elements id.
*/
void set_id(id_t id);
/**
* @brief Return elements' diagram alias.
*
* @todo This is a PlantUML specific method - it shouldn't be here.
*
* @return PlantUML's diagram element alias.
*/
virtual std::string alias() const;
/**
* Set diagram elements name.
*
* @param name Elements name.
*/
void set_name(const std::string &name) { name_ = name; }
/**
* Return diagram's name.
*
* @return Diagram's name.
*/
std::string name() const { return name_; }
/**
* Return the type name of the diagram.
*
* @return Diagrams type name.
*/
virtual std::string type_name() const { return "__undefined__"; };
/**
* @brief Return the elements fully qualified name.
*
* This method should be implemented in each subclass, and ensure that
* for instance it includes fully qualified namespace, template params, etc.
*
* @return Full elements name.
*/
virtual std::string full_name(bool /*relative*/) const { return name(); }
/**
* Return all relationships outgoing from this element.
*
* @return List of relationships.
*/
std::vector<relationship> &relationships();
/**
* Return all relationships outgoing from this element.
*
* @return List of relationships.
*/
const std::vector<relationship> &relationships() const;
/**
* Add relationships, whose source is this element.
*
* @param cr Relationship to another diagram element.
*/
void add_relationship(relationship &&cr);
/**
* Add element to the diagram.
*
* @param e Diagram element.
*/
void append(const decorated_element &e);
friend bool operator==(const diagram_element &l, const diagram_element &r);
@@ -66,14 +137,39 @@ public:
friend std::ostream &operator<<(
std::ostream &out, const diagram_element &rhs);
/**
* Return elements inja JSON context.
*
* @return Element context.
*/
virtual inja::json context() const;
/**
* Whether this element is nested in another element.
*
* @return
*/
bool is_nested() const;
/**
* Set element's nested status.
*
* @param nested
*/
void nested(bool nested);
/**
* Returns the diagrams completion status.
*
* @return Whether the diagram is complete.
*/
bool complete() const;
/**
* Set the diagrams completion status.
*
* @param completed
*/
void complete(bool completed);
private:

View File

@@ -36,7 +36,15 @@
namespace clanguml::common::model {
enum filter_t { kInclusive, kExclusive };
/**
* Diagram filters can be add in 2 modes:
* - inclusive - the elements that match are included in the diagram
* - exclusive - the elements that match are excluded from the diagram
*/
enum class filter_t {
kInclusive, /*!< Filter is inclusive */
kExclusive /*!< Filter is exclusve */
};
namespace detail {
template <typename ElementT, typename DiagramT>
@@ -56,6 +64,16 @@ clanguml::common::id_t destination_comparator(
const common::model::source_file &f);
} // namespace detail
/**
* @brief Base class for any diagram filter.
*
* This class acts as a visitor for diagram elements. It provides a set of
* common methods which can be overriden by specific filters. If a filter
* does not implement a specific method, it is ignored through the 3 value
* logic implemented in @see clanguml::common::model::tvl
*
* @embed{filter_visitor_hierarchy_class.svg}
*/
class filter_visitor {
public:
filter_visitor(filter_t type);
@@ -111,6 +129,10 @@ private:
std::vector<std::unique_ptr<filter_visitor>> filters_;
};
/**
* Match namespace or diagram element to a set of specified namespaces or
* regex patterns.
*/
struct namespace_filter : public filter_visitor {
namespace_filter(
filter_t type, std::vector<common::namespace_or_regex> namespaces);
@@ -125,6 +147,9 @@ private:
std::vector<common::namespace_or_regex> namespaces_;
};
/**
* Match element's name to a set of names or regex patterns.
*/
struct element_filter : public filter_visitor {
element_filter(
filter_t type, std::vector<common::string_or_regex> elements);
@@ -137,6 +162,9 @@ private:
std::vector<common::string_or_regex> elements_;
};
/**
* Match diagram elements based on elements type (e.g. class).
*/
struct element_type_filter : public filter_visitor {
element_type_filter(filter_t type, std::vector<std::string> element_types);
@@ -148,6 +176,9 @@ private:
std::vector<std::string> element_types_;
};
/**
* Match class methods based on their category (e.g. operator).
*/
struct method_type_filter : public filter_visitor {
method_type_filter(
filter_t type, std::vector<config::method_type> method_types);
@@ -161,6 +192,10 @@ private:
std::vector<config::method_type> method_types_;
};
/**
* Match element based on whether it is a subclass of a set of base classes,
* or one of them.
*/
struct subclass_filter : public filter_visitor {
subclass_filter(filter_t type, std::vector<common::string_or_regex> roots);
@@ -172,6 +207,10 @@ private:
std::vector<common::string_or_regex> roots_;
};
/**
* Match element based on whether it is a parent of a set of children, or one
* of them.
*/
struct parents_filter : public filter_visitor {
parents_filter(filter_t type, std::vector<common::string_or_regex> roots);
@@ -183,6 +222,20 @@ private:
std::vector<common::string_or_regex> children_;
};
/**
* @brief Common template for filters involving traversing relationship graph.
*
* This class template provides a common implementation of a diagram
* relationship graph traversal. It is used for filters, which need to check
* for instance, whether an element is in some kind of relationship with other
* element.
*
* @tparam DiagramT Diagram type
* @tparam ElementT Element type
* @tparam ConfigEntryT Type of configuration option used to specify initial
* elements for traversal
* @tparam MatchOverrideT Type of the matched element
*/
template <typename DiagramT, typename ElementT,
typename ConfigEntryT = std::string,
typename MatchOverrideT = common::model::element>
@@ -337,6 +390,9 @@ private:
bool forward_;
};
/**
* Match relationship types.
*/
struct relationship_filter : public filter_visitor {
relationship_filter(
filter_t type, std::vector<relationship_t> relationships);
@@ -350,6 +406,9 @@ private:
std::vector<relationship_t> relationships_;
};
/**
* Match class members and methods based on access (public, protected, private).
*/
struct access_filter : public filter_visitor {
access_filter(filter_t type, std::vector<access_t> access);
@@ -361,6 +420,10 @@ private:
std::vector<access_t> access_;
};
/**
* Match diagram elements which are in direct relationship to any of the
* elements specified in context.
*/
struct context_filter : public filter_visitor {
context_filter(filter_t type, std::vector<common::string_or_regex> context);
@@ -372,6 +435,10 @@ private:
std::vector<common::string_or_regex> context_;
};
/**
* Match elements based on their source location, whether it matches to
* a specified file paths.
*/
struct paths_filter : public filter_visitor {
paths_filter(filter_t type, const std::filesystem::path &root,
const std::vector<std::string> &p);
@@ -389,6 +456,9 @@ private:
std::filesystem::path root_;
};
/**
* Match class method based on specified method categories.
*/
struct class_method_filter : public filter_visitor {
class_method_filter(filter_t type, std::unique_ptr<access_filter> af,
std::unique_ptr<method_type_filter> mtf);
@@ -403,6 +473,9 @@ private:
std::unique_ptr<method_type_filter> method_type_filter_;
};
/**
* Match class members.
*/
struct class_member_filter : public filter_visitor {
class_member_filter(filter_t type, std::unique_ptr<access_filter> af);
@@ -415,16 +488,49 @@ private:
std::unique_ptr<access_filter> access_filter_;
};
/**
* @brief Composite of all diagrams filters.
*
* Instances of this class contain all filters specified in configuration file
* for a given diagram.
*
* @embed{diagram_filter_context_class.svg}
*
* @see clanguml::common::model::filter_visitor
*/
class diagram_filter {
public:
diagram_filter(const common::model::diagram &d, const config::diagram &c);
/**
* Add inclusive filter.
*
* @param fv Filter visitor.
*/
void add_inclusive_filter(std::unique_ptr<filter_visitor> fv);
/** Add exclusive filter.
*
* @param fv Filter visitor.
*/
void add_exclusive_filter(std::unique_ptr<filter_visitor> fv);
/**
* `should_include` overload for namespace and name.
*
* @param ns Namespace
* @param name Name
* @return Match result.
*/
bool should_include(const namespace_ &ns, const std::string &name) const;
/**
* Generic `should_include` overload for various diagram elements.
*
* @tparam T Type to to match - must match one of filter_visitor's match(T)
* @param e Value of type T to match
* @return Match result.
*/
template <typename T> bool should_include(const T &e) const
{
auto exc = tvl::any_of(exclusive_.begin(), exclusive_.end(),
@@ -440,11 +546,22 @@ public:
}
private:
/**
* @brief Initialize filters.
*
* Some filters require initialization.
*
* @param c Diagram config.
*/
void init_filters(const config::diagram &c);
/*! List of inclusive filters */
std::vector<std::unique_ptr<filter_visitor>> inclusive_;
/*! List of exclusive filters */
std::vector<std::unique_ptr<filter_visitor>> exclusive_;
/*! Reference to the diagram model */
const common::model::diagram &diagram_;
};

View File

@@ -32,36 +32,83 @@
namespace clanguml::common::model {
/**
* @brief Base class for any element qualified by namespace.
*/
class element : public diagram_element {
public:
element(namespace_ using_namespace);
~element() override = default;
/**
* Return the elements fully qualified name, but without template
* arguments or function params.
*
* @return Fully qualified element name.
*/
std::string name_and_ns() const
{
auto ns = ns_ | name();
return ns.to_string();
}
/**
* Set elements namespace.
*
* @param ns Namespace.
*/
void set_namespace(const namespace_ &ns) { ns_ = ns; }
/**
* Return elements namespace.
*
* @return Namespace.
*/
namespace_ get_namespace() const { return ns_; }
/**
* Return elements relative namespace.
*
* @return Namespace.
*/
namespace_ get_relative_namespace() const
{
return ns_.relative_to(using_namespace_);
}
/**
* Return elements namespace as path.
*
* Namespace is a nested path in diagrams where packages are generated
* from namespaces.
*
* @return Namespace.
*/
const namespace_ &path() const { return ns_; }
/**
* Return elements full name.
*
* @return Fully qualified elements name.
*/
std::string full_name(bool /*relative*/) const override
{
return name_and_ns();
}
/**
* Return elements full name but without namespace.
*
* @return Elements full name without namespace.
*/
virtual std::string full_name_no_ns() const { return name(); }
/**
* Return the relative namespace from config.
*
* @return Namespace.
*/
const namespace_ &using_namespace() const;
friend bool operator==(const element &l, const element &r);

View File

@@ -25,6 +25,18 @@
#include <vector>
namespace clanguml::common::model {
/**
* @brief Base class for elements nested in the diagram.
*
* This class provides a common trait for diagram elements which can contain
* other nested elements, e.g. packages.
*
* @embed{nested_trait_hierarchy_class.svg}
*
* @tparam T Type of element
* @tparam Path Type of nested path (e.g. namespace or directory path)
*/
template <typename T, typename Path> class nested_trait {
public:
nested_trait() = default;
@@ -37,6 +49,13 @@ public:
virtual ~nested_trait() = default;
/**
* Add element at the current nested level.
*
* @tparam V Type of element
* @param p Element
* @return True, if element was added.
*/
template <typename V = T>
[[nodiscard]] bool add_element(std::unique_ptr<V> p)
{
@@ -53,6 +72,14 @@ public:
return true;
}
/**
* Add element at a nested path.
*
* @tparam V Type of element
* @param path Nested path (e.g. list of namespaces)
* @param p Element
* @return True, if element was added.
*/
template <typename V = T>
bool add_element(const Path &path, std::unique_ptr<V> p)
{
@@ -77,6 +104,13 @@ public:
"No parent element found for " + path.to_string());
}
/**
* Get element at path, if exists.
*
* @tparam V Element type.
* @param path Path to the element.
* @return Optional reference to the element.
*/
template <typename V = T> auto get_element(const Path &path) const
{
if (path.is_empty() || !has_element(path[0])) {
@@ -100,18 +134,13 @@ public:
return optional_ref<V>{};
}
template <typename V = T> auto get_element_parent(const T &element) const
{
auto path = element.path();
auto parent = get_element(path);
if (parent.has_value())
return optional_ref<V>{
std::ref<V>(dynamic_cast<V &>(parent.value()))};
return optional_ref<V>{};
}
/**
* Get element by name at the current nested level.
*
* @tparam V Type of element.
* @param name Name of the element (cannot contain namespace or path)
* @return Optional reference to the element.
*/
template <typename V = T> auto get_element(const std::string &name) const
{
assert(!util::contains(name, "::"));
@@ -130,6 +159,13 @@ public:
return optional_ref<V>{};
}
/**
* Returns true of this nested level contains an element with specified
* name.
*
* @param name Name of the element.
* @return True if element exists.
*/
bool has_element(const std::string &name) const
{
return std::find_if(elements_.cbegin(), elements_.cend(),
@@ -137,6 +173,12 @@ public:
elements_.end();
}
/**
* Return result of functor f applied to all_of elements.
* @tparam F Functor type
* @param f Functor value
* @return True, if functor return true for elements, including nested ones.
*/
template <typename F> bool all_of(F &&f) const
{
return std::all_of(
@@ -151,6 +193,11 @@ public:
});
}
/**
* Check if nested element is empty.
*
* @return True if this nested element is empty.
*/
bool is_empty() const
{
return elements_.empty() ||
@@ -170,6 +217,13 @@ public:
auto begin() const { return elements_.begin(); }
auto end() const { return elements_.end(); }
/**
* Print the nested trait in the form of a tree.
*
* This method is used for debugging only.
*
* @param level Tree level
*/
void print_tree(const int level)
{
const auto &d = *this;

View File

@@ -32,6 +32,11 @@
namespace clanguml::common::model {
/**
* @brief Diagram element representing namespace or directory package
*
* @embed{package_hierarchy_class.svg}
*/
class package : public element,
public stylable_element,
public nested_trait<element, path> {
@@ -47,10 +52,25 @@ public:
std::string full_name(bool relative) const override;
/**
* Returns whether the namespace is deprecated.
*
* @return True, if namespace is deprecated.
*/
bool is_deprecated() const;
/**
* Set namespace deprecation status.
*
* @param deprecated True, if namespace is deprecated
*/
void set_deprecated(bool deprecated);
/**
* Add subpackage.
*
* @param p Package.
*/
void add_package(std::unique_ptr<common::model::package> &&p);
private:

View File

@@ -22,6 +22,9 @@
namespace clanguml::common::model {
/**
* @brief Base class of all diagram elements that have source location.
*/
class source_location {
public:
source_location() = default;
@@ -32,31 +35,96 @@ public:
{
}
/**
* Return absolute source file path.
*
* @return Absolute file path.
*/
const std::string &file() const { return file_; }
/**
* Set absolute file path.
*
* @param file Absolute file path.
*/
void set_file(const std::string &file) { file_ = file; }
/**
* Return source file path relative to `relative_to` config option.
*
* @return Relative file path.
*/
const std::string &file_relative() const { return file_relative_; }
/**
* Set relative file path.
*
* @param file Relative file path.
*/
void set_file_relative(const std::string &file) { file_relative_ = file; }
/**
* Get the translation unit, from which this source location was visited.
*
* @return Path to the translation unit.
*/
const std::string &translation_unit() const { return translation_unit_; }
/**
* Set the path to translation unit, from which this source location was
* visited.
*
* @param translation_unit Path to the translation unit.
*/
void set_translation_unit(const std::string &translation_unit)
{
translation_unit_ = translation_unit;
}
/**
* Get the source location line number.
*
* @return Line number.
*/
unsigned int line() const { return line_; }
/**
* Set the source location line number.
*
* @param line Line number.
*/
void set_line(const unsigned line) { line_ = line; }
/**
* Get the source location column number.
*
* @return Column number.
*/
unsigned int column() const { return column_; }
/**
* Set the source location column number.
*
* @param line Column number.
*/
void set_column(const unsigned column) { column_ = column; }
/**
* Get the source location id.
*
* The location id is equivalent to Clang's SourceLocation::getHashValue()
*
* @return Location id.
*/
unsigned int location_id() const { return hash_; }
/**
* Set the source location id.
*
* The location id is equivalent to Clang's SourceLocation::getHashValue()
*
* @param h Location id.
*/
void set_location_id(unsigned int h) { hash_ = h; }
private:

View File

@@ -31,10 +31,32 @@ namespace clanguml::common {
using id_t = int64_t;
enum class generator_type_t { plantuml, json };
/**
* Type of output diagram format generator.
*/
enum class generator_type_t {
plantuml, /*!< Diagrams will be gnerated in PlantUML format */
json /*!< Diagrams will be generated in JSON format */
};
std::string to_string(const std::string &s);
/**
* @brief Simple optional reference type.
*
* This class template provides a convenient way around the std::optional
* limitation of not allowing references. This is useful for storing
* references to diagram elements in various `views`, and writing methods
* which return can return an empty value if the diagram does not contain
* something.
*
* This is not an owning type - it will not accept an rvalue - the actual
* value must be stored somewhere else as lvalue or some kind of smart pointer.
*
* @note Probably unsafe - do not use at home.
*
* @tparam T Type of reference
*/
template <typename T> class optional_ref {
public:
using optional_type = T;
@@ -159,6 +181,13 @@ using reference_set = std::unordered_set<std::reference_wrapper<T>>;
* @brief Wrapper around std::regex, which contains original pattern
*/
struct regex {
/**
* @brief Constructor
*
* @param r Parsed regular expression
* @param p Raw regular expression pattern used for regenerating config and
* debugging
*/
regex(std::regex r, std::string p)
: regexp{std::move(r)}
, pattern{std::move(p)}
@@ -170,18 +199,36 @@ struct regex {
return std::regex_match(v, regexp);
}
std::regex regexp;
std::string pattern;
std::regex regexp; /*!< Parsed regular expression */
std::string pattern; /*!< Original regular expression pattern */
};
/**
* @brief Convenience class for configuration options with regex support
*
* This template class provides a convenient way of handling configuraiton
* options, which can be either some basic type like std::string or regex.
*
* @tparam T Type of alternative to regex (e.g. std::string)
*/
template <typename T> struct or_regex {
or_regex() = default;
/**
* @brief Constructor from alternative type
*/
or_regex(T v)
: value_{std::move(v)}
{
}
/**
* @brief Constructor from regex
*
* @param r Parsed regular expression
* @param p Raw regular expression pattern used for regenerating config and
* debugging
*/
or_regex(std::regex r, std::string p)
: value_{regex{std::move(r), std::move(p)}}
{

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,10 @@
namespace clanguml {
namespace config {
/**
* @brief Return YAML with predefined diagram templates
* @return YAML definition of predefined diagram templates
*/
const std::string &get_predefined_diagram_templates();
} // namespace config

View File

@@ -24,9 +24,28 @@ namespace clanguml {
namespace config {
template <typename T> void append_value(T &l, const T &r) { l = r; }
/**
* Possible option inheritance methods from top level to diagram level.
*/
enum class option_inherit_mode {
kOverride, /*!< Override entire options */
kAppend /*!< Append to list options */
};
enum class option_inherit_mode { kOverride, kAppend };
/**
* @brief Generic configuration option type
*
* This class template represents a single configuration option, which can
* be either a simple type such as bool or std::string or can be a list
* or dictionary.
*
* If the option is constructed only from default value, it's `is_declared`
* member is false, so we can deduce whether user provided the option or not.
*
* For each option type, there has to be defined a YAML decoder and emitter.
*
* @tparam T The type of the configuration option
*/
template <typename T> struct option {
option(std::string name_,
option_inherit_mode im = option_inherit_mode::kOverride)
@@ -44,6 +63,11 @@ template <typename T> struct option {
{
}
/**
* @brief Set the option value
*
* @param v Option value
*/
void set(const T &v)
{
value = v;
@@ -51,6 +75,13 @@ template <typename T> struct option {
has_value = true;
}
/**
* @brief Override option value
*
* This method overrides the option depending on it's inheritance type.
*
* @param o New option value
*/
void override(const option<T> &o)
{
if (o.is_declared && inheritance_mode == option_inherit_mode::kAppend) {
@@ -73,10 +104,22 @@ template <typename T> struct option {
operator bool() const { return has_value; }
/*! Option name, it is also the YAML key in the configuration file */
std::string name;
/*! Option value */
T value;
/*! Whether or not the value was provided by the user or default */
bool is_declared{false};
/*!
* Whether the option has value, if the option has no default value
* and wasn't provided in the config this is set to `false`.
*/
bool has_value{false};
/*! The inheritance mode for this option */
option_inherit_mode inheritance_mode;
};
} // namespace config

View File

@@ -1809,7 +1809,11 @@ void translation_unit_visitor::process_template_specialization_argument(
// Otherwise just set the name for the template argument to
// whatever clang says
argument.set_name(type_name);
if (template_params.size() > argument_index &&
template_params[argument_index].type())
argument.set_type(type_name);
else
argument.set_name(type_name);
}
else
argument.set_type(type_name);