Table of Contents
1. Preface
Sometimes – or even often – you need different shapes than those from standard UML. That is something resembling a technical device rather than a rectangle or a stickman. In that case Enterprise Architect1 offers a nice feature which is called Shape Script. As the name suggest you will find a scripting language which allows you to define the shape of elements and even connectors.
This book is intended as tutorial and reference for the Shape Script language. It offers a step by step introduction, a lot of examples and quite some tricks you need to know when using Shape Scripts.
Anyhow, a common use of Shape Script is in combination with MDG Technology files. Any stereotypes defined therein come along with a number of stereotype properties (aka. tagged values). These can be used to show different shapes and/or text in the element. Quite some MDGs being delivered with Enterprise Architect use this technique.
2. Copyright and Disclaimer
Also all of the information in this book has been tested by me in many circumstances I can not hold any liability for use of the here presented information2. However, I’d be glad to receive any kind of feedback to correct future updates of this book which you will receive for free in turn. Having said this, all information presented here is subject to change without notice.
The names of actual companies and products mentioned herein may be the trademarks of their respective owners.
3. Basic Concept
Shape Scripts in Enterprise Architect are a way to assign individual shapes to stereotyped elements and connectors. The language is somewhat C-like but is limited to quite a small subset of control structures and it offers a number of build-in graphic drawing methods. The advantage is that you can learn it rather fast. The drawback is that you soon reach limits when trying to do more advanced graphics. This however should be acceptable as elements should not be complex graphic art but meaningful symbols.
We will start with shapes for elements before explaining connectors. An element shape has a size of 100 units in width and height. The top left (X|Y) coordinate is (0|0) and the bottom right coordinate is (100|100). These coordinates will be scaled to whatever you size the element on a diagram.
Before going into details of the language itself let us try a simple example. Open Settings/UML Types/Stereotypes
in any temporary Enterprise Architect repository you would like to use as sandbox.
Then create a new stereotype by entering ae
3 in the Stereotype
field and choosing <all>
4 from the Base Class
drop down. That will allow to assign the stereotype to all elements and see what the shape will look like for them.
Now the important part: check the Shape Script
radio button and click Assign
5. This will open the Shape Script editor where you can type the script in the left pane. By clicking Refresh
you will see the resulting image in the right one6.
While typing the opening bracket after the moveto
and lineto
you will notice that the parameter list for the method is shown as balloon help.
Further when hitting Ctrl-Space
at any time the editor will open a drop down with possible methods starting with the characters already typed for the current word (top of the list if at new line or at a space):
Once you have saved the script in the editor and the ≪ae
≫ stereotype as such you may apply the stereotype to see how it looks like on a real diagram. This may differ from the preview in certain cases. Also you can test scaling and later then conditional drawings.
The result looks a bit dull. But it shows the basic principle how to assign and test Shape Scripts.
3.1 Control Structures
As already mentioned Shape Script is very limited in its capabilities. So are control structures. You only have if
and else
to control the flow of statements. There are no loops at all.
The format for that is:
if (<query>) <block-or-statement>
- where
<query>
is one of the methods described below.<block-or-statement>
is either a sequence of the graphic methods described above enclosed in { and }-braces or a single statement . And of course anyif
-construct counts as statement as well.
if (<query>) <block-or-statement> else <block-or-statement>
- is hopefully obvious. Syntactically you can abbreviate
if
-cascades by usingif (<query>) <block-or-statement> else if (<query>) <block-or-statement> ...
return
- is a single statement that may appear at any position. It will stop further processing of the Shape Script immediately. You can use this statement to mimic a
case
statement instead of using anif
-cascade.
3.2 Query Methods
The use of queries is a more advanced8 feature and will be used later in chapter Advanced Usage. So here’s just a general overview of the operations.
HasTag(tagName)
- will evaluate
true
if the tagged value namedtagName
exists at all. HasTag(tagName,tagValue)
- will evaluate
true
if the tagged value namedtagName
exists and has a value equal totagValue
.
The values for property
in the two operations below are explained in chapter Properties. This and the parameter value
must be supplied as string, i.e. enclosed in either single or double quotes.
HasProperty(<property>)
- will evaluate
true
if the property namedproperty
exists at all. E.g.HasProperty("alias");
will evaluatetrue
only of a alias has been defined in the properties. HasProperty(<property>,<value>)
- is the same as the previous method except that it checks for equivalence of
value
and the result ofproperty
. So you could check if an element is named specifically (which only makes limited sense).
4. Shaping Elements
As already mentioned the Shape Script language is a bit C-like. So probably most people will not have much trouble to learn the syntax. Anyway it’s very limited. An EBNF syntax description can be found in the appendix.
Generally all keywords and even strings are case insensitive. So it does not matter whether you write LineTo
rather than lineto
in the above example. The auto-completion suggests the first variant in camel case which is definitely better to read.
4.1 The Main Shape
Or to talk Sparxian: shape main
. As you already have seen, these two keywords introduce the body wrapped in curly brackets. The instructions inside will be executed each time a stereotyped element is shown on a diagram. As already mentioned each element has 100 units in width and depth (as opposed to height since the units increase downwards). Even shapes which appear oval (like Use Case) have that rectangular 100² units frame.
Now let’s see what can be done to actually draw something. Let’s start with the two methods LineTo
and MoveTo
used in the introductory example.
Simple Lines
There is not much to say:
MoveTo(x,y)
- moves the graphic cursor to the specified coordinate. Initially in the script the cursor is located at (0|0).
LineTo(x,y)
- draws a line from the current coordinate to the new
(x|y)
and sets the current cursor to that position.
A somehow more advanced way to draw a line is a bezier curve.
BezierTo(xStart,yStart,xBend,yBend,xEnd,yEnd)
- draws a line from
(xStart|yStart)
to(xEnd|yEnd)
bending it towards(xBend|yBend)
Other ways to get some rounded shapes are the Arc
and ArcTo
methods:
Arc(left,top,right,bottom,xStart,yStart,xEnd,yEnd)
- will draw a partial ellipse (see
Ellipse
below in the next chapter) with the bounding specified byleft, top, right
andbottom
. But it is only drawn from the intersection of the line crossing the ellipse which is drawn from its center to(xStartX|yStart)
to that of the intersection center/(xEnd|yEnd)
.
ArcTo(left,top,right,bottom,xStart,yStart,xEnd,yEnd)
- will draw the same as
Arc
except it will continue a straight line from the current pen position to the start of the arc and it will set the current pen position to the end of the arc (which is the counter-clockwise end)
Closed Lines
In contrast to the simple lines explained above the closed lines have an inside which can be filled with a solid color. There are different ways to create such a graphic element.
Rectangle(left,top,right,bottom)
- will do as the name suggests where the edges are located at
left,top,right
andbottom
.
RoundRect(left,top,right,bottom,cornerWidth,cornerHeight)
- is almost the same as
Rectangle
but it will have rounded edges. The rounding is not related to the internal 100² units[^square] but given in absolute pixels! That means it will not scale with the shape as you can see below with the two differently sized shapes.
Polygon(xCenter,yCenter,numberOfSides,radius,rotation)
- will draw a polygon with
numberOfSides
sides. The center will be at(xCenter|yCenter)
and the radius (distance from center to an edge) isradius
. Further the whole object will be rotatedrotation
degrees counter-clockwise. While all other parameters are integers,rotation
is a float. Makes perfect sense to distinguish half a degree in 100² units. Ellipse(left,top,right,bottom)
- draws an ellipse inside the specified boundaries. For good reasons there is no
Circle
method as resizing the element on the diagram will in almost all cases distort it to an ellipse.
The following picture shows a number of example shapes.
The pentagon drawn in line 3 has the same radius as the (circular) ellipse drawn on line 2. The ellipses on line 4 and 5 mark the center and the right (and start-) margin of the pentagon.
A similar pentagon as on line 3 is drawn on line 7 except that it is rotated by 90 degrees counter-clockwise (and it is located at the right end of the shape).
Lines 8 and 9 show a triangle and a hexagon – hopefully self-explanatory.
There is also a way to draw non-regular shapes by specifying the edges and filling the contents.
StartPath()
- introduces a sequences of
MoveTo
andLineTo
commands describing the shape to be filled.
StartCloudPath(puffWidth,puffHeight,noise)
- is almost the same as
StartPath
but it will make the shape cloudy. If you pass an empty parameter list EA will take some default values which mostly resemble a cloud. A value tuple of 40|20 forpuffWidth|puffHeight
will give a summer time cloud. Small values like 4|2 will result more in ripped paper shapes. Suppling zero asnoise
will result in absolutely regular fluffs. A value of about 1 will make it naturally irregular. All values can be supplied as float. Any integer will suffice however. EndPath()
- tells that the path is ended and going to be filled.
FillAndStrokePath()
- Will fill the inside of the previously specified path and draw a border line.
FillPath()
- is almost the same except it does not draw the border line.
StrokePath()
- is another quite useless method as it just draws the border. Something you would achieve when simply not using all that path-stuff.
Finally there is a way to draw the native shape which will normally be used inside control structures (as we will see later).
DrawNativeShape()
- will draw exactly the shape as if no Shape Script were applied.
The example above shows the preview and how a class will appear in the diagram. Since the preview does not know anything about the element where the Shape Script will be applied it will not draw anything for DrawNativeShape
.
Painting
All of the above methods use default fill, border and font colors and border style. These can be changed using the following methods.
SetFillColor(red,green,blue)
- replaces the fill color for the methods described in the previous chapter. The parameters take the RGB values as integer in the range from 0 to 255.
SetFillColor(newColor)
- is the same as the previous except that it takes a color object as parameter. Currently9 there are just three methods to deliver a color object:
GetUser***Color()
(see below). You can not (yet?) define any color object in Shape Script.
SetFontColor(red,green,blue)
- replaces the color for any text appearing in a shape.
SetFontColor(newColor)
- is the same as the last method. Like for the above describe
newColor
you can only use one of theGetUser***Color()
methods. SetLineStyle( lineStyle)
- sets the line style according to the
lineStyle
supplied as string. The string itself is interpreted case insensitive. You need to choose from one of the valuessolid, dash, dot, dashdot
ordashdotdot
. Choosing a different string value will result in an unpredictable line style
SetPen(red,green,blue)
- replaces the color for border lines with the one supplied as RGB.
SetPen(red,green,blue,penWidth)
- is the same as the previous one except that the additional parameter
penWidth
will set the width of the lines being drawn. The value for it must be between 1 (default thin line) and 5 (thickest line). SetPen(newColor)
- see the remark about color objects above.
SetPen(newColor,penWidth)
- ditto
SetPenColor(red,green,blue)
- yet another way to say
SetPen(red,green,blue)
. SetPenColor(newColor)
- I must not repeat myself, must I?
SetPenWidth(penWidth)
- just like
SetPen(red,green,blue,penWidth)
but without changing the color.
SetDefaultColors()
- will reset any of the previously changed color attributes to the defaults.
GetUserFillColor()
- returns a color object with the user defined color for use in one of the previously mentioned color parameters.
GetUserBorderColor()
- ditto for the border
GetUserFontColor()
- and for the font color.
GetUserPenSize()
- returns the width (why be orthogonal?) of the pen as defined by the user. The only place where you can use this method is inside
SetPen
/SetPenWidth
.
As you can see the colors shown in the painter for border, font and fill are used to color the three rectangles. Additionally the lower right rectangle is drawn with a thick border.
4.2 Text
Of course you need to be able to put some text inside a shape. The following methods can be used for that purpose. Note that any text goes into the whole active shape rectangle, which is shape main
. So currently you will only be able to print text from top left to bottom right of the shape. We will see later how to place text at certain positions.
Print(text)
- Renders the string
text
at the current print position (which starts top left of the actual shape).
Println(text)
- is the same except that it renders a new-line at the end of the supplied text string.
PrintWrapped(text)
- actually the same as
Print
. According to the help: Prints the specified text string, wrapped over multiple lines if the text is wider than its containing shape. Let’s see:
And the difference is —
None. Both texts are wrapped inside the shape. Further the text protrudes the shape on the bottom. Regardless of which Print
variant is used.
PrintIfDefined(propertyname,truePart,falsePart)
- will print the string
truePart
if the property is defined and has a not-empty value assigned. Else it will printfalsePart
.
Strings
The above mentioned Print
methods and some of the methods described in the following take a string parameter. Since Shape Script is very limited you can not manipulate strings. To overcome this in a certain extent you can let Shape Script replace strings by some meaningful contents. That is e.g. the name of the element in place or the contents of tagged values. The mechanism is very simple. Shape Script interprets hash-tags and replaces them textually. E.g. the hash-tag #name#
will be replaced by the name of the element. There is quite a number of properties you can use instead of name
which will be covered in detail later in chapter Properties.
4.3 Shape Attributes
Before going on with more sophisticated methods we need to look at shape attributes. Some of these have global influence on the shape while others influence the behavior of certain methods. The general format for an attribute assignment is
<attribute> = <value>
- where
<attribute>
is one of the names below and<value>
is either a string (e.g."string"
), and integer or a tuple (e.g.(0,5)
). bottomAnchorOffset
- is a tuple defining the offset for an embedded element related to the bottom of the element. E.g. a port will be (-7,-7) while a part will be (0,0). This offset is applied for embedded elements attached to the bottom of the parent element.
topAnchorOffset, leftAnchorOffset, rightAnchorOffset
- are analogously attributes for the other edges.
dockable
- is either
standard
oroff
.
editableField
-
Defines a shape as an editable region of the element. Valid Values:
alias, name, note
andstereotype
.
fixedAspectRatio
- either
true
orfalse
. In the first case the element will scale always proportionally in width and height. h_Align
- Affects horizontal placement of printed text and sub-shapes (see next chapter) depending on the
layoutType
attribute. Valid values:left, center
orright
. v_Align
- Affects vertical placement of printed text and sub-shapes (see next chapter) depending on the
layoutType
attribute. Valid values:top, center
orbottom
. layoutType
- Determines how sub-shapes are sized and positioned. Valid values:
leftright, topdown
orborder
. noShadow
- can be
true
orfalse
. Set totrue
if you want to turn of the shadow rendering. orientation
- Applies to decoration shapes (see below) only, to determine where the decoration is positioned within the containing element glyph. Valid values:
NW, N, NE, E, SE, S, SW
orW
.
preferredHeight, preferredWidth
- is used in sub-shapes (see next chapter) at
N/S
(height) orE/W
(width) orientation. The defined size is used to calculate the offset for the center shape. You can supply both values in a single shape. In that case only the relevant size will be evaluated. scaleable
- Set to false to prevent rotation of the shape. This attribute is only applicable to the source and target shapes for line glyphs. Valid values: true or false (default = true)
4.4 Sub-Shapes
A so-called sub-shape is a rectangle placed inside10 of shape main
. Its purpose is mainly to either place text blocks inside a shape and/or to repeat a certain graphic figure inside a shape. Each sub-shape has a limited width and a certain height. There are two variants of sub-shapes: local and global. Subsequently invoked sub-shapes will be stacked depending on the layoutType
property:
topdown
- The first sub-shape is placed on top of the main shape. The next one directly below the previous one.
leftright
- Similarly but the sub-shapes join to their right edge.
border
- when this value is set you can only use a sub-shape invocation with 2 parameters where the first is the name and the second is a compass orientation (
N, S, E, W
orCENTER
). According to the help the shape shall be placed in the according part.
Local sub-shapes have an additional offset you must specify which can be used to compensate the offset and place the sub-shapes arbitrarily inside the main shape11.
A sub-shape is defined by
shape <non-reserved-name> { <block> }
- where
<non-reserved-name>
is any string exceptmain
,childelement
,label
,target
,source
,LeftTopLabel
,MiddleTopLabel
,RightTopLabel
,LeftBottomLabel
,MiddleBottomLabel
andRightBottomLabel.
<block>consists of any of the previously defined graphic methods like e.g.
LineTo,
Rectangle,
Printand so on. Global ones appears at any top level in a Shape Script like
shape main. Local ones are declared inside
shape main` at any statement position.
A global sub-shape is invoked inside shape main
by calling
AddSubShape(shapeName,width,height)
- where
shapeName
is the<non-reserved-name>
of a sub-shape defined in the script. It must be supplied as a string. Thewidth
tells how many units of the width forshape main
are being used.height
specifies the height analogously. Placement of sub-shapes will be according tolayoutType
as described above.
A local sub-shape has more parameters and is invoked inside shape main
by calling
AddSubShape(shapeName,width,height,xOffset,yOffset)
- where
shapeName
is the<non-reserved-name>
of a sub-shape defined locally in the script. It must be supplied as a string. The defined shape will be placed insideshape main
according to the rules described for the global variant. Additionally, as said, the position will changed by addingxOffset|yOffset
.
As you can see the stacking of the sub-shapes is compensated by the -100, -200 and -300 yOffset
values. Since the x-coordinate always starts at zero the xOffset
is actually always the x-coordinate where the shape starts.
Finally, as mentioned,
AddSubShape(shapeName,compass)
- which is required for
layoutType = "border"
.
4.5 Compartments
In the rectangular UML element representation you can find compartments beneath the top compartment showing the name and a few other things. Usually those compartments show attributes and methods. But you can also list anything else inside those compartments. In Shape Script you can create compartments for child elements of an element. For the main element you can have a shape main
section. In that case you need to call DrawNativeShape
or you will not see any compartments. Now for each child element the Shape Script part shape ChildElement
is being processed. Here you can assign a compartment name using SetCompartmentName
which will be a grouping criterium. By calling AddCompartmentText
you can add the name (and additional information) to the compartment.
shape ChildElement { <block> }
- defines the script to be executed for any child element.
SetCompartmentName(name)
- will group a subsequent
AppendCompartmentText
under the specifiedname
. The list of unique compartment names will appear AppendCompartmentText(text)
- will add the supplied text under the according compartment.
To make an example we assume the following element structure where main
contains two differently stereotyped elements.
The diagram representation if achieved with the following script12.
As you can see the compartments are listed according to the found stereotypes part
and mystereotype
and they appear in the middle of the rendered element. The names (being print using hash-tags) appear left aligned under each compartment.
4.6 Decoration
If you mainly use the rectangular notation for an element but want to show a fancy icon somewhere you can use
decoration <some-unused-name> { <block> }
- where
<some-unused-name>
is an arbitrary name. It is used at no place but should simply describe the shape in one word. However, the name must be unique amongst the declareddecoration
s.<block>
is any graphical description as explained previously.
The default decoration will appear top-left of the shape (NW
). If you want it to appear somewhere else you need to assign the orientation
property inside to one of the valid compass values.
The following Shape Script
displays as
You can see that the 100² units are located at fixed positions inside the main shape (the picture shows an enlarged class element). They do not scale with the element. Also if you print text in decorations the font is that being used inside elements in general. There is only one font setting in Enterprise Architect for general use, one for an element in general and one per diagram use. You can not have multiple fonts in one displayed element.
4.7 Labels
It is possible to place labels along with an element oriented at major compass positions. To actually create a label you use
shape label { <block> }
- which will contain the instructions to create the label. Usually you will use
Print
statements and/or graphic methods. SetOrigin(relativeTo,xOffset,yOffset)
- will tell where to actually place the label.
relativeTo
must be a string constant being one ofN, NE, E, SE, S, SW, W, NW
orCENTER
. TheOffset
values are measured in screen pixels and can be negative.
4.8 Miscellaneous
Here are a number of methods which will not group elsewhere:
DefSize(width,height)
- sets the default size of the shape when it’s initially placed on a diagram or when
Alt-Z
is used to set the default size. Both parameters define screen pixels. The default for a class element is about 100|80. DrawParentShape()
- is similar to
DrawNativeShape
. This is used when referring to non-UML shapes defined in MDGs where it will use the shape of the element’s parent from which it is derived. For non-derived elements it will simply behave likeDrawNativeShape
. DrawCompositeDiagram()
- will enable the rendering of a possibly existing composite diagram.
Image(imageName,left,top,right,bottom)
- will place a scaled image inside the units of the shape specified by
left, top, right
andbottom
.
5. Shaping Connectors
Basically the shape for connectors will be defined similarly to that for elements. So most of the previously explained methods can be used for connectors too (of course some – like compartments – do not make sense).
A major difference between both shapes is that elements all have that 100² unit frame. Connectors are not that easy. Shape Script distinguishes between different parts of a connector: source, target, the main connector line and the six labels. For each of them you can define a shape
routine:
shape main { <block> }
- will define what appears for the connector line.
shape source { <block> }
- will define an extra shape at the source end of the connector.
shape target { <block> }
- will define an extra shape at the target end of the connector.
shape <labeltype> { <block> }
- will draw the
<labeltype>
labels according to the statements in<block>
. Here<labeltype>
is one ofLeftTopLabel
,MiddleTopLabel
,RightTopLabel
,LeftBottomLabel
,MiddleBottomLabel
andRightBottomLabel
.
Now let’s see what happens when we put some code in these shapes.
5.1 The Connector
We will start with a simple example: using a dash-dotted line which – if my memory does not deceive me – is not used in UML. The following Shape Script is assigned to the base class dependency.
When used it will produce the following:
That was easy. But what happens when you actually draw something else? Well, let’s try by using the whole 100² units.
It looks like that the pixels below the connector are actually also used to draw something defined in shape main
. Going a bit further using this code
will render as
As you can see the rectangle is rendered on the first section of the connector if there are less than 3 sections. Else it renders on the 2nd section.
5.2 Source and Target
Now that the connector construction is clear let us have a look at the shapes at the endpoints. One of the samples from the Enterprise Architect help will do:
Here the connector is rendered as dashed line. The source will render a dot and the target a triangle. Finally it will look like this:
Now let us again endeavor a bit more at the endpoints. Let us simply produce a rectangle filling the 100² units and a small circle indicating the 0|0
coordinate:
and analogously for shape target
. Those will render as follows:
As you can see the source shape is rendered below the connector and the target above. I haven’t measured it, but it looks like both occupy 100² pixels. Further you can see that the connector itself will bend even inside the end-shapes.
Actually this means that you need to draw your shapes ±height
around the 0-coordinate when you want some symmetrical endpoint. And your endpoint is top left of the shape
meaning that you need to draw it reverse (so a possible arrow will point right to left with the tip at 0|0
).
5.3 Labels
Simply let us see what happens when we use above methods to render labels:
which renders directly as:
Actually the shapes are rendered as expected and scaled to the size of the containing text. One could expect the left and right top/bottom labels to be a bit more apart (the top being a bit more moved up), but that’s how it is. The top and bottom labels in the middle are placed directly at the same position which looks like a bug13.
5.4 Connector Drawing Methods
There a three drawing methods which are used only in conjunction with connectors.
SetFixedRegion(xStart,yStart,xEnd,yEnd)
- defines areas along the connector where symbols should appear.
As you can see there are two regions defined. One for a square in the middle which shows a triangle oriented according to the general rotationdirection
(see hash-tags below) of the connector. The second region simply shows a circle.
- `HideLabel(labelname)’
- will hide the accordingly named label which is a string holding one of the
<labeltype>
values.
ShowLabel(labelname)
- Reveals the hidden label specified by … according to the help. Well.
5.5 Attributes
Analogously to the attributes described in the Shape Attributes chapter here is a list for connectors.
endPointY, endPointX
- Only used for the reserved target and source shapes for connectors; this point determines where the main connector line connects to the end shapes.
rotatable
- Set to false to prevent rotation of the shape. This attribute is only applicable to the source and target shapes for line glyphs.
6. Properties
As mentioned earlied, Shape Script makes use of a number of properties which can either be used in Print
statements enclosed in #
(see chapter Strings) or as parameter in the HasProperty
operations (see chapter Query Methods).
EA’s documentation of the possible properties leaves some room for improvements - or speculations. So I went through the list and looked for the results found in diagrams when applied.
Basically the properties are strings - and most of them work as expected. While all (well almost) of the strings can be used in a Print
statement, their use in HasProperty
is partially restricted to either element or connector shapes.
6.1 Element properties
Trivial
These properties just relate to the ones matching the element properties dialog: alias, author, complexity, datecreated, datemodified, keywords, language, metatype
(like defined in a MDG), name, notes, scope, status, stereotype, type
(e.g. “Class”)
The two date
properties are pointless as you can only test for in-/equality in Shape Script. The format to test is exactly that what you copy from the properties. In my localization it’s “DD.MM.YYYY hh:mm:ss” and I wonder how the guys over the Atlantic ocean will see this to work commonly. I guess it won’t work.
Classifier
If an element (usually Object
) has a classifier, its name is available through classifier
. Addtionally a couple of (obvious) properties of that classifier are available via classifier.alias, classifier.metatype, classifier.name, classifier.stereotype, classifier.type
Details/Advanced
Properties that appear under the Details
tab which deliver either “true” or “false” are
isabstract, isactive, isleaf, isroot, isspec, persistence
The context menu Advanced/Multiplicity
is reflected under multiplicity
.
qualifiedname
is what appears for “normal” rendering in the name field of the element. For Object
this would include name and classifier.
If rectanglenotation
is queried, the context menu Use Rectangle Notation
for the diagram object will additionally appear in the Advanced
context menu.
This will allow you to draw an alternative shape which can be shown by manually turning off this option.
Miscellaneous
haslinkeddocument
- returns “true” or “false” depending on whether a linked document is present.
-
incomingedge
andoutgoingedge
- return “none”, “left”, “right”, “top”, “bottom”, or “multiple” depending on where one or more in-/outgoing connectors are attached to the shape.
iscomposite
- returns “true” or “false” if the element has set a composite structure diagram.
isdrawcompositelinkicon
- returns “true” or “false” if the lying 8 should be rendered. This is the case for elements with a composite structure diagram. But not if
showcomposeddiagram
is “true”. showcomposeddiagram
- returns “true” or “false”. If the element has a composite structure diagram the context for
New Child Diagram
offers aShow...in Compartment
whose state is reflected here.
-
isembedded
andisinparent
- return “true” or “false”. Both seem to be the same and return “true” in case an embedded element (e.g.
Port
orPart
) appears. Since those can only appear embedded the query seems pointless. However, I confess that I forgot about embedded rendering. There has been something… islocked
- returns “true” or “false” depending on whether the element has been locked.
istagged
- returns “true” or “false” depending on whether the element is bookmarked (see the Advanced Patterns chapter for an example).
isvisible
- makes sense for child elements to be processed (see chapter Compartments) depending on their visibility. The main shape is of course always visible.
packagename
- usually the name of the package where the element is located.
parentedge
- is either “right”, “left”, “top” or “bottom” for
Port
andInterface
depending on which side they are attached to their parent. parent.metatype
- returns the metatype of the parent element or a null string.
Not working
The following either do not work (YAEAB) or I was not able to figure out what they should deliver:
cardinality, concurrency, packagepath, partition
(though in a print
the string appears correctly the query always succeeds),
priority, propertytype
(and derivatives), stereotypehidden, subtype, visibility
6.2 Connector
Trivial
-
alias
,name
andtype
(e.g. “Association”) - corresponds to the connector’s properties.
effect
- value of
Effect
property forTransition
. guard
- value of
Guard
property forControlFlow
andTransition
. direction
- returns one of “Unspecified”, “Bi-Directional”, “Source -> Destination” or “Destination -> Source”.
weight
- value of
Weight
property forControlFlow
. rotationdirection
- returns either “up”, “down”, “left” or “right”. This is calculated from the angle the last part of the connector takes (-45° to +45° will be
right
etc.).
Source and Target related
A number of properties are available for the source
and target
(<ep>
to be replaced) properties of a connector. E.g. the source
part of name
would be written as source.name
.
-
alias, constraints, multiplicity, name
andstereotype
- refer to the role properties with the same name like the following.
aggregation
- can be “none”, “shared” or “composite”
- changeable
- “none”, “frozen”, “addOnly”. Located in the
Advanced
section of the role properties. multiplicityisordered
- “0” or “1” - not “true/false” as the menu suggests. Why be consistent?
qualifiers
- semicolon separated list of defined qaulifiers.
targetscope
- “instance” or “classifier”. And yes, this is prefixed with
target
just to confuse the reader. Of course it refers to theScope
role property. -
element.name, element.stereotype
andmetatype
- return the properties for the element connected at the specific side. The element’s metatype (trivially corresponding to its type, e.g. “Class”) is (why consistency?) not prefixed with
element
.
6.3 Diagram
It is possible to query a number of properties of the diagram where the element is currently being rendered. The following work for both element and connector Shape Scripts: diagram.handdrawn
returns “0” or “1”, diagram.mdgtype
the FQN (e.g. BPMN2.0::Conversation
), and also obvious diagram.name, diagram.stereotype, diagram.type
(e.g. Logical
or Sequence
).
Additionally diagram.connectornotation
is only available in connector Shape Scripts. It corresponds to the value of diagram properties Connectors/Connector Notation
.
7. Advanced Usage
Probably you do not want to define Shape Scripts directly in the stereotypes. More likely you are going to deploy them along with an MDG Technology file. I can not explain how to create MDGs in this book as it would lead much too far. So you either know already how to do that, or you need some outside help.
Another advanced usage is when you simply need more than Shape Script can deliver in respect to querying model contexts. That is where add-ins come into play.
7.1 Shape Script in MDG
Any Shape Script for a profile element must be defined by adding a property named _image
and clicking the ellipsis button next to the Initial Value
field. This will open the Shape Script editor.
7.2 Add-in
Since Shape Script is so limited in performing algorithms there is an escape through the use of external code hosted in an add-in. If you want to use this feature you need to know how to write add-ins at all. I can not explain how to do that so you need to get outside help for that. But if you know it then here is what you need to take advantage of this escape.
Basically you can retrieve a string value from your add-in which you can evaluate by HasProperty
or by directly printing it using a properties. The format is
addin:<addin_name>, <function_name> {, <parameter>}
- where
<addin_name>
is the name you had chosen for your add-in (the identifier) and<function_name>
is the name of the function inside your add-in. An arbitrary list of comma separated parameters can be supplied which are passed by value to the called add-in procedure where the repository, the element-GUID and the additional parameters14 are passed. Since Shape Script knows neither variables nor string substitution you need to write those by hand in any case. So a single parameter will usually suffice – or you just have named functions. The called function must return a string as result.
E.g.
will print the result returned by the function pFunc1
inside your add-in framework.
Similarly
will evaluate to true
if your function pFunc2
returns the string value 1
.
8. Advanced Patterns
This chapter simply shows a couple of shapes you could adapt for your own use. Currently they are not ordered in any way.
And if the following are not enough you should visit Geert Bellekens’ public repository at https://github.com/GeertBellekens/Enterprise-Architect-Shapescript-Library. It contains all disassembled Shape Scripts that Sparx has used in their MDGs. And, not to forget, Sparx has given permission to make them public.
8.1 Different Actor
This nice actor symbol comes courtesy of Andy J (from the Enterprise Architect forum).
displays as
8.2 Composite Symbol
In case you want to show the lying 8 (composite symbol) on your shape you simply need to add the following at the end of your script:
8.3 Non-/Rectangular Notation
If you want the user to allow switching between rectangular and iconic representation you simply can do that with
If the shape script finds this query it will show the Advanced/Use Rectangular Notation
in the context menu.
8.4 Rotating According to Incoming Edge
Unfortunately you can not use a sub-shape and have something rotated. You need to draw each rotation individually. However, the following pattern will enable you to do that:
8.5 Cloud Compensation
When using a cloud path it is not predictable how much the puffs will extend outside the frame. However, it is possible to get it into the bounding frame to some extend by adding padding shapes:
8.6 Remove Connector Stereotype
If you define a stereotyped connector the stereotype itself will be displayed in the middle. If you don’t want it to appear add the following to your connector Shape Script:
Of course there is another way leading to Rome:
inside shape main
will have the same effect.
8.7 Highlighting
There was a time when almost every week a post about an obscure little red triangle was found at Sparx’ Enterprise Architect forum. This feature to highlight elements seems to be a bit underestimated. When you want to highlight elements you can do so by Shift-Space
. This will also set the tagged
property. By adding the following in your Shape Scripts you can render a red triangle which is a bit larger than the original one.
And here is how it looks like:
8.8 Envelope
A commonly used shape for message objects is an envelope:
8.9 Annotation
Quite some profiles use a non-closed rectangle for annotations. The following draws the open side on the right except when the incoming note link is attached to the right. In that case the open side is on the left.
The above envelope and an annotation looks like this:
8.10 Arrows
The following – a bit lengthy – example shows how to create large arrows. Using tagged values the orientation, fill and name display can be changed.
This could look like the following:
8.11 Lines
Sometimes you want to place a simple line on a diagram to make some kind of separation. Here’s a neat trick how to do that. First we define a connector shape based on a dependency:
When you connect two elements with this dependency you will just see a thick line.
Now the tricky part: Since a connector needs to be connected to something just create a shape
which will actually draw nothing. Now place two boundaries on the diagram and connect them with the connector defined above. Finally you need to assign the invisible shape to the boundaries. Now you will only see the line itself.
The following screen shot shows an arbitrary shaped line with one invisible boundary selected:
8.12 Perfect Circles
Nothing is perfect, but you can approach perfection. If you need circles in your shape which will not distort to ellipses when scaling you need to do it like this:
The fixedAspectRatio
will scale X
and Y
always in the same ratio. The DefSize
needs two identical pixel sizes to form a square. Inside that all circles will remain circles.
9. Shape Script Syntax
This is the EBNF for Enterprise Architect’s Shape Script language. The start symbol is ShapeScript. Any spaces and tabs between non-terminals are ignored.
ShapeScript = { Shape | Decoration };
Shape = “shape”15 ShapeName ShapeBody;
ShapeName = /* any reserved or non-reserved string literal depending on the context */;
Decoration = “decoration” Name ShapeBody;
Name = /* an arbitrary string that should describe the form of the decoration */;
ShapeBody = “{“ {InitializationAttributeAssignment} {DrawingStatement} {SubShape} “}”
SubShape = Shape /* with a non-reserved name */;
InitializationAttributeAssignment = Attribute “=” Value “;”;
Attribute = /* see chapter Shape Attributes for a list of values /*;
Value = StringLiteral | Integer | Tuple;
StringLiteral = Quote { Character } Quote;
Quote = /* the double quote “ or a single quote ‘ */;
Character = /* any printable character except the used Quote */;
Integer = [”-“] {“0” .. “9”};
Tuple = “(“ Integer “,” Integer “)”;
Block = “{“ {DrawingStatement} “}” | DrawingStatement;
DrawingStatement = IfElseSection | Method;
IfElseSection = “if” “(“ QueryExpression “)” Block [“else” Block];
QueryExpression = /* see chapter Query Methods for the 2 methods and their parameters */;
Method = /* see chapter Shaping Elements for possible methods and their parameters */;
Notes
1The EA version used to create this book was actually 10.0 (build 1009). However, most of the references are also valid for earlier versions of EA. And of course all this still works with version 13.5 when the latest edit was made on this book. Though I have not cross checked which bugs were fixed until 13.5. Honestly, V14 itself was so buggy I never used it. I might cross check with V15.2 but I’m not to keen installing newer EA versions.↩
2I really loathe writing such legal blurb since it should be obvious. By the way: German Law applies! (Does that change anything?)↩
3This name will appear on top of the existing list of stereotypes so you can edit your test cases faster than with one named test
. I use ae
as prefix for element stereotypes and ac
for connector shapes in this book. You are asked to use meaningful names for you stereotypes instead.↩
4For a real case you should limit it it the base element where you want to appear the stereotype.↩
5Once a script is assigned the button will appear as Edit
and the Preview
below will contain what it says.↩
6The Next Shape
button will only be relevant if there are more than one shapes defined in a script. The preview will simply loop through the shapes and display one after the other.↩
7I highlighted the top left and bottom right coordinates in the screen shot.↩
8Not so say “the most advanced feature in Shape Script”.↩
9I have no idea whether it is planned to extend the Shape Script language. Here it looks like there should be a possibility to work with objects in the future. The current implementation does not look very meaningful else.↩
10Actually you can place it also outside the 100² units by using coordinates above 100 or below 0.↩
11This is one of the moments where I think that language designers which are responsible for that should be clubbed to death with a teaspoon. Preferably using one made of plastic so it takes longer.↩
12The script already uses control structures being explained later in this book. But hopefully it’s already understandably here.↩
13Feel free to report a bug.↩
14Since I use Perl, I only can see the repository and the GUID parameter so I have to use unique functions. This is perfect anyhow as these add-in methods should be used in rare cases only.↩
15Actually you can arbitrarily replace “shape” with “label” and “text”. You may do so to confuse others.↩