From 8b0db7081ffa6c928e6e5041a34e1eeb45223b82 Mon Sep 17 00:00:00 2001 From: Alexey Fedoseev Date: Tue, 23 Apr 2024 11:37:39 +0300 Subject: [PATCH] initial & final vertexes in the doc interface + tests --- cyberiadamlpp.cpp | 97 ++++++++++++++++++++++++++++ cyberiadamlpp.h | 15 ++++- tests/06-two-statemachines.cpp | 5 ++ tests/08-state-hierarchy.cpp | 18 +++++- tests/09-initial.cpp | 57 ++++++++++++++++ tests/09-initial.test-output.graphml | 50 ++++++++++++++ tests/09-output.txt | 1 + tests/10-final.cpp | 52 +++++++++++++++ tests/10-final.test-output.graphml | 50 ++++++++++++++ tests/10-output.txt | 1 + 10 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 tests/09-initial.cpp create mode 100644 tests/09-initial.test-output.graphml create mode 100644 tests/09-output.txt create mode 100644 tests/10-final.cpp create mode 100644 tests/10-final.test-output.graphml create mode 100644 tests/10-output.txt diff --git a/cyberiadamlpp.cpp b/cyberiadamlpp.cpp index 7b7f92f..f9e1d77 100644 --- a/cyberiadamlpp.cpp +++ b/cyberiadamlpp.cpp @@ -668,6 +668,16 @@ size_t ElementCollection::elements_count() const return count; } +bool ElementCollection::has_initial() const +{ + for (ElementList::const_iterator i = children.begin(); i != children.end(); i++) { + if ((*i)->get_type() == elementInitial) { + return true; + } + } + return false; +} + void ElementCollection::add_element(Element* e) { CYB_ASSERT(e); @@ -1182,6 +1192,8 @@ StateMachine* Document::new_state_machine(const String& sm_name, const Rect& r) StateMachine* Document::new_state_machine(const ID& _id, const String& sm_name, const Rect& r) { + check_id_uniqueness(_id); + StateMachine* sm = new StateMachine(this, _id, sm_name, r); add_element(sm); update_metainfo_element(); @@ -1190,6 +1202,8 @@ StateMachine* Document::new_state_machine(const ID& _id, const String& sm_name, State* Document::new_state(ElementCollection* _parent, const String& state_name, const Rect& r, const Color& _color) { + check_parent_element(_parent); + State* state = new State(_parent, generate_vertex_id(_parent), state_name, r, _color); _parent->add_element(state); return state; @@ -1197,11 +1211,73 @@ State* Document::new_state(ElementCollection* _parent, const String& state_name, State* Document::new_state(ElementCollection* _parent, const ID& state_id, const String& state_name, const Rect& r, const Color& _color) { + check_parent_element(_parent); + check_id_uniqueness(state_id); + State* state = new State(_parent, state_id, state_name, r, _color); _parent->add_element(state); return state; } +InitialPseudostate* Document::new_initial(ElementCollection* _parent, const Point& p) +{ + check_parent_element(_parent); + check_single_initial(_parent); + + InitialPseudostate* initial = new InitialPseudostate(_parent, generate_vertex_id(_parent), p); + _parent->add_element(initial); + return initial; +} + +InitialPseudostate* Document::new_initial(ElementCollection* _parent, const Name& initial_name, const Point& p) +{ + check_parent_element(_parent); + check_single_initial(_parent); + + InitialPseudostate* initial = new InitialPseudostate(_parent, generate_vertex_id(_parent), initial_name, p); + _parent->add_element(initial); + return initial; +} + +InitialPseudostate* Document::new_initial(ElementCollection* _parent, const ID& _id, const Name& initial_name, const Point& p) +{ + check_parent_element(_parent); + check_single_initial(_parent); + check_id_uniqueness(_id); + + InitialPseudostate* initial = new InitialPseudostate(_parent, _id, initial_name, p); + _parent->add_element(initial); + return initial; +} + +FinalState* Document::new_final(ElementCollection* _parent, const Point& point) +{ + check_parent_element(_parent); + + FinalState* fin = new FinalState(_parent, generate_vertex_id(_parent), point); + _parent->add_element(fin); + return fin; +} + +FinalState* Document::new_final(ElementCollection* _parent, const Name& _name, const Point& point) +{ + check_parent_element(_parent); + + FinalState* fin = new FinalState(_parent, generate_vertex_id(_parent), _name, point); + _parent->add_element(fin); + return fin; +} + +FinalState* Document::new_final(ElementCollection* _parent, const ID& _id, const Name& _name, const Point& point) +{ + check_parent_element(_parent); + check_id_uniqueness(_id); + + FinalState* fin = new FinalState(_parent, _id, _name, point); + _parent->add_element(fin); + return fin; +} + void Document::check_cyberiada_error(int res, const String& msg) const { switch (res) { @@ -1217,6 +1293,27 @@ void Document::check_cyberiada_error(int res, const String& msg) const } } +void Document::check_parent_element(const ElementCollection* _parent) const +{ + if (!_parent) { + throw ParametersException("No parent element"); + } +} + +void Document::check_id_uniqueness(const ID& _id) const +{ + if (find_element_by_id(_id)) { + throw ParametersException(String("New element id ") + _id + " is not unique"); + } +} + +void Document::check_single_initial(const ElementCollection* _parent) const +{ + if (_parent->has_initial()) { + throw ParametersException("Parent already has initial element"); + } +} + void Document::import_nodes_recursively(ElementCollection* collection, CyberiadaNode* nodes) { CYB_ASSERT(collection); diff --git a/cyberiadamlpp.h b/cyberiadamlpp.h index 1f8882d..df844b0 100644 --- a/cyberiadamlpp.h +++ b/cyberiadamlpp.h @@ -285,6 +285,7 @@ namespace Cyberiada { ConstElementList find_elements_by_types(const ElementTypes& types) const; ElementList find_elements_by_type(ElementType type); ElementList find_elements_by_types(const ElementTypes& types); + bool has_initial() const; virtual void add_element(Element* e); virtual void add_first_element(Element* e); @@ -543,7 +544,13 @@ namespace Cyberiada { const Rect& r = Rect(), const Color& color = Color()); State* new_state(ElementCollection* parent, const ID& id, const String& state_name, const Rect& r = Rect(), const Color& color = Color()); - + InitialPseudostate* new_initial(ElementCollection* parent, const Point& p = Point()); + InitialPseudostate* new_initial(ElementCollection* parent, const Name& initial_name, const Point& p = Point()); + InitialPseudostate* new_initial(ElementCollection* parent, const ID& id, const Name& initial_name, const Point& p = Point()); + FinalState* new_final(ElementCollection* parent, const Point& point = Point()); + FinalState* new_final(ElementCollection* parent, const Name& name, const Point& point = Point()); + FinalState* new_final(ElementCollection* parent, const ID& id, const Name& name, const Point& point = Point()); + void load(const String& path, DocumentFormat f = formatDetect); void save(const String& path, DocumentFormat f = formatCyberiada10) const; @@ -558,7 +565,6 @@ namespace Cyberiada { virtual std::ostream& dump(std::ostream& os) const; private: - void check_cyberiada_error(int res, const String& msg = "") const; void update_metainfo_element(); ID generate_sm_id() const; ID generate_vertex_id(const Element* parent) const; @@ -568,6 +574,11 @@ namespace Cyberiada { void export_edges(CyberiadaEdge** edges, const StateMachine* sm) const; CyberiadaMetainformation* export_meta() const; + void check_cyberiada_error(int res, const String& msg = "") const; + void check_parent_element(const ElementCollection* parent) const; + void check_id_uniqueness(const ID& id) const; + void check_single_initial(const ElementCollection* parent) const; + String format; DocumentMetainformation metainfo; Comment* metainfo_element; diff --git a/tests/06-two-statemachines.cpp b/tests/06-two-statemachines.cpp index 715a77a..9c85ad3 100644 --- a/tests/06-two-statemachines.cpp +++ b/tests/06-two-statemachines.cpp @@ -29,6 +29,11 @@ int main(int argc, char** argv) { Document d; d.new_state_machine("SM1"); + try { + // check id uniqueness + d.new_state_machine("G0", "SM2"); + } catch (const Cyberiada::ParametersException&){ + } d.new_state_machine("SM2"); try { cout << d << endl; diff --git a/tests/08-state-hierarchy.cpp b/tests/08-state-hierarchy.cpp index 9b70185..4b4c19f 100644 --- a/tests/08-state-hierarchy.cpp +++ b/tests/08-state-hierarchy.cpp @@ -33,7 +33,17 @@ int main(int argc, char** argv) StateMachine* sm = d.new_state_machine("SM"); State* parent1 = d.new_state(sm, "Parent 0"); - d.new_state(parent1, "State 0-0"); + + try { + // check id uniqueness + d.new_state(sm, "n0", "test"); + } catch (const Cyberiada::ParametersException&){ + } + + CYB_ASSERT(parent1->is_simple_state()); + d.new_state(parent1, "State 0-0"); + CYB_ASSERT(parent1->is_composite_state()); + State* subparent = d.new_state(parent1, "Subparent 0-1"); d.new_state(subparent, "State 0-1-0"); d.new_state(subparent, "State 0-1-1"); @@ -41,6 +51,12 @@ int main(int argc, char** argv) State* parent2 = d.new_state(sm, "Parent 1"); d.new_state(parent2, "State 1-0"); d.new_state(parent2, "State 1-1"); + try { + // check id uniqueness on the deep level + d.new_state(parent2, "n1::n1", "test"); + } catch (const Cyberiada::ParametersException&){ + } + try { cout << d << endl; d.save(string(argv[0]) + ".graphml", formatCyberiada10); diff --git a/tests/09-initial.cpp b/tests/09-initial.cpp new file mode 100644 index 0000000..540213a --- /dev/null +++ b/tests/09-initial.cpp @@ -0,0 +1,57 @@ +/* ----------------------------------------------------------------------------- + * The Cyberiada GraphML C++ library implemention + * + * The test + * + * 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 "cyberiadamlpp.h" +#include "testutils.h" + +using namespace Cyberiada; +using namespace std; + +int main(int argc, char** argv) +{ + Document d; + + StateMachine* sm = d.new_state_machine("SM"); + + d.new_initial(sm); + try { + // check id uniqueness + d.new_initial(sm, "n0"); + } catch (const Cyberiada::ParametersException&){ + } + + State* parent = d.new_state(sm, "State"); + d.new_initial(parent, "Local init"); + try { + // check double initial + d.new_initial(parent); + } catch (const Cyberiada::ParametersException&){ + } + + try { + cout << d << endl; + d.save(string(argv[0]) + ".graphml", formatCyberiada10); + } catch (const Cyberiada::Exception&) { + return 1; + } + return 0; +} diff --git a/tests/09-initial.test-output.graphml b/tests/09-initial.test-output.graphml new file mode 100644 index 0000000..cbe73d6 --- /dev/null +++ b/tests/09-initial.test-output.graphml @@ -0,0 +1,50 @@ + + + Cyberiada-GraphML-1.0 + + + + + + + + + + + + + + + + + + + + + + SM + + formal + CGML_META + standardVersion/ 1.0 + +transitionOrder/ transitionFirst + +eventPropagation/ block + + + + + initial + + + State + + + initial + Local init + + + + + diff --git a/tests/09-output.txt b/tests/09-output.txt new file mode 100644 index 0000000..77c4744 --- /dev/null +++ b/tests/09-output.txt @@ -0,0 +1 @@ +Document: {id: '', name: '', format: 'Cyberiada-GraphML-1.0', meta: {standard version: '1.0', transition order: transition first, event propagation: block events}, elements: {State Machine: {id: 'G0', name: 'SM', elements: {Initial: {id: 'n0'}, Composite State: {id: 'n1', name: 'State', elements: {Initial: {id: 'n1::n0', name: 'Local init'}}}}}} diff --git a/tests/10-final.cpp b/tests/10-final.cpp new file mode 100644 index 0000000..7112c32 --- /dev/null +++ b/tests/10-final.cpp @@ -0,0 +1,52 @@ +/* ----------------------------------------------------------------------------- + * The Cyberiada GraphML C++ library implemention + * + * The test + * + * 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 "cyberiadamlpp.h" +#include "testutils.h" + +using namespace Cyberiada; +using namespace std; + +int main(int argc, char** argv) +{ + Document d; + + StateMachine* sm = d.new_state_machine("SM"); + + d.new_final(sm); + try { + // check id uniqueness + d.new_final(sm, "n0", "test"); + } catch (const Cyberiada::ParametersException&){ + } + + State* parent = d.new_state(sm, "State"); + d.new_final(parent, "Local exit"); + + try { + cout << d << endl; + d.save(string(argv[0]) + ".graphml", formatCyberiada10); + } catch (const Cyberiada::Exception&) { + return 1; + } + return 0; +} diff --git a/tests/10-final.test-output.graphml b/tests/10-final.test-output.graphml new file mode 100644 index 0000000..a458dab --- /dev/null +++ b/tests/10-final.test-output.graphml @@ -0,0 +1,50 @@ + + + Cyberiada-GraphML-1.0 + + + + + + + + + + + + + + + + + + + + + + SM + + formal + CGML_META + standardVersion/ 1.0 + +transitionOrder/ transitionFirst + +eventPropagation/ block + + + + + final + + + State + + + final + Local exit + + + + + diff --git a/tests/10-output.txt b/tests/10-output.txt new file mode 100644 index 0000000..2585e1f --- /dev/null +++ b/tests/10-output.txt @@ -0,0 +1 @@ +Document: {id: '', name: '', format: 'Cyberiada-GraphML-1.0', meta: {standard version: '1.0', transition order: transition first, event propagation: block events}, elements: {State Machine: {id: 'G0', name: 'SM', elements: {Final: {id: 'n0'}, Composite State: {id: 'n1', name: 'State', elements: {Final: {id: 'n1::n0', name: 'Local exit'}}}}}}