Skip to content

Commit

Permalink
Ability to use a previously generated PlantUML file when exporting to…
Browse files Browse the repository at this point in the history
… plantUML
  • Loading branch information
AlexandreDecan committed Mar 23, 2018
1 parent 2c52868 commit 8da1689
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 14 deletions.
7 changes: 5 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
Changelog
=========

Unreleased
----------
0.26.8 (2018-03-23)
-------------------

- (Added) ``import_from_yaml`` accepts a ``filepath`` argument.
- (Added) ``based_on`` and ``based_on_filepath`` parameters for ``export_to_plantuml`` so a previously generated
PlantUML file can be used as a basis for a new one (including its modifications related to the direction and length
of transitions).


0.26.7 (2018-03-21)
Expand Down
26 changes: 24 additions & 2 deletions docs/format.rst
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,31 @@ some of them being used by third-party tools that support visualising (or editin

Notably, module :py:mod:`sismic.io` contains a function :py:func:`~sismic.io.export_to_plantuml` that export a given statechart to
`PlantUML <http://plantuml.com/>`__, a tool based on graphviz that can automatically render statecharts (to some extent).
An online version of PlantUML can be found `here <www.plantuml.com/plantuml/>`__.
An online version of PlantUML can be found `here <http://www.plantuml.com/plantuml/>`__.

For example, the elevator statechart can be exported to the following PlantUML file, which in turns
was used to generate the previously given representation of the elevator.
can be used to generate the previously given representation of the elevator.

.. literalinclude:: /examples/elevator/elevator.plantuml



.. seealso:: PlantUML's rendering can be modified to some extent by adjusting the notation used for transitions.
By default, ``-->`` transitions correspond to downward transitions of good length.

A transition can be shortened by using ``->`` instead of ``-->``, and the direction of a transition can be
changed by using ``-up->`, ``-right->`, ``-down->` or ``-left->``. Both changes can be applied at the same time
using `-u->`, ``-r->`, ``-d->`` or ``-l->``.
See `PlantUML documentation <http://plantuml.com/state-diagram>`__ for more information.

If you have already exported a statechart to PlantUML and made some changes to the direction or length of the
transitions, it is likely that you will want to retrieve these changes when you export the (possibly modified)
statechart again to PlantUML.

The :py:func:`~sismic.io.export_to_plantuml` function accepts two optional (mutually exclusive) parameters ``based_on``
and ``based_on_filepath`` that can be used to provide an earlier version of a PlantUML text representation
(or a path to such a version if ``based_on_filepath`` is used).
This will then be used to incorporate as much as possible the changes made on the transitions.

.. autofunction:: sismic.io.export_to_plantuml
:noindex:
2 changes: 1 addition & 1 deletion sismic/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__description__ = 'Sismic Interactive Statechart Model Interpreter and Checker'
__version__ = '0.26.7'
__version__ = '0.26.8'
__url__ = 'https://github.com/AlexandreDecan/sismic/'
__author__ = 'Alexandre Decan'
__email__ = 'alexandre.decan@lexpage.net'
Expand Down
52 changes: 44 additions & 8 deletions sismic/io/plantuml.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import re

from typing import Mapping, List
from ..model import (
DeepHistoryState, FinalState, Transition, CompoundState,
Expand All @@ -12,6 +14,7 @@ class PlantUMLExporter:
def __init__(
self,
statechart: Statechart, *,
based_on: str=None,
statechart_name: bool=True,
statechart_description: bool=True,
statechart_preamble: bool=True,
Expand All @@ -20,6 +23,7 @@ def __init__(
transition_contracts: bool=True,
transition_action: bool=True) -> None:
self.statechart = statechart
self.based_on = based_on
self.statechart_name = statechart_name
self.statechart_description = statechart_description
self.statechart_preamble = statechart_preamble
Expand All @@ -33,7 +37,20 @@ def __init__(
self._indent = 0

def arrow(self, source, target):
return '-->'
# source = None --> initial state
if not self.based_on:
return '-->'
else:
if source is None:
source = '[*]'
if isinstance(self.statechart.state_for(target), FinalState):
target = '[*]'

for line in self.based_on.split('\n'):
matches = re.findall(r'(\[\*\]|[a-zA-Z0-9]+) -(.*)> (\[\*\]|[a-zA-Z0-9]+)', line)
if matches and matches[0][0] == source and matches[0][2] == target:
return '-{}>'.format(matches[0][1])
return '-->'

def indent(self):
self._indent += 2
Expand Down Expand Up @@ -218,19 +235,31 @@ def export_to_plantuml(
statechart: Statechart,
filepath: str=None,
*,
statechart_name=True,
statechart_description=False,
statechart_preamble=False,
state_contracts=False,
state_action=True,
transition_contracts=False,
transition_action=True) -> str:
based_on: str=None,
based_on_filepath: str=None,
statechart_name: bool=True,
statechart_description: bool=False,
statechart_preamble: bool=False,
state_contracts: bool=False,
state_action: bool=True,
transition_contracts: bool=False,
transition_action: bool=True) -> str:
"""
Export given statechart to plantUML (see http://plantuml/plantuml).
If a filepath is provided, also save the output to this file.
Due to the way statecharts are representing, and due to the presence of features that are specific to Sismic,
the resulting statechart representation does not include all the informations.
For example, final states and history states won't have name, actions and contracts.
If a previously exported representation for the statechart is provided, either as text (based_on parameter)
or as a filepath (based_on_filepath parameter), it will attempt to reuse the modifications made
to the transitions (their direction and length).
:param statechart: statechart to export
:param filepath: save output to given filepath, if provided
:param based_on: existing representation of the statechart in PlantUML
:param based_on_filepath: filepath to an existing representation of the statechart in PlantUML
:param statechart_name: include the name of the statechart
:param statechart_description: include the description of the statechart
:param statechart_preamble: include the preamble of the statechart
Expand All @@ -241,8 +270,15 @@ def export_to_plantuml(
:return: textual representation using plantuml
"""

if based_on and based_on_filepath:
raise TypeError('Parameters based_on and based_on_filepath cannot both be provided at the same time.')
if based_on_filepath:
with open(based_on_filepath, 'r') as f:
based_on = f.read()

exporter = PlantUMLExporter(
statechart,
based_on=based_on,
statechart_name=statechart_name,
statechart_description=statechart_description,
statechart_preamble=statechart_preamble,
Expand Down
13 changes: 12 additions & 1 deletion tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,21 @@ def test_identity_for_example_from_tests(self, example_from_tests):
def test_identity_for_example_from_docs(self, example_from_docs):
compare_statecharts(example_from_docs, import_from_yaml(export_to_yaml(example_from_docs)))

def test_export_based_on_filepath(self, elevator):
filepath = 'docs/examples/elevator/elevator.plantuml'
statechart = elevator.statechart
with open(filepath, 'r') as f:
p1 = f.read()

assert p1 != export_to_plantuml(statechart)
assert p1 == export_to_plantuml(statechart, based_on=p1)
assert p1 == export_to_plantuml(statechart, based_on_filepath=filepath)


class TestExportToPlantUML:
def test_export_example_from_tests(self, example_from_tests):
assert len(export_to_plantuml(example_from_tests)) > 0

def test_export_example_from_docs(self, example_from_docs):
assert len(export_to_plantuml(example_from_docs)) > 0
assert len(export_to_plantuml(example_from_docs)) > 0

0 comments on commit 8da1689

Please sign in to comment.