User Tools

Site Tools


v070:diagrams

Diagram Language Description for version 0.7.0

In txtUML, class and state machine diagrams can be defined to visualize model structure and behavior. Moreover, test cases for expected communication can also be specified in the form of sequence diagrams.

Class and State Machine Diagrams

Currently txtUML supports diagram definitions for class- and flat state machine diagrams. The language is universal, but only supports flat (one level) diagrams. For simplicity in this description, whenever a box is referenced, it stands for either a class, state or composition role name. Also if a line is present, it stands for association or transition.

General structure

A diagram definition is a Java class inheriting from the appropriate type:

This class must contain an inner class definition, inheriting from Layout. The actual layout constraints can be written as annotations on this inner class.

class MyDiagram extends ClassDiagram {
  // layout constraints as annotations
  @North(val = A.class, from = B.class)
  class MyLayout extends Layout {}
}

Please note that diagrams are not considered as part of the referenced model, therefore diagram definitions must be placed in a separate package from the model.

Simple layout constraints for nodes

In this section A and B are boxes (classes, states, compositions) already defined in a txtUML model. Relative layout for these boxes can be given as presented:

  • @North(val = A.class, from = B.class) : The diagram can be cut horizontally such that A is in the upper half and B is in the lower half.
  • @East(val = A.class, from = B.class) : The diagram can be cut vertically such that A is in the right half and B is in the left half.
  • @South(val = A.class, from = B.class) : The diagram can be cut horizontally such that A is in the lower half and B is in the upper half.
  • @West(val = A.class, from = B.class) : The diagram can be cut vertically such that A is in the left half and B is in the right half.
  • @Above(val = A.class, from = B.class) : Box A is north from B and is right next to it.
  • @Right(val = A.class, from = B.class) : Box A is east from B and is right next to it.
  • @Below(val = A.class, from = B.class) : Box A is south from B and is right next to it.
  • @Left(val = A.class, from = B.class) : Box A is west from B and is right next to it.

Example: Model contains these classes: A, B and C.

class MyClassDiagram extends ClassDiagram {
  @North(val = A.class, from = B.class)
  @Below(val = A.class, from = C.class)
  @West(val = C.class, from = B.class)
  class MyLayout extends Layout {}
}

Example: The class A contains these states: Init, Disabled and Active.

class MyStateDiagram extends StateMachineDiagram<A> {
  @Left(val = Init.class, from = Disabled.class)
  @Left(val = Disabled.class, from = Active.class)
  class MyLayout extends Layout {}
}

Compound layout constraints for nodes

In this section A, B, C and D are boxes already defined in a txtUML model. Complex layout for these boxes can be given as presented:

  • @Column({A.class, B.class, C.class}) : The boxes A, B and C are arranged in a column, with the top being A and preserving the order. Any number of boxes can be given here.
  • @Row({A.class, B.class, C.class, D.class}) : The boxes A, B, C and D are arranged in a row, with the left most being A and preserving the order. Any number of boxes can be given here.
  • @Diamond(top = A.class, right = B.class, bottom = C.class, left = D.class) : The boxes A, B, C and D are arranged in a diamond shape where the four boxes are in the corners and an empty spot remains between them.
  • @TopMost(A.class) : Box A is north from every other box.
  • @RightMost(A.class) : Box A is east from every other box.
  • @BottomMost(A.class) : Box A is south from every other box.
  • @LeftMost(A.class) : Box A is west from every other box.

Example: Model contains these classes: C, E, D, O, R, S1 and S2.

class MyDiagram extends ClassDiagram {
  @Row({R.class, E.class, D.class})
  @Column({C.class, R.class, O.class, S1.class, S2.class})
  class MyLayout extends Layout {}
}

In this section A is a box already defined in a txtUML model and L is a line with an endpoint at box A. Relative layout for the line and box can be given as presented. For reflexive lines a third parameter must be added which is a value of LinkEnd enum.

  • @North(val = L.class, from = A.class) : Line L will connect to box A on A's northern side.
  • @North(val = L.class, from = A.class, end = LinkEnd.Start) : Reflexive line L's starting point will connect to box A on A's northern side.
  • @North(val = L.class, from = A.class, end = LinkEnd.End) : Reflexive line L's ending point will connect to box A on A's northern side.
  • Can be used similarly with @East, @South and @West.
  • @Priority(val = L.class, prior = 50) : Gives line L a priority value of 50. Higher priority means shorter route and less turns.

Example: Model contains these classes: A and B. Model contains these links: L (A - B) and R (A - A).

class MyDiagram extends ClassDiagram {
  @North(val = L.class, from = A.class)
  @East(val = L.class, from = B.class)
  @Above(val = A.class, from = B.class)
  @West(val = R.class, from = A.class, end = LinkEnd.Start)
  @South(val = R.class, from = A.class, end = LinkEnd.End)
  class MyLayout extends Layout {}
}

Groups

In this section A, B, C are boxes already defined in a txtUML model and L and J are lines with an endpoint at box A.

A group can be specified by defining a class inheriting either from NodeGroup or from LinkGroup. The elements of the group can be specified with the @Contains annotation. A group can contain model elements of the corresponding type or other groups containing elements of the corresponding type.

  @Contains({A.class, B.class})
  class NG1 extends NodeGroup {}
  @Contains({L.class, J.class})
  class LG1 extends LinkGroup {}

If the given group is a NodeGroup, the annotation @Alignment can be used to set the alignment of its elements, using items of enum hu.elte.txtuml.layout.lang.AlignmentType, i.e. TopToBottom, BottomToTop, RightToLeft or LeftToRight.

  @Contains({NG1.class, C.class})
  @Alignment(AlignmentType.TopToBottom)
  class NG2 extends NodeGroup {} 

This notation of groups is equivalent to using {…} symbols and enumerate the contents within. This is called an unnamed group, while the previously shown is a named group. Both can be used in the same way. The previously mentioned statements can be used with these groups as parameters as well.

  • @North(val = {A.class, B.class}, from = C.class) : Both boxes A and B are north from C. The layout between A and B is not defined.
  • @North(val = A.class, from = {B.class, C.class}) : Box A is north from box B and from box C as well. The layout between B and C is not defined.
  • @North(val = LG1.class, from = A.class) : Every element of group LG1 is north from box A. The layout between the elements of LG1 is not defined.
  • Can be used similarly with @East, @South and @West.
  • @TopMost({A. class, B.class}) : Boxes A and B are north from every other boxes in the model.
  • @TopMost(NG1.class) : Boxes which are elements of LG1 are north from every other boxes in the model.
  • Can be used similarly with @RightMost, @BottomMost and @LeftMost.

Example: Model contains these classes: A, B, C and D.

class MyDiagram extends ClassDiagram {
  @Contains({C.class, D.class})
  @Alignment(AlignmentType.TopToBottom)
  class MyGroup extends NodeGroup{}
 
  @Row({A.class, MyGroup.class, B.class})
  class MyLayout extends Layout {}
}

Phantom nodes

In this section A and B are boxes already defined in a txtUML model. Phantoms are defined by extending class Phantom and can be used as nodes in layout statements.

Example:

  class MyPhantom extends Phantom {}
  //...
  @Above(val = A.class, from = MyPhantom.class)
  @Above(val = MyPhantom.class, from = B.class)
  //...

Additional features

Displaying elements without constraints

If we do not include a box into any statements (annotations) then the box won't appear on the diagram by itself. We can explicitly make it appear - without restricting it's position with other statements - using @Show.

@Show(A.class) : Box A is present in the diagram.

Example: Model contains these classes: A and B.

class MyDiagram extends ClassDiagram {
  @Show(A.class)
  class MyLayout extends Layout {}
}

Modifying spacing

We can reduce/increase the space between boxes in the generated diagram by using @Spacing statement (annotation), with a percentage given.

@Spacing(0.8) : The empty space between diagrams will be 80% of the default. Note: the space between boxes has a lower limit, so depending on a specific diagram using the @Spacing statement with “too low” values will not affect the generated diagram.

Example: Model contains these classes: A and B.

class MyDiagram extends ClassDiagram {
  @Spacing(0.6)
  @Row({A.class, B.class})
  class MyLayout extends Layout {}
}

Contradictions and other errors

There may be certain errors that blocks the running of the diagram layout generation process. This may occur because of:

  • Contradiction in user given statements
    • @North(val = A.class, from = B.class) and @South(val = A.class, from = B.class)
  • Lack of statements, so two boxes are arranged into the same place. The algorithm might try to solve this, but it is not guaranteed that it will succeed.

Sequence Diagrams

General structure

A sequence diagram description is a Java class inheriting from class SequenceDiagram.

Lifelines can be defined as class members in a diagram description. To give an ordering between them on the generated diagram, the Position annotation can be used. The parameter is a positive priority value, and lifelines will appear in ascending order according to the given position.

Sequence diagrams inherit two public methods: initialize and run. By overriding the former, model initialization can be specified (typically Action.create and Action.link calls are placed here). Expected communication between lifelines can be defined in method run. Please note that only action code is allowed in initialize and only sequence diagram code (static methods of class Sequence) is allowed in run, otherwise an exception is thrown during the execution.

Specifying expected communication between lifelines

Expected communication can be specified with the provided sequence diagram communication API (see class Sequence), which contains the following methods:

  • Sequence.send(from, msg, to): from is expected to send a message to to which is equal to msg.
  • Sequence.fromActor(msg, to): msg should be sent to to by an actor. Reaching this call, the sequence diagram executor actually sends the message to the target.
  • Sequence.assertState(obj, s): immediately after the target of the most recent Sequence.send or Sequence.fromActor call has received the message, obj is in state s (also see Condition evaluation during execution).

Example sequence diagram

class MySeqDiagram extends SequenceDiagram {
  @Position(1)
  A a; // A is a ModelClass
 
  @Position(2)
  B b; // B is a ModelClass
 
  @Override
  public void initialize() {
    // initialization
  }
 
  @Override
  public void run() {
    Sequence.send(a, new MySignal(), b); // MySignal is a signal
  }
}

Fragments

Similarly to the UML standard, there are also some advanced constructions called fragments, which can be expressed with Java keywords.

  • Alternative and optional fragments can be written as if-then(-else) statements.
  • Loop fragments are denoted by for, while and do-while loops.
  • Parallel behavior can be expressed via parallel fragments, e.g. when the exact order of messaging is unknown or not relevant between sets of signal sendings. These sets can be defined as lambda expressions. See the demo models for concrete examples.

Execution

The execution of the diagram can be configured by setting the execution mode. For this purpose, the ExecutionMode annotation can be used on the run method .

Regardless of the chosen mode, the entire messaging defined in run should occur in the model during the execution. ExecMode.STRICT does not allow any other communication, while ExecMode.LENIENT does.

Condition evaluation during execution

During testing, the model and the sequence diagram run in parallel, and the executor constantly checks whether the communication in the model conforms to the expected behavior. A model-dependent condition in the sequence diagram code (e.g. in an OPT, ALT or LOOP fragment) is evaluated in the actual state of the model. In sequence diagram code, in general, between two communication assertions (i.e. Sequence.send and Sequence.fromActor calls), the model stays in the state where it was at the moment when the signal specified in the previous assertion was received and processed by the target of that assertion.

Limitations

Currently, the sequence diagram communication API cannot be used to express expected communication when a reference of a sender or target is not visible from sequence diagram code, that is, in the case you simply don't have the object references to pass to Sequence methods.

v070/diagrams.txt · Last modified: 2018/10/08 11:27 by djnemeth