Blog

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.

 

The Bellekens EA Toolpack combines EA-Matic, EA Navigator and ECDM Message Composer

The three free and open source add-ins for Enterprise Architect : EA Navigator, EA-Matic and ECDM Message Composer have been combined in a convenient package called Bellekens EA Toolpack.

Bellekens EA Toolpack

Downloading any of the add-ins from the site now gets you an installer for the Bellekens EA Toolpack. With this installer you can install all three add-ins, or only the ones you require.

Bellekens EA Toolpack installer

The main reason for this change is the shared EA Addin Framework. Because all three add-ins use the same framework, there was a risk at version conflicts when two add-ins were installed on the same machine because each add-in installed its own version of the framework. With the new installer the framework gets installed only once thus avoiding any possible version conflicts which are known as the DLL Hell.

An added benefit of the Bellekens EA Toolpack is the fact that is now installs the applications for all users on the machine, making it easier to distribute in a corporate organisation.

Because the Bellekens EA Toolpack is a different application from Windows point of view you’ll have to uninstall any versions of EA Navigator, EA-Matic or ECDM Message Composer you might have installed previously. Uninstalling is the easiest from the add/remove programs feature in the control panel.

See Also

Download

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

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

Automatically trace elements to domain model in Enterprise Architect

This script will search the text in the comments, scenario’s and linked document of the selected elements for terms in the domain model. If it finds a match it will create a trace from your selected element to the element in the domain model.

The idea is that when we write descriptions and scenario’s we often use terms already defined in the domain model, so it would be good if we had a real trace from the element in the domain model and the element using it in its description.

Link to domain model example

But it is hard to keep the descriptions and scenario’s in sync with the traces to the domain model. This script does the hard part for us and keeps those traces in sync. All you need to do is type your description and run the script. The script will find the referenced elements in the domain model and create a trace link to it. It will also remove any automatic traces that are not valid anymore.

Next to the NameNotes, and the Linked Document, this script will also search the scenarios, both the Scenario Description, as the structured Scenario Steps. In case of a Scenario Step the script will insert to the name of the domain model element in the Uses column.Link to domain model scenario

Note that the script is case sensitive. That is the reason why RoomType does not appear in the uses column. Enterprise Architect itself isn’t that picky, and it underlines Roomtype anyway because it finds a relationship to RoomType.

In order to execute the script you have to select either one or more elements in the project browser, or select a package in the project browser.Link to domain model menu option

Then select Script|Link To Domain Model

Free download

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

The script

In the first part we ask the user to select the domain model package.
If you want to use this script on a particular model you may want to replace that by a hard coding the Domain models GUID into the script.
Since this script may run for a while on a large model, we also create an output tab so we can inform the user of our progress.

sub main
	'let the user select the domain model package
	dim response
	response = Msgbox ("Please select the domain model package", vbOKCancel+vbQuestion, "Selecte domain model")
	if response = vbOK then
		
		dim domainModelPackage as EA.Package
		set domainModelPackage = selectPackage()
		if not domainModelPackage is nothing then
			Repository.CreateOutputTab "Link to Domain Model"
			Repository.ClearOutput "Link to Domain Model"
			Repository.EnsureOutputVisible "Link to Domain Model"
			'link selected elements to the domain model elements
			linkSelectionToDomainModel(domainModelPackage)
			'tell the user we are done
			Repository.WriteOutput "Link to Domain Model", "Finished!", 0
		end if
	end if
end sub

Next we create a dictionary of all classes in domain model package and all of the sub-packages recursively.

	'first get the pattern from all the classes in the domain model
	dim dictionary
	Set dictionary = CreateObject("Scripting.Dictionary")
	
	'create domain model dictionary
	'tell the user what we are doing
	Repository.WriteOutput "Link to Domain Model", "Creating domain model dictionary", 0
	addToClassDictionary domainModelPackage.PackageGUID, dictionary

Based on this dictionary we can create one large Regular Expression that will tell us which of the terms in the dictionary are used in a certain text.

	' and prepare the regex object
	dim pattern
	'create the pattern based on the names in the dictionary
	pattern = createRegexPattern(dictionary)
	Dim regExp  
	Set regExp = CreateObject("VBScript.RegExp")
	regExp.Global = True   
	regExp.IgnoreCase = False
	regExp.Pattern = pattern

Then we check what is selected in the project browser. If the user has selected one or more elements we start process those elements. If a package is selected then we process the elements in the selected package.

	' Get the type of element selected in the Project Browser
	dim treeSelectedType
	treeSelectedType = Repository.GetTreeSelectedItemType()
	' Either process the selected Element, or process all elements in the selected package
	select case treeSelectedType
		case otElement
			' Code for when an element is selected
			dim selectedElements as EA.Collection
			set selectedElements = Repository.GetTreeSelectedElements()
			'link the selected elements with the 
			linkDomainClassesWithElements dictionary,regExp,selectedElements 
		case otPackage
			' Code for when a package is selected
			dim selectedPackage as EA.Package
			set selectedpackage = Repository.GetTreeSelectedObject()
			'link use domain classes with the elements in the selected package
			linkDomainClassesWithElementsInPackage dictionary, regExp,selectedPackage
		case else
			' Error message
			Session.Prompt "You have to select Elements or a Package", promptOK
	end select

Here’s the complete code for the script.

option explicit

!INC Local Scripts.EAConstants-VBScript

'
' Script Name: Link to Domain Model
' Author: Geert Bellekens
' Purpose: Link elements with classes in the domain model based on their name.
' Date: 15/11/2015
'


sub main
	'let the user select the domain model package
	dim response
	response = Msgbox ("Please select the domain model package", vbOKCancel+vbQuestion, "Selecte domain model")
	if response = vbOK then
		
		dim domainModelPackage as EA.Package
		set domainModelPackage = selectPackage()
		if not domainModelPackage is nothing then
			Repository.CreateOutputTab "Link to Domain Model"
			Repository.ClearOutput "Link to Domain Model"
			Repository.EnsureOutputVisible "Link to Domain Model"
			'link selected elements to the domain model elements
			linkSelectionToDomainModel(domainModelPackage)
			'tell the user we are done
			Repository.WriteOutput "Link to Domain Model", "Finished!", 0
		end if
	end if
end sub

'this is the actual call to the main function
main

function linkSelectionToDomainModel(domainModelPackage)
	'first get the pattern from all the classes in the domain model
	dim dictionary
	Set dictionary = CreateObject("Scripting.Dictionary")
	
	'create domain model dictionary
	'tell the user what we are doing
	Repository.WriteOutput "Link to Domain Model", "Creating domain model dictionary", 0
	addToClassDictionary domainModelPackage.PackageGUID, dictionary
	
	'tell the user what we are doing
	Repository.WriteOutput "Link to Domain Model", "Interpreting dictionary", 0
	' and prepare the regex object
	dim pattern
	'create the pattern based on the names in the dictionary
	pattern = createRegexPattern(dictionary)
	Dim regExp  
	Set regExp = CreateObject("VBScript.RegExp")
	regExp.Global = True   
	regExp.IgnoreCase = False
	regExp.Pattern = pattern
	
	' Get the type of element selected in the Project Browser
	dim treeSelectedType
	treeSelectedType = Repository.GetTreeSelectedItemType()
	' Either process the selected Element, or process all elements in the selected package
	select case treeSelectedType
		case otElement
			' Code for when an element is selected
			dim selectedElements as EA.Collection
			set selectedElements = Repository.GetTreeSelectedElements()
			'link the selected elements with the 
			linkDomainClassesWithElements dictionary,regExp,selectedElements 
		case otPackage
			' Code for when a package is selected
			dim selectedPackage as EA.Package
			set selectedpackage = Repository.GetTreeSelectedObject()
			'link use domain classes with the elements in the selected package
			linkDomainClassesWithElementsInPackage dictionary, regExp,selectedPackage
		case else
			' Error message
			Session.Prompt "You have to select Elements or a Package", promptOK
	end select
end function

'this function will get all elements in the given package and subpackages recursively and link them to the domain classes
function linkDomainClassesWithElementsInPackage(dictionary,regExp,selectedPackage)
	dim packageList 
	set packageList = getPackageTree(selectedPackage)
	dim packageIDString
	packageIDString = makePackageIDString(packageList)
	dim getElementsSQL
	getElementsSQL = "select o.Object_ID from t_object o where o.Package_ID in (" &amp; packageIDString &amp; ")"
	dim usecases
	set usecases = getElementsFromQuery(getElementsSQL)
	linkDomainClassesWithElements dictionary,regExp,usecases
end function


function linkDomainClassesWithElements(dictionary,regExp,elements)
	dim element as EA.Element
	'loop de elements
	for each element in elements
		'tell the user what we are doing
		Repository.WriteOutput "Link to Domain Model", "Linking element: " &amp; element.Name, 0
		'first remove all automatic traces
		removeAllAutomaticTraces element
		'match based on notes and linked document
		dim elementText
		'get full text (name +notes + linked document + scenario names + scenario notes)
		elementText = element.Name
		elementText = elementText &amp; vbNewLine &amp; Repository.GetFormatFromField("TXT",element.Notes)
		elementText = elementText &amp; vbNewLine &amp; getLinkedDocumentContent(element, "TXT")
		elementText = elementText &amp; vbNewLine &amp; getTextFromScenarios(element)
		dim matches
		set matches = regExp.Execute(elementText)
		'for each match create a «trace» link with the element
		linkMatchesWithelement matches, element, dictionary
		'link based on text in scenariosteps
		dim scenario as EA.Scenario
		'get all dependencies left
		dim dependencies
		set dependencies = getDependencies(element)
		'loop scenarios
		for each scenario in element.Scenarios
			dim scenarioStep as EA.ScenarioStep
			for each scenarioStep in scenario.Steps
				'first remove any additional terms in the uses field
				scenarioStep.Uses = removeAddionalUses(dependencies, scenarioStep.Uses)
				set matches = regExp.Execute(scenarioStep.Name)
				dim classesToMatch 
				set classesToMatch = getClassesToMatchDictionary(matches, dictionary)
				dim classToMatch as EA.Element
				for each classToMatch in classesToMatch.Items
					if not instr(scenarioStep.Uses,classToMatch.Name) &gt; 0 then
						scenarioStep.Uses = scenarioStep.Uses &amp; " " &amp; classToMatch.Name
					end if
					'create the dependency between the use case and the domain model class
					linkElementsWithAutomaticTrace element, classToMatch
				next
				'save scenario step
				scenarioStep.Update
				scenario.Update
			next
		next
	next
end function

function linkMatchesWithelement(matches, element, dictionary)
	dim classesToMatch
	'get the classes to match
	Set classesToMatch = getClassesToMatchDictionary(matches,dictionary)
	dim classToMatch as EA.Element
	'actually link the classes
	for each classToMatch in classesToMatch.Items
		linkElementsWithAutomaticTrace element, classToMatch
	next
end function


'get the text from the scenarios name and notes
function getTextFromScenarios(element)
	dim scenario as EA.Scenario
	dim scenarioText
	scenarioText = "" 
	for each scenario in element.Scenarios
		scenarioText = scenarioText &amp; vbNewLine &amp; scenario.Name
		scenarioText = scenarioText &amp; vbNewLine &amp; Repository.GetFormatFromField("TXT",scenario.Notes)
	next
	getTextFromScenarios = scenarioText
end function

function removeAddionalUses(dependencies, uses)
	dim dependency
	dim filteredUses
	filteredUses = ""
	if len(uses) &gt; 0 then
		for each dependency in dependencies.Keys
			if Instr(uses,dependency) &gt; 0 then
				if len(filteredUses) &gt; 0 then
					filteredUses = filteredUses &amp; " " &amp; dependency
				else
					filteredUses = dependency
				end if
			end if
		next
	end if
	removeAddionalUses = filteredUses
end function

'returns a dictionary of elements with the name as key and the element as value.
function getDependencies(element)
	dim getDependencySQL
	getDependencySQL =  "select dep.Object_ID from ( t_object dep " &amp; _
						" inner join t_connector con on con.End_Object_ID = dep.Object_ID)   " &amp; _
						" where con.Connector_Type = 'Dependency'  " &amp; _
						" and con.Start_Object_ID = " &amp; element.ElementID   
	set getDependencies = getElementDictionaryFromQuery(getDependencySQL)
end function

function removeAllAutomaticTraces(element)
		dim i
		dim connector as EA.Connector
		'remove all the traces to domain model classes
		for i = element.Connectors.Count -1 to 0 step -1
			set connector = element.Connectors.GetAt(i)
			if connector.Alias = "automatic" and connector.Stereotype = "trace" then
				element.Connectors.DeleteAt i,false 
			end if
		next
end function

function getClassesToMatchDictionary(matches, allClassesDictionary)
	dim match
	dim classesToMatch
	dim className
	Set classesToMatch = CreateObject("Scripting.Dictionary")
	'create list of elements to link
	For each match in matches
		if not allClassesDictionary.Exists(match.Value) then
			'strip the last 's'
			className = left(match.Value, len(match.Value) -1)
		else
			className = match.Value
		end if
		if not classesToMatch.Exists(className) then
			classesToMatch.Add className, allClassesDictionary(className)
		end if
	next
	set getClassesToMatchDictionary = classesToMatch
end function

'Create a «trace» relation between source and target with "automatic" as alias
function linkElementsWithAutomaticTrace(sourceElement, targetElement)
	dim linkExists 
	linkExists = false
	'first make sure there isn't already a trace relation between the two
	dim existingConnector as EA.Connector
	'make sure we are using the up-to-date connectors collection
	sourceElement.Connectors.Refresh
	for each existingConnector in sourceElement.Connectors
		if existingConnector.SupplierID = targetElement.ElementID _
		   and existingConnector.Stereotype = "trace" then
		   linkExists = true
		   exit for
		end if
	next
	if not linkExists then
		'tell the user what we are doing
		Repository.WriteOutput "Link to Domain Model", "Adding trace between " &amp;sourceElement.Name &amp; " and " &amp; targetElement.Name, 0
		dim trace as EA.Connector
		set trace = sourceElement.Connectors.AddNew("","trace")
		trace.Alias = "automatic"
		trace.SupplierID = targetElement.ElementID
		trace.Update
	end if
end function

function addToClassDictionary(PackageGUID, dictionary)
	dim package as EA.Package
	set package = Repository.GetPackageByGuid(PackageGUID)
	
	'get the classes in the dictionary (recursively
	addClassesToDictionary package, dictionary
end function

function addClassesToDictionary(package, dictionary)
	dim classElement as EA.Element
	dim subpackage as EA.Package
	'process owned elements
	for each classElement in package.Elements
		if classElement.Type = "Class" AND len(classElement.Name) &gt; 0 AND not dictionary.Exists(classElement.Name) then
			dictionary.Add classElement.Name,  classElement
		end if
	next
	'process subpackages
	for each subpackage in package.Packages
		addClassesToDictionary subpackage, dictionary
	next
end function


'Create a reges pattern like this "\b(name1|name2|name3)\b" based on the 
function createRegexPattern(dictionary)
	Dim patternString
	dim className
	'add begin
	patternString = "\b("
	dim addPipe
	addPipe = FALSE
	for each className in dictionary.Keys
			if addPipe then
				patternString = patternString &amp; "|"
			else
				addPipe = True
			end if
			patternString = patternString &amp; className
	next
	'add end
	patternString = patternString &amp; ")s?\b"
	'return pattern
	createRegexPattern = patternString
end function

'returns an ArrayList of the given package and all its subpackages recursively
function getPackageTree(package)
	dim packageList
	set packageList = CreateObject("System.Collections.ArrayList")
	addPackagesToList package, packageList
	set getPackageTree = packageList
end function

'make an id string out of the package ID of the given packages
function makePackageIDString(packages)
	dim package as EA.Package
	dim idString
	idString = ""
	dim addComma 
	addComma = false
	for each package in packages
		if addComma then
			idString = idString &amp; ","
		else
			addComma = true
		end if
		idString = idString &amp; package.PackageID
	next 
	'if there are no packages then we return "0"
	if packages.Count = 0 then
		idString = "0"
	end if
	'return idString
	makePackageIDString = idString
end function

'returns an ArrayList with the elements accordin tot he ObjectID's in the given query
function getElementsFromQuery(sqlQuery)
	dim elements 
	set elements = Repository.GetElementSet(sqlQuery,2)
	dim result
	set result = CreateObject("System.Collections.ArrayList")
	dim element
	for each element in elements
		result.Add Element
	next
	set getElementsFromQuery = result
end function

'returns a dictionary of all elements in the query with their name as key, and the element as value.
'for elements with the same name only one will be returned
function getElementDictionaryFromQuery(sqlQuery)
	dim elements 
	set elements = Repository.GetElementSet(sqlQuery,2)
	dim result
	set result = CreateObject("Scripting.Dictionary")
	dim element
	for each element in elements
		if not result.Exists(element.Name) then
		result.Add element.Name, element
		end if
	next
	set getElementDictionaryFromQuery = result
end function

'gets the content of the linked document in the given format (TXT, RTF or EA)
function getLinkedDocumentContent(element, format)
	dim linkedDocumentRTF
	dim linkedDocumentEA
	dim linkedDocumentPlainText
	linkedDocumentRTF = element.GetLinkedDocument()
	if format = "RTF" then
		getLinkedDocumentContent = linkedDocumentRTF
	else
		linkedDocumentEA = Repository.GetFieldFromFormat("RTF",linkedDocumentRTF)
		if format = "EA" then
			getLinkedDocumentContent = linkedDocumentEA
		else
			linkedDocumentPlainText = Repository.GetFormatFromField("TXT",linkedDocumentEA)
			getLinkedDocumentContent = linkedDocumentPlainText
		end if
	end if
end function

'let the user select a package
function selectPackage()
	dim documentPackageElementID 		
	documentPackageElementID = Repository.InvokeConstructPicker("IncludedTypes=Package") 
	if documentPackageElementID &gt; 0 then
		dim packageElement as EA.Element
		set packageElement = Repository.GetElementByID(documentPackageElementID)
		dim package as EA.Package
		set package = Repository.GetPackageByGuid(packageElement.ElementGUID)
	else
		set package = nothing
	end if 
	set selectPackage = package
end function

'add the given package and all subPackges to the list (recursively
function addPackagesToList(package, packageList)
	dim subPackage as EA.Package
	'add the package itself
	packageList.Add package
	'add subpackages
	for each subPackage in package.Packages
		addPackagesToList subPackage, packageList
	next
end function

Set all line styles in an Enterprise Architect diagram automatically

With this script you can change set all the lines styles on a diagram at once, to your preferred style per type of connector.

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

Line Styles

Unfortunately you can only choose from the first three to be the default line style for new connectors. Additionally you can also specify the default for Generalization to be Tree Style, but that is it.

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

With this script I can set all the line styles on a diagram to the styles that I like.  All I need to do is right click on a diagram and choose Scripts|Set Line Styles

Set Linestyles menu option

And the script will set all the line styles to the defaults set in the script. If you would like the line styles to be set to your preferences automatically without having to run the script you can use EA-Matic version of this script.

You can set your own preferences in this section of 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****************

Free download

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

Video

This script has been previously featured in the webinar Introduction to Scripting with Enterprise Architect presented by yours truly.

The script

option explicit

!INC Local Scripts.EAConstants-VBScript

' Script Name: DefaultLineStyles
' Author: Geert Bellekens
' Purpose: Allows to change the linestyles to their default
' Date: 27/04/2015
'
dim lsDirectMode, lsAutoRouteMode, lsCustomMode, lsTreeVerticalTree, lsTreeHorizontalTree, _
lsLateralHorizontalTree, lsLateralVerticalTree, lsOrthogonalSquareTree, lsOrthogonalRoundedTree

lsDirectMode = "1"
lsAutoRouteMode = "2" 
lsCustomMode = "3"
lsTreeVerticalTree = "V"
lsTreeHorizontalTree = "H"
lsLateralHorizontalTree = "LH"
lsLateralVerticalTree = "LC"
lsOrthogonalSquareTree = "OS"
lsOrthogonalRoundedTree = "OR"

dim defaultStyle
dim menuDefaultLines


'*********EDIT BETWEEN HERE*************


' 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 "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****************


sub main
		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 sub

main


'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

'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

function getConnectorStyle(diagramLink)
	'split the style
	dim styleparts
	styleparts = Split(diagramLink.Style,";")
	dim stylePart
	dim mode
	dim tree
	mode = ""
	tree = ""
	for each stylepart in styleparts
		if Instr(stylepart,"Mode=") > 0 then
			mode = right(stylepart, 1)
		elseif Instr(stylepart,"TREE=") > 0 then
			tree = replace(stylepart, "TREE=", "")
		end if
	next
	if tree <> "" then
		getConnectorStyle = tree
	else
		getConnectorStyle = mode
	end if
end function

Tutorial: Generate complex documents from Enterprise Architect with a two-step semi-automated approach

  • Step 1: Create the virtual document with a script
  • Step 2: Generate the document from the virtual document

Document generation is important in most organisations that work with Enterprise Architect. Storing information in the model is good, but most stakeholders still require documents.

Documents are using as a tool for validation, as a work order, or just to give you an overview of a specific set of thing in the model. The main advantages of a document versus a model is that it has a limited scope and a linear order. You can read a document from start to finish, whereas you cannot do that with a model. Documents are, and will always be an important tool in developing applications.

Enterprise Architect comes with a sophisticated document generator that has seen great improvements in the last versions (v10, v11, v12). You also get a standard set of document templates that will quickly generate a document for a part of your model.

The problems arise however the moment you try to do something a bit more sophisticated with the document templates. Get information from different packages, use different templates for the same (set of) elements, etc… You will quickly realize that the approach of having one large template that generates the whole document is not useful for real-world document requirements.

Luckily EA has an alternative to the single template approach: Virtual Documents

Free Download

I’ve put together an example model and an MDG to demonstrate this approach. The MDG contains all scripts, templates and SQL Searches to generate a decent Use Case Document.

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

 

After you open the Hotellio.eap file, right click on the Reservations use case diagram and choose Create Use Case Document

Create Use Case Document

Then select the package to store the virtual document in and fill in the version.

Finally select the «master document» package and press F8 so start the document generation. Make sure to select the correct cover page and style sheet

Generate Document

Virtual Documents

Virtual Documents are used to define the contents of a document as a set of individual sections, each with its own source and template.

Virtual Documents structure

There are two types of elements that are used for virtual documents

  • Master Document
    Represents the document to be printed. You can give it some parameters that can be used in the document itself such as title, Version, Status, etc…
  • Model Document
    Represents a section in your document. The model document gets an RTFTemplate, and you have to define a source for this section. The source tell us which element(s) from the model should be used in this section. There are two ways to define the source. You can either drag one or more packages onto it, or you can fill in the Model Search and Search Term.

Master Documents get stored in the model as stereotyped packages and they contain stereotypes classes to represent the Model Documents

Virtual Document example

In the project browser this looks like thisVirtual Document project browser.

The Master Documents get the stereotype «master document» They contain tagged values to store things like ReportAlias, ReportTitle. Event though you can assign an RTF template to the master document this seems to be pointless since it gets ignored when printing a document.

Model Documents get the stereotype «model document». The links to the packages that serve as the source for a model document are created by dragging the package from the project browser onto the model document. This creates an attribute that maintains the link to the package.
Tagged values are used to indicate the template to use, the model search to use, and the search value to use on that search.

This mechanism of model documents allows you to very precisely control what will be printed in the document.

Templates

RTF templates are stored in the Resources section of your model. In order to see and manage them enable the resources view using View|Resources

template sections

Templates for document that have a package as source can start from the package tag and access all element, diagram etc.. in that package.

If your source is an SQL search that returns elements your template content can be placed in the element section

SQL Search

The SQL search I use to select a single element based on its GUID this following:

select c.ea_guid AS CLASSGUID,c.object_type AS CLASSTYPE,c.name AS Name, c.stereotype AS Stereotype,
package.name AS PackageName ,package_p1.name AS PackageLevel1,package_p2.name AS PackageLevel2,package_p3.name AS PackageLevel3 
from ((((t_object c 
inner join t_package  package on c.package_id = package.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 
c.ea_guid like '<Search Term>'

Printing

When you select the Master Document you can open up the document generation dialog by pressing F8

Generate Document

Here you can set the cover page, table of contents, and the style sheet to use.

Side note: Invest in the style sheet. It will save you time and frustration afterwards. It is difficult, and awkward to set styles using the RTF editor, but if you do it once in the stylesheet you don’t have to to it over and over again in each template. Also make sure to create your own “normal” style and call it “my normal” or something like that. Use this instead of the regular “normal”. There seems to be some weird behavior associated by the “normal” style, so I learned it’s best not to use it at all.

Then press generate, and the document generator will create one big document containing

  • The cover page
  • The table of contents page
  • All model documents in the order they appear in the project browser.

If you are not happy with the content of the document, or the order of the model documents then you can change them in EA and re-print the document.

One of the advantages is that the content of the document is stored in EA. So after a revision of the model you can print exactly the same content again and compare it with the previous version.

 Automatically generate virtual documents

Using virtual documents like that is all nice and good, but it still requires quite a lot of work to assemble the virtual document itself. For a somewhat realistic document you easily end up with tens of model documents, all pointing to different element or packages, and different template.
And you have to repeat that process for all the different documents you want to generate.

Doing the same thing over and over again is exactly what computers are build for. Instead of having to assemble the virtual document manually we will write a little script to do it for us.

The first thing well need to do is to make the Master Document package. This VBScript function does that for us.

function addMasterDocument(packageGUID, documentName,documentVersion,documentAlias)
	dim ownerPackage as EA.Package
	set ownerPackage = Repository.GetPackageByGuid(packageGUID)
	dim masterDocumentPackage as EA.Package
	set masterDocumentPackage = ownerPackage.Packages.AddNew(documentName, "package")
	masterDocumentPackage.Update
	masterDocumentPackage.Element.Stereotype = "master document"
	masterDocumentPackage.Alias = documentAlias
	masterDocumentPackage.Version = documentVersion
	masterDocumentPackage.Update
	'link to the master template
	dim templateTag as EA.TaggedValue
	for each templateTag in masterDocumentPackage.Element.TaggedValues
		if templateTag.Name = "RTFTemplate" then
			templateTag.Value = "(model document: master template)"
			templateTag.Notes = "Default: (model document: master template)"
			templateTag.Update
			exit for
		end if
	next
	'return
	set addMasterDocument= masterDocumentPackage
end function

Then we need to make the Model Documents.To make one with a Package as source we use this function

function addModelDocumentForPackage(masterDocument,package,name, treepos, template)
	dim modelDocElement as EA.Element
	set modelDocElement = masterDocument.Elements.AddNew(name, "Class")
	'set the position
	modelDocElement.TreePos = treepos
	modelDocElement.StereotypeEx = "model document"
	modelDocElement.Update
	'add tagged values
	dim templateTag as EA.TaggedValue
	for each templateTag in modelDocElement.TaggedValues
		if templateTag.Name = "RTFTemplate" then
			templateTag.Value = template
			templateTag.Notes = "Default: Model Report"
			templateTag.Update
			exit for
		end if
	next
	'add attribute
	dim attribute as EA.Attribute
	set attribute = modelDocElement.Attributes.AddNew(package.Name, "Package")
	attribute.ClassifierID = package.Element.ElementID
	attribute.Update
end function

To make one with an SQL Search as source we use this function

function addModelDocumentWithSearch(masterDocument, template,elementName, elementGUID, treepos, searchName)
	dim modelDocElement as EA.Element;
	set modelDocElement = masterDocument.Elements.AddNew(elementName, "Class")
	'set the position
	modelDocElement.TreePos = treepos
	modelDocElement.StereotypeEx = "model document"
	modelDocElement.Update
	dim templateTag as EA.TaggedValue
	if len(elementGUID) > 0 then
		for each templateTag in modelDocElement.TaggedValues
			if templateTag.Name = "RTFTemplate" then
				templateTag.Value = template
				templateTag.Notes = "Default: Model Report"
				templateTag.Update
			elseif templateTag.Name = "SearchName" then
				templateTag.Value = searchName
				templateTag.Update
			elseif templateTag.Name = "SearchValue" then
				templateTag.Value = elementGUID
				templateTag.Update
			end if
		next
	else
		'add tagged values
		for each templateTag in modelDocElement.TaggedValues
			if templateTag.Name = "RTFTemplate" then
				templateTag.Value = template
				templateTag.Notes = "Default: Model Report"
				templateTag.Update
				exit for
			end if
		next
		'no GUID provided. Set masterdocument package ID as dummy attribute to make the template work
		dim attribute as EA.Attribute
		set attribute = modelDocElement.Attributes.AddNew(masterDocument.Name, "Package")
		attribute.ClassifierID = masterDocument.Element.ElementID
		attribute.Update
	end if
end function

The the last script we is one to actually use these functions in order to create the virtual document. In the example provided I’ve split the script in two parts. One part that remains in the library, and one script that is put in the Diagram Group so we can call it from a diagram directly.

The script in the Diagram Group takes care of getting the user input

option explicit

!INC Bellekens DocGen.UseCaseDocument

' Script Name: Create Use Case Document
' Author: Geert Bellekens
' Purpose: Create the virtual document for a Use Case Document based on the open diagram
' 			Copy this script in a Diagram Group to call it from the diagram directly.
' Date: 11/11/2015
'

sub OnDiagramScript()
	dim documentsPackage as EA.Package
	'select the package to generate the virtual document in
	Msgbox "Please select the package to generate the virtual document in",vbOKOnly+vbQuestion,"Document Package"
	set documentsPackage = selectPackage()
	if not documentsPackage is nothing then
		' Get a reference to the current diagram
		dim currentDiagram as EA.Diagram
		set currentDiagram = Repository.GetCurrentDiagram()
		if not currentDiagram is nothing then
			createUseCaseDocument currentDiagram, documentsPackage.PackageGUID 
			Msgbox "Select the Master Document and press F8 to generate document",vbOKOnly+vbInformation,"Finished!"
		else
			Session.Prompt "This script requires a diagram to be visible", promptOK
		end if
	end if
end sub

OnDiagramScript

The script in the library will use the functions mentioned above to start building the virtual document


!INC Local Scripts.EAConstants-VBScript
!INC Bellekens DocGen.DocGenHelpers
!INC Bellekens DocGen.Util
'

' Script Name: UseCaseDocuemnt
' Author: Geert Bellekens
' Purpose: Create the virtual document for a Use Case Document based on the given diagram
' Date: 11/11/2015
'

dim useCaseDocumentsPackageGUID

function createUseCaseDocument( diagram, documentsPackageGUID)
	
	useCaseDocumentsPackageGUID = documentsPackageGUID
	'first create a master document
	dim masterDocument as EA.Package
	set masterDocument = makeUseCaseMasterDocument(diagram)
	if not masterDocument is nothing then
		dim i
		i = 0
		'use case diagram part 1
		addModelDocumentForDiagram masterDocument,diagram, i, "UCD_Use Case Diagram"
		i = i + 1
		'add Actors
		dim diagramPackage as EA.Package
		set diagramPackage = Repository.GetPackageByID(diagram.PackageID)
		addModelDocumentForPackage masterDocument, diagramPackage, diagram.Name & " Actors", i, "UCD_Actors"
		i = i + 1
		' We only want to report the use cases that are shown within the scope boundary on this diagram
		'get the boundary diagram object in the diagram
		dim boundaries
		set boundaries = getDiagramObjects(diagram,"Boundary")
		Session.Output boundaries.Count
		'get the use cases
		dim usecases		
		if boundaries.Count > 0 then
			set usecases = getElementsFromDiagramInBoundary(diagram, "UseCase",boundaries(0))
			Session.Output "boundary found"
		else
			set usecases = getElementsFromDiagram(diagram, "UseCase")
		end if
		
		'sort use cases alphabetically
		set usecases = sortElementsByName(usecases)
		
		'add the use cases
		i = addUseCases(masterDocument, usecases, i)
		
		Repository.RefreshModelView(masterDocument.PackageID)
		'select the created master document in the project browser
		Repository.ShowInProjectView(masterDocument)
	end if
end function


function makeUseCaseMasterDocument(currentDiagram)
	'we should ask the user for a version
	dim documentTitle
	dim documentVersion
	dim documentName
	dim diagramName
	set makeUseCaseMasterDocument = nothing
	diagramName = currentDiagram.Name
	'to make sure document version is filled in
	documentVersion = ""
	documentVersion = InputBox("Please enter the version of this document", "Document version", "x.y.z" )
	if documentVersion <> "" then
		'OK, we have a version, continue
		documentName = "UCD - " & diagramName & " v. " & documentVersion
		dim masterDocument as EA.Package
		set masterDocument = addMasterDocumentWithDetails(useCaseDocumentsPackageGUID, documentName,documentVersion,diagramName)
		set makeUseCaseMasterDocument = masterDocument
	end if
end function

'add the use cases to the document
function addUseCases(masterDocument, usecases, i)
	dim usecase as EA.Element
	for each usecase in usecases
		'use case part 1
		addModelDocument masterDocument, "UCD_Use Case details part1", "UC " & usecase.Name & " Part 1", usecase.ElementGUID, i
		i = i + 1
		
		'get the nested scenario diagram
		dim activity as EA.Element
		set activity = getActivityForUsecase(usecase)
		
		'add scenario diagram
		if not activity is nothing then
		addModelDocument masterDocument, "UCD_Use Case Scenarios Diagram", "UC " & usecase.Name & " Scenarios diagram", activity.ElementGUID, i
			i = i + 1
		end if
		
		'use case part 2
		addModelDocument masterDocument, "UCD_Use Case details part2","UC " &  usecase.Name & " Part 2", usecase.ElementGUID, i
		i = i + 1
		
	next
	'return the new i
	addUseCases = i
end function

function getActivityForUsecase(usecase)
	set getActivityForUsecase = getNestedDiagramOnwerForElement(usecase, "Activity")
end function

function getInteractionForUseCase(usecase)
	set getInteractionForUseCase = getNestedDiagramOnwerForElement(usecase, "Interaction")
end function

function getNestedDiagramOnwerForElement(element, elementType)
	dim diagramOnwer as EA.Element
	set diagramOnwer = nothing
	dim nestedElement as EA.Element
	for each nestedElement in element.Elements
		if nestedElement.Type = elementType and nestedElement.Diagrams.Count > 0 then
			set diagramOnwer = nestedElement
			exit for
		end if
	next
	set getNestedDiagramOnwerForElement = diagramOnwer
end function


'sort the elements in the given ArrayList of EA.Elements by their name 
function sortElementsByName (elements)
	dim i
	dim goAgain
	goAgain = false
	dim thisElement as EA.Element
	dim nextElement as EA.Element
	for i = 0 to elements.Count -2 step 1
		set thisElement = elements(i)
		set nextElement = elements(i +1)
		if  elementIsAfter(thisElement, nextElement) then
			elements.RemoveAt(i +1)
			elements.Insert i, nextElement
			goAgain = true
		end if
	next
	'if we had to swap an element then we go over the list again
	if goAgain then
		set elements = sortElementsByName (elements)
	end if
	'return the sorted list
	set sortElementsByName = elements
end function

'check if the name of the next element is bigger then the name of the first element
function elementIsAfter (thisElement, nextElement)
	dim compareResult
	compareResult = StrComp(thisElement.Name, nextElement.Name,1)
	if compareResult > 0 then
		elementIsAfter = True
	else
		elementIsAfter = False
	end if
end function

Simple VBA Excel to EA importer v4

Version 4 on the Excel to EA Importer now introduces a feature to export Tagged Values to Excel, edit them in Excel and import the changes back into Enterprise Architect.

The Excel to EA importer is a rather simple tool to import stuff into EA. It has been available on the Sparx Community Site since 2009 and it has always been one of the most popular resources.

Version 2 was a small improvement allowing for different types of Elements.

Version 3 added the feature to import the glossary into EA.

Now in version 4 we have yet another tab Tagged Values that will be used to export and import tagged values.

TaggedValuesTab

 

Free download

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

Elements and Attributes

Columns A till F are used in the sheet ElementsAndAttributes . Fill in the the columns with your own data.

  • Type
    Fill in the type of the Element you wish to import. This column should contain either Attribute or one of the EA Element types as defined in the documentation of EA.Element.Type. An attribute will be added to the last specified Element.
  • Name
    The name of the element or attribute.
  • Stereotype
    The stereotype of the element or attribute. Use null to specify an empty stereotype.
  • Description
    The description of the element or attribute.
  • Attribute Type
    The type of the attributeThis column should not be used for Elements. Use null to specify an empty type.
  • Attribute Length
    This value will be added as a tagged value with the name length on the attribute.  Again this is only for attributes.

Glossary

In the 2nd sheet Glossary you can fill in columns A until C with:

  • Term
    The term name is mandatory.
  • Type
    The type of the term is mandatory as well
  • Meaning
    The meaning of the term is optional.

Tagged Values

The Tagged Values sheet can only be used to edit existing tagged values, not for importing new tagged values into EA. It contains the following columns

  • GUID
    The unique identifier of the tagged value. Used to find the tagged value in the model. – Do not change.
  • Type
    Used to indicate if the tagged value is on an element, attribute, operation or connector. – Do not change
  • Property
    The name of the tagged value. This field can be used to rename tagged values
  • Value
    The value of the tagged value. This field can be edited.
  • Notes
    The notes of the tagged value. This field can be edited.
  • Owner
    The name of the element that owns the tagged value. Used as a context indicator. Changing this field will have no effect.

Export/Import

Once all you data has been filled in you are ready to import the data into EA.

Excel Addin tab

  • Open the sheet you wish to import
  • If you are on the ElementsAndAttributes sheet press the button Import, or run the macro (Alt-F8) importFromExcel
  • If you are on the Glossary sheet press the button Import Glossary, or  run the macro (Alt-F8) importGlossaryFromExcel
  • On the Tagged Values  sheet you can press the button Export Tagged Values, or run the macro (Alt-F8) exportTaggedValuesFromEA in order to get all tagged values into the sheet.
  • After modifying the content of the tagged values press the button Import Tagged Values, or run the macro (Alt-F8) importTaggedValuesFromExcel and press start to start import.
    TaggedValuesDialog

 

Customizing

The code of this little tool is embedded as VBA in the document, and as such can be edited by anyone with basic programming skills.

To open the code editor press Alt-F11.

There are three major parts for the code

  • ExcelImporter
    The module that contains the importFromExcel macro. This is where you want to make changes.
  • EAConnector
    A facade for EA’s API containing all kinds of useful functions. You probably don’t need to change anything here.
  • ExcelConnector
    A facade for Excel with the code to get the values of the sheet. Again probably no need to change anything here.

Both the Excel file as the VBA code embedded in it is free for use. Just be kind and leave the reference to my name in the code if you use it.

Searching for REAL orphans in Enterprise Architect

Enterprise Architect comes standard with an Find Orphans search, which is supposed to give you a list of all elements that are no longer used in the model, and thus better be removed from the model altogether.

Find Orphans

The problem with this search is that it will only report elements that are not shown on a diagram; which should not be the only criterion to determine if an element is an orphan or not. Not being on a diagram is just fine. I have lots of elements in my model that are not on a diagram, that doesn’t mean they are not used anymore.

If you are going to delete all elements reported in the standard orphans search you might do serious damage to your model.

So in order to aid with the maintenance of a large model I wrote the following SQL Search.

select distinct o.ea_guid as CLASSGUID,o.Object_Type as CLASSTYPE,o.Name as Name, o.Stereotype,
package.name as PackageName ,package_p1.name as PackageLevel1,package_p2.name as PackageLevel2 ,package_p3.name as PackageLevel3 
from ((((((((((((((((( t_object o
left join t_diagramobjects dob on dob.[Object_ID] = o.[Object_ID])
left join t_object inst on inst.[Classifier] = o.[Object_ID])
left join t_objectproperties otv on otv.VALUE = o.[ea_guid])
left join t_connectortag ctv on ctv.VALUE = o.[ea_guid])
left join t_attributetag atv on atv.VALUE = o.[ea_guid])
left join t_operationtag optv on optv.VALUE = o.[ea_guid])
left join t_object u on u.Pdata1 = o.ea_guid)
left join t_connector cs on cs.Start_Object_ID = o.Object_ID)
left join t_connector ce on ce.End_Object_ID = o.Object_ID)
left join t_attribute att on att.Classifier like o.Object_ID)
left join t_operation op on op.Classifier like o.Object_ID)
left join t_operationparams opp on opp.Classifier like o.Object_ID)
left join t_object owned on owned.ParentID = o.Object_ID)
inner join t_package package on o.package_id = package.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 
o.Package_ID in (#Branch#)
and (o.name is null or o.Name like '#WC#<Search Term>#WC#')
and o.Object_Type not in ('Package')
and (o.Stereotype is null or o.Stereotype not in ('model document'))
and dob.Object_ID is null
and inst.Object_ID is null
and otv.VALUE is null
and ctv.Value is null
and atv.Value is null
and optv.Value is null
and u.Object_ID is null
and cs.Start_Object_ID is null
and ce.End_Object_ID is null 
and att.Classifier is null
and op.Classifier is null
and opp.Classifier is null
and owned.Object_ID is null

This search will look for all elements that

  • Are located in the currently selected package branch
  • Are not a package or a model document  (section in a virtual document)
  • Are not used on a diagram
  • Are not used as the Classifier of an instance
  • Are not referenced by any tagged value
  • Are not used as the source or target of any relation
  • Are not used as the type of an attribute
  • Are not used as the return type of an operation
  • Are not used as the type of a parameter
  • Do not own any other elements

Elements that show up in this search are most probably real orphans that clutter your model and can be safely deleted.

Of course, before you delete anything from the model, make sure you have a backup!

Find Real OrphansIn order to use this search create a new search of type SQL and paste the above query into the query builder field.

If you find anything that should be added to this search please let me know.

Related