Diagram services

Modelio API diagram services provide a programmatic access to Modelio UML diagrams.

The diagram services provide functions to:

  1. Open a diagram
  2. List a diagram contents
  3. Manipulate the diagram graphics

Quick outline:

  1. Diagrams
  2. Diagram graphics
  3. Masking unmasking
  4. Changing the style properties
  5. Layouting the diagram
  6. Jython code example


Diagrams

A few concepts

Model elements in diagrams

Diagrams in Modelio only represent the UML model elements, they do not own those elements. Therefore, a given model element can appear in several diagrams. In other words, think of diagrams showing model elements of your model just like real-life pictures showing your friends or your family.

Modelio diagrams and your family pictures indeed share the same characteristics:

  • if you destroy the picture of your uncle Tom, you will not destroy your uncle Tom.
  • you can take several pictures of your uncle Tom without cloning Tom himself.
  • you can take pictures of your uncle Tom alone or with any other person you want, depending on the circumstances or the picture expected usage.
  • if you copy a picture of your uncle Tom you just get a new picture of him, not a new uncle.

However, Modelio pictures of a UML element are a little more magic than your pictures of Tom: they are updated when the model element changes! It is just as if, should your uncle Tom change his hair cut or color, all the pictures showing Tom would immediately display Tom’s new hair.

Diagram organization

Exactly in the same way you can organize your personal pictures as you want to do it (you do not have to keep all the pictures of your favorite uncle TOM in Tom’s house), you can organize your diagrams independently of the model structure. This is simply because diagrams do not belong to any of the model elements they represent. This is a useful feature of Modelio. Want to show your model to a board of analyst? Just create a set of simplified diagrams only showing high level elements like components or sub-systems. Need to discuss some code design with a bunch of hardcore developers? Build a set of highly detailed diagrams showing methods, associations, sequences stats and all the required stuff. All these different diagram sets build for different usages will simply not mess up with your UML model organization.

Diagram context

Diagrams have a context which is a model element that is exclusively used when creating new model elements directly in the diagram background. This is a very valuable feature of Modelio diagram. Once you have established that the context of the diagram you are working with is, for example, a particular package of your model, then any model element created in the diagram editor background will systematically be owned by this package. Furthermore, Modelio will ensure that you cannot create anything that cannot be owned by the diagram context element.

Can a diagram have no context at all?

Yes, with the direct consequence that nothing can be created in its background.

A no-context diagram is therefore useless, isn’t it ?

No. The diagram can be currently displaying several elements (packages classes…) in which new element creation remains possible. Only the ‘background’ of the diagram cannot be used a target for creating elements.

The operation which consists in making a model element visible in a diagram is called unmasking, its opposite, hiding an element from a diagram is called masking. Note that unmasking concretely creates a new graphic in the diagram for an already existing model element while masking actually deletes a graphic from the diagram but without deleting the element from the model.

IDiagramHandle

In order to access to a diagram contents, the developer has to obtain a DiagramHandle instance that will be the entry point for further manipulations of the diagram. Getting a IDiagramHandle on a diagram allows to manipulate the diagram content like a user could do by opening an editor for that diagram. As any resource, a IDiagramHandle should be closed when it is no longer used. This is done by calling the close() method.

The following code fragment finds all the static diagrams whose context is the the root package, and for each of them print their names, get a IDiagramHandle and open them in separate diagram editors.

 1...
 2try {
 3        IModelingSession session = Modelio.getInstance().getModelingSession();
 4        IDiagramService diagramServices = Modelio.getInstance().getDiagramService();
 5        IEditionService editionService = Modelio.getInstance().getEditionService();
 6
 7        IPackage root = session.getModel().getRoot();
 8
 9        // loop on the static diagrams whose context is the root package (if some)
10        for (IStaticDiagram diagram : session.getModel()
11                                             .getUmlProject()
12                                             .getDiagramRoot()
13                                             .getDiagram(IStaticDiagram.class)) {
14            // if diagram context is root
15            if (root.equals(diagram.getOrigin())) {
16                // Print the diagram name
17                Modelio.out.println(diagram.getName());
18
19                // Get a IDiagramHandle for content manipulation.
20                IDiagramHandle dh = diagramServices.getDiagramHandle(diagram);
21
22                // <<Your manipulation code goes here>>
23
24                // Save all diagram modifications
25                dh.save();
26
27                // Close the handle now it is no longer used
28                dh.close();
29
30                // Open a visual editor for this diagram.
31                // Note: this step is optional. Diagram content manipulation 
32                // through API can be done with or without GUI.
33                editionService.openEditor(diagram);
34            }
35        }
36    }
37} catch ( )
38...
39}

Modifying diagrams

The Diagram API allows for diagram contents modification. These modifications must be saved through a call to IDiagramHandle# save() to be effective. However, as for any modification of the model in Modelio, the developer should take care of embedding the changes in a transaction.

Diagram graphics

The elements displayed by a diagram are IDiagramGraphic instances. Diagram graphics can be of two kinds: IDiagramNode which represents ‘box-like’ elements and IDiagramLink which surprisingly represents links between nodes. For example a UML package is represented as a IDiagramNode while a UML import between packages is represented by a IDiagramLink.

Nodes and links of a diagram can be navigated by calling specific methods

classclassclass
IDiagramHandlegetDiagramNode()Returns the graphic representing the diagram itself (approximatively the background)
IDiagramNodegetNodes()Return the list of children nodes of this node.
IDiagramNodegetFromLinks()Return the links that are starting (ie outgoing links) from this node.
IDiagramNodegetToLinks()Return the links that are ending (ie incoming links) at this node.
IDiagramLinkgetFrom()Return the link's source.
IDiagramLinkgetTo()Return the link's target.

The starting point is the node representing the diagram itself obtained by the getDiagramNode() method of IDiagramHandle. Each IDiagramNode has zero or more sub-nodes that are accessible by its getNodes()method.

Examples:

  • the sub-nodes of the diagram node are the nodes which are directly laid out on the diagram background (also called the diagram top level nodes).
  • the sub-nodes of the node representing a package are the nodes representing the currently unmasked classes, sub-packages and so on of the package.
  • the sub-nodes of the node representing a class are the nodes representing its unmasked attributes, operations and so on

Links are accessible by the nodes they are currently tying. Links are always running from their ‘from’ node to their ‘to’ node(s), but this is a graphic convention which is established when they are first drawn, it is not metamodel based. For example, an association link navigability, although it is displayed as an oriented arrow in the diagram, does not designate the ‘from’ and ‘to’ nodes of the IDiagramLink representing the association. Therefore, the ‘get From /To’ family of methods must only be used to navigate at the graphical level and not to navigate in the UML Model where semantics are different.

Diagram graphics and model elements

Each DiagramGraphic in a diagram is associated to a UML element, this element is actually returned by the getElement() method applied to the diagram graphic.

The following code snippet prints out the names of the top level diagram graphics of a diagram.

 1...
 2IDiagramService diagramServices = Modelio.getInstance().getDiagramService();
 3IDiagramHandle dh = diagramServices.getDiagramHandle(aDiagram);
 4
 5// loop on the top level diagram graphics
 6for (IDiagramGraphic dg: dh.getdiagramNode().getNodes()) {
 7    Modelio.out.println(dg.getElement().getName());
 8}
 9...

Masking unmasking

Masking and unmasking model elements is the means of composing the actual contents of a diagram.

Masking elements

Masking an element hides the element in the diagram by removing all its graphic representations.

In order to mask an element in a diagram you first have to get its representing graphics and then call the mask() method on each of them. Alternatively, you can use the mask() method of IDiagramHandle which works the same way taking the graphic to mask as its first parameter.

Here is a code snippet that masks all the top elements of a diagram, thereby erasing all its contents.

 1...
 2IDiagramService diagramServices = Modelio.getInstance().getDiagramService();
 3IDiagramHandle dh = diagramServices.getDiagramHandle(aDiagram);
 4
 5// loop on the top level diagram graphics
 6for (IDiagramGraphic dg: dh.getdiagramNode().getNodes()) {
 7    dg.mask();
 8    // the alternative code using the diagram handle would have been: dh.mask(dg);
 9}
10...

Unmasking an element

Unmasking an element makes it visible in the diagram (when possible and allowed by the diagram type). To unmask an element in a diagram you simply need the element itself and a diagram handle.

1...
2IDiagramService diagramServices = Modelio.getInstance().getDiagramService();
3IDiagramHandle dh = diagramServices.getDiagramHandle(aDiagram);
4IElement element = ...;  // some code to get an element
5
6// unmask the element at position x=100, y=50
7dh.unmask(element, 100, 50);
8...

Changing the style properties

Diagram graphics have style properties that define their look and presentation options. Using the diagram API you can:

  • set the value of a given property (ex: set the background color of a displayed class)
  • get the current value of a given property (ex: get the current background color of a displayed class)

Getting a property value

The getProperty() method of IDiagramGraphic is used to get the current value of a given property of diagram graphic.

interface IDiagramgraphic {
  ...
  String getProperty(String key)
  ...
}

The key parameter indicates which property value is returned. Not every diagram graphic can return values for any possible key, so the method may return null if the requested property is not supported. The exact list of the possible keys for each diagram graphic type can be consulted in the JavaDoc.

Setting a property value

The setProperty() method of IDiagramGraphic is used to get the current value of a given property of diagram graphic.

interface IDiagramgraphic {
  ...
  void setProperty(String key, String value);
  ...
}

The key parameter indicates which property value is to be set. Not every diagram graphic can take values for any possible key, so the method simply does nothing if the requested property is not supported. The exact list of the possible keys for each diagram graphic type can be consulted in the JavaDoc.

Convenience methods

For the most frequently used properties, the diagram API proposes several convenience methods that spare the developper the burden of finding which specific property key has to be used.

For a IDiagramNodethese methods are:

  • Color getFillColor()
  • void setFillColor(Color fillcolor)
  • int getFillMode()
  • void setFillMode(int mode)
  • Font getFont()
  • void setFont(Font font)
  • Color getLineColor()
  • void setLineColor(Color linecolor)
  • int getLineWidth()
  • void setLineWidth(int linewidth)
  • Color getTextColor()
  • void setTextColor(Color color)
  • int getRepresentationMode()
  • void setRepresentationMode(int mode)

and for a IDiagramLink:

  • LinkRouterKind getRouterKind()
  • void setRouterKind(LinkRouterKind routerKind)
  • Font getFont()
  • void setFont(Font font)
  • Color getLineColor()
  • void setLineColor(Color linecolor)
  • int getLinePattern()
  • void setLinePattern(int pattern)
  • int getLineRadius()
  • void setLineRadius(int radius)
  • int getLineWidth()
  • void setLineWidth(int linewidth)
  • Color getTextColor()
  • void setTextColor(Color color)
  • boolean isDrawLineBridges()
  • void setDrawLineBridges(final boolean value)

These methods being convenience methods their name should be significant enough to guess what they do. However, full details can be found in the Modelio API Javadoc

Layouting the diagram

Layouting the diagram nodes consists in positionning the differents graphics in the diagram for the top level elements or in their containers for sub-elements. Layouting the links consists in fixing they current path in the diagram, most of the time too limit crossing between links or between links an nodes.

Unfortunately Modelio currently comes with no global layout facility that would layout a complete diagram. The diagram API only allows for moving and resizing the node and for changing the link paths.

Moving and resizing nodes

The bounds of a node defines the rectangle it is currently occupying in the diagram. The diagram API provides accessors for the bounds of a node, allowing for both positionning ans resizing it in only one operation.

interface IDiagramNode {
  ...
  Rectangle getBounds();
  void setBounds(Rectangle bounds);
  ...
}

In order to simply move a diagram node, one has therefore to:

  1. get the current bounds of the node (getBounds())
  2. modify the x and y coordinates of the returned rectagle without chnaging its widht and height
  3. set the node bounds to the modified rectangle

Simpler convenience methods are also proposed to only move or to only resize a node. The returned boolean indicates whether the operation has been allowed or not by the diagram.

interface IDiagramNode {
  ...
  boolean setLocation(int x, int y);
  boolean setSize(int width, int height);
  void fitToContent();
  ...
}

The fitToContent() simply resize the node to its preferred size.

Layouting links

The actual shape of a link depends on:

  • its source and destination nodes
  • its router kind
  • the point that are composing its path

The diagram API allows for modifying some of these characteristics.

interface IDiagramLink {
  ...
  LinkRouterKind getRouterKind();
  void setRouterKind(LinkRouterKind routerKind);

  ILinkPath getPath();
  void setPath(final ILinkPath linkPath);
  ...
}

The router kind defines the shape of the links between the points of its path. Combined with the points composing the path this gives the overall appearance and layout of the link. Different routers may use the points of the link path in different ways.
For example the LinkRouterKind.DIRECT router completely ignores these points and draws the link directly from its source to its destination while the LinkRouterKind.BENDPOINT will zigzag between the nodes, scrupulously passing through each of the path’s points.

Tip

The easiest way to experiment with link routers and path points, consists in doing it directly in a diagram editor, playing around with the router property of the links. The values of the router kind' style property in the property box is currently matching the values of the LinkRouterKind enumeration in the API.

Code example

The following code example is written in Jython so that it can be easily pasted in the Modelio script view to be tested and played around with. Still this Jython code illustrates the Java API thank to Jython’s tight integration with Java which results in a code very similar to equivalent Java code. In the code fragment, a bunch of diagram API methods are used to dump an XML representation of a diagram’s contents.

 1def dumpDiagram(diagram):
 2  dh = Modelio.getInstance().getDiagramService().getDiagramHandle(diagram)
 3  diagramNode = dh.getDiagramNode()
 4  print '<diagram name="' + diagramNode.getName() + '" class="' + diagramNode.getElement().getMetaclassName() + '">'
 5  for topLevelNode in diagramNode.getNodes():
 6    dumpNode(topLevelNode, "  ")
 7  print '</diagram>'
 8  dh.close()
 9
10def dumpNode(node, indent):
11  if node != None:
12    bounds = node.getBounds()
13    print indent, '<node name="' +  node.getName() + '" class="' + node.getElement().getMetaclassName() + '">'
14    print indent, '  <bounds x="' + str(bounds.x) + '" y="' + str(bounds.y) + '" w="' + str(bounds.width) + '" h="' + str(bounds.height) + '"/>' 
15    for n in node.getNodes():
16      dumpNode(n, indent+"  ")
17    for l in node.getFromLinks():
18      dumpToLink(l, indent+"  ")
19    for l in node.getToLinks():
20      dumpFromLink(l, indent+"  ")
21    print indent, '</node>'  
22
23def dumpToLink(link, indent):
24  print indent, '<outgoing-link class="' + link.getElement().getMetaclassName() + '">'
25  print indent, '   <to name="' + link.getTo().getName() + '" class="' + link.getTo().getElement().getMetaclassName() + '"/>'
26  print indent, '</outgoing-link>'
27
28def dumpFromLink(link, indent):
29  print indent, '<incoming-link class="' + link.getElement().getMetaclassName() + '">'
30  print indent, '   <from name="' + link.getFrom().getName() + '" class="' + link.getFrom().getElement().getMetaclassName() + '"/>'
31  print indent, '</incoming-link>'
32
33# macro code starts here
34selected = selectedElements.get(0)
35
36if isinstance(selected, IAbstractDiagram):
37  dumpDiagram(selected)
38else:
39  print "Error: please select a diagram in the explorer before launching the macro."