-
Notifications
You must be signed in to change notification settings - Fork 129
Diagram_services
Modelio v4.0
The diagram service provides a programmatic access to Modelio diagrams:
-
the diagram service interface is
IDiagramService
. -
the
IDiagramService
instance can be obtained from theIModuleContext
of the module viaIModelioServices
.
The diagram services provide functions to:
-
Open a diagram
-
List a diagram contents
-
Manipulate the diagram graphics
Quick outline:
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 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.
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.
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.
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.
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.
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.
Nodes and links of a diagram can be navigated by calling specific methods
class | method | description |
---|---|---|
IDiagramHandle |
getDiagramNode() |
Returns the graphic representing the diagram itself (approximatively the background) |
IDiagramNode |
getNodes() |
Return the list of children nodes of this node. |
IDiagramNode |
getFromLinks() |
Return the links that are starting (ie outgoing links) from this node. |
IDiagramNode |
getToLinks() |
Return the links that are ending (ie incoming links) at this node. |
IDiagramLink |
getFrom() |
Return the link’s source. |
IDiagramLink |
getTo() |
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.
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 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 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 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...
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)
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.
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.
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 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.
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:
-
get the current bounds of the node (
getBounds()
) -
modify the x and y coordinates of the returned rectangle without changing its width and height
-
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.
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.
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."