From 8303a2de9b289f0fabe81e4b8ebd438834a239a0 Mon Sep 17 00:00:00 2001 From: Alexey Fedoseev Date: Sat, 13 Apr 2024 22:54:55 +0300 Subject: [PATCH] save document to graphml implemented --- Makefile | 6 +- cyberiadamlpp.cpp | 461 +++++++++++++++++++++++++++++++++++++++++++--- cyberiadamlpp.h | 201 +++++++++++--------- main.cpp | 93 ++++++++++ test.cpp | 52 ------ 5 files changed, 643 insertions(+), 170 deletions(-) create mode 100644 main.cpp delete mode 100644 test.cpp diff --git a/Makefile b/Makefile index a4384b7..7054a1c 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ endif TEST_TARGET := cyberiadapp_test LIB_SOURCES := cyberiadamlpp.cpp -TEST_SOURCES := test.cpp +TEST_SOURCES := main.cpp LIB_OBJECTS := $(patsubst %.cpp, %.o, $(LIB_SOURCES)) TEST_OBJECTS := $(patsubst %.cpp, %.o, $(TEST_SOURCES)) @@ -41,8 +41,8 @@ $(TEST_TARGET): $(TEST_OBJECTS) $(LIB_TARGET) $(LIB_ORJECTS) clean: rm -f *~ *.o $(TARGET) $(TEST_TARGET) $(LIB_TARGET_STATIC) $(LIB_TARGET_DYNAMIC) -test: $(TEST_TARGET) +main: $(TEST_TARGET) all: $(LIB_TARGET) $(TEST_TARGET) -.PHONY: all clean test +.PHONY: all clean main diff --git a/cyberiadamlpp.cpp b/cyberiadamlpp.cpp index 0c770b6..3933575 100644 --- a/cyberiadamlpp.cpp +++ b/cyberiadamlpp.cpp @@ -91,6 +91,31 @@ Element* Element::find_root() } } +#include + +CyberiadaNode* Element::to_node() const +{ + CyberiadaNode* node = cyberiada_new_node(get_id().c_str()); + switch (type) { + case elementSM: node->type = cybNodeSM; break; + case elementSimpleState: node->type = cybNodeSimpleState; break; + case elementCompositeState: node->type = cybNodeCompositeState; break; + case elementComment: node->type = cybNodeComment; break; + case elementFormalComment: node->type = cybNodeFormalComment; break; + case elementInitial: node->type = cybNodeInitial; break; + case elementFinal: node->type = cybNodeFinal; break; + case elementChoice: node->type = cybNodeChoice; break; + default: + std::cerr << id << " " << type << std::endl; + CYB_ASSERT(false); + } + if (has_name()) { + cyberiada_copy_string(&(node->title), &(node->title_len), + get_name().c_str()); + } + return node; +} + std::ostream& Element::dump(std::ostream& os) const { String type_str; @@ -136,6 +161,18 @@ Point::Point(CyberiadaPoint* p) } } +CyberiadaPoint* Point::c_point() const +{ + if (valid) { + CyberiadaPoint* p = cyberiada_new_point(); + p->x = x; + p->y = y; + return p; + } else { + return NULL; + } +} + Rect::Rect(CyberiadaRect* r) { if (r) { @@ -149,6 +186,39 @@ Rect::Rect(CyberiadaRect* r) } } +CyberiadaRect* Rect::c_rect() const +{ + if (valid) { + CyberiadaRect* r = cyberiada_new_rect(); + r->x = x; + r->y = y; + r->width = width; + r->height = height; + return r; + } else { + return NULL; + } +} + +CyberiadaPolyline* Cyberiada::c_polyline(const Polyline& polyline) +{ + CyberiadaPolyline* result = NULL; + for (Polyline::const_iterator i = polyline.begin(); i != polyline.end(); i++) { + const Point& point = *i; + CyberiadaPolyline* pl = cyberiada_new_polyline(); + pl->point.x = point.x; + pl->point.y = point.y; + if (result) { + CyberiadaPolyline* last_pl = result; + while (last_pl->next) last_pl = last_pl->next; + last_pl->next = pl; + } else { + result = pl; + } + } + return result; +} + // ----------------------------------------------------------------------------- // Action // ----------------------------------------------------------------------------- @@ -165,17 +235,24 @@ Action::Action(const Event& _trigger, const Guard& _guard, const Behavior& _beha std::ostream& Action::dump(std::ostream& os) const { - if (!trigger.empty()) { + if (type != actionTransition) { + if (type == actionEntry) { + os << "entry"; + } else { + CYB_ASSERT(type == actionExit); + os << "exit"; + } + } else if (!trigger.empty()) { os << "trigger: '" << trigger << "'"; } if (!guard.empty()) { - if (!trigger.empty()) { + if (type != actionTransition || !trigger.empty()) { os << ", "; } os << "guard: '" << guard << "'"; } if (!behavior.empty()) { - if (!trigger.empty() || !guard.empty()) { + if (type != actionTransition || !trigger.empty() || !guard.empty()) { os << ", "; } os << "behavior: '" << behavior << "'"; @@ -224,20 +301,22 @@ std::ostream& Cyberiada::operator<<(std::ostream& os, const Polyline& pl) // Comment // ----------------------------------------------------------------------------- -CommentSubject::CommentSubject(Element* _element, +CommentSubject::CommentSubject(const ID& _id, Element* _element, const Point& source, const Point& target, const Polyline& pl): - type(commentSubjectElement), element(_element), has_frag(false), source_point(source), target_point(target), polyline(pl) + type(commentSubjectElement), id(_id), element(_element), has_frag(false), source_point(source), target_point(target), polyline(pl) { } -CommentSubject::CommentSubject(Element* _element, CommentSubjectType _type, const String& _fragment, +CommentSubject::CommentSubject(const ID& _id, Element* _element, CommentSubjectType _type, const String& _fragment, const Point& source, const Point& target, const Polyline& pl): - type(_type), element(_element), has_frag(true), fragment(_fragment), source_point(source), target_point(target), polyline(pl) + type(_type), id(_id), element(_element), has_frag(true), fragment(_fragment), source_point(source), target_point(target), polyline(pl) { } CommentSubject& CommentSubject::operator=(const CommentSubject& cs) { + type = cs.type; + id = cs.id; element = cs.element; has_frag = cs.has_frag; fragment = cs.fragment; @@ -255,19 +334,19 @@ std::ostream& Cyberiada::operator<<(std::ostream& os, const CommentSubject& cs) std::ostream& CommentSubject::dump(std::ostream& os) const { - os << "{"; + String type_str; + if (type == commentSubjectElement) { + type_str = "element"; + } else if (type == commentSubjectName) { + type_str = "name"; + } else { + CYB_ASSERT(type == commentSubjectData); + type_str = "data"; + } + os << "{ id: '" << id << "'"; + os << ", type: " << type_str; if (element) { - String type_str; - if (type == commentSubjectElement) { - type_str = "element"; - } else if (type == commentSubjectName) { - type_str = "name"; - } else { - CYB_ASSERT(type == commentSubjectData); - type_str = "data"; - } - os << "to: '" << element->get_id(); - os << "', type: " << type_str; + os << ", to: '" << element->get_id() << "'"; if (has_frag) { os << ", fragment: '" << fragment << "'"; } @@ -328,6 +407,67 @@ void Comment::update_comment_type() } } +CyberiadaNode* Comment::to_node() const +{ + CyberiadaNode* node = Element::to_node(); + CyberiadaCommentData* data = cyberiada_new_comment_data(); + if (!body.empty()) { + cyberiada_copy_string(&(data->body), &(data->body_len), body.c_str()); + } + if (!markup.empty()) { + cyberiada_copy_string(&(data->markup), &(data->markup_len), markup.c_str()); + } + node->comment_data = data; + if (has_geometry()) { + node->geometry_rect = geometry_rect.c_rect(); + if (has_color()) { + cyberiada_copy_string(&(node->color), &(node->color_len), color.c_str()); + } + } + return node; +} + +CyberiadaEdge* Comment::subjects_to_edge() const +{ + CyberiadaEdge* result = NULL; + if (has_subjects()) { + for (std::list::const_iterator i = subjects.begin(); i != subjects.end(); i++) { + CyberiadaEdge *edge = cyberiada_new_edge(i->get_id().c_str(), + get_id().c_str(), + i->get_element()->get_id().c_str()); + edge->type = cybEdgeComment; + CyberiadaCommentSubjectType t; + if (i->get_type() == commentSubjectElement) { + t = cybCommentSubjectNode; + } else if (i->get_type() == commentSubjectName) { + t = cybCommentSubjectNameFragment; + } else { + CYB_ASSERT(i->get_type() == commentSubjectData); + t = cybCommentSubjectDataFragment; + } + CyberiadaCommentSubject* cs = cyberiada_new_comment_subject(t); + + if (i->has_fragment()) { + cyberiada_copy_string(&(cs->fragment), &(cs->fragment_len), i->get_fragment().c_str()); + } + edge->comment_subject = cs; + + if (i->has_geometry()) { + if (i->get_geometry_source_point().valid) { + edge->geometry_source_point = i->get_geometry_source_point().c_point(); + } + if (i->get_geometry_target_point().valid) { + edge->geometry_target_point = i->get_geometry_target_point().c_point(); + } + if (i->has_polyline()) { + edge->geometry_polyline = c_polyline(i->get_geometry_polyline()); + } + } + } + } + return result; +} + std::ostream& Comment::dump(std::ostream& os) const { os << "Comment { " << (human_readable ? "informal" : "formal") << ", "; @@ -372,6 +512,15 @@ std::ostream& Vertex::dump(std::ostream& os) const return os; } +CyberiadaNode* Vertex::to_node() const +{ + CyberiadaNode* node = Element::to_node(); + if (has_geometry()) { + node->geometry_point = geometry_point.c_point(); + } + return node; +} + // ----------------------------------------------------------------------------- // Collection of Elements // ----------------------------------------------------------------------------- @@ -576,6 +725,34 @@ std::list ElementCollection::get_vertexes() return result; } +CyberiadaNode* ElementCollection::to_node() const +{ + CyberiadaNode* node = Element::to_node(); + if (has_geometry()) { + node->geometry_rect = geometry_rect.c_rect(); + } + if (has_color()) { + cyberiada_copy_string(&(node->color), &(node->color_len), color.c_str()); + } + for (ElementList::const_iterator i = children.begin(); i != children.end(); i++) { + const Element* e = *i; + CYB_ASSERT(e); + if (e->get_type() == elementTransition) { + continue; + } + CyberiadaNode* child = e->to_node(); + CYB_ASSERT(child); + if (node->children) { + CyberiadaNode* n = node->children; + while (n->next) n = n->next; + n->next = child; + } else { + node->children = child; + } + } + return node; +} + std::ostream& ElementCollection::dump(std::ostream& os) const { if (has_geometry()) { @@ -650,6 +827,18 @@ ChoicePseudostate::ChoicePseudostate(Element* _parent, const ID& _id, const Name { } +CyberiadaNode* ChoicePseudostate::to_node() const +{ + CyberiadaNode* node = Element::to_node(); + if (has_geometry()) { + node->geometry_rect = geometry_rect.c_rect(); + if (has_color()) { + cyberiada_copy_string(&(node->color), &(node->color_len), color.c_str()); + } + } + return node; +} + std::ostream& ChoicePseudostate::dump(std::ostream& os) const { os << "Choice {"; @@ -752,6 +941,37 @@ void State::update_state_type() } } +CyberiadaNode* State::to_node() const +{ + CyberiadaNode* node = ElementCollection::to_node(); + if (has_actions()) { + for (std::list::const_iterator i = actions.begin(); i != actions.end(); i++) { + const Action& a = *i; + CyberiadaActionType at; + if (a.get_type() == actionEntry) { + at = cybActionEntry; + } else if (a.get_type() == actionExit) { + at = cybActionExit; + } else { + CYB_ASSERT(a.get_type() == actionTransition); + at = cybActionTransition; + } + CyberiadaAction* action = cyberiada_new_action(at, + a.get_trigger().c_str(), + a.get_guard().c_str(), + a.get_behavior().c_str()); + if (node->actions) { + CyberiadaAction* last_a = node->actions; + while (last_a->next) last_a = last_a->next; + last_a->next = action; + } else { + node->actions = action; + } + } + } + return node; +} + std::ostream& State::dump(std::ostream& os) const { if (is_simple_state()) { @@ -763,7 +983,7 @@ std::ostream& State::dump(std::ostream& os) const if (has_actions()) { os << ", actions: {"; for (std::list::const_iterator i = actions.begin(); i != actions.end(); i++) { - os << *i; + os << "a {" << *i << "}"; if (std::next(i) != actions.end()) { os << ", "; } @@ -787,6 +1007,40 @@ Transition::Transition(Element* _parent, const ID& _id, Element* _source, Elemen { } +CyberiadaEdge* Transition::to_edge() const +{ + CYB_ASSERT(source); + CYB_ASSERT(target); + CyberiadaEdge* edge = cyberiada_new_edge(get_id().c_str(), + source->get_id().c_str(), + target->get_id().c_str()); + edge->type = cybEdgeTransition; + if (has_action()) { + edge->action = cyberiada_new_action(cybActionTransition, + action.get_trigger().c_str(), + action.get_guard().c_str(), + action.get_behavior().c_str()); + } + if (has_geometry()) { + if (source_point.valid) { + edge->geometry_source_point = source_point.c_point(); + } + if (target_point.valid) { + edge->geometry_target_point = target_point.c_point(); + } + if (label_point.valid) { + edge->geometry_label_point = label_point.c_point(); + } + if (has_polyline()) { + edge->geometry_polyline = c_polyline(polyline); + } + if (has_color()) { + cyberiada_copy_string(&(edge->color), &(edge->color_len), color.c_str()); + } + } + return edge; +} + std::ostream& Transition::dump(std::ostream& os) const { os << "Transition {"; @@ -912,7 +1166,7 @@ StateMachine* Document::new_state_machine(const ID& _id, const String& sm_name, return sm; } -void Document::check_cyberiada_error(int res, const String& msg) +void Document::check_cyberiada_error(int res, const String& msg) const { switch (res) { case CYBERIADA_XML_ERROR: throw XMLException(msg); @@ -1104,7 +1358,7 @@ void Document::import_edges(ElementCollection* collection, CyberiadaEdge* edges) comment = static_cast(source_element); if (e->comment_subject->type == cybCommentSubjectNode) { - comment->add_subject(CommentSubject(target_element, source_point, target_point, polyline)); + comment->add_subject(CommentSubject(e->id, target_element, source_point, target_point, polyline)); } else { CommentSubjectType cst; if (e->comment_subject->type == cybCommentSubjectNameFragment) { @@ -1115,7 +1369,7 @@ void Document::import_edges(ElementCollection* collection, CyberiadaEdge* edges) throw CybMLException("Unsupported comment subject type " + std::to_string(e->comment_subject->type)); } CYB_ASSERT(e->comment_subject->fragment); - comment->add_subject(CommentSubject(target_element, cst, e->comment_subject->fragment, + comment->add_subject(CommentSubject(e->id, target_element, cst, e->comment_subject->fragment, source_point, target_point, polyline)); } break; @@ -1128,14 +1382,14 @@ void Document::import_edges(ElementCollection* collection, CyberiadaEdge* edges) } } -void Document::load(const String& path) +void Document::load(const String& path, DocumentFormat f) { reset(); CyberiadaDocument doc; int res = cyberiada_init_sm_document(&doc); CYB_ASSERT(res == CYBERIADA_NO_ERROR); - res = cyberiada_read_sm_document(&doc, path.c_str(), cybxmlUnknown); + res = cyberiada_read_sm_document(&doc, path.c_str(), CyberiadaXMLFormat(f)); if (res != CYBERIADA_NO_ERROR) { cyberiada_cleanup_sm_document(&doc); CYB_CHECK_RESULT(res); @@ -1179,8 +1433,11 @@ void Document::load(const String& path) if (doc.meta_info->date) { metainfo.date = doc.meta_info->date; } - metainfo.transition_order_flag = bool(doc.meta_info->transition_order_flag); - metainfo.event_propagation_flag = bool(doc.meta_info->event_propagation_flag); + if (doc.meta_info->markup_language) { + metainfo.markup_language = doc.meta_info->markup_language; + } + metainfo.transition_order_flag = doc.meta_info->transition_order_flag == 2; + metainfo.event_propagation_flag = doc.meta_info->event_propagation_flag == 2; for (CyberiadaSM* sm = doc.state_machines; sm; sm = sm->next) { CyberiadaNode* root = sm->nodes; @@ -1209,13 +1466,154 @@ void Document::load(const String& path) cyberiada_cleanup_sm_document(&doc); throw AssertException("Internal load error: " + e.str()); } - + cyberiada_cleanup_sm_document(&doc); } -void Document::save(const String& path) const +void Document::export_edges(CyberiadaEdge** edges, const StateMachine* sm) const { - // TODO + CyberiadaEdge* edge; + std::list transitions = sm->get_transitions(); + for (std::list::const_iterator i = transitions.begin(); i != transitions.end(); i++) { + const Transition* t = *i; + edge = t->to_edge(); + if (*edges) { + CyberiadaEdge* e = *edges; + while (e->next) e = e->next; + e->next = edge; + } else { + *edges = edge; + } + } + std::list comments = sm->get_comments(); + for (std::list::const_iterator j = comments.begin(); j != comments.end(); j++) { + const Comment* c = *j; + edge = c->subjects_to_edge(); + if (*edges) { + CyberiadaEdge* e = *edges; + while (e->next) e = e->next; + e->next = edge; + } else { + *edges = edge; + } + } +} + +void Document::save(const String& path, DocumentFormat f) const +{ + CyberiadaDocument doc; + int res; + + if (f == formatDetect) { + throw ParametersException("Bad save format " + std::to_string(f)); + } else if (f == formatLegacyYED) { + if (children_count() != 1) { + throw ParametersException("Legacy Berloga-YED format supports single-SM documents only"); + } + } + + std::list state_machines = get_state_machines(); + if (state_machines.empty()) { + throw ParametersException("At least one state machine required"); + } + + res = cyberiada_init_sm_document(&doc); + CYB_ASSERT(res == CYBERIADA_NO_ERROR); + + try { + if (f == formatCyberiada10) { + cyberiada_copy_string(&(doc.format), &(doc.format_len), + DEFAULT_GRAPHML_FORMAT.c_str()); + } + + doc.meta_info = cyberiada_new_meta(); + CYB_ASSERT(metainfo.standard_version == String(doc.meta_info->standard_version)); + + if (!metainfo.platform_name.empty()) { + cyberiada_copy_string(&(doc.meta_info->platform_name), + &(doc.meta_info->platform_name_len), + metainfo.platform_name.c_str()); + } + if (!metainfo.platform_version.empty()) { + cyberiada_copy_string(&(doc.meta_info->platform_version), + &(doc.meta_info->platform_version_len), + metainfo.platform_version.c_str()); + } + if (!metainfo.platform_language.empty()) { + cyberiada_copy_string(&(doc.meta_info->platform_language), + &(doc.meta_info->platform_language_len), + metainfo.platform_language.c_str()); + } + if (!metainfo.target_system.empty()) { + cyberiada_copy_string(&(doc.meta_info->target_system), + &(doc.meta_info->target_system_len), + metainfo.target_system.c_str()); + } + if (!metainfo.name.empty()) { + cyberiada_copy_string(&(doc.meta_info->name), + &(doc.meta_info->name_len), + metainfo.name.c_str()); + } + if (!metainfo.author.empty()) { + cyberiada_copy_string(&(doc.meta_info->author), + &(doc.meta_info->author_len), + metainfo.author.c_str()); + } + if (!metainfo.contact.empty()) { + cyberiada_copy_string(&(doc.meta_info->contact), + &(doc.meta_info->contact_len), + metainfo.contact.c_str()); + } + if (!metainfo.description.empty()) { + cyberiada_copy_string(&(doc.meta_info->description), + &(doc.meta_info->description_len), + metainfo.description.c_str()); + } + if (!metainfo.version.empty()) { + cyberiada_copy_string(&(doc.meta_info->version), + &(doc.meta_info->version_len), + metainfo.version.c_str()); + } + if (!metainfo.date.empty()) { + cyberiada_copy_string(&(doc.meta_info->date), + &(doc.meta_info->date_len), + metainfo.date.c_str()); + } + if (!metainfo.markup_language.empty()) { + cyberiada_copy_string(&(doc.meta_info->markup_language), + &(doc.meta_info->markup_language_len), + metainfo.markup_language.c_str()); + } + + doc.meta_info->transition_order_flag = metainfo.transition_order_flag ? 2: 1; + doc.meta_info->event_propagation_flag = metainfo.event_propagation_flag ? 2: 1; + + for (std::list::const_iterator i = state_machines.begin(); i != state_machines.end(); i++) { + const StateMachine* orig_sm = *i; + CYB_ASSERT(orig_sm); + CyberiadaSM* new_sm = cyberiada_new_sm(); + new_sm->nodes = orig_sm->to_node(); + export_edges(&(new_sm->edges), orig_sm); + if (doc.state_machines) { + CyberiadaSM* sm = doc.state_machines; + while(sm->next) sm = sm->next; + sm->next = new_sm; + } else { + doc.state_machines = new_sm; + } + } + } catch (const Exception& e) { + cyberiada_cleanup_sm_document(&doc); + throw AssertException("Internal save error: " + e.str()); + } + + res = cyberiada_write_sm_document(&doc, path.c_str(), CyberiadaXMLFormat(f)); + if (res != CYBERIADA_NO_ERROR) { + cyberiada_cleanup_sm_document(&doc); + CYB_CHECK_RESULT(res); + } + + cyberiada_cleanup_sm_document(&doc); } std::list Document::get_state_machines() const @@ -1277,6 +1675,9 @@ std::ostream& Document::dump(std::ostream& os) const if (!metainfo.date.empty()) { params.push_back("date: '" + metainfo.date + "'"); } + if (!metainfo.markup_language.empty()) { + params.push_back("markup language: '" + metainfo.markup_language + "'"); + } params.push_back(String("transition order: ") + (metainfo.transition_order_flag ? "transition first": "exit first")); params.push_back(String("event propagation: ") + (metainfo.event_propagation_flag ? "block events": "propagate events")); for (std::list::const_iterator i = params.begin(); i != params.end(); i++) { diff --git a/cyberiadamlpp.h b/cyberiadamlpp.h index 7988655..cd17179 100644 --- a/cyberiadamlpp.h +++ b/cyberiadamlpp.h @@ -69,36 +69,37 @@ namespace Cyberiada { Element(Element* parent, ElementType type, const ID& id, const Name& name); virtual ~Element() {} - ElementType get_type() const { return type; } + ElementType get_type() const { return type; } - const ID& get_id() const { return id; } + const ID& get_id() const { return id; } - bool has_name() const { return name_is_set; } - const Name& get_name() const { return name; } - void set_name(const Name& name); - bool has_qualified_name() const; - QualifiedName qualified_name() const; + bool has_name() const { return name_is_set; } + const Name& get_name() const { return name; } + void set_name(const Name& name); + bool has_qualified_name() const; + QualifiedName qualified_name() const; - bool is_root() const { return !parent; } - Element* get_parent() { return parent; } - virtual bool has_children() const { return false; } - virtual size_t elements_count() const { return 1; } + bool is_root() const { return !parent; } + Element* get_parent() { return parent; } + virtual bool has_children() const { return false; } + virtual size_t elements_count() const { return 1; } - virtual bool has_geometry() const = 0; + virtual bool has_geometry() const = 0; - friend std::ostream& operator<<(std::ostream& os, const Element& e); + friend std::ostream& operator<<(std::ostream& os, const Element& e); + virtual CyberiadaNode* to_node() const; protected: - Element* find_root(); - void set_type(ElementType t) { type = t; }; - virtual std::ostream& dump(std::ostream& os) const; + Element* find_root(); + void set_type(ElementType t) { type = t; }; + virtual std::ostream& dump(std::ostream& os) const; private: - ElementType type; - ID id; - Name name; - bool name_is_set; - Element* parent; + ElementType type; + ID id; + Name name; + bool name_is_set; + Element* parent; }; std::ostream& operator<<(std::ostream& os, const Element& e); @@ -111,6 +112,8 @@ namespace Cyberiada { Point(float _x, float _y): valid(true), x(_x), y(_y) {} Point(CyberiadaPoint* p); + + CyberiadaPoint* c_point() const; bool valid; float x, y; @@ -121,6 +124,8 @@ namespace Cyberiada { Rect(float _x, float _y, float _width, float _height): valid(true), x(_x), y(_y), width(_width), height(_height) {} Rect(CyberiadaRect* r); + + CyberiadaRect* c_rect() const; bool valid; float x, y; @@ -132,6 +137,8 @@ namespace Cyberiada { std::ostream& operator<<(std::ostream& os, const Point& p); std::ostream& operator<<(std::ostream& os, const Rect& r); std::ostream& operator<<(std::ostream& os, const Polyline& pl); + + CyberiadaPolyline* c_polyline(const Polyline& polyline); // ----------------------------------------------------------------------------- // Comment @@ -144,37 +151,40 @@ namespace Cyberiada { class CommentSubject { public: - CommentSubject(Element* element, + CommentSubject(const ID& id, Element* element, const Point& source = Point(), const Point& target = Point(), const Polyline& pl = Polyline()); - CommentSubject(Element* element, CommentSubjectType type, const String& fragment, + CommentSubject(const ID& id, Element* element, CommentSubjectType type, const String& fragment, const Point& source = Point(), const Point& target = Point(), const Polyline& pl = Polyline()); - CommentSubject& operator=(const CommentSubject& cs); - - CommentSubjectType get_type() const { return type; } - const Element* get_element() const { return element; } - Element* get_element() { return element; } + CommentSubject& operator=(const CommentSubject& cs); - bool has_fragment() const { return has_frag; } - const String& get_fragment() const { return fragment; } + const ID& get_id() const { return id; } + CommentSubjectType get_type() const { return type; } + const Element* get_element() const { return element; } + Element* get_element() { return element; } + + bool has_fragment() const { return has_frag; } + const String& get_fragment() const { return fragment; } - bool has_geometry() const { return source_point.valid || target_point.valid || !polyline.empty(); } - const Point& get_geometry_source_point() const { return source_point; } - const Point& get_geometry_target_point() const { return target_point; } - const Polyline& get_geometry_polyline() const { return polyline; } + bool has_geometry() const { return source_point.valid || target_point.valid || has_polyline(); } + bool has_polyline() const { return !polyline.empty(); } + const Point& get_geometry_source_point() const { return source_point; } + const Point& get_geometry_target_point() const { return target_point; } + const Polyline& get_geometry_polyline() const { return polyline; } protected: - std::ostream& dump(std::ostream& os) const; - friend std::ostream& operator<<(std::ostream& os, const CommentSubject& cs); + std::ostream& dump(std::ostream& os) const; + friend std::ostream& operator<<(std::ostream& os, const CommentSubject& cs); private: - CommentSubjectType type; - Element* element; - bool has_frag; - String fragment; - Point source_point; - Point target_point; - Polyline polyline; + CommentSubjectType type; + ID id; + Element* element; + bool has_frag; + String fragment; + Point source_point; + Point target_point; + Polyline polyline; }; std::ostream& operator<<(std::ostream& os, const CommentSubject& cs); @@ -205,6 +215,9 @@ namespace Cyberiada { bool has_markup() const { return !markup.empty(); } const String& get_markup() const { return markup; } + virtual CyberiadaNode* to_node() const; + virtual CyberiadaEdge* subjects_to_edge() const; + protected: virtual std::ostream& dump(std::ostream& os) const; @@ -227,16 +240,18 @@ namespace Cyberiada { Vertex(Element* parent, ElementType type, const ID& id, const Point& pos = Point()); Vertex(Element* parent, ElementType type, const ID& id, const Name& name, const Point& pos = Point()); - virtual bool has_geometry() const { return geometry_point.valid; } - const Point& get_geometry_point() const { return geometry_point; } + virtual bool has_geometry() const { return geometry_point.valid; } + const Point& get_geometry_point() const { return geometry_point; } - virtual bool has_children() const { return false; } + virtual bool has_children() const { return false; } + virtual CyberiadaNode* to_node() const; + protected: - virtual std::ostream& dump(std::ostream& os) const; + virtual std::ostream& dump(std::ostream& os) const; private: - Point geometry_point; + Point geometry_point; }; // ----------------------------------------------------------------------------- @@ -279,6 +294,8 @@ namespace Cyberiada { bool has_color() const { return !color.empty(); } const Color& get_color() const { return color; } + virtual CyberiadaNode* to_node() const; + protected: virtual std::ostream& dump(std::ostream& os) const; @@ -320,17 +337,19 @@ namespace Cyberiada { ChoicePseudostate(Element* parent, const ID& id, const Name& name, const Rect& r = Rect(), const Color& color = Color()); - virtual bool has_geometry() const { return geometry_rect.valid; } - const Rect& get_geometry_rect() const { return geometry_rect; } + virtual bool has_geometry() const { return geometry_rect.valid; } + const Rect& get_geometry_rect() const { return geometry_rect; } - bool has_color() const { return !color.empty(); } - const Color& get_color() const { return color; } + bool has_color() const { return !color.empty(); } + const Color& get_color() const { return color; } + virtual CyberiadaNode* to_node() const; + protected: - virtual std::ostream& dump(std::ostream& os) const; + virtual std::ostream& dump(std::ostream& os) const; - Rect geometry_rect; - Color color; + Rect geometry_rect; + Color color; }; // ----------------------------------------------------------------------------- @@ -368,11 +387,11 @@ namespace Cyberiada { ActionType get_type() const { return type; } bool has_trigger() const { return !trigger.empty(); } - const Event& get_trigger() { return trigger; } + const Event& get_trigger() const { return trigger; } bool has_guard() const { return !guard.empty(); } - const Guard& get_guard() { return guard; } + const Guard& get_guard() const { return guard; } bool has_behavior() const { return !behavior.empty(); } - const Behavior& get_behavior() { return behavior; } + const Behavior& get_behavior() const { return behavior; } protected: std::ostream& dump(std::ostream& os) const; @@ -409,6 +428,8 @@ namespace Cyberiada { std::list& get_actions() { return actions; } void add_action(const Action& a); + virtual CyberiadaNode* to_node() const; + protected: virtual std::ostream& dump(std::ostream& os) const; void update_state_type(); @@ -425,39 +446,41 @@ namespace Cyberiada { const Polyline& pl = Polyline(), const Point& sp = Point(), const Point& tp = Point(), const Point& label = Point(), const Color& color = Color()); - const Element* source_element() const { return source; } - const Element* target_element() const { return target; } + const Element* source_element() const { return source; } + const Element* target_element() const { return target; } - bool has_action() const { return (action.has_trigger() || + bool has_action() const { return (action.has_trigger() || action.has_guard() || action.has_behavior()); } - const Action& get_action() const { return action; } - Action& get_action() { return action; } + const Action& get_action() const { return action; } + Action& get_action() { return action; } - bool has_geometry() const { return (source_point.valid || + bool has_geometry() const { return (source_point.valid || target_point.valid || label_point.valid || has_polyline()); } - bool has_polyline() const { return !polyline.empty(); } - const Polyline& get_geometry_polyline() const { return polyline; } - const Point& get_source_point() const { return source_point; } - const Point& get_target_point() const { return target_point; } - const Point& get_label_point() const { return label_point; } + bool has_polyline() const { return !polyline.empty(); } + const Polyline& get_geometry_polyline() const { return polyline; } + const Point& get_source_point() const { return source_point; } + const Point& get_target_point() const { return target_point; } + const Point& get_label_point() const { return label_point; } - bool has_color() const { return !color.empty(); } + bool has_color() const { return !color.empty(); } + + virtual CyberiadaEdge* to_edge() const; protected: - virtual std::ostream& dump(std::ostream& os) const; + virtual std::ostream& dump(std::ostream& os) const; private: - Element* source; - Element* target; - Action action; - Point source_point; - Point target_point; - Point label_point; - Polyline polyline; - Color color; + Element* source; + Element* target; + Action action; + Point source_point; + Point target_point; + Point label_point; + Polyline polyline; + Color color; }; // ----------------------------------------------------------------------------- @@ -479,7 +502,13 @@ namespace Cyberiada { // ----------------------------------------------------------------------------- // Cyberiada-GraphML document // ----------------------------------------------------------------------------- - + + enum DocumentFormat { + formatCyberiada10 = 0, // Cyberiada 1.0 format + formatLegacyYED = 1, // Legacy YED-based Berloga/Ostranna format + formatDetect = 99 // Format is not specified and will be detected while loading + }; + struct DocumentMetainformation { String standard_version; // PRIMS standard version String platform_name; // target platform name @@ -492,8 +521,9 @@ namespace Cyberiada { String description; // document description String version; // document version String date; // document date - bool transition_order_flag; - bool event_propagation_flag; + String markup_language; // default comments' markup language + bool transition_order_flag; // false = transition first; true = exit first + bool event_propagation_flag;// false = block events; true = propagate events }; class Document: public ElementCollection { @@ -503,8 +533,8 @@ namespace Cyberiada { void reset(); StateMachine* new_state_machine(const String& sm_nam, const Rect& r = Rect()); StateMachine* new_state_machine(const ID& id, const String& sm_name, const Rect& r = Rect()); - void load(const String& path); - void save(const String& path) const; + void load(const String& path, DocumentFormat f = formatDetect); + void save(const String& path, DocumentFormat f = formatCyberiada10) const; const DocumentMetainformation& meta() const { return metainfo; } DocumentMetainformation& meta() { return metainfo; } @@ -516,12 +546,13 @@ namespace Cyberiada { virtual std::ostream& dump(std::ostream& os) const; private: - void check_cyberiada_error(int res, const String& msg = ""); + void check_cyberiada_error(int res, const String& msg = "") const; ID generate_sm_id() const; ID generate_vertex_id(const Element* element) const; ID generate_transition_id(const String& source_id, const String& target_id) const; void import_nodes_recursively(ElementCollection* collection, CyberiadaNode* nodes); void import_edges(ElementCollection* collection, CyberiadaEdge* edges); + void export_edges(CyberiadaEdge** edges, const StateMachine* sm) const; String format; DocumentMetainformation metainfo; diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..58fb7ba --- /dev/null +++ b/main.cpp @@ -0,0 +1,93 @@ +/* ----------------------------------------------------------------------------- + * The Cyberiada GraphML C++ library implemention + * + * The testing program + * + * Copyright (C) 2024 Alexey Fedoseev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/ + * ----------------------------------------------------------------------------- */ + +#include +#include +#include "cyberiadamlpp.h" + +using namespace Cyberiada; +using namespace std; + +void usage(const char* program) +{ + cerr << program << " [[-f ] -o ] " << endl; + cerr << "\tprint\tPrint the graphml SM structure of the file " << endl; + cerr << "\tconvert\tConvert the graphml SM file from to using format:" << endl; + cerr << "\t\t\tcyberiada Cyberiada-GraphML 1.0 format" << endl; + cerr << "\t\t\tyed Legacy Berloga-YED format" << endl; + exit(1); +} + +int main(int argc, char** argv) +{ + Document d; + DocumentFormat format; + string command, from_file, to_file, format_str; + + if (argc < 3) { + usage(argv[0]); + } + + command = argv[1]; + if (command == "print" && argc == 3) { + from_file = argv[2]; + } else if (command == "convert" && (argc == 5 || argc == 7)) { + if (argc == 5 && string(argv[2]) == "-o") { + to_file = argv[3]; + from_file = argv[4]; + } else if (argc == 7) { + if (string(argv[2]) == "-f" && string(argv[4]) == "-o") { + to_file = argv[5]; + format_str = argv[3]; + } else if (string(argv[2]) == "-o" && string(argv[4]) == "-f") { + to_file = argv[3]; + format_str = argv[5]; + } else { + usage(argv[0]); + } + from_file = argv[6]; + } else { + usage(argv[0]); + } + if (format_str.empty() || format_str == "cyberiada") { + format = formatCyberiada10; + } else if (format_str == "yes") { + format = formatLegacyYED; + } else { + usage(argv[0]); + } + } else { + usage(argv[0]); + } + + try { + d.load(from_file); + if (argc == 3) { + cout << d << endl; + } else { + d.save(to_file, format); + } + } catch (const Cyberiada::Exception& e) { + cerr << "Error while processing graphml file: " << e.str() << endl; + return 2; + } + return 0; +} diff --git a/test.cpp b/test.cpp deleted file mode 100644 index 5f6053b..0000000 --- a/test.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* ----------------------------------------------------------------------------- - * The Cyberiada GraphML C++ library implemention - * - * The testing program - * - * Copyright (C) 2024 Alexey Fedoseev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see https://www.gnu.org/licenses/ - * ----------------------------------------------------------------------------- */ - -#include -#include -#include "cyberiadamlpp.h" - -using namespace Cyberiada; -using namespace std; - -void usage(const char* program) -{ - cerr << program << " " << endl; - cerr << "\tPrint the graphml SM structure of the file " << endl; - exit(1); -} - -int main(int argc, char** argv) -{ - Document d; - - if (argc != 2) { - usage(argv[0]); - } - - try { - d.load(argv[1]); - cout << d << endl; - } catch (const Cyberiada::Exception& e) { - cerr << "Error while opening graphml file: " << e.str() << endl; - return 2; - } - return 0; -}