0.6.2
C++ to UML diagram generator based on Clang
Loading...
Searching...
No Matches
Public Member Functions | Private Member Functions | Private Attributes | List of all members
clanguml::sequence_diagram::model::diagram Class Reference

Model of a sequence diagram. More...

Detailed Description

Model of a sequence diagram.

Definition at line 57 of file diagram.h.

#include <diagram.h>

Public Member Functions

 diagram ()=default
 
 diagram (const diagram &)=delete
 
 diagram (diagram &&)=default
 
diagramoperator= (const diagram &)=delete
 
diagramoperator= (diagram &&)=default
 
common::model::diagram_t type () const override
 Get the diagram model type - in this case sequence.
 
common::optional_ref< common::model::diagram_elementget (const std::string &full_name) const override
 Search for element in the diagram by fully qualified name.
 
common::optional_ref< common::model::diagram_elementget (eid_t id) const override
 Search for element in the diagram by id.
 
template<typename T >
common::optional_ref< T > get_participant (eid_t id) const
 Get participant by id.
 
void add_participant (std::unique_ptr< participant > p)
 Add sequence diagram participant.
 
void add_active_participant (eid_t id)
 Set participant with id as active.
 
bool has_activity (eid_t id) const
 Check if diagram has activity identified by caller id.
 
const activityget_activity (eid_t id) const
 Get reference to current activity of a participant.
 
activityget_activity (eid_t id)
 Get reference to current activity of a participant.
 
void add_message (model::message &&message)
 Add message to current activity.
 
void add_block_message (model::message &&message)
 Add block message to the current activity.
 
void end_block_message (model::message &&message, common::model::message_t start_type)
 End current block message.
 
void add_case_stmt_message (model::message &&m)
 Add switch block case statement.
 
std::map< eid_t, activity > & sequences ()
 Get all sequences in the diagram.
 
const std::map< eid_t, activity > & sequences () const
 Get all sequences in the diagram.
 
std::map< eid_t, std::unique_ptr< participant > > & participants ()
 Get map of all participants in the diagram.
 
const std::map< eid_t, std::unique_ptr< participant > > & participants () const
 Get map of all participants in the diagram.
 
std::set< eid_t > & active_participants ()
 Get all active participants in the diagram.
 
const std::set< eid_t > & active_participants () const
 Get all active participants in the diagram.
 
std::string to_alias (const std::string &full_name) const
 Convert element full name to PlantUML alias.
 
void print () const
 Debug method for printing entire diagram to console.
 
bool should_include (const sequence_diagram::model::participant &p) const
 Convenience should_include overload for participant.
 
std::vector< std::string > list_from_values () const
 Get list of all possible 'from' values in the model.
 
std::vector< std::string > list_to_values () const
 Get list of all possible 'to' values in the model.
 
std::vector< message_chain_tget_all_from_to_message_chains (eid_t from_activity, eid_t to_activity) const
 Generate a list of message chains matching a from_to constraint.
 
void build_reverse_call_graph (reverse_call_graph_activity_node &node, std::set< eid_t > visited_callers={}) const
 Build reverse call graph.
 
std::vector< eid_tget_to_activity_ids (const config::source_location &to_location) const
 Get ids of activities matching 'to'.
 
std::vector< eid_tget_from_activity_ids (const config::source_location &from_location) const
 Get ids of activities matching 'from'.
 
void finalize () override
 Once the diagram is complete, run any final processing.
 
bool is_empty () const override
 Check whether the diagram is empty.
 
void inline_lambda_operator_calls ()
 
void handle_invalid_from_condition (const config::source_location &sf) const
 
void handle_invalid_to_condition (const config::source_location &sf) const
 
- Public Member Functions inherited from clanguml::common::model::diagram
 diagram ()
 
 diagram (const diagram &)=delete
 
 diagram (diagram &&) noexcept
 
diagramoperator= (const diagram &)=delete
 
diagramoperator= (diagram &&) noexcept
 
virtual ~diagram ()
 
virtual diagram_t type () const =0
 Return type of the diagram.
 
virtual opt_ref< clanguml::common::model::diagram_elementget (const std::string &full_name) const =0
 
virtual common::optional_ref< clanguml::common::model::diagram_elementget (eid_t id) const =0
 
virtual common::optional_ref< clanguml::common::model::diagram_elementget_with_namespace (const std::string &name, const namespace_ &ns) const
 
void set_name (const std::string &name)
 
std::string name () const
 
void set_filter (std::unique_ptr< diagram_filter > filter)
 
const diagram_filterfilter () const
 
void set_complete (bool complete)
 Set diagram in a complete state.
 
bool complete () const
 Whether the diagram is complete.
 
virtual void finalize ()
 Once the diagram is complete, run any final processing.
 
bool should_include (const element &e) const
 
bool should_include (const namespace_ &ns) const
 
bool should_include (const source_file &path) const
 
bool should_include (relationship r) const
 
bool should_include (relationship_t r) const
 
bool should_include (access_t s) const
 
bool should_include (const std::string &s) const =delete
 
virtual bool has_element (const eid_t) const
 
virtual bool should_include (const namespace_ &ns, const std::string &name) const
 
virtual bool is_empty () const =0
 Check whether the diagram is empty.
 
virtual void apply_filter ()
 

Private Member Functions

bool inline_lambda_operator_call (eid_t id, model::activity &new_activity, const model::message &m)
 
void fold_or_end_block_statement (message &&m, common::model::message_t statement_begin, std::vector< message > &current_messages) const
 
bool is_begin_block_message (common::model::message_t mt)
 
bool is_end_block_message (common::model::message_t mt)
 

Private Attributes

std::map< eid_t, activityactivities_
 
std::map< eid_t, std::unique_ptr< participant > > participants_
 
std::set< eid_tactive_participants_
 

Additional Inherited Members

- Protected Member Functions inherited from clanguml::common::model::diagram
diagram_filterfilter ()
 

Constructor & Destructor Documentation

◆ diagram() [1/3]

clanguml::sequence_diagram::model::diagram::diagram ( )
default

◆ diagram() [2/3]

clanguml::sequence_diagram::model::diagram::diagram ( const diagram )
delete

◆ diagram() [3/3]

clanguml::sequence_diagram::model::diagram::diagram ( diagram &&  )
default

Member Function Documentation

◆ active_participants() [1/2]

std::set< eid_t > & clanguml::sequence_diagram::model::diagram::active_participants ( )

Get all active participants in the diagram.

Returns
Set of all active participant ids

Definition at line 198 of file diagram.cc.

198{ return active_participants_; }

◆ active_participants() [2/2]

const std::set< eid_t > & clanguml::sequence_diagram::model::diagram::active_participants ( ) const

Get all active participants in the diagram.

Returns
Set of all active participant ids

Definition at line 200 of file diagram.cc.

201{
203}

◆ add_active_participant()

void clanguml::sequence_diagram::model::diagram::add_active_participant ( eid_t  id)

Set participant with id as active.

Parameters
idId of participant to activate

Definition at line 112 of file diagram.cc.

113{
114 active_participants_.emplace(id);
115}

◆ add_block_message()

void clanguml::sequence_diagram::model::diagram::add_block_message ( model::message &&  message)

Add block message to the current activity.

Block messages represent sequence diagram blocks such as alt or loop.

The block messages can be stacked.

Parameters
messageMessage model

Definition at line 145 of file diagram.cc.

146{
147 add_message(std::move(message));
148}

◆ add_case_stmt_message()

void clanguml::sequence_diagram::model::diagram::add_case_stmt_message ( model::message &&  m)

Add switch block case statement.

Parameters
mMessage model

Definition at line 163 of file diagram.cc.

164{
166 const auto caller_id = m.from();
167
168 if (activities_.find(caller_id) != activities_.end()) {
169 auto &current_messages = get_activity(caller_id).messages();
170
171 if (current_messages.back().type() == message_t::kCase) {
172 // Do nothing - fallthroughs not supported yet...
173 }
174 else {
175 current_messages.emplace_back(std::move(m));
176 }
177 }
178}

◆ add_message()

void clanguml::sequence_diagram::model::diagram::add_message ( model::message &&  message)

Add message to current activity.

Parameters
messageMessage model

Definition at line 126 of file diagram.cc.

127{
128 const auto caller_id = message.from();
129 const auto callee_id = message.to();
130
131 if (activities_.find(caller_id) == activities_.end()) {
132 activity a{caller_id};
133 activities_.insert({caller_id, std::move(a)});
134 }
135
136 if (activities_.find(callee_id) == activities_.end()) {
137 activity a{callee_id};
138 activities_.insert({callee_id, std::move(a)});
139 }
140
141 get_activity(callee_id).add_caller(caller_id);
142 get_activity(caller_id).add_message(std::move(message));
143}

◆ add_participant()

void clanguml::sequence_diagram::model::diagram::add_participant ( std::unique_ptr< participant p)

Add sequence diagram participant.

Parameters
pSequence diagram participant model

Definition at line 93 of file diagram.cc.

94{
95 const auto participant_id = p->id();
96
97 p->complete(true);
98
99 assert(participant_id.is_global());
100
101 if (participants_.find(participant_id) == participants_.end()) {
102 LOG_DBG("Adding '{}' participant: {}, {} [{}]", p->type_name(),
103 p->full_name(false), p->id(),
104 p->type_name() == "method"
105 ? dynamic_cast<method *>(p.get())->method_name()
106 : "");
107
108 participants_.emplace(participant_id, std::move(p));
109 }
110}

◆ build_reverse_call_graph()

void clanguml::sequence_diagram::model::diagram::build_reverse_call_graph ( reverse_call_graph_activity_node node,
std::set< eid_t visited_callers = {} 
) const

Build reverse call graph.

This method builds a reverse call graph tree based on callers() list stored in each activity.

Parameters
nodeThe current reverse call graph node
visited_callersThis is necessary for breaking recursive calls

Definition at line 311 of file diagram.cc.

313{
314 LOG_INFO("Building reverse call graph for activity: {}", node.activity_id);
315
316 if (sequences().count(node.activity_id) == 0)
317 return;
318
319 visited_callers.insert(node.activity_id);
320
321 const auto &callers = sequences().at(node.activity_id).callers();
322
323 for (const auto &caller : callers) {
324 if (visited_callers.count(caller) > 0) {
325 // break recursive calls
326 continue;
327 }
328
329 reverse_call_graph_activity_node caller_node;
330 caller_node.activity_id = caller;
331
332 build_reverse_call_graph(caller_node, visited_callers);
333
334 node.callers.emplace_back(std::move(caller_node));
335 }
336}

◆ end_block_message()

void clanguml::sequence_diagram::model::diagram::end_block_message ( model::message &&  message,
common::model::message_t  start_type 
)

End current block message.

Parameters
messageMessage model
start_typeType of block statement.

Definition at line 150 of file diagram.cc.

152{
153 const auto caller_id = message.from();
154
155 if (activities_.find(caller_id) != activities_.end()) {
156 auto &current_messages = get_activity(caller_id).messages();
157
159 std::move(message), start_type, current_messages);
160 }
161}

◆ finalize()

void clanguml::sequence_diagram::model::diagram::finalize ( )
overridevirtual

Once the diagram is complete, run any final processing.

This method should be overriden by specific diagram models to do some final tasks like cleaning up the model (e.g. some filters only work on completed diagrams).

Reimplemented from clanguml::common::model::diagram.

Definition at line 663 of file diagram.cc.

664{
665 // Apply diagram filters and remove any empty block statements
667
668 // First in each sequence (activity) filter out any remaining
669 // uninteresting calls
670 for (auto &[id, act] : activities_) {
671 util::erase_if(act.messages(), [this](auto &m) {
672 if (m.type() != message_t::kCall)
673 return false;
674
675 const auto &to = get_participant<model::participant>(m.to());
676 if (!to || to.value().skip())
677 return true;
678
679 if (!should_include(to.value())) {
680 LOG_DBG("Excluding call from [{}] to {} [{}]", m.from(),
681 to.value().full_name(false), m.to());
682 return true;
683 }
684
685 return false;
686 });
687 }
688
689 // Now remove any empty block statements, e.g. if/endif
690 for (auto &[id, act] : activities_) {
691 int64_t block_nest_level{0};
692 std::vector<std::vector<message>> block_message_stack;
693 // Add first stack level - this level will contain the filtered
694 // message sequence
695 block_message_stack.emplace_back();
696
697 // First create a recursive stack from the messages and
698 // message blocks (e.g. if statements)
699 for (auto &m : act.messages()) {
700 if (is_begin_block_message(m.type())) {
701 block_nest_level++;
702 block_message_stack.push_back({m});
703 }
704 else if (is_end_block_message(m.type())) {
705 block_nest_level--;
706
707 block_message_stack.back().push_back(m);
708
709 // Check the last stack for any calls, if yes, collapse it
710 // on the previous stack
711 if (std::count_if(block_message_stack.back().begin(),
712 block_message_stack.back().end(), [](auto &m) {
713 return (m.type() == message_t::kCall) ||
714 (m.type() == message_t::kReturn) ||
715 (m.type() == message_t::kCoReturn);
716 }) > 0) {
717 std::copy(block_message_stack.back().begin(),
718 block_message_stack.back().end(),
719 std::back_inserter(
720 block_message_stack.at(block_nest_level)));
721 }
722
723 block_message_stack.pop_back();
724
725 assert(block_nest_level >= 0);
726 }
727 else {
728 if (m.type() == message_t::kCall) {
729 // Set the message return type based on the callee return
730 // type
731 auto to_participant =
732 get_participant<sequence_diagram::model::function>(
733 m.to());
734 if (to_participant.has_value()) {
735 m.set_return_type(to_participant.value().return_type());
736 }
737 }
738 block_message_stack.back().push_back(m);
739 }
740 }
741
742 act.messages().clear();
743
744 for (auto &m : block_message_stack[0]) {
745 act.add_message(m);
746 }
747 }
748}

◆ fold_or_end_block_statement()

void clanguml::sequence_diagram::model::diagram::fold_or_end_block_statement ( message &&  m,
common::model::message_t  statement_begin,
std::vector< message > &  current_messages 
) const
private

This method checks the last messages in sequence (current_messages), if they represent a block sequence identified by statement_begin (e.g. if/else) and there are no actual call expressions within this block statement the entire block statement is removed from the end of the sequence.

Otherwise the block statement is ended with a proper statement (e.g. endif)

Parameters
mMessage to add to the sequence
statement_beginType of message which begins this type of block statement (e.g. message_t::kIf)
current_messagesReference to the sequence messages which should be amended

Definition at line 630 of file diagram.cc.

633{
634 bool is_empty_statement{true};
635
636 auto rit = current_messages.rbegin();
637 for (; rit != current_messages.rend(); rit++) {
638 if (rit->type() == statement_begin) {
639 break;
640 }
641 if (rit->type() == common::model::message_t::kReturn) {
642 is_empty_statement = false;
643 break;
644 }
645 if (rit->type() == common::model::message_t::kCoReturn) {
646 is_empty_statement = false;
647 break;
648 }
649 if (rit->type() == common::model::message_t::kCall) {
650 is_empty_statement = false;
651 break;
652 }
653 }
654
655 if (is_empty_statement) {
656 current_messages.erase((rit + 1).base(), current_messages.end());
657 }
658 else {
659 current_messages.emplace_back(std::move(m));
660 }
661}

◆ get() [1/2]

common::optional_ref< common::model::diagram_element > clanguml::sequence_diagram::model::diagram::get ( const std::string &  full_name) const
overridevirtual

Search for element in the diagram by fully qualified name.

Parameters
full_nameFully qualified element name.
Returns
Optional reference to a diagram element.

Implements clanguml::common::model::diagram.

Definition at line 68 of file diagram.cc.

70{
71 for (const auto &[id, participant] : participants_) {
72 if (participant->full_name(false) == full_name)
73 return {*participant};
74 }
75
76 return {};
77}

◆ get() [2/2]

common::optional_ref< common::model::diagram_element > clanguml::sequence_diagram::model::diagram::get ( eid_t  id) const
overridevirtual

Search for element in the diagram by id.

Parameters
idElement id.
Returns
Optional reference to a diagram element.

Implements clanguml::common::model::diagram.

Definition at line 79 of file diagram.cc.

81{
82 if (participants_.find(id) != participants_.end())
83 return {*participants_.at(id)};
84
85 return {};
86}

◆ get_activity() [1/2]

activity & clanguml::sequence_diagram::model::diagram::get_activity ( eid_t  id)

Get reference to current activity of a participant.

Parameters
idParticipant id
Returns

Definition at line 124 of file diagram.cc.

124{ return activities_.at(id); }

◆ get_activity() [2/2]

const activity & clanguml::sequence_diagram::model::diagram::get_activity ( eid_t  id) const

Get reference to current activity of a participant.

Parameters
idParticipant id
Returns

Definition at line 117 of file diagram.cc.

118{
119 return activities_.at(id);
120}

◆ get_all_from_to_message_chains()

std::vector< message_chain_t > clanguml::sequence_diagram::model::diagram::get_all_from_to_message_chains ( eid_t  from_activity,
eid_t  to_activity 
) const

Generate a list of message chains matching a from_to constraint.

If 'from_activity' is 0, this method will return all message chains ending in 'to_activity'.

Parameters
from_activitySource activity for from_to message chain
to_activityTarget activity for from_to message chain
Returns
List of message chains

Definition at line 338 of file diagram.cc.

340{
341 // Message (call) chains matching the specified from_to condition
342 std::vector<message_chain_t> message_chains;
343
344 // First, build reverse call graph starting from target `to_activity`
345 // `target_roots` should contain all activities which call `to_activity`
346 reverse_call_graph_activity_node target_roots;
347 target_roots.activity_id = to_activity;
348
349 for (const auto &[k, v] : sequences()) {
350 for (const auto &m : v.messages()) {
351 if (m.type() != common::model::message_t::kCall)
352 continue;
353
354 if (m.to() == to_activity) {
355 reverse_call_graph_activity_node node;
356 node.activity_id = m.from();
357 target_roots.callers.emplace_back(std::move(node));
358 }
359 }
360 }
361
362 // Now recurse from the initial target activities based on reverse
363 // callers list stored in each activity
364 for (auto &caller : target_roots.callers)
366
367 // Convert the reverse call graph into a list of call chains using
368 // depth first search
369 auto activity_id_chains = find_reverse_message_chains(target_roots);
370
371 // Make sure the activity call chains list is unique
372 sort(begin(activity_id_chains), end(activity_id_chains));
373 activity_id_chains.erase(
374 unique(begin(activity_id_chains), end(activity_id_chains)),
375 end(activity_id_chains));
376
377 // Convert the call chains with activity ids to lists of actual messages
378 for (const auto &chain : activity_id_chains) {
379 message_chain_t message_chain;
380 for (auto it = begin(chain); it != end(chain); it++) {
381 const auto next_it = it + 1;
382 if (next_it == end(chain))
383 break;
384
385 auto from_id = *it;
386 if (activities_.count(from_id) == 0)
387 continue;
388
389 auto to_id = *(next_it);
390
391 const auto &act = activities_.at(from_id);
392
393 for (const auto &m : act.messages()) {
394 if (m.to() == to_id) {
395 message_chain.push_back(m);
396 break;
397 }
398 }
399 }
400 message_chains.emplace_back(std::move(message_chain));
401 }
402
403 // Perform final filtering of the message chains
404 std::vector<message_chain_t> message_chains_filtered{};
405
406 for (auto &mc : message_chains) {
407 // Skip empty chains
408 if (mc.empty())
409 continue;
410
411 // Make sure the chain has valid starting point
412 if (from_activity.value() == 0 ||
413 (mc.front().from() == from_activity)) {
414 message_chains_filtered.push_back(mc);
415 }
416 }
417
418 int message_chain_index{};
419 for (const auto &mc : message_chains_filtered) {
420 LOG_INFO("\t{}: {}", message_chain_index++,
421 fmt::join(util::map<std::string>(mc,
422 [](const model::message &m) -> std::string {
423 return m.message_name();
424 }),
425 "->"));
426 }
427
428 return message_chains_filtered;
429}

◆ get_from_activity_ids()

std::vector< eid_t > clanguml::sequence_diagram::model::diagram::get_from_activity_ids ( const config::source_location from_location) const

Get ids of activities matching 'from'.

Parameters
from_locationSource activity
Returns
Activity id

Definition at line 283 of file diagram.cc.

285{
286 std::vector<eid_t> from_activities{};
287
288 for (const auto &[k, v] : sequences()) {
289 if (participants().count(v.from()) == 0)
290 continue;
291
292 const auto &caller = *participants().at(v.from());
293 std::string vfrom = caller.full_name(false);
294 if (from_location.location == vfrom) {
295 LOG_DBG("Found sequence diagram start point '{}': {}", vfrom, k);
296 from_activities.push_back(k);
297 }
298 }
299
300 if (from_activities.empty()) {
301 LOG_WARN("Failed to find 'from' participant {} for from "
302 "condition: ",
303 from_location.location.to_string());
304 }
305
306 util::remove_duplicates(from_activities);
307
308 return from_activities;
309}

◆ get_participant()

template<typename T >
common::optional_ref< T > clanguml::sequence_diagram::model::diagram::get_participant ( eid_t  id) const
inline

Get participant by id.

Parameters
idParticipant id.
Returns
Optional reference to a diagram element.

Definition at line 98 of file diagram.h.

99 {
100 if (participants_.find(id) == participants_.end()) {
101 return {};
102 }
103
104 return common::optional_ref<T>(
105 dynamic_cast<T *>(participants_.at(id).get()));
106 }

◆ get_to_activity_ids()

std::vector< eid_t > clanguml::sequence_diagram::model::diagram::get_to_activity_ids ( const config::source_location to_location) const

Get ids of activities matching 'to'.

Parameters
to_locationTarget activity
Returns
Activity id

Definition at line 252 of file diagram.cc.

254{
255 std::vector<eid_t> to_activities{};
256
257 for (const auto &[k, v] : sequences()) {
258 for (const auto &m : v.messages()) {
259 if (m.type() != common::model::message_t::kCall)
260 continue;
261
262 const auto &callee = *participants().at(m.to());
263 std::string vto = callee.full_name(false);
264 if (to_location.location == vto) {
265 LOG_DBG(
266 "Found sequence diagram end point '{}': {}", vto, m.to());
267 to_activities.push_back(m.to());
268 }
269 }
270 }
271
272 if (to_activities.empty()) {
273 LOG_WARN("Failed to find 'to' participant {} for to "
274 "condition: ",
275 to_location.location.to_string());
276 }
277
278 util::remove_duplicates(to_activities);
279
280 return to_activities;
281}

◆ handle_invalid_from_condition()

void clanguml::sequence_diagram::model::diagram::handle_invalid_from_condition ( const config::source_location sf) const

Definition at line 750 of file diagram.cc.

752{
753 std::string error_message =
754 fmt::format("Failed to find participant matching '{}' for "
755 "'from' condition.",
756 sf.location.to_string());
757
758 if (!sf.location.is_regex()) {
759 std::vector<std::string> from_participants;
760 for (const auto &[k, v] : sequences()) {
761 if (participants().count(v.from()) == 0)
762 continue;
763
764 const auto &caller = *participants().at(v.from());
765 from_participants.emplace_back(caller.full_name(false));
766 }
767
768 auto possible_matches = util::get_approximate_matches(
769 from_participants, sf.location.to_string());
770
771 if (!possible_matches.empty()) {
772 error_message +=
773 fmt::format(" Did you mean: '{}'?", possible_matches.at(0));
774 }
775 }
776
777 throw error::invalid_sequence_from_condition(type(), name(), error_message);
778}

◆ handle_invalid_to_condition()

void clanguml::sequence_diagram::model::diagram::handle_invalid_to_condition ( const config::source_location sf) const

Definition at line 780 of file diagram.cc.

782{
783 std::string error_message =
784 fmt::format("Failed to find participant matching '{}' for "
785 "'to' condition.",
786 to_location.location.to_string());
787
788 if (!to_location.location.is_regex()) {
789 std::vector<std::string> to_participants;
790 for (const auto &[k, v] : sequences()) {
791 for (const auto &m : v.messages()) {
792 if (m.type() != common::model::message_t::kCall)
793 continue;
794
795 const auto &callee = *participants().at(m.to());
796 to_participants.emplace_back(callee.full_name(false));
797 }
798 }
799
800 auto possible_matches = util::get_approximate_matches(
801 to_participants, to_location.location.to_string());
802
803 if (!possible_matches.empty()) {
804 error_message +=
805 fmt::format(" Did you mean: '{}'?", possible_matches.at(0));
806 }
807 }
808
809 throw error::invalid_sequence_to_condition(type(), name(), error_message);
810}

◆ has_activity()

bool clanguml::sequence_diagram::model::diagram::has_activity ( eid_t  id) const

Check if diagram has activity identified by caller id.

Parameters
idCaller id representing the activity
Returns
True, if an activity already exists

Definition at line 122 of file diagram.cc.

122{ return activities_.count(id) > 0; }

◆ inline_lambda_operator_call()

bool clanguml::sequence_diagram::model::diagram::inline_lambda_operator_call ( eid_t  id,
model::activity new_activity,
const model::message m 
)
private

Definition at line 517 of file diagram.cc.

519{
520 using namespace std::string_literals;
521
522 bool message_call_to_lambda{false};
523 auto maybe_lambda_operator = get_participant<model::method>(m.to());
524
525 if (maybe_lambda_operator) {
526 const auto parent_class_id = maybe_lambda_operator.value().class_id();
527 auto maybe_parent_class =
528 get_participant<model::class_>(parent_class_id);
529
530 if (maybe_parent_class && maybe_parent_class.value().is_lambda()) {
531 if (has_activity(m.to())) {
532 auto lambda_operator_activity = get_activity(m.to());
533
534 // For each call in that lambda activity - reattach this
535 // call to the current activity
536 for (auto &mm : lambda_operator_activity.messages()) {
537 if (!inline_lambda_operator_call(id, new_activity, mm)) {
538
539 // Do not propagate return calls from nested lambdas
540 if (mm.type() == common::model::message_t::kReturn) {
541 continue;
542 }
543
544 auto new_message{mm};
545
546 new_message.set_from(id);
547
548 new_activity.add_message(new_message);
549 }
550 }
551 }
552
553 message_call_to_lambda = true;
554 }
555 }
556
557 return message_call_to_lambda;
558}

◆ inline_lambda_operator_calls()

void clanguml::sequence_diagram::model::diagram::inline_lambda_operator_calls ( )

If option to inline lambda calls is enabled, we need to modify the sequences to skip the lambda calls. In case lambda call does not lead to a non-lambda call, omit it entirely

Definition at line 436 of file diagram.cc.

437{
438 using namespace std::string_literals;
439
440 std::map<eid_t, activity> activities;
441 std::map<eid_t, std::unique_ptr<participant>> participants;
442 std::set<eid_t> active_participants;
443
444 for (auto &[id, act] : sequences()) {
445 model::activity new_activity{id};
446
447 // If activity is a lambda operator() - skip it, we're only processing
448 // normal activities at this level, and descend recursively into
449 // lambda activities when encountering a lambda call expression
450 auto maybe_lambda_activity = get_participant<model::method>(id);
451
452 if (maybe_lambda_activity) {
453 const auto parent_class_id =
454 maybe_lambda_activity.value().class_id();
455 auto maybe_parent_class =
456 get_participant<model::class_>(parent_class_id);
457
458 if (maybe_parent_class && maybe_parent_class.value().is_lambda()) {
459 continue;
460 }
461 }
462
463 // For other activities, check each message - if it calls lambda
464 // operator() - reattach the message to the next activity in the chain
465 // (assuming it's not lambda)
466 for (auto &m : act.messages()) {
467
468 auto message_call_to_lambda{false};
469
470 message_call_to_lambda =
471 inline_lambda_operator_call(id, new_activity, m);
472
473 if (!message_call_to_lambda)
474 new_activity.add_message(m);
475 }
476
477 // Add activity
478 activities.insert({id, std::move(new_activity)});
479 }
480
481 for (auto &&[id, p] : this->participants()) {
482 // Skip participants which are lambda classes
483 if (const auto *maybe_class =
484 dynamic_cast<const model::class_ *>(p.get());
485 maybe_class != nullptr && maybe_class->is_lambda()) {
486 continue;
487 }
488
489 // Skip participants which are lambda operator methods
490 if (const auto *maybe_method =
491 dynamic_cast<const model::method *>(p.get());
492 maybe_method != nullptr) {
493 auto maybe_class =
494 get_participant<model::class_>(maybe_method->class_id());
495 if (maybe_class && maybe_class.value().is_lambda()) {
496 continue;
497 }
498 }
499
500 // Otherwise move the participant to the new diagram model
501 auto participant_id = p->id();
502 participants.emplace(participant_id, std::move(p));
503 }
504
505 // Skip active participants which are not in lambdaless_diagram participants
506 for (auto id : this->active_participants()) {
507 if (participants.count(id) > 0) {
508 active_participants.emplace(id);
509 }
510 }
511
512 activities_ = std::move(activities);
513 participants_ = std::move(participants);
515}

◆ is_begin_block_message()

bool clanguml::sequence_diagram::model::diagram::is_begin_block_message ( common::model::message_t  mt)
inlineprivate

Definition at line 355 of file diagram.h.

356 {
358 static std::set<message_t> block_begin_types{message_t::kIf,
359 message_t::kWhile, message_t::kDo, message_t::kFor, message_t::kTry,
360 message_t::kSwitch, message_t::kConditional};
361
362 return block_begin_types.count(mt) > 0;
363 };

◆ is_empty()

bool clanguml::sequence_diagram::model::diagram::is_empty ( ) const
overridevirtual

Check whether the diagram is empty.

Returns
True, if diagram is empty

Implements clanguml::common::model::diagram.

Definition at line 431 of file diagram.cc.

432{
433 return activities_.empty() || participants_.empty();
434}

◆ is_end_block_message()

bool clanguml::sequence_diagram::model::diagram::is_end_block_message ( common::model::message_t  mt)
inlineprivate

Definition at line 365 of file diagram.h.

366 {
368 static std::set<message_t> block_end_types{message_t::kIfEnd,
369 message_t::kWhileEnd, message_t::kDoEnd, message_t::kForEnd,
370 message_t::kTryEnd, message_t::kSwitchEnd,
371 message_t::kConditionalEnd};
372
373 return block_end_types.count(mt) > 0;
374 };

◆ list_from_values()

std::vector< std::string > clanguml::sequence_diagram::model::diagram::list_from_values ( ) const

Get list of all possible 'from' values in the model.

Returns
List of all possible 'from' values

Definition at line 213 of file diagram.cc.

214{
215 std::vector<std::string> result;
216
217 for (const auto &[from_id, act] : activities_) {
218
219 const auto &from_activity = *(participants_.at(from_id));
220 const auto &full_name = from_activity.full_name(false);
221 if (!full_name.empty())
222 result.push_back(full_name);
223 }
224
225 std::sort(result.begin(), result.end());
226 result.erase(std::unique(result.begin(), result.end()), result.end());
227
228 return result;
229}

◆ list_to_values()

std::vector< std::string > clanguml::sequence_diagram::model::diagram::list_to_values ( ) const

Get list of all possible 'to' values in the model.

Returns
List of all possible 'to' values

Definition at line 231 of file diagram.cc.

232{
233 std::vector<std::string> result;
234
235 for (const auto &[from_id, act] : activities_) {
236 for (const auto &m : act.messages()) {
237 if (participants_.count(m.to()) > 0) {
238 const auto &to_activity = *(participants_.at(m.to()));
239 const auto &full_name = to_activity.full_name(false);
240 if (!full_name.empty())
241 result.push_back(full_name);
242 }
243 }
244 }
245
246 std::sort(result.begin(), result.end());
247 result.erase(std::unique(result.begin(), result.end()), result.end());
248
249 return result;
250}

◆ operator=() [1/2]

diagram & clanguml::sequence_diagram::model::diagram::operator= ( const diagram )
delete

◆ operator=() [2/2]

diagram & clanguml::sequence_diagram::model::diagram::operator= ( diagram &&  )
default

◆ participants() [1/2]

std::map< eid_t, std::unique_ptr< participant > > & clanguml::sequence_diagram::model::diagram::participants ( )

Get map of all participants in the diagram.

Returns
Map of participants in the diagram

Definition at line 187 of file diagram.cc.

188{
189 return participants_;
190}

◆ participants() [2/2]

const std::map< eid_t, std::unique_ptr< participant > > & clanguml::sequence_diagram::model::diagram::participants ( ) const

Get map of all participants in the diagram.

Returns
Map of participants in the diagram

Definition at line 193 of file diagram.cc.

194{
195 return participants_;
196}

◆ print()

void clanguml::sequence_diagram::model::diagram::print ( ) const

Debug method for printing entire diagram to console.

Definition at line 560 of file diagram.cc.

561{
562 LOG_TRACE(" --- Participants ---");
563 for (const auto &[id, participant] : participants_) {
564 LOG_DBG("{} - {}", id, participant->to_string());
565 }
566
567 LOG_TRACE(" --- Activities ---");
568 for (const auto &[from_id, act] : activities_) {
569 if (participants_.count(from_id) == 0)
570 continue;
571
572 LOG_TRACE("Sequence id={}:", from_id);
573
574 const auto &from_activity = *(participants_.at(from_id));
575
576 LOG_TRACE(" Activity id={}, from={}:", act.from(),
577 from_activity.full_name(false));
578
579 for (const auto &message : act.messages()) {
580 if (participants_.find(message.from()) == participants_.end())
581 continue;
582
583 const auto &from_participant = *participants_.at(message.from());
584
585 if (message.type() == common::model::message_t::kReturn) {
586 if (message.to() == 0)
587 LOG_TRACE(
588 " Return from={}, from_id={}, name={}, type={}",
589 from_participant.full_name(false),
590 from_participant.id(), message.message_name(),
591 to_string(message.type()));
592 else {
593 const auto &to_participant =
594 *participants_.at(message.to());
595
596 LOG_TRACE(" Return from={}, from_id={}, "
597 "to={}, to_id={}, name={}, type={}",
598 from_participant.full_name(false),
599 from_participant.id(), to_participant.full_name(false),
600 message.to(), message.message_name(),
601 to_string(message.type()));
602 }
603 }
604 else if (participants_.find(message.to()) == participants_.end()) {
605 LOG_TRACE(" Message from={}, from_id={}, "
606 "to={}, to_id={}, name={}, type={}",
607 from_participant.full_name(false), from_participant.id(),
608 "__UNRESOLVABLE_ID__", message.to(), message.message_name(),
609 to_string(message.type()));
610 }
611 else {
612 const auto &to_participant = *participants_.at(message.to());
613
614 std::string message_comment{"None"};
615 if (const auto &cmt = message.comment(); cmt.has_value()) {
616 message_comment = cmt.value().at("comment");
617 }
618
619 LOG_TRACE(" Message from={}, from_id={}, "
620 "to={}, to_id={}, name={}, type={}, comment={}",
621 from_participant.full_name(false), from_participant.id(),
622 to_participant.full_name(false), to_participant.id(),
623 message.message_name(), to_string(message.type()),
624 message_comment);
625 }
626 }
627 }
628}

◆ sequences() [1/2]

std::map< eid_t, activity > & clanguml::sequence_diagram::model::diagram::sequences ( )

Get all sequences in the diagram.

Returns
Map of sequences in the diagram

Definition at line 180 of file diagram.cc.

180{ return activities_; }

◆ sequences() [2/2]

const std::map< eid_t, activity > & clanguml::sequence_diagram::model::diagram::sequences ( ) const

Get all sequences in the diagram.

Returns
Map of sequences in the diagram

Definition at line 182 of file diagram.cc.

183{
184 return activities_;
185}

◆ should_include()

bool clanguml::sequence_diagram::model::diagram::should_include ( const sequence_diagram::model::participant p) const

Convenience should_include overload for participant.

Parameters
pParticipant model
Returns
True, if the participant should be included in the diagram

Definition at line 205 of file diagram.cc.

207{
208 return filter().should_include(p) &&
210 dynamic_cast<const common::model::source_location &>(p));
211}

◆ to_alias()

std::string clanguml::sequence_diagram::model::diagram::to_alias ( const std::string &  full_name) const

Convert element full name to PlantUML alias.

Todo:
This method does not belong here - refactor to PlantUML specific code.
Parameters
full_nameFull name of the diagram element.
Returns
PlantUML alias.

Definition at line 88 of file diagram.cc.

89{
90 return full_name;
91}

◆ type()

common::model::diagram_t clanguml::sequence_diagram::model::diagram::type ( ) const
overridevirtual

Get the diagram model type - in this case sequence.

Returns
Type of sequence diagram.

Implements clanguml::common::model::diagram.

Definition at line 63 of file diagram.cc.

Member Data Documentation

◆ active_participants_

std::set<eid_t> clanguml::sequence_diagram::model::diagram::active_participants_
private

Definition at line 380 of file diagram.h.

◆ activities_

std::map<eid_t, activity> clanguml::sequence_diagram::model::diagram::activities_
private

Definition at line 376 of file diagram.h.

◆ participants_

std::map<eid_t, std::unique_ptr<participant> > clanguml::sequence_diagram::model::diagram::participants_
private

Definition at line 378 of file diagram.h.


The documentation for this class was generated from the following files: