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

<br />
'gets a new instance of the EAAddinFramework and initializes it with the EA.Repository<br />
function getEAAddingFrameworkModel()<br />
    'Initialize the EAAddinFramework model<br />
    dim model<br />
    set model = CreateObject(&quot;TSF.UmlToolingFramework.Wrappers.EA.Model&quot;)<br />
    model.initialize(Repository)<br />
    set getEAAddingFrameworkModel = model<br />
end function<br />

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

<br />
set operation = model.getOperationByGUID(GUID)<br />

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

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

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.

<br />
'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<br />
'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<br />
'have a different signature<br />
function EA_OnContextItemChanged(GUID, ot)<br />
	'we only want to do something when the selected element is an operation<br />
	if ot = otMethod then<br />
		'get the model<br />
		dim model<br />
		set model = getEAAddingFrameworkModel()<br />
		'get the operation<br />
		dim operation<br />
		set operation = model.getOperationByGUID(GUID)<br />
		'remember the operationID<br />
		operationID = operation.id<br />
		'remember the overrides<br />
		set overrides = getOverrides(operation, model)<br />
		Repository.WriteOutput &quot;EA-Matic&quot;, overrides.Count &amp; &quot; overrides found for: &quot; &amp; operation.name,0<br />
	end if<br />
end function</p>
<p>'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.<br />
function EA_OnNotifyContextItemModified(GUID, ot)<br />
	'we only want to do something when the selected element is an operation<br />
	if ot = otElement then<br />
		'get the operation<br />
		'Here we use the EA API object directly as most set methods are not implemented in EA Addin Framework<br />
		dim wrappedOperation<br />
		set wrappedOperation = Repository.GetMethodByID(operationID)<br />
		dim modifiedElement<br />
		set modifiedElement = Repository.GetElementByGuid(GUID)<br />
		if not wrappedOperation is Nothing and not modifiedElement is Nothing then<br />
			'check to be sure we have the same operation<br />
			if modifiedElement.ElementID = wrappedOperation.ParentID AND overrides.Count &gt; 0 then<br />
				dim synchronizeYes<br />
				synchronizeYes = MsgBox(&quot;Found &quot; &amp; overrides.Count &amp; &quot; override(s) for operation &quot;&amp; modifiedElement.Name &amp; &quot;.&quot; &amp; wrappedOperation.Name &amp; vbNewLine &amp; &quot;Synchronize?&quot; _<br />
										,vbYesNo or vbQuestion or vbDefaultButton1, &quot;Synchronize overrides?&quot;)<br />
				if synchronizeYes = vbYes then<br />
					synchronizeOverrides wrappedOperation<br />
					'log to output</p>
<p>					Repository.WriteOutput &quot;EA-Matic&quot;, &quot;Operation: &quot; &amp; wrappedOperation.name &amp;&quot; synchronized&quot; ,0<br />
				end if<br />
				'reset operationID to avoid doing it all again<br />
				operationID = 0<br />
			end if<br />
		end if<br />
	end if<br />
end function<br />

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.

<br />
'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<br />
function getOverrides(operation, model)<br />
	'first get all operations with the exact same signature<br />
	dim overrideQuery<br />
	overrideQuery = &quot;select distinct op2.OperationID from (((t_operation op &quot; &amp; _<br />
					&quot;inner join t_operation op2 on op2.[Name] = op.name) &quot;&amp; _<br />
					&quot;left join t_operationparams opp on op.OperationID = opp.OperationID) &quot;&amp; _<br />
					&quot;left join t_operationparams opp2 on opp2.OperationID = op2.OperationID) &quot;&amp; _<br />
					&quot;where op.OperationID = &quot;&amp; operation.id &amp;&quot; &quot;&amp; _<br />
					&quot;and op2.ea_guid &lt;&gt; op.ea_guid &quot;&amp; _<br />
					&quot;and (op2.TYPE = op.Type OR (op2.TYPE is null AND op.Type is null)) &quot;&amp; _<br />
					&quot;and (op2.Classifier = op.Classifier OR (op2.Classifier is null AND op.Classifier is null)) &quot;&amp; _<br />
					&quot;and (opp.Name = opp2.Name OR (opp.Name is null AND opp2.Name is null)) &quot;&amp; _<br />
					&quot;and (opp.TYPE = opp2.TYPE OR (opp.TYPE is null AND opp2.Type is null)) &quot;&amp; _<br />
					&quot;and (opp.DEFAULT = opp2.DEFAULT OR (opp.DEFAULT is null AND opp2.DEFAULT is null)) &quot;&amp; _<br />
					&quot;and (opp.Kind = opp2.Kind OR (opp.Kind is null AND opp2.Kind is null)) &quot;&amp; _<br />
					&quot;and (opp.Classifier = opp2.Classifier OR (opp.Classifier is null AND opp2.Classifier is null)) &quot;<br />
	dim candidateOverrides<br />
	set candidateOverrides = model.ToArrayList(model.getOperationsByQuery(overrideQuery))<br />
	'then get the descendants of the owner<br />
	dim descendants<br />
	dim descendant<br />
	'first find all elements that either inherit from the owner or realize it<br />
	dim owner<br />
	set owner = model.toObject(operation.owner)<br />
	set descendants = getDescendants(owner, model)<br />
	'then filter the candidates to only those of the descendants<br />
	'loop operations backwards<br />
	dim i<br />
	for i = candidateOverrides.Count -1 to 0 step -1<br />
		dim found<br />
		found = false<br />
		for each descendant in descendants<br />
			if descendant.id = model.toObject(candidateOverrides(i).owner).id then<br />
				'owner is a descendant, operation can stay<br />
				found = true<br />
				exit for<br />
			end if<br />
		next<br />
		'remove operation from non descendants<br />
		if not found then<br />
			candidateOverrides.RemoveAt(i)<br />
		end if<br />
	next<br />
	set getOverrides = candidateOverrides<br />
end function</p>
<p>'gets all descendant of an element. That is all subclasses and classes that Realize the element.<br />
'Works recursively to get them all.<br />
function getDescendants(element, model)<br />
	dim descendants<br />
	dim getdescendantsQuery<br />
	getdescendantsQuery = &quot;select c.Start_Object_ID as Object_ID from (t_object o &quot; _<br />
					&amp; &quot;inner join t_connector c on c.End_Object_ID = o.Object_ID) &quot; _<br />
					&amp; &quot;where &quot;_<br />
					&amp; &quot;(c.[Connector_Type] like 'Generali_ation' &quot;_<br />
					&amp; &quot;or c.[Connector_Type] like 'Reali_ation' )&quot;_<br />
					&amp; &quot;and o.Object_ID = &quot; &amp; element.id<br />
	set descendants = model.toArrayList(model.getElementWrappersByQuery(getdescendantsQuery))<br />
	'get the descendants descendants as well<br />
	dim descendant<br />
	dim descendantsChildren<br />
	for each descendant in descendants<br />
		if IsEmpty(descendantsChildren) then<br />
			set descendantsChildren = getDescendants(descendant, model)<br />
		else<br />
			descendantsChildren.AddRange(getDescendants(descendant, model))<br />
		end if<br />
	next<br />
	'add the descendantsChildren to the descendants<br />
	if not IsEmpty(descendantsChildren) then<br />
		if  descendantsChildren.Count &gt; 0 then<br />
			descendants.AddRange(descendantsChildren)<br />
		end if<br />
	end if<br />
	set getDescendants = descendants<br />
end function<br />

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.

<br />
'Synchronizes the operation with it's overrides<br />
function synchronizeOverrides(wrappedOperation)<br />
	dim override<br />
	for each override in overrides<br />
		dim wrappedOverride<br />
		set wrappedOverride = override.WrappedOperation<br />
		'synchronize the operation with the override<br />
		synchronizeOperation wrappedOperation, wrappedOverride<br />
		'tell EA something might have changed<br />
		Repository.AdviseElementChange wrappedOverride.ParentID<br />
	next<br />
end function</p>
<p>'Synchronizes the operation with the given override<br />
function synchronizeOperation(wrappedOperation, wrappedOverride)<br />
	dim update<br />
	update = false<br />
	'check name<br />
	if wrappedOverride.Name &lt;&gt; wrappedOperation.Name then<br />
		wrappedOverride.Name = wrappedOperation.Name<br />
		update = true<br />
	end if<br />
	'check return type<br />
	if wrappedOverride.ReturnType &lt;&gt; wrappedOperation.ReturnType then<br />
		wrappedOverride.ReturnType = wrappedOperation.ReturnType<br />
		update = true<br />
	end if<br />
	'check return classifier<br />
	if wrappedOverride.ReturnType &lt;&gt; wrappedOperation.ReturnType then<br />
		wrappedOverride.ReturnType = wrappedOperation.ReturnType<br />
		update = true<br />
	end if<br />
	if update then<br />
		wrappedOverride.Update<br />
	end if<br />
	'check parameters<br />
	synchronizeParameters wrappedOperation, wrappedOverride<br />
end function</p>
<p>'Synchronizes the parameters of the given operatin with that of the overrride<br />
function synchronizeParameters(wrappedOperation, wrappedOverride)<br />
	'first make sure they both have the same number of parameters<br />
	if wrappedOverride.Parameters.Count &lt; wrappedOperation.Parameters.Count then<br />
		'add parameters as required<br />
		dim i<br />
		for i = 0 to wrappedOperation.Parameters.Count - wrappedOverride.Parameters.Count -1<br />
			dim newParameter<br />
			set newParameter = wrappedOverride.Parameters.AddNew(&quot;parameter&quot; &amp; i,&quot;&quot;)<br />
			newParameter.Update<br />
		next<br />
		wrappedOverride.Parameters.Refresh<br />
	elseif wrappedOverride.Parameters.Count &gt; wrappedOperation.Parameters.Count then<br />
		'remove parameters as required<br />
		for i = wrappedOverride.Parameters.Count -1 to wrappedOperation.Parameters.Count step -1<br />
			wrappedOverride.Parameters.DeleteAt i,false<br />
		next<br />
		wrappedOverride.Parameters.Refresh<br />
	end if<br />
	'make parameters equal<br />
	dim wrappedParameter<br />
	dim overriddenParameter<br />
	dim j<br />
	for j = 0 to wrappedOperation.Parameters.Count -1<br />
		dim parameterUpdated<br />
		parameterUpdated = false<br />
		set wrappedParameter = wrappedOperation.Parameters.GetAt(j)<br />
		set overriddenParameter = wrappedOverride.Parameters.GetAt(j)<br />
		'name<br />
		if overriddenParameter.Name &lt;&gt; wrappedParameter.Name then<br />
			overriddenParameter.Name = wrappedParameter.Name<br />
			parameterUpdated = true<br />
		end if<br />
		'type<br />
		if overriddenParameter.Type &lt;&gt; wrappedParameter.Type then<br />
			overriddenParameter.Type = wrappedParameter.Type<br />
			parameterUpdated = true<br />
		end if<br />
		'default<br />
		if overriddenParameter.Default &lt;&gt; wrappedParameter.Default then<br />
			overriddenParameter.Default = wrappedParameter.Default<br />
			parameterUpdated = true<br />
		end if<br />
		'kind<br />
		if overriddenParameter.Kind &lt;&gt; wrappedParameter.Kind then<br />
			overriddenParameter.Kind = wrappedParameter.Kind<br />
			parameterUpdated = true<br />
		end if<br />
		'classifier<br />
		if overriddenParameter.ClassifierID &lt;&gt; wrappedParameter.ClassifierID then<br />
			overriddenParameter.ClassifierID = wrappedParameter.ClassifierID<br />
			parameterUpdated = true<br />
		end if<br />
		'update the parameter if it was changed<br />
		if parameterUpdated then<br />
			overriddenParameter.Update<br />
		end if<br />
	next<br />
end function<br />

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.