Diagram services

Modelio v3.8

The diagram service provides a programmatic access to Modelio diagrams:

  • the diagram service interface is IDiagramService.
  • the IDiagramService instance can be obtained from the IModuleContext of the module via IModelioServices.

The diagram services provide functions to:

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

Quick outline:

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

In order to simplify the code examples of this page, the diagram service instance is supposed to be available.
As a reminder the following code fragment shows how to get the diagram service instance using the module context.

1...
2   IModuleContext ctx = MyModule.getInstance().getModuleContext();
3   IDiagramService diagramService = ctx.getModelioServices().getDiagramService();
4...

Diagrams – 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 diagrams. 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.

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

For the sake of simplicity this example uses System.out which is not recommended in real life.

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
  • 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.

The following class diagram shows the underlying model for the diagram API.

fig1

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

classmethoddescription
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 diagramService = ...;
 3IDiagramHandle dh = diagramServices.getDiagramHandle(aDiagram);
 4
 5// loop on the top level diagram graphics
 6for (IDiagramGraphic dg: dh.getDiagramNode().getNodes()) {
 7    System.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.

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.

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 = ...;
 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 = ...;
3IDiagramHandle dh = diagramServices.getDiagramHandle(aDiagram);
4Element 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 properties list.

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 properties list.

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 IDiagramNode these methods are:

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

and for a IDiagramLink:

  • LinkRouterKind getRouterKind()
  • Font getFont()
  • Color getLineColor()
  • int getLinePattern()
  • int getLineRadius()
  • int getLineWidth()
  • Color getTextColor()
  • boolean isDrawLineBridges()
  • void setRouterKind(LinkRouterKind routerKind)
  • void setFont(Font font)
  • void setLineColor(Color linecolor)
  • void setLinePattern(int pattern)
  • void setLineRadius(int radius)
  • void setLineWidth(int linewidth)
  • void setTextColor(Color color)
  • 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 positioning the different 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 to 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 its rectangle area in the diagram. The diagram API provides accessors for the bounds of a node, allowing for both positioning 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 rectangle without changing its width 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 in the symbol view. 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 thanks to Jython’s tight integration with Java resulting 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 dir(diagramNode)
 5    print '<diagram name="' + diagramNode.getName() +  '" class="' + diagramNode.getClass().getSimpleName()  + '">'
 6    for topLevelNode in diagramNode.getNodes():
 7        dumpNode(topLevelNode, "  ")
 8    print '</diagram>'
 9    dh.close()
10
11def dumpNode(node, indent):
12    if node != None:
13        bounds = node.getBounds()
14        print indent, '<node name="' +  node.getElement().getMClass().getName() + '" class="' + node.getElement().getMClass().getName() + '">'
15        print indent, '  <bounds x="' + str( bounds.x()) + '" y="' + str(bounds.y()) + '" w="' + str(bounds.width()) + '" h="' + str(bounds.height()) + '"/>' 
16        for n in node.getNodes():
17            dumpNode(n, indent+"  ")
18        for l in node.getFromLinks():
19            dumpToLink(l, indent+"  ")
20        for l in node.getToLinks():
21            dumpFromLink(l, indent+"  ")
22        print indent, '</node>'  
23
24def dumpToLink(link, indent):
25    print indent, '<outgoing-link class="' + link.getElement().getMClass().getName() + '">'
26    print indent, '   <to name="' + link.getTo().getName() + '" class="' + link.getTo().getElement().getMClass().getName() + '"/>'
27    print indent, '</outgoing-link>'
28
29def dumpFromLink(link, indent):
30    print indent, '<incoming-link class="' +  link.getElement().getMClass().getName() + '">'
31    print indent, '   <from name="' + link.getFrom().getName() + '" class="' + link.getFrom().getElement().getMClass().getName() + '"/>'
32    print indent, '</incoming-link>'
33
34# macro code starts here
35selected = selectedElements.get(0)
36
37if isinstance(selected, AbstractDiagram):
38    dumpDiagram(selected)
39else:
40    print "Error: please select a diagram in the explorer before launching the macro."
41
42
43
44# macro code starts here
45selected = selectedElements.get(0)
46
47if isinstance(selected, AbstractDiagram):
48  dumpDiagram(selected)
49else:
50  print "Error: please select a diagram in the explorer before launching the macro."

DiagramAPI.png (21 KB) admin admin, 05 October 2018 14:22