EA Mapping tool the next steps

Since my last post EA mapping EA Mapping Tool => Who wants it? there has been quite some response.

There are at least three parties that are seriously committed in joining, and a number of parties that are interested but for any number of reasons aren’t able to commit to anything just yet.

So the next logical step in the process is start building a requirements list. I’ve started with list of basis requirements listed below. Every sponsor now will now be given the opportunity to review those requirements and add some of their own.

If your organisation is still in doubt maybe this list of requirements will help you to get them convinced of the usefulness of this add-in. There is still time to join in as a sponsor.

The requirements

I’ve divided the requirements into three categories: base, GUI and exchange. Below there’s a detailed list of these requirements.

Base Requirements

RQ001 The tool should be able to map an attribute to one or more attributes or associations
RQ002 The tool should be able to map one association to one or more associations or Attributes
RQ003 For each mapping between (possibly) multiple attributes and association the tool should be able to document a mapping logic
RQ004 Mapping logic should be re-usable between mappings
RQ005 The tool should be able to use either RefGUID tagged values or “link to element features” or a combination thereof to make the link between attributes.
RQ006 A mapping should be able to depend on the “path” of the attribute. Contract.Seller.Name may be mapped to another attribute then Contract.Buyer.Name, although both are Party.Name.
RQ007 All mapping informatio is stored inside an Enterprise Architect model

GUI Requirements

RQ010 The tool should represent one model at one side and the other model at the other side,showing the links in a visual obvious way
RQ011 If a mapping logic is applied this should be visually apparent.
RQ012 The mapping logic should be easily accessible from the GUI
RQ013 It should be possible create or change mappings in a very intuitive way, such as drag and drop.
RQ014 The tool should be able to filter only the mapped items from both data model in order to not clutter the user interface with unused items.
RQ015 The GUI should be entirely in Enteprise Architect and the main mapping window should use the “diagram” space of EA.

Mockup

Visually the GUI should look somewhat like this

mapping tool

Exchange requirements

RQ020 The tool should be able to import mappings from a CVS file
RQ021 The tool should be able to export mappings to a CSV file

Download

Of course all of these requirements where modeled in EA and you can download the initial requirement model below.

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

Join us

If you feel these requirements are a good fit to your requirements for a mapping tools send me a message at geert@bellekens.com and you may still join this community driven initiative.

EA Mapping tool => Who wants it?

I’m thinking about developing a mapping tool add-in for EA.
The idea is that you can manage a mapping between two types of (data) models. For example between your logical data model and your message model, or between your logical data model and your database model etc…

I already have a number of add-ins and scripts that somehow create traceability between models on element, attribute and association level, but I don’t have a tool yet to visualize and manage those traces.

To give you an idea the GUI of the thing could look similar to this:

mapping tool
Functions could include
– ability to add notes to each mapping
– ability to export the mapping to excel
– drag-n-drop to create mappings
– …
Now it seems to me like there could be many parties interested in such a tool.
I would like to develop this as a free and open source add-in (as I do for all my add-ins) but I’m still looking for sponsors.
My initial rough estimate for the development of such a tool is 15 man days.

I already have two parties that might be interested, but the more sponsors I can find the lower the price would get for each individual party.

So if you are interested in such a tool please let me know: geert@bellekens.com and we might be able to work something out.

Presenting the new open source Enterprise Architect Shapescript Library

The new open source Enterprise Architect Shapescript Library in Github is an initiative to share shapescripts for Enterprise Architect within the user community.

Shapescripts in Enterprise Architect are used to define the presentation of elements on a diagram. They are mostly used when defining a UML profile in Enterprise Architect that extends UML or another modelling language with project specific elements and properties.

Shapescript examples

When developing UML profiles these shapescripts are stored in the _image attribute on the stereotype element, an they can be easily edited using the profile helper.

Shapescript editor

After a while however you’ll have a bunch of shapescripts scattered over different profiles, and projects, and it becomes hard to manage, or do any kind of version control. Finding a specific shapescript where you used a particular feature is next to impossible if you don’t remember exactly which stereotype this shapescript belonged to.

Exporting shapescripts

With the shapescript editor you can export each shapescript individually, but once you have more then a couple of shapescripts that quickly becomes a drag to save your shapescripts to files.

So I wrote a script called ExportAllShapeScripts that searches a repository for all stereotypes that have a shapescript, and exports all of these shapescripts in one go.

This script is part of the Enterprise-Architect-VBScript-Library. Read the article How to use the Enterprise Architect VBScript Library for instructions on downloading and using the Library in Enterprise Architect.

Running this script will save each shapescript as a textfile with extension .shapeScript, grouped in packages per profile.

Shapescripts saved

The code explained

The export script first uses an SQL query to find all shapescripts. Technically the shapescripts are stored in the default property of the attribute with the name _image. The script is stored as an xml tag Image in a zipped and base64 encoded format. A typical shapescript might look like this in the database:

<Image type="EAShapeScript 1.0" xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="bin.base64">
UEsDBBQAAAAIABpRIkdk2qflMQIAADIHAAAHABEAc3RyLmRhdFVUDQAHncrmVZ3K5lWdyuZV
jVXJTsMwFJxrkfiHCC5FqsRSaIsQhx44sohKcC5toUihqUjYxb8z8xw3jksLsmIn9sx43vOS
HFMMMccECZ749ogZNrGBL6sbSNn3gQwvKFg+DHmKLfZPcM+eZzIeqFGw76Tk3LPvneNj9JEb
Y8TxayoVHMmML+YLRyqWq8emmxP3ybcmuthDC222OyXS4fpEjjGgxh1rH0OTejP2PbF3whly
frewTw2puDbUcVp5kIM62437XHiPDeyy3FnkCXEuviG5D5YXjxrwvcAV6xluiR7za0qP7YUH
oa6X+E1zWzmOncdObsytcjuiSkpPqa3jZBFBgzFleDU/GcfloW1zhJqeJYxHLM8cK3VY1is5
xLJSGINW+5lo33dGDanMGYMyInYLB+hZLWddtqvUMpv9N8dNHJKnHdVZ4/hoMdtfmNhFPNdv
TmMdZSfGhNEUeAsy02eeRnYyuuZR7GPmp1U66lmODlmkKkwca91jj2zh1+XD6cQe6xjnIcbU
45gaI2Gb2c3xaSulvR/uWs9ZdX7210SjDCgPq336MxWjQqe53YXagQnPeWa3WXWWtCMLu+WE
GHE8tXiqc1tXXr4hHNKtkR7tgb8YB+U6Ca8V/R+rY7vdsTqm8B/Wsek7VpWvOi9ufXHf39bq
qd70KLvujteaPML9EbQnMvYldKF9MLQbODVGeDvXR76sTsr1uCz/RdLVf2Bgd391yyUlem44
7bq0RG7jgqfqnHfONr8qbN25f34AUEsBAhcLFAAAAAgAGlEiR2Tap+UxAgAAMgcAAAcACQAA
AAAAAAAAAACAAAAAAHN0ci5kYXRVVAUAB53K5lVQSwUGAAAAAAEAAQA+AAAAZwIAAAAA
</Image>

So the SQL selects all attributes owned by a element with stereotype «stereotype» with the name _image that have the tag <Image>  in their Default field.

'get all attributes with name _image that have shapescript in the default field and a parent with stereotype «stereotype»
dim sqlGetShapescriptAttributes
sqlGetShapescriptAttributes = "select a.ID from (t_attribute a " & _
							  " inner join t_object o on (o.Object_ID = a.Object_ID " & _
							  "						and o.Stereotype = 'stereotype')) " & _
							  " where a.Name = '_image' " & _
							  " and a.[Default] like '<Image type=""EAShapeScript" & getWC & "'"
dim shapeScriptAttributes
set shapeScriptAttributes = getAttributesByQuery(sqlGetShapescriptAttributes)

Then we loop the schapescript attributes, decode the shapescript and save it as an individual text file.

'loop the shape script attributes
for each shapeScriptAttribute in shapeScriptAttributes
	'get the stereotype
	dim stereotype as EA.Element
	set stereotype = Repository.GetElementByID(shapeScriptAttribute.ParentID)
	dim profile as EA.Package
	set profile = findProfilePackage(stereotype)
	'load the resultset in the xml document
	dim shapeScript
	shapeScript = decodeBase64zippedXML(shapeScriptAttribute.Default,"Image")
	if len(shapeScript) > 0 then
		dim scriptFile
		set scriptFile = New TextFile
		scriptfile.Contents = shapeScript
		'save the script
		scriptFile.FullPath = selectedFolder.FullPath & "\" & profile.Name & "\" & stereotype.Name & ".shapeScript"
		scriptFile.Save
		'debug info
		Session.Output "saving script: " & scriptFile.FullPath
	end if
next

The real difficulty int his script was to decode the shapescript, which is hidden in the method decodeBase64zippedXML  defined in the XML utility script. It first base64 decodes the contents of the tag, then saves it as a zip file, unzips the zip file, gets the text file in the zip file, and returns the contents of the text file. Then it deletes the temporary zip file and folder it was extracted to.

 Contributing

If you have shapescripts of your own that you wish to share you can send a github pull request, or an email to geert@bellekens.com.

Any bright ideas on how to document each of the shapescripts with an image of shape are welcome too.

 

How to use the Enterprise Architect VBScript Library

The Enterprise Architect VBScript Library is an open source library of VBScripts written to be used in  Enterprise Architect.

This article explains how to download, import and use the library in Enterprise Architect.

Initial Import

Initial scripts

The Enterprise Architect VBScript Library contains some scripts to load and save scripts from/to your file system, but before we can use those we have to import an initial set of scripts to get started.

After downloading the file select menu option Project|Data Management|Import Reference Data and choose the downloaded file. Make sure to select Automation scripts and click Import.

This will import the minimum set of scripts required to load other scripts from the file system

 

 

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

Download library from GitHub

From the Enterprise Architect VBScript Library project page on GitHub you can either choose to download the library as a zip file, or download the repository in GitHub desktop.

VBScript library github

Load library into Enterprise Architect

Select scripts folderOne of the scripts in the initial set is the script LoadScripts in the group Script Management. If you execute this script you can choose the library folder from the library downloaded from GitHub.

The script will then scan the entire folder tree and it will load any .vbs file it can find.

For starters it might be interesting to only load the Frameworks scripts as they have the most chance of being useful to anyone.

If the LoadScripts script finds a script that already exists in Enterprise Architect it will ask to overwrite the existing scripts.

In order to know in which group the script belongs it will look for a the group indicator in the script.

'[group=Script Management]

This will tell the script that this script should go in the group Script Management. If the script indicator is absent it will assume the group name is the name of the folder where it was found.

After loading the scripts into EA make sure to press the refresh button to make the scripts appear in the GUI.

Refresh script tree

Saving all your scripts

In the script management group there also a script to save all your scripts to the file system.

In order to control where the script should go you can add the path indicator to the script like this

'[path=\Framework\Tools\Script Management]

The path indicator will control where to place the script relative to the chosen folder.

If the path indicator is absent the name of the script group will be used as name of the folder.

Being able to save and load the scripts from/to the file system now allows us to use version control on these scripts.

The library structure

The library is structured in two main parts.

  • Projects
    Contains an assortment of various scripts written for various projects.
  • Framework
    Contains the framework scripts that are meant to be used by other scripts

    • Utils
      Contains helper scripts and classes such as TextFile, SQL, XML etc..
    • Wrappers
      Contains the wrapper classes for EA elements such as TaggedValue, Script, ScriptGroup

The scripts in the projects folder can be used for inspiration, but it is mainly the scripts int he Framework part that are useful when writing scripts.

Using the library

The first thing you need to do when you want to use any of the framework classes is to include the framework in your script

!INC Wrappers.Include

This will include the “Include” script that takes care of including all the other scripts of the framework, including those of the Utils folder.

Then you can use the classes defined in the library. For example, if you want to have the user select a directory you can use following code

'get the folder from the user
dim folder
set folder = new FileSystemFolder
set folder = folder.getUserSelectedFolder("")

The classes in the library contain both properties as operations. You can use the properties as you would expect from “real” classes.

'show messagebox with the name of each subfolder
dim subfolders, subfolder
for each subfolder in folder.SubFolders
    msgbox "subfolder name: " & subfolder.Name
next

Contributing

Have you written any VBScripts for Enterprise Architect you would like to share? If you would like to contribute to the Enterprise Architect VBScript library you can email me at geert@bellekens.com

Navigate from and to Composite Diagrams with EA Navigator

With EA Navigator version 2.4.12.0 you can now navigate from and to composite diagrams of an element.

Recently I’ve been working a lot with BPMN models. BPMN models make extensive use of the composite diagram mechanism in EA to drill-down on the processes and sub-processes.

In a classical UML model we usually place the composite diagram under the element, in which case finding the composite diagram, or finding the element(s) that have a diagram as composite diagram, is trivial. They are at the same location in the project browser.BPMN_Library_Model

In most BPMN models however there’s separation between model and library where the library contains the elements, and the model the diagrams. In such a setup it suddenly becomes much harder to find the composite diagram (find in all diagram, then double click the element) and almost impossible to find the element(s) that have a specific diagram as composite diagram.

 

In other words drilling down is relatively easy, but drilling up becomes nearly impossible.

Navigate_Composite_diagramThis problem is now solved by the EA Navigator.  From the Business Process I can now easily navigate to the composite diagram, but more importantly, I can navigate back up to any elements that have this diagram set as their composite diagram.

 

More Information

More information about the EA Navigator add-in for Enterprise Architect can be found here:

Agenda for the Brussels EA User Group Event 18th September 2015

The agenda for the EA User Group event in Brussels on the 18th September 2015 has been published.

It promises a day full of interesting topics for all EA users.

9:00 Registration,
Coffee
9:30 Welcome
9:40 Modelling Microservices in EA
Sander Hoogendoorn
10:25 Coffee break
10:55 Track 1: EA @ Track
2: Architecture and Products
Track
3: Workshops and How-to’s
10:55 EA@ACV-CSC
Geert Bellekens
Making Agile Work in Non-Agile Organizations: Collaborating with EA
Jackie and Ian Mitchell
Hidden Gems in EA
Peter Doomen
11:40 Switch
11:55 EA@ [TBD] Smartfacts
Christoph Bergner
BPMN from Use Cases – Development of an EA Extension
Phil Chudley
12:40 Lunch break
14:10 EA@Atrias
Tom Geerts
Enterprise Architect for an Enterprise Architecture
Bert Dingemans
Manage complexity with Model Based Systems Engineering
Rob de Jong
14:55 Switch
15:10 EA@ [TBD] Modelling the DNA of your company
Christian Gijsels
Implementing company standards in Enterprise Architect
Dennis Geluk
15:55 Coffee break
16:25 EA@ [TBD] Aligning international standards TOGAF® ADM and ArchiMate® in Sparx EA: A Case Study from the Automotive Industry
Tobias Ivarsson
Extending EA with Scripting or Add-ins? Get the best of both worlds with EA-Matic
Geert Bellekens
17:10 What’s new in EA v12
Daniel Siegl
17:55 Thank you
18:05 Finish break
18:30 Finish

Make sure to book your tickets in time

Tickets for EA User Group event Brussels September 2015 are available

On the 18th September we are organizing the first Belgian EA User group event in Brussels.

This event, that will take a whole day, will feature three English speaking tracks with presentations from many Enterprise Architect and IT industry experts such as Sander Hoogendoorn, Phil Chudley, Peter Doomen and myself of course.

Meet your fellow EA users, share your EA user experiences and learn new or better ways to use Enterprise Architect.

EA v12

 

For only € 75 you get a whole day of the best the Enterprise Architect community has to offer, including lunch and refreshments. You also get access to all the presentations of this event, and those of the Paris event of the 17th September.

Buy your tickets now from the EA User Group website

This EA User Group event is hosted by

and sponsored by

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

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