Configure your own default line styles in Enterprise Architect with EA-Matic

With EA-Matic you can develop add-ins for Enterprise Architect using the built-in scripting feature of EA.

With this EA-Matic script you can configure your own default line-styles depending on the type of connector.

In Enterprise Architect you can choose from no less then 9 different line style for the connectors.

Line Styles

Unfortunately you can only choose from the first three to be the default line style for new connectors.

line style default options

Additionally you can also specify the default for Generalization to be Tree Style.

For me that is not enough. I have my own habits when making UML diagrams. For most connector types I use Orthogonal – Square, but not for dependencies, use case relations and note links. For those I like the Direct style. The last exception are the control flow, state flow and object flows, for which I use Orthogonal – Rounded.

LineStylesDiagram

This EA-Matic script gives me the control I want. Not only does it set the default line style for new connectors, with the context menu I can reset all connectors on a diagram to my own default styles.

Free download

[ecwid_product store_id=”6824265″ product_id=”48249914″]

The code

In order to define your own preferred style you can edit the indicated part in the script

'*********EDIT BETWEEN HERE*************
' set here the menu name
menuDefaultLines = "&Set default linestyles"

' set here the default style to be used
defaultStyle = lsOrthogonalSquareTree

' set there the style to be used for each type of connector
function determineStyle(connector)
    dim connectorType
    connectorType = connector.Type
    select case connectorType
        case "ControlFlow", "StateFlow","ObjectFlow","InformationFlow"
            determineStyle = lsOrthogonalRoundedTree
        case "Generalization", "Realization", "Realisation"
            determineStyle = lsTreeVerticalTree
        case "UseCase", "Dependency","NoteLink"
            determineStyle = lsDirectMode
        case else
            determineStyle = defaultStyle
    end select
end function
'************AND HERE****************

This script uses 3 EA events. The first one is the EA_OnPostNewConnector which gets triggered by EA the moment you create  a new connector.

'the event called by EA
function EA_OnPostNewConnector(Info)
    'get the connector id from the Info
    dim connectorID
    connectorID = Info.Get("ConnectorID")
    dim connector 
    set connector = Repository.GetConnectorByID(connectorID)
    'get the current diagram
    dim diagram
    set diagram = Repository.GetCurrentDiagram()
    if not diagram is nothing then
        'first save the diagram
        Repository.SaveDiagram diagram.DiagramID
        'get the diagramlink for the connector
        dim diagramLink
        set diagramLink = getdiagramLinkForConnector(connector, diagram)
        if not diagramLink is nothing then
            'set the connectorstyle
            setConnectorStyle diagramLink, determineStyle(connector)
            'save the diagramlink
            diagramLink.Update
            'reload the diagram to show the link style
            Repository.ReloadDiagram diagram.DiagramID
        end if
    end if
end function

The next even we use is the EA_GetMenuItems so we can show a menu option in the context menu of a diagram.

'Tell EA what the menu options should be
function EA_GetMenuItems(MenuLocation, MenuName)
    if MenuName = "" and MenuLocation = "Diagram" then
        'Menu Header
        EA_GetMenuItems = menuDefaultLines
    end if 
end function

The last EA event is the EA_MenuClick, triggered when the user actually clicks on the menu item.

'react to user clicking a menu option
function EA_MenuClick(MenuLocation, MenuName, ItemName)
    if ItemName = menuDefaultLines then
        dim diagram 
        dim diagramLink
        dim connector
        dim dirty
        dirty = false
        set diagram = Repository.GetCurrentDiagram
        'save the diagram first
        Repository.SaveDiagram diagram.DiagramID
        'then loop all diagramLinks
        if not diagram is nothing then
            for each diagramLink in diagram.DiagramLinks
                set connector = Repository.GetConnectorByID(diagramLink.ConnectorID)
                if not connector is nothing then
                    'set the connectorstyle
                    setConnectorStyle diagramLink, determineStyle(connector)
                    'save the diagramlink
                    diagramLink.Update
                    dirty = true
                end if
            next
            'reload the diagram if we changed something
            if dirty then
                'reload the diagram to show the link style
                Repository.ReloadDiagram diagram.DiagramID
            end if
        end if
    end if
end function

Then we have a small helper function to get the diagramLink object from the diagram

'gets the diagram link object
function getdiagramLinkForConnector(connector, diagram)
    dim diagramLink 
    set getdiagramLinkForConnector = nothing
    for each diagramLink in diagram.DiagramLinks
        if diagramLink.ConnectorID = connector.ConnectorID then
            set getdiagramLinkForConnector = diagramLink
            exit for
        end if
    next
end function

And last but not least the method that will actually set the connector style.

'actually sets the connector style
function setConnectorStyle(diagramLink, connectorStyle)
    'split the style into its parts
    dim styleparts
    dim styleString
    styleString = diagramLink.Style
    styleparts = Split(styleString,";")
    dim stylePart
    dim mode
    dim modeIndex
    modeIndex = -1
    dim tree
    dim treeIndex
    treeIndex = -1
    mode = ""
    tree = ""
    dim i
    'find if Mode and Tree are already defined
    for i = 0 to Ubound(styleparts) -1 
        stylePart = styleparts(i)
        if Instr(stylepart,"Mode=") > 0 then
            modeIndex = i
        elseif Instr(stylepart,"TREE=") > 0 then
            treeIndex = i
        end if
    next
    'these connectorstyles use mode=3 and the tree
    if  connectorStyle = lsTreeVerticalTree or _
        connectorStyle = lsTreeHorizontalTree or _
        connectorStyle = lsLateralHorizontalTree or _
        connectorStyle = lsLateralVerticalTree or _
        connectorStyle = lsOrthogonalSquareTree or _
        connectorStyle = lsOrthogonalRoundedTree then
        mode = "3"
        tree = connectorStyle
    else
        mode = connectorStyle
    end if
    'set the mode value
    if modeIndex >= 0 then
        styleparts(modeIndex) = "Mode=" & mode
        diagramLink.Style = join(styleparts,";")
    else
        diagramLink.Style = "Mode=" & mode& ";"& diagramLink.Style
    end if
    'set the tree value
    if treeIndex >= 0 then
        if len(tree) > 0 then
            styleparts(treeIndex) = "TREE=" & tree
            diagramLink.Style = join(styleparts,";")
        else
            'remove tree part
            diagramLink.Style = replace(diagramLink.Style,styleparts(treeIndex)&";" , "")
        end if
    else
        diagramLink.Style = diagramLink.Style & "TREE=" & tree & ";"
    end if
end function

Hierarchical usage search in Enterprise Architect with SQL

How can I find out where an element or one of its specialized elements is used on a diagram in Enteprise Architect?

With this SQL search you can answer that question immediately and in a useful way.

Hierarchical usage search regults

Free download

[ecwid_product store_id=”6824265″ product_id=”48156490″]

 The model

Suppose we have modeled an inheritance hierarchy of animals as such

Animals Hierarchy

And our animals are being used as the classifier of Activity partitions in various Activity diagrams

Activity2

Activity1

And on the occasional Sequence diagram

Interaction1

Even with a simple example like this one it is not trivial to find all diagrams that use a rodent, or a bird.

The query

The query should return all diagrams that use an element either as link or as classifier. It should also include the diagrams for the sub-classes of the element we are looking for.

In theory a search like this should be carried out recursively, traversing the hierarchy until it reaches the bottom of the hierarchy. Unfortunately SQL and recursive are not the best friends, so we will have to limit the search to a pre-defined number of levels. In this case we go three levels down, so from the parent, to the the children, the grandchildren and the great-grandchildren.

Each of these levels is queried individually and each of the queries are added together with unions.

The first part of the query is to find diagrams that use the parent element as a link:

select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  po.Name as UsedElement, 'Link' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from ((((((t_object po
inner join t_diagramObjects do on do.Object_ID = po.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'

Then we get the diagrams that use the parent element as a classifier

union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  po.Name as UsedElement,'Classifier' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((t_object po
inner join t_object poi on po.Object_ID = poi.Classifier)
inner join t_diagramObjects do on do.Object_ID = poi.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'

Then descending a level we get the diagrams showing the children as link. Note that we include all possible variations of Generalization and Realization just to be sure we have everything. EA has been know to be not too consistent with these connector types in the past.

union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c1.Name as UsedElement,'Link' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from ((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_diagramObjects do on do.Object_ID = c1.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')

And we do the same for the children being used a classifier.

union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c1.Name as UsedElement,'Classifier' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_object c1i on c1.Object_ID = c1i.Classifier)
inner join t_diagramObjects do on do.Object_ID = c1i.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')

Descending another level to the grandchildren

union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c2.Name as UsedElement,'Link' as UsageType,
 package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
 from ((((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_connector poc2 on poc2.End_Object_ID = c1.Object_ID)
inner join t_object c2 on c2.Object_ID = poc2.Start_Object_ID)
inner join t_diagramObjects do on do.Object_ID = c2.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc2.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')

And the grandchildren as classifier

union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c2.Name as UsedElement,'Classifier' as UsageType,
 package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
 from (((((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_connector poc2 on poc2.End_Object_ID = c1.Object_ID)
inner join t_object c2 on c2.Object_ID = poc2.Start_Object_ID)
inner join t_object c2i on c2.Object_ID = c2i.Classifier)
inner join t_diagramObjects do on do.Object_ID = c2i.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc2.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')

Then to the last level, the great-grandchildren as link

union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c3.Name as UsedElement,'Link' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from ((((((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_connector poc2 on poc2.End_Object_ID = c1.Object_ID)
inner join t_object c2 on c2.Object_ID = poc2.Start_Object_ID)
inner join t_connector poc3 on poc3.End_Object_ID = c2.Object_ID)
inner join t_object c3 on c3.Object_ID = poc3.Start_Object_ID)
inner join t_diagramObjects do on do.Object_ID = c3.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc2.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc3.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')

An the last one, the great-grandchildren as classifier

union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c3.Name as UsedElement,'Classifier' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_connector poc2 on poc2.End_Object_ID = c1.Object_ID)
inner join t_object c2 on c2.Object_ID = poc2.Start_Object_ID)
inner join t_connector poc3 on poc3.End_Object_ID = c2.Object_ID)
inner join t_object c3 on c3.Object_ID = poc3.Start_Object_ID)
inner join t_object c3i on c3.Object_ID = c3i.Classifier)
inner join t_diagramObjects do on do.Object_ID = c3i.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc2.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc3.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')

The complete query then becomes

select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  po.Name as UsedElement, 'Link' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from ((((((t_object po
inner join t_diagramObjects do on do.Object_ID = po.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  po.Name as UsedElement,'Classifier' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((t_object po
inner join t_object poi on po.Object_ID = poi.Classifier)
inner join t_diagramObjects do on do.Object_ID = poi.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c1.Name as UsedElement,'Link' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from ((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_diagramObjects do on do.Object_ID = c1.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c1.Name as UsedElement,'Classifier' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_object c1i on c1.Object_ID = c1i.Classifier)
inner join t_diagramObjects do on do.Object_ID = c1i.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c2.Name as UsedElement,'Link' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from ((((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_connector poc2 on poc2.End_Object_ID = c1.Object_ID)
inner join t_object c2 on c2.Object_ID = poc2.Start_Object_ID)
inner join t_diagramObjects do on do.Object_ID = c2.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc2.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c2.Name as UsedElement,'Classifier' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_connector poc2 on poc2.End_Object_ID = c1.Object_ID)
inner join t_object c2 on c2.Object_ID = poc2.Start_Object_ID)
inner join t_object c2i on c2.Object_ID = c2i.Classifier)
inner join t_diagramObjects do on do.Object_ID = c2i.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc2.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c3.Name as UsedElement,'Link' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from ((((((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_connector poc2 on poc2.End_Object_ID = c1.Object_ID)
inner join t_object c2 on c2.Object_ID = poc2.Start_Object_ID)
inner join t_connector poc3 on poc3.End_Object_ID = c2.Object_ID)
inner join t_object c3 on c3.Object_ID = poc3.Start_Object_ID)
inner join t_diagramObjects do on do.Object_ID = c3.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc2.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc3.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
union
select d.ea_guid AS CLASSGUID, d.Diagram_Type as CLASSTYPE, d.Name as DiagramName,  c3.Name as UsedElement,'Classifier' as UsageType,
package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((((((((t_object po
inner join t_connector poc1 on poc1.End_Object_ID = po.Object_ID)
inner join t_object c1 on c1.Object_ID = poc1.Start_Object_ID)
inner join t_connector poc2 on poc2.End_Object_ID = c1.Object_ID)
inner join t_object c2 on c2.Object_ID = poc2.Start_Object_ID)
inner join t_connector poc3 on poc3.End_Object_ID = c2.Object_ID)
inner join t_object c3 on c3.Object_ID = poc3.Start_Object_ID)
inner join t_object c3i on c3.Object_ID = c3i.Classifier)
inner join t_diagramObjects do on do.Object_ID = c3i.Object_ID)
inner join t_diagram d on d.Diagram_ID = do.Diagram_ID)
inner join t_package package on package.Package_ID = d.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where po.Name = '<Search Term>'
and poc1.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc2.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')
and poc3.Connector_Type in ('Generalization', 'Realization', 'Generalisation', 'Realisation')

The results

If you execute the search for Rodent you get something like following

Hierarchical usage search results_raw

By dragging the column UsedElement up to the grouping header you get an even better result

Hierarchical usage search regults

Although the icon for diagrams in search results is a bit weird, you can still use them as you would other search results. You can double-click a line to open the diagram, or you can right click and select it in the project browser.

More about SQL Searches

Instant impact analyses in Enterprise Architect with SQL searches

One of the major benefits of using a tool such as Enterprise Architect is that you create traceability. You store the relations between different model elements in your model.

The tricky part is to effectively use that traceability for your impact analyses. Following these instructions you can create SQL Searches in EA that instantly show the impact a certain element has on the rest of the model and present them in a clear and useful way.

Traceability_results_final

The meta model

In order to do impact analysis you have to know, and preferable document, your meta model. The meta model describes which elements from your model are to connected to each other, and it also describes which relation is used.

For the benefit of this example lets start with an imaginary meta model.

Traceability_metamodel

While this view of the meta model already helps a lot we still need to know exactly how these meta-concepts are realized in our Enterprise Architect model.

The model

Traceability_example model

So we end up with a model that looks a bit like this. You see that even for a miniature model like this it comes rather complicated very quickly. The questions that we need to answer when doing impact analysis questions such as

  • If I change requirement RQ-002, which other elements in my model are impacted?
  • I I change the CreateNewOrder function, where can I expect impact?

In general you’ll want to know

  • If I change X how does that impact the rest of my model.

Graphically that looks a bit like this:

Traceability_views

Enterprise Architect has an excellent view called traceabilty view

Traceability_view

This is great when working on the model, but it doesn’t really give you an overview, and it can’t be used for exporting or document generation.

The query

In order to get an overview of all impacted elements will make a custom SQL Search. See Harvesting the power of EA’s SQL searches for more information on how to make and use SQL searches.

We’ll want to create a search starting from each of the different levels. From their we work our way through the meta-models layer using a union for each level.

Lets start with the first one, from the Requirements to Use Case,  to Business Process, and to Application Service.

It’s best to work through this gradually. The first query will only return the requirement itself.

select req.ea_guid AS CLASSGUID, req.Object_Type AS CLASSTYPE,'1_Requirement' as TraceLevel, req.Name, req.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((( t_object req
inner join t_package package on package.Package_ID = req.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'

Traceability_searchFromReq1

The we tackle the next level and we go from the Requirement to the Use Case. We add this query with a union to the previous query.

union
select uc.ea_guid AS CLASSGUID, uc.Object_Type AS CLASSTYPE,'3_Use Case' as TraceLevel, uc.Name, uc.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((( t_object req
inner join t_connector requc on requc.End_Object_ID = req.Object_ID)
inner join t_object uc on requc.Start_Object_ID = uc.Object_ID)
inner join t_package package on package.Package_ID = uc.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
and uc.Object_Type = 'UseCase'

Notice that we make a join to t_connector and then back to t_object for the Use Case. Mind the direction of the connector between use case and requirement. Since it starts at the use case and goes to the requirement we need to join them using the appropriate End_Object_ID or Start_Object_ID.

Next part is the Business Process. Again we join t_connector and t_object to reach the business processes

union
select bp.ea_guid AS CLASSGUID, bp.Object_Type AS CLASSTYPE,'2_Business Process' as TraceLevel, bp.Name, bp.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((( t_object req
inner join t_connector requc on requc.End_Object_ID = req.Object_ID)
inner join t_object uc on requc.Start_Object_ID = uc.Object_ID)
inner join t_connector ucbp on ucbp.Start_Object_ID = uc.Object_ID)
inner join t_object bp on ucbp.End_Object_ID = bp.Object_ID)
inner join t_package package on package.Package_ID = bp.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
and uc.Object_Type = 'UseCase'
and bp.Object_Type = 'Activity' and bp.Stereotype = 'process'

From the business process we can reach the Business Information Items. Notice that we only return the information items that are the results of the business processes. Information items that serve as input to our business are not impacted when we change our business process.

union
select bi.ea_guid AS CLASSGUID, bi.Object_Type AS CLASSTYPE,'2_Business Information Items' as TraceLevel, bi.Name, bi.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((((( t_object req
inner join t_connector requc on requc.End_Object_ID = req.Object_ID)
inner join t_object uc on requc.Start_Object_ID = uc.Object_ID)
inner join t_connector ucbp on ucbp.Start_Object_ID = uc.Object_ID)
inner join t_object bp on ucbp.End_Object_ID = bp.Object_ID)
inner join t_connector bpbi on bpbi.Start_Object_ID = bp.Object_ID )
inner join t_object bi on bpbi.End_Object_ID = bi.Object_ID )
inner join t_package package on package.Package_ID = bi.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
and uc.Object_Type = 'UseCase'
and bp.Object_Type = 'Activity' and bp.Stereotype = 'process'
and bi.Object_Type = 'InformationItem'

The last level we want to add to our impact analysis query is the Application Function. These functions are again linked to the use cases. Notice that we both test the stereotype for NULL as for an empty string. That is just to be sure to get all the Application Functions. EA is know to be inconsistent in some areas, sometimes using NULL, sometimes an empty string or 0.

union
select af.ea_guid AS CLASSGUID, af.Object_Type AS CLASSTYPE,'4_Application Function' as TraceLevel, af.Name, af.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((( t_object req
inner join t_connector requc on requc.End_Object_ID = req.Object_ID)
inner join t_object uc on requc.Start_Object_ID = uc.Object_ID)
inner join t_connector ucaf on ucaf.End_Object_ID = uc.Object_ID)
inner join t_object af on ucaf.Start_Object_ID = af.Object_ID)
inner join t_package package on package.Package_ID = af.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
and uc.Object_Type = 'UseCase'
and af.Object_Type = 'Activity' and (af.Stereotype is null or af.Stereotype = '')

Now if you paste all the different parts of the query together you can start doing impact analysis and see the impact a single requirement has to the rest of the system.

If at first you run the query the results look a bit like this

Traceability_results_raw

But if you drag the “TraceLevel” up to the grouping part

Traceability_results_group

and then remove the column altogether from the results then you get a much nicer looking result

Traceability_results_final

Now you can create similar searches from the other elements in your meta-model, each time working your way up or down the model.

Below the complete SQL code for the search described here.

select req.ea_guid AS CLASSGUID, req.Object_Type AS CLASSTYPE,'1_Requirement' as TraceLevel, req.Name, req.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((( t_object req
inner join t_package package on package.Package_ID = req.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
union
select uc.ea_guid AS CLASSGUID, uc.Object_Type AS CLASSTYPE,'3_Use Case' as TraceLevel, uc.Name, uc.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((( t_object req
inner join t_connector requc on requc.End_Object_ID = req.Object_ID)
inner join t_object uc on requc.Start_Object_ID = uc.Object_ID)
inner join t_package package on package.Package_ID = uc.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
and uc.Object_Type = 'UseCase'
union
select bp.ea_guid AS CLASSGUID, bp.Object_Type AS CLASSTYPE,'2_Business Process' as TraceLevel, bp.Name, bp.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((( t_object req
inner join t_connector requc on requc.End_Object_ID = req.Object_ID)
inner join t_object uc on requc.Start_Object_ID = uc.Object_ID)
inner join t_connector ucbp on ucbp.Start_Object_ID = uc.Object_ID)
inner join t_object bp on ucbp.End_Object_ID = bp.Object_ID)
inner join t_package package on package.Package_ID = bp.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
and uc.Object_Type = 'UseCase'
and bp.Object_Type = 'Activity' and bp.Stereotype = 'process'
union
select bi.ea_guid AS CLASSGUID, bi.Object_Type AS CLASSTYPE,'2_Business Information Items' as TraceLevel, bi.Name, bi.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((((( t_object req
inner join t_connector requc on requc.End_Object_ID = req.Object_ID)
inner join t_object uc on requc.Start_Object_ID = uc.Object_ID)
inner join t_connector ucbp on ucbp.Start_Object_ID = uc.Object_ID)
inner join t_object bp on ucbp.End_Object_ID = bp.Object_ID)
inner join t_connector bpbi on bpbi.Start_Object_ID = bp.Object_ID )
inner join t_object bi on bpbi.End_Object_ID = bi.Object_ID )
inner join t_package package on package.Package_ID = bi.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
and uc.Object_Type = 'UseCase'
and bp.Object_Type = 'Activity' and bp.Stereotype = 'process'
and bi.Object_Type = 'InformationItem'
union
select af.ea_guid AS CLASSGUID, af.Object_Type AS CLASSTYPE,'4_Application Function' as TraceLevel, af.Name, af.Stereotype
, package.name as PackageName ,package_p1.name as Package_level1,package_p2.name as Package_level2 ,package_p3.name as Package_level3
from (((((((( t_object req
inner join t_connector requc on requc.End_Object_ID = req.Object_ID)
inner join t_object uc on requc.Start_Object_ID = uc.Object_ID)
inner join t_connector ucaf on ucaf.End_Object_ID = uc.Object_ID)
inner join t_object af on ucaf.Start_Object_ID = af.Object_ID)
inner join t_package package on package.Package_ID = af.Package_ID)
left join t_package package_p1 on package_p1.package_id = package.parent_id)
left join t_package package_p2 on package_p2.package_id = package_p1.parent_id)
left join t_package package_p3 on package_p3.package_id = package_p2.parent_id)
where req.name like '#WC#<Search Term>#WC#'
and req.Object_Type = 'Requirement'
and uc.Object_Type = 'UseCase'
and af.Object_Type = 'Activity' and (af.Stereotype is null or af.Stereotype = '')

Automatically synchronize overrides in Enterprise Architect with EA-Matic

With EA-Matic you can develop add-ins for Enterprise Architect using the built-in scripting feature of EA.

This example EA-Matic script keeps the signature of the overriding operations in sync with the signature of the overridden operation.

This is especially useful when you are modelling an interface and a number of classes that realize this interface. EA has this neat feature Overrides and Implementations accessed by Ctrl-Shift-O to copy operations from an interface or superclass to the realizing/specializing class.

EA-Matic Overides and Implmentations

This works great when you have created a new operation, or added a new realizing class, but once you change the signature of your interface operation you are on your own. You need to track down all operations that once overrode your interface operation and change their signature accordingly. Not a pretty task.

EA-Matic Overides

This script ensures that the signature of the overrides is kept in sync with that of the overridden operation. Whenever you change the signature it alerts the user to the fact that there are overrides and asks if they need to be synchronized.

EA-Matic Synchronize

Usage of Enterprise Architect Add-in Framework

This script also illustrates how you can use the Enterprise Architect Add-in Framework within your script.

This extensive open source framework is the basis for the add-ins EA-Matic and EA Navigator, and offers a much more functional interface to the model then the standard EA API.

This code inializes the model object with the current Repository

'gets a new instance of the EAAddinFramework and initializes it with the EA.Repository
function getEAAddingFrameworkModel()
    'Initialize the EAAddinFramework model
    dim model
    set model = CreateObject("TSF.UmlToolingFramework.Wrappers.EA.Model")
    model.initialize(Repository)
    set getEAAddingFrameworkModel = model
end function

It is then further used to get the operation based on the GUID passed as parameter.

set operation = model.getOperationByGUID(GUID)

The model object also can be used to search elements based on an sql query

dim descendants
dim getdescendantsQuery
getdescendantsQuery = "select c.Start_Object_ID as Object_ID from (t_object o " _
				& "inner join t_connector c on c.End_Object_ID = o.Object_ID) " _
				& "where "_
				& "(c.[Connector_Type] like 'Generali_ation' "_
				& "or c.[Connector_Type] like 'Reali_ation' )"_
				& "and o.Object_ID = " & element.id
set descendants = model.toArrayList(model.getElementWrappersByQuery(getdescendantsQuery))

Note that we need to use model.toArrayList to convert the C# List<> that cannot be used by VBScript to an ArrayList that can be used.

The code

Download the complete script: EA-Matic Sychronize overrides. This script requires EA-Matic version 1.0.12.2 or higher to function.

There are two events that we use. EA_OnNotifyContextChanged to react to remember the overrides of the operation before it is changed, and EA_OnContextItemModified to update the overrides one the operation has been changed.

'Event Called when a new element is selected in the context. We use this operation to keep the id of the selected operation and a list of its overrides
'Because now is the only moment we are able to find it's overrides. Once changed we cannot find the overrides anymore because then they already
'have a different signature
function EA_OnContextItemChanged(GUID, ot)
	'we only want to do something when the selected element is an operation
	if ot = otMethod then
		'get the model
		dim model
		set model = getEAAddingFrameworkModel()
		'get the operation
		dim operation
		set operation = model.getOperationByGUID(GUID)
		'remember the operationID
		operationID = operation.id
		'remember the overrides
		set overrides = getOverrides(operation, model)
		Repository.WriteOutput "EA-Matic", overrides.Count & " overrides found for: " & operation.name,0
	end if
end function

'Event called when an element is changed. Unfortunately EA doesn't call it for an operation, only for the owner so we have to work with that.
function EA_OnNotifyContextItemModified(GUID, ot)
	'we only want to do something when the selected element is an operation
	if ot = otElement then
		'get the operation
		'Here we use the EA API object directly as most set methods are not implemented in EA Addin Framework
		dim wrappedOperation
		set wrappedOperation = Repository.GetMethodByID(operationID)
		dim modifiedElement
		set modifiedElement = Repository.GetElementByGuid(GUID)
		if not wrappedOperation is Nothing and not modifiedElement is Nothing then
			'check to be sure we have the same operation
			if modifiedElement.ElementID = wrappedOperation.ParentID AND overrides.Count > 0 then
				dim synchronizeYes
				synchronizeYes = MsgBox("Found " & overrides.Count & " override(s) for operation "& modifiedElement.Name & "." & wrappedOperation.Name & vbNewLine & "Synchronize?" _
										,vbYesNo or vbQuestion or vbDefaultButton1, "Synchronize overrides?")
				if synchronizeYes = vbYes then
					synchronizeOverrides wrappedOperation
					'log to output

					Repository.WriteOutput "EA-Matic", "Operation: " & wrappedOperation.name &" synchronized" ,0
				end if
				'reset operationID to avoid doing it all again
				operationID = 0
			end if
		end if
	end if
end function

In OnContextItemChanged we get the overrides by first selecting all operations with the same signature from the model with a query. Then we get all descendants of the owner of the operation (recursively) and filter the operation to only those owned by a descendant.

'gets the overrides of the given operation by first getting all operations with the same signature and then checking if they are owned by a descendant
function getOverrides(operation, model)
	'first get all operations with the exact same signature
	dim overrideQuery
	overrideQuery = "select distinct op2.OperationID from (((t_operation op " & _
					"inner join t_operation op2 on op2.[Name] = op.name) "& _
					"left join t_operationparams opp on op.OperationID = opp.OperationID) "& _
					"left join t_operationparams opp2 on opp2.OperationID = op2.OperationID) "& _
					"where op.OperationID = "& operation.id &" "& _
					"and op2.ea_guid <> op.ea_guid "& _
					"and (op2.TYPE = op.Type OR (op2.TYPE is null AND op.Type is null)) "& _
					"and (op2.Classifier = op.Classifier OR (op2.Classifier is null AND op.Classifier is null)) "& _
					"and (opp.Name = opp2.Name OR (opp.Name is null AND opp2.Name is null)) "& _
					"and (opp.TYPE = opp2.TYPE OR (opp.TYPE is null AND opp2.Type is null)) "& _
					"and (opp.DEFAULT = opp2.DEFAULT OR (opp.DEFAULT is null AND opp2.DEFAULT is null)) "& _
					"and (opp.Kind = opp2.Kind OR (opp.Kind is null AND opp2.Kind is null)) "& _
					"and (opp.Classifier = opp2.Classifier OR (opp.Classifier is null AND opp2.Classifier is null)) "
	dim candidateOverrides
	set candidateOverrides = model.ToArrayList(model.getOperationsByQuery(overrideQuery))
	'then get the descendants of the owner
	dim descendants
	dim descendant
	'first find all elements that either inherit from the owner or realize it
	dim owner
	set owner = model.toObject(operation.owner)
	set descendants = getDescendants(owner, model)
	'then filter the candidates to only those of the descendants
	'loop operations backwards
	dim i
	for i = candidateOverrides.Count -1 to 0 step -1
		dim found
		found = false
		for each descendant in descendants
			if descendant.id = model.toObject(candidateOverrides(i).owner).id then
				'owner is a descendant, operation can stay
				found = true
				exit for
			end if
		next
		'remove operation from non descendants
		if not found then
			candidateOverrides.RemoveAt(i)
		end if
	next
	set getOverrides = candidateOverrides
end function

'gets all descendant of an element. That is all subclasses and classes that Realize the element.
'Works recursively to get them all.
function getDescendants(element, model)
	dim descendants
	dim getdescendantsQuery
	getdescendantsQuery = "select c.Start_Object_ID as Object_ID from (t_object o " _
					& "inner join t_connector c on c.End_Object_ID = o.Object_ID) " _
					& "where "_
					& "(c.[Connector_Type] like 'Generali_ation' "_
					& "or c.[Connector_Type] like 'Reali_ation' )"_
					& "and o.Object_ID = " & element.id
	set descendants = model.toArrayList(model.getElementWrappersByQuery(getdescendantsQuery))
	'get the descendants descendants as well
	dim descendant
	dim descendantsChildren
	for each descendant in descendants
		if IsEmpty(descendantsChildren) then
			set descendantsChildren = getDescendants(descendant, model)
		else
			descendantsChildren.AddRange(getDescendants(descendant, model))
		end if
	next
	'add the descendantsChildren to the descendants
	if not IsEmpty(descendantsChildren) then
		if  descendantsChildren.Count > 0 then
			descendants.AddRange(descendantsChildren)
		end if
	end if
	set getDescendants = descendants
end function

Then when the signature of the operation has been changed we synchronize its signature with that of the with the overrides and we tell EA that the owner of the override has been changed.

'Synchronizes the operation with it's overrides
function synchronizeOverrides(wrappedOperation)
	dim override
	for each override in overrides
		dim wrappedOverride
		set wrappedOverride = override.WrappedOperation
		'synchronize the operation with the override
		synchronizeOperation wrappedOperation, wrappedOverride
		'tell EA something might have changed
		Repository.AdviseElementChange wrappedOverride.ParentID
	next
end function

'Synchronizes the operation with the given override
function synchronizeOperation(wrappedOperation, wrappedOverride)
	dim update
	update = false
	'check name
	if wrappedOverride.Name <> wrappedOperation.Name then
		wrappedOverride.Name = wrappedOperation.Name
		update = true
	end if
	'check return type
	if wrappedOverride.ReturnType <> wrappedOperation.ReturnType then
		wrappedOverride.ReturnType = wrappedOperation.ReturnType
		update = true
	end if
	'check return classifier
	if wrappedOverride.ReturnType <> wrappedOperation.ReturnType then
		wrappedOverride.ReturnType = wrappedOperation.ReturnType
		update = true
	end if
	if update then
		wrappedOverride.Update
	end if
	'check parameters
	synchronizeParameters wrappedOperation, wrappedOverride
end function

'Synchronizes the parameters of the given operatin with that of the overrride
function synchronizeParameters(wrappedOperation, wrappedOverride)
	'first make sure they both have the same number of parameters
	if wrappedOverride.Parameters.Count < wrappedOperation.Parameters.Count then
		'add parameters as required
		dim i
		for i = 0 to wrappedOperation.Parameters.Count - wrappedOverride.Parameters.Count -1
			dim newParameter
			set newParameter = wrappedOverride.Parameters.AddNew("parameter" & i,"")
			newParameter.Update
		next
		wrappedOverride.Parameters.Refresh
	elseif wrappedOverride.Parameters.Count > wrappedOperation.Parameters.Count then
		'remove parameters as required
		for i = wrappedOverride.Parameters.Count -1 to wrappedOperation.Parameters.Count step -1
			wrappedOverride.Parameters.DeleteAt i,false
		next
		wrappedOverride.Parameters.Refresh
	end if
	'make parameters equal
	dim wrappedParameter
	dim overriddenParameter
	dim j
	for j = 0 to wrappedOperation.Parameters.Count -1
		dim parameterUpdated
		parameterUpdated = false
		set wrappedParameter = wrappedOperation.Parameters.GetAt(j)
		set overriddenParameter = wrappedOverride.Parameters.GetAt(j)
		'name
		if overriddenParameter.Name <> wrappedParameter.Name then
			overriddenParameter.Name = wrappedParameter.Name
			parameterUpdated = true
		end if
		'type
		if overriddenParameter.Type <> wrappedParameter.Type then
			overriddenParameter.Type = wrappedParameter.Type
			parameterUpdated = true
		end if
		'default
		if overriddenParameter.Default <> wrappedParameter.Default then
			overriddenParameter.Default = wrappedParameter.Default
			parameterUpdated = true
		end if
		'kind
		if overriddenParameter.Kind <> wrappedParameter.Kind then
			overriddenParameter.Kind = wrappedParameter.Kind
			parameterUpdated = true
		end if
		'classifier
		if overriddenParameter.ClassifierID <> wrappedParameter.ClassifierID then
			overriddenParameter.ClassifierID = wrappedParameter.ClassifierID
			parameterUpdated = true
		end if
		'update the parameter if it was changed
		if parameterUpdated then
			overriddenParameter.Update
		end if
	next
end function

Synchronize object and classifier name in Enterprise Architect with EA-Matic

With EA-Matic you can develop add-ins for Enterprise Architect using the built-in scripting feature of EA.

This example EA-Matic script keeps the names of objects synchronized with the name of their classifier.

When modelling with objects the name of the object is often left empty because it doesn’t have any significance at that point. One of the downsides of this practice is that in things like the traceability view, or in the links view the object shows up without a name, which of course doesn’t help much.

EA-Matic synchronize classifier

One of the solutions for this problem is to give the object the same name as its classifier. But that is a manual tedious process, and in case the classifier changes name you have the change the name of all the instances.

EA-Matic synchronize classifier_after

The script ensures that the name of the instances is always the same as the name of the classifier.

Usage of Enterprise Architect Add-in Framework

This script also illustrates how you can use the Enterprise Architect Add-in Framework within your script.

This extensive open source framework is the basis for the add-ins EA-Matic and EA Navigator, and offers a much more functional interface to the model then the standard EA API.

This code inializes the model object with the current Repository

'gets a new instance of the EAAddinFramework and initializes it with the EA.Repository
function getEAAddingFrameworkModel()
    'Initialize the EAAddinFramework model
    dim model
    set model = CreateObject("TSF.UmlToolingFramework.Wrappers.EA.Model")
    model.initialize(Repository)
    set getEAAddingFrameworkModel = model
end function

It is then further used to get the element based on the GUID passed as parameter

set element = model.getElementWrapperByGUID(GUID)

The model element can be used to search elements based on an sql query

 query = "select o.Object_ID from t_object o where o.classifier =" & element.id
 dim objects
 set objects = model.toArrayList(model.getElementWrappersByQuery(query))

Note that we need to use model.toArrayList to convert the C# List<> that cannot be used by VBScript to an ArrayList that can be used.

The code

Download the complete script: EA-Matic Sychronize object names

There are two events that we use. EA_OnNotifyContextItemModified to react to a name change of either the object or the classifier and EA_OnPostNewElement to change the name of a new object immediately after it has been created.

function EA_OnNotifyContextItemModified(GUID, ot)
    dim model
    set model = getEAAddingFrameworkModel()
    'only do something when the changed object is an element
    if ot = otElement then
        dim element
        set element = model.getElementWrapperByGUID(GUID)
        synchronizeObjectNames element, model
    end if
end function

function EA_OnPostNewElement(Info)
    'Get the model
    dim model
    set model = getEAAddingFrameworkModel()
    'get the elementID from Info
    dim elementID
    elementID = Info.Get("ElementID")
    'get the element being deleted
    dim element
    set element = model.getElementWrapperByID(elementID)
    synchronizeObjectNames element, model
end function

These two events then use the function synchronizeObjectNames to make sure the name of the object is the same as the name of the classifier

function synchronizeObjectNames(element, model)
    'first check if this is an object
    if element.WrappedElement.Type = "Object" AND element.WrappedElement.ClassifierID > 0 then
        dim classifier
        set classifier = model.getElementWrapperByID(element.WrappedElement.ClassifierID)
        if not classifier is nothing AND classifier.name <> element.name then
            element.name = classifier.name
            element.save
        end if
    else
        'get all objects having this element as their classifier
        dim query
        query = "select o.Object_ID from t_object o where o.classifier =" & element.id
        dim objects
        set objects = model.toArrayList(model.getElementWrappersByQuery(query))
        'loop objects
        dim obj
        for each obj in objects
            'rename the object if the name is different from the classifiers name
            if obj.name <> element.name then
                obj.name = element.name
                obj.save
            end if
        next
    end if
end function

Self-maintaining diagrams in Enterprise Architect with EA-Matic

With EA-Matic you can develop add-ins for Enterprise Architect using the built-in scripting feature of EA.

This example shows how to make diagrams that maintain themselves to make sure they are always up-to-date.

The idea is that we want a diagram that shows all the relations of the owning object. The diagram should always be up-to-date without manual intervention.

EA-Matic auto_diagram

To indicate that this diagram is an automatic diagram we nest it under the class and prefix it with AUTO_ and on the diagram we place the owning object and its related elements

EA-Matic self-maintaining diagram

Usage of Enterprise Architect Add-in Framework

This script also illustrates how you can use the Enterprise Architect Add-in Framework within your script.

This extensive open source framework is the basis for the add-ins EA-Matic and EA Navigator, and offers a much more functional interface to the model then the standard EA API.

This code inializes the model object with the current Repository

'gets a new instance of the EAAddinFramework and initializes it with the EA.Repository
function getEAAddingFrameworkModel()
    'Initialize the EAAddinFramework model
    dim model
    set model = CreateObject("TSF.UmlToolingFramework.Wrappers.EA.Model")
    model.initialize(Repository)
    set getEAAddingFrameworkModel = model
end function

It is then further used to get the ConnectorWrapper based on the ElementID from the Info object

set connector = model.getRelationByID(connectorID)

This connector is then used to access the elements that are connected by this Connector

 'get the related elements
 dim relatedElements
 set relatedElements = model.toArrayList(connector.relatedElements)

Note that we need to use model.toArrayList to convert the C# generic List<> that cannot be used by VBScript to an ArrayList that can be used.

The code

Download the complete script: EA-Matic Self-maintaining diagram

In the first part we use the magic EA-Matic keyword to in order to tell EA-Matic it should use this script.

We also declare the important ID’s of the selected connector so we can compare that with the ID’s of this connector once it has changed.

The first event we use is the EA_OnPostNewConnector. This event will be fired after you create a new connector in EA.
In this event we add the newly related elements to each others auto-diagram, if any.

!INC Local Scripts.EAConstants-VBScript

' EA-Matic
' This script, when used with EA-Matic will maintain auto-updating diagrams for elements.
' A nested diagram with prefix AUTO_ will be considered an auto-updating diagram.
' The diagram will keep track of all elements related to the owner of the auto diagram.
'
' Author:     Geert Bellekens
' EA-Matic: http://bellekens.com/ea-matic/
'
'maintain a reference to the connector in context
dim contextConnectorID
dim oldClientID
dim oldSupplierID

'a new connector has been created. Add the related elements to the auto-diagram
function EA_OnPostNewConnector(Info)
     'get the connector id from the Info
     dim connectorID
     connectorID = Info.Get("ConnectorID")
     dim model
     'get the model
     set model = getEAAddingFrameworkModel()
     dim connector
     set connector = model.getRelationByID(connectorID)
     'get the related elements
     dim relatedElements
     set relatedElements = model.toArrayList(connector.relatedElements)
     'for i = 0 to attributes.Count - 1
     if relatedElements.Count = 2 then
        'once with the first
        addRelatedElementoAutoDiagram relatedElements(0), relatedElements(1), model
        'then with the second
        addRelatedElementoAutoDiagram relatedElements(1), relatedElements(0), model
     end if
end function

'adds the related element to the auto_updatediagrams if any
function addRelatedElementoAutoDiagram(element,relatedElement, model)
    'get the diagram owned by this element
    dim ownedDiagrams
    set ownedDiagrams = model.toArrayList(element.ownedDiagrams)
    for each diagram In ownedDiagrams
        'check the name of the diagram
        if Left(diagram.name,LEN("AUTO_")) = "AUTO_" then
            'add the related element to the diagram
            diagram.addToDiagram(relatedElement)
        end if
    next
end function

The next part deals with the deletion of a connector. Each time a connector is about to be deleted EA fires the EA_OnPreDeleteConnector event.
We remove the connected elements from their respective auto-diagrams, but only if this connector was the last connector between the two elements.

' A connector will be deleted. Remove the elements from the auto-diagram
function EA_OnPreDeleteConnector(Info)
     'get the connector id from the Info
     dim connectorID
     connectorID = Info.Get("ConnectorID")
     dim model
     'get the model
     set model = getEAAddingFrameworkModel()
     dim connector
     set connector = model.getRelationByID(connectorID)
     'get the related elements
     dim relatedElements
     set relatedElements = model.toArrayList(connector.relatedElements)
     'for i = 0 to attributes.Count - 1
     if relatedElements.Count = 2 then
        'we only need to remove the related element if they are not connected anymore after deleting the connector
        'so only if there is only one relationship between the two elements
        if sharedRelationsCount(relatedElements(0), relatedElements(1), model) <= 1 then
            'once with the first
            removeRelatedElemenFromAutoDiagram relatedElements(0), relatedElements(1), model
            'then with the second
            removeRelatedElemenFromAutoDiagram relatedElements(1), relatedElements(0), model
        end if
     end if
end function

'returns the number of relations that connecto both elements
function sharedRelationsCount(elementA, elementB, model)
    'start counting at zero
    sharedRelationsCount = 0
    'get the relationships for both objects
    dim relationsA
    set relationsA = model.toArrayList(elementA.relationships)
    dim relationsB
    set relationsB = model.toArrayList(elementB.relationships)
    for each relationA in relationsA
        for each relationB in relationsB
            'if both relations have the same ID then we have a shared relation
            if relationA.id = relationB.id then
                sharedRelationsCount = sharedRelationsCount +1
            end if
        next
    next
end function

' Removes the related element from the auto update diagram if any.
function removeRelatedElemenFromAutoDiagram(element,relatedElement, model)
    'get the diagram owned by this element
    dim ownedDiagrams
    set ownedDiagrams = model.toArrayList(element.ownedDiagrams)
    for each diagram In ownedDiagrams
        dim diagram
        set diagram = ownedDiagrams(i)
        'check the name of the diagram
        if Left(diagram.name,LEN("AUTO_")) = "AUTO_" then
            'Removing elements from a diagram in unfortunately not implemented in the EAAddinFramework so we'll have to do it in the script
            dim eaDiagram
            set eaDiagram = diagram.wrappedDiagram
            for i = 0 to eaDiagram.DiagramObjects.Count -1
                dim diagramObject
                set diagramObject = eaDiagram.DiagramObjects.GetAt(i)
                if diagramObject.ElementID = relatedElement.id then
                    'remove the diagramObject
                    eaDiagram.DiagramObjects.Delete(i)
                    'refresh the diagram after we changed it
                    diagram.reFresh()
                    'exit the loop we have delete the diagramobject
                    exit for
                end if
            next
        end if
    next
end function

The next part deals with the diagram being opened, or shown. Since we are adding elements automatically, we want to use EA’s automatic layout to make the diagram presentable. The downside of this however is that EA also opens the diagram when doing a layout. So instead of doing an auto-layout directly after editing the diagram, we do one as soon as the user opens the diagram, or switches to an already opened diagram. We use the events EA_OnPostOpenDiagram and EA_OnTabChanged

'autodiagrams are automatically layouted when opened
function EA_OnPostOpenDiagram(DiagramID)
    dim model
    'get the model
    set model = getEAAddingFrameworkModel()
    layoutAutoDiagram DiagramID, model
end function

'autodiagrams are automatically layouted when we tab is switched to them
function EA_OnTabChanged(TabName, DiagramID)
    if  DiagramID > 0 then
        dim model
        'get the model
        set model = getEAAddingFrameworkModel()
        layoutAutoDiagram DiagramID, model
    end if
end function

'layout the auto diagram
function layoutAutoDiagram(diagramID, model)
    dim diagram
    set diagram = model.getDiagramByID(DiagramID)
    'if the diagram is an auto diagram then we do an automatic layout
    if Left(diagram.name,LEN("AUTO_")) = "AUTO_" then
        'auto layout diagram
        dim diagramGUIDXml
        'The project interface needs GUID's in XML format, so we need to convert first.
        diagramGUIDXml = Repository.GetProjectInterface().GUIDtoXML(diagram.wrappedDiagram.DiagramGUID)
        'Then call the layout operation
        Repository.GetProjectInterface().LayoutDiagramEx diagramGUIDXml, lsDiagramDefault, 4, 20 , 20, false
    end if
end function

The only thing left now is to update the diagram in case a user changes a connector, and for instance drags one of the ends to another element. In order to do this we need to record the id’s of both ends of the connector once it is selected in EA_OnContextItemChanged and compare that to the id’s of this same connector after it has been changed in EA_OnNotifyContextItemModified

'keep a reference to the selected connector
function EA_OnContextItemChanged(GUID, ot)
    'we only do something when the context item is a connector
    if ot = otConnector then
        dim model
        'get the model
        set model = getEAAddingFrameworkModel()
        'get the connector
        dim contextConnector
        set contextConnector = model.getRelationByGUID(GUID)
        'MsgBox(TypeName(contextConnector))
        contextConnectorID = contextConnector.id
        oldClientID = contextConnector.WrappedConnector.ClientID
        oldSupplierID = contextConnector.WrappedConnector.SupplierID
    end if
end function

'a connector has changed, we need to update the auto-diagrams
function EA_OnNotifyContextItemModified(GUID, ot)
    'we only do something when the context item is a connector
    if ot = otConnector then
        dim model
        'get the model
        set model = getEAAddingFrameworkModel()
        'get the connector
        dim changedConnector
        set changedConnector = model.getRelationByGUID(GUID)
        'check if we are talking about the same connector
        if changedConnector.WrappedConnector.ConnectorID = contextConnectorID then
            dim supplier
            dim client
            'check the client side
            if changedConnector.WrappedConnector.ClientID <>  oldClientID then
                'get supplier
                set supplier = model.getElementWrapperByID(changedConnector.WrappedConnector.SupplierID)
                'remove old client from supplier and vice versa
                set client = model.getElementWrapperByID(oldClientID)
                if not client is nothing then
                    removeRelatedElemenFromAutoDiagram supplier,client, model
                    removeRelatedElemenFromAutoDiagram client, supplier, model
                end if
                'add new client
                set client = model.getElementWrapperByID(changedConnector.WrappedConnector.ClientID)
                addRelatedElementoAutoDiagram supplier,client, model
            end if
            'check the supplier side
            if changedConnector.WrappedConnector.SupplierID <> oldSupplierID then
                'get client
                set client = model.getElementWrapperByID(changedConnector.WrappedConnector.ClientID)
                'remove old supplier from client and vice versa
                set supplier = model.getElementWrapperByID(oldSupplierID)
                if not supplier is nothing then
                    removeRelatedElemenFromAutoDiagram client,supplier, model
                    removeRelatedElemenFromAutoDiagram supplier,client, model
                end if
                'add new supplier
                set supplier = model.getElementWrapperByID(changedConnector.WrappedConnector.SupplierID)
                addRelatedElementoAutoDiagram client,supplier, model
            end if
        end if
    end if
end function

Prevent accidental deletes with EA-Matic

With EA-Matic you can develop add-ins for Enterprise Architect using the built-in scripting feature of EA.

This example shows how you can prevent deleting elements that are still being used as a type in either an attribute or parameter.

The script uses the EA_OnPreDeleteElement event to first check whether the element is not still used.

If an attribute using this element as type is found then the element is not deleted and the user is informed with a messagebox.

EA-Matic cannot delete attribute

If it isn’t used as attribute but is used as a type in a parameter or the return type of an operation you get the following messagebox:

EA-Matic cannot delete parameter

Usage of Enterprise Architect Add-in Framework

This script also illustrates how you can use the Enterprise Architect Add-in Framework within your script.

This extensive open source framework is the basis for the add-ins EA-Matic and EA Navigator, and offers a much more functional interface to the model then the standard EA API.

This code inializes the model object with the current Repository

set model = CreateObject("TSF.UmlToolingFramework.Wrappers.EA.Model")
model.initialize(Repository)

It is then further used to get the element based on the ElementID from the Info object

set element = model.getElementWrapperByID(elementID)

The element then gives us access the attributes and parameters using this element by means of

set usingAttributes =  model.toArrayList(element.getUsingAttributes())
set usingParameters = model.toArrayList(element.getUsingParameters())

Note that we need to use model.toArrayList to convert the C# HashSet that cannot be used by VBScript to an ArrayList that can be used.

The code

Download the complete script: EA-Matic Prevent accidental deletes

option explicit
'EA-Matic

function EA_OnPreDeleteElement(Info)
     'Start by setting false
     EA_OnPreDeleteElement = false
     dim usage
     'Initialize the EAAddinFramework model
     dim model
     set model = CreateObject("TSF.UmlToolingFramework.Wrappers.EA.Model")
     model.initialize(Repository)
     'get the elementID from Info
     dim elementID
     elementID = Info.Get("ElementID")
     'get the element being deleted
     dim element
     set element = model.getElementWrapperByID(elementID)
     'Manual override is triggered by the name. If it starts with DELETED_ then the element may be deleted.
     if Left(element.name,LEN("DELETED_")) = "DELETED_" then
        'OK the element may be deleted
        EA_OnPreDeleteElement = true
     else
        dim usingAttributes
        set usingAttributes =  model.toArrayList(element.getUsingAttributes())
        'Check if the element is used as type in attributes
        if usingAttributes.Count = 0 then
            'Check if the element is used as type in a parameter
            dim usingParameters
            set usingParameters = model.toArrayList(element.getUsingParameters())
            if usingParameters.Count = 0 then
                'OK, no attributes or parameters use this element, it may be deleted
            EA_OnPreDeleteElement = true
            else
                usage = "parameter(s)"
            end if
        else
            usage = "attribute(s)"
        end if
     end if
     if EA_OnPredeleteElement = false then
          'NO the element cannot be deleted
          MsgBox "I'm sorry Dave, I'm afraid I can't do that" & vbNewLine _
          & element.name & " is used as type in " & usage , vbExclamation, "Cannot delete element"
     end if
end function

Using the add-in menu with EA-Matic

With EA-Matic you can develop add-ins for Enterprise Architect using the built-in scripting feature of EA.

This example shows how to use the add-in menu to add your own menu and menu option, and react to the user clicking such a menu option.

With this script you can use menu options as a regular add-in would

EA-Matic Menu in Action

Download the complete script: EA-Matic MenuHandling

Step 1: defining the menu

'EA-Matic
'Tell EA what the menu options should be
function EA_GetMenuItems(MenuLocation, MenuName)
	if MenuName = "" then
		'Menu Header
		EA_GetMenuItems = "-&MyAddinMenu"
	else
		if MenuName = "-&MyAddinMenu" then
			'Menu items
			Dim menuItems(1)
			 menuItems(0) = "TreeViewMenu"
			 menuItems(1) = "DiagramMenu"
			 EA_GetMenuItems = menuItems
		 end if
	end if
end function

Step 2: define the menu state

'Define the state of the menu options
function EA_GetMenuState(MenuLocation, MenuName, ItemName, IsEnabled, IsChecked)
	if MenuName = "-&MyAddinMenu" then
		Select Case ItemName
			case "TreeViewMenu"
				if MenuLocation = "TreeView" then
					IsEnabled = true
				else
					IsEnabled = false
				end if
			case "DiagramMenu"
				if MenuLocation = "Diagram" then
					IsEnabled = true
				else
					IsEnabled = false
				end if
		end select
	end if
	'to return out parameter values we should return an array with all parameters
	EA_GetMenuState = Array(MenuLocation, MenuName, ItemName, IsEnabled, IsChecked)
end function

Step 3: React to user clicking a menu option

'react to user clicking a menu option
function EA_MenuClick(MenuLocation, MenuName, ItemName)
	 	if MenuName = "-&MyAddinMenu" then
		Select Case ItemName
			case "TreeViewMenu"
				Dim Package
				Set Package = Repository.GetTreeSelectedPackage()
				MsgBox ("Current Package is: " & Package.Name)
			case "DiagramMenu"
				Dim Diagram
				Set Diagram = Repository.GetCurrentDiagram()
				MsgBox("Current Diagram is: " & Diagram.Name)
		end select
	end if
end function