0.6.1
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 ()
 
- 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 196 of file diagram.cc.

196{ 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 198 of file diagram.cc.

199{
201}

◆ 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 110 of file diagram.cc.

111{
112 active_participants_.emplace(id);
113}

◆ 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 143 of file diagram.cc.

144{
145 add_message(std::move(message));
146}

◆ 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 161 of file diagram.cc.

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

◆ add_message()

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

Add message to current activity.

Parameters
messageMessage model

Definition at line 124 of file diagram.cc.

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

◆ 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 91 of file diagram.cc.

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

◆ 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 309 of file diagram.cc.

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

◆ 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 148 of file diagram.cc.

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

◆ 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 661 of file diagram.cc.

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

◆ 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 628 of file diagram.cc.

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

◆ 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 66 of file diagram.cc.

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

◆ 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 77 of file diagram.cc.

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

◆ 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 122 of file diagram.cc.

122{ 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 115 of file diagram.cc.

116{
117 return activities_.at(id);
118}

◆ 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 336 of file diagram.cc.

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

◆ 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 281 of file diagram.cc.

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

◆ 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 250 of file diagram.cc.

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

◆ 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 120 of file diagram.cc.

120{ 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 515 of file diagram.cc.

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

◆ 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 434 of file diagram.cc.

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

◆ is_begin_block_message()

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

Definition at line 352 of file diagram.h.

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

◆ 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 429 of file diagram.cc.

430{
431 return activities_.empty() || participants_.empty();
432}

◆ is_end_block_message()

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

Definition at line 362 of file diagram.h.

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

◆ 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 211 of file diagram.cc.

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

◆ 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 229 of file diagram.cc.

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

◆ 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 185 of file diagram.cc.

186{
187 return participants_;
188}

◆ 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 191 of file diagram.cc.

192{
193 return participants_;
194}

◆ print()

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

Debug method for printing entire diagram to console.

Definition at line 558 of file diagram.cc.

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

◆ 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 178 of file diagram.cc.

178{ 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 180 of file diagram.cc.

181{
182 return activities_;
183}

◆ 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 203 of file diagram.cc.

205{
206 return filter().should_include(p) &&
208 dynamic_cast<const common::model::source_location &>(p));
209}

◆ 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 86 of file diagram.cc.

87{
88 return full_name;
89}

◆ 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 61 of file diagram.cc.

Member Data Documentation

◆ active_participants_

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

Definition at line 377 of file diagram.h.

◆ activities_

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

Definition at line 373 of file diagram.h.

◆ participants_

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

Definition at line 375 of file diagram.h.


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