Chapter 9. Writing Add-In COM Object

Basic Concepts of Add-In COM Object

As discussed in 'Chapter 3. Hello world Example' simple Script codes can be defined to add new functionalities to StarUML™. However, to facilitate more complex and useful functionalities, it is better to use a program development environment that supports COM objects. For implementing StarUML™ Add-In COM Objects, it does not matter whether Delphi, Visual Basic or any other programming environment is used, as long as it supports COM technology.

The most important point about implementing StarUML™ Add-In COM Objects is that the IStarUMLAddIn interface defined by StarUML™ must be used.


As illustrated above, the IStarUMLAddIn interface inherits IUnknown and defines the three additional interface methods: InitializeAddIn(), FinalizeAddIn(), and DoMenuAction().

IStarUMLAddIn Interface Methods

The methods to be defined for implementing the IStarUMLAddIn interface are as follows.

Method Description
InitializeAddIn() The InitializeAddIn() method is used by the StarUMLApplication object to initialize each Add-In COM Object when it is created. As will be discussed in below section this is used to define the actions required for initialization of an Add-In COM Object such as event subscription registration.
FinalizeAddIn() The FinalizeAddIn() method is called by the StarUMLApplication object just before disconnecting reference from an Add-In COM Object. As will be discussed in below section this is used to define the actions required before terminating an Add-In COM Object such as event subscription removal.
DoMenuAction(ActionID: Integer) As seen in 'Chapter 8. Extending Menu' the DoMenuAction() method is called when the user selects an extension menu item defined by each Add-In. The 'actionId' value of each menu item defined by the menu extension file is passed on as an argument.

Add-In COM Object Example

The following is a simple example of an StarUML™ Add-In COM Object implementing the IStarUMLAddIn interface. This is written in the Delphi Pascal syntax.

type
    AddInExample = class(TComObject, IStarUMLAddIn)
    private
        StarUMLApp: IStarUMLApplication;
    protected
        function InitializeAddIn: HResult; stdcall;
        function FinalizeAddIn: HResult; stdcall;
        function DoMenuAction(ActionID: Integer): HResult; stdcall;
        ...
    public
        procedure Initialize; override;
        destructor Destroy; override;
        ...
    end;

...

implementation

procedure AddInExample.Initialize;
begin
    inherited;
    StarUMLApp := CreateOleObject('StarUML.StarUMLApplication') as IStarUMLApplication;
    ...
end;

destructor AddInExample.Destroy;
begin
    ...
    StarUMLApp := nil;
    inherited;
end;

function AddInExample.InitializeAddIn: HResult;
begin
    ...
    Result := S_OK;
end;

function AddInExample.FinalizeAddIn: HResult;
begin
    ...
    Result := S_OK;
end;

function AddInExample.DoMenuAction(ActionID: Integer): HResult; stdcall;
begin
    Result := S_OK;
    ...
end;

Writing Add-In Description File

Basic Concept of Add-In Description File

Add-In Description file(*.aid) is XML based text file. All add-Ins plug-ined in StarUML must offer one add-in description file. StarUML registers Add-In object at system registry and initializes the Add-In object and menu extension file associated with it on the reference of add-in description file context.

Note: Add-In description file must have *.aid extension file name and placed in the subdirectory of StarUML module directory(<install-dir>\modules).

Structure of Approach Document File

Add-In description files follow the XML document conventions, and user-defined menu items are contained within the 'ADDIN' element.

<?xml version="1.0" encoding="..."?>

<ADDIN>
    <NAME>...</NAME>
    <DISPLAYNAME>...</DISPLAYNAME>
    <COMOBJ>...</COMOBJ>
    <FILENAME>...</FILENAME>
    <COMPANY>...</COMPANY>
    <COPYRIGHT>...</COPYRIGHT>
    <HELPFILE>...</HELPFILE>
    <ICONFILE>...</ICONFILE>
    <ISACTIVE>...</ISACTIVE>
    <MENUFILE>...</MENUFILE>
    <VERSION>...</VERSION>
    <MODULES>
        <MODULEFILENAME>...</MODULEFILENAME>
    </MODULES>
</ADDIN>

Registering Add-In Description File

To make an add-in description file to be recognized automatically by StarUML, must place the file in the subdirectory of  StarUML module directory(<install-dir>\modules). StarUML searches and reads all add-in description files in the module directory and registers them at the program automatically when StarUML is initializing. If add-in description file is invalid or it's extension file name is not .aid, StarUML will not read the add-in description file and ignore it. It is recommended that make a subdirectory in the StarUML module directory and place the add-in description file in there to avoid modules being out of order.

Note: Delete the add-in description file from the StarUML module directory(<install-dir>\modules) not to use the add-in any more.

Option Extension

Basic Concept of Option Extension

StarUML supports setup options to adjust environment and detail functions of StarUML. Options are necessary not to StarUML application self but also add-ins supplies by third-party vendors. StarUML option extension enables Add-Ins to equip option configuring function without additional implementation. For using option extension, Add-In developer just defines option items with text file and places it in the Add-In directory. These option definitions are loaded on the program in initializing and displayed on option dialog. Add-In developer can save their time and efforts for implementing Add-In, and provide consistent user interface to users.

Follow the steps below to support setup options in Add-In.

  1. Create an option schema document file (.opt) to define option items for the Add-In.
  2. Copy the option schema document file (.opt) to subdirectory of module directory.

Hierarchy of Option Schema

StarUML constructs the option schema hierarchically as follows to manage many option items that defined in the application and add-ins in integrative.

Writing Option Schema

Option schema file to define option items is XML based text file which extension file name is *.opt. The option schema contents are contained within the OPTIONSCHEMA element, and there must not be any errors in syntax or contents.

<?xml version=¡±1.0¡± encoding=¡±...¡± ?>
<OPTIONSCHEMA id="..."> 
    <HEADER>
    ...
    </HEADER>
    <BODY>
    ...
    </BODY>
</OPTIONSCHEMA>

Header Contents

The HEADER section of an option schema document contains general information for the option schema such as the option schema title and description. Structure of the header section is as follows.

<HEADER>
    <CAPTION>...</CAPTION> 
    <DESCRIPTION>...</DESCRIPTION> 
</HEADER>

Body Contents

The BODY section of an option schema document contains definition of all option items hierarchically.

<BODY>
    <OPTIONCATEGORY>
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <OPTIONCLASSIFICATION>
            <CAPTION>...</CAPTION>
            <DESCRIPTION>...</DESCRIPTION>
            <OPTIONITEM>
               ...
            </OPTIONITEM>
            ...             
        </OPTIONCLASSIFICATION>
         ... 
    </OPTIONCATEGORY>
     ... 
</BODY>

Option Item Definition

OPTIONCLASSIFICATION element can contain a number of option item definitions. Option item type are defined as several types such as integer, real, boolean, enumeration and so on. Option dialog supports information for inputting value or restricts value according to option item type.

Available types of option item are as follows.

Option item type XML element name Input in the option dialog
Integer OPTIONITEM-INTEGER Input only integer value.
Real OPTIONITEM-REAL Input only real number.
String OPTIONITEM-STRING Input only string.
Boolean OPTIONITEM-BOOLEAN Input true or false with check box.
Text OPTIONITEM-TEXT Input multiple line of text in pop-up text box.
Enumeration OPTIONITEM-ENUMERATION Select one of items that defined with OPTION-ENUMERATIONITEM in combo box.
Font name OPTIONITEM-FONTNAME Select one of font names installed in the system.
File name OPTIONITEM-FILENAME Input file name or select the file in the open file dialog.
Path name OPTIONITEM-PATHNAME Input directory name or select the directory in the open directory dialog.
Color OPTIONITEM-COLOR Select a color in the color combo box or select the color in the color dialog.
Range OPTIONITEM-RANGE Input an integer value within specified range. Can change the value as amount of specified step with spin button.

The following represents format of option item definitions that belongs to OPTIONCLASSIFICATION in the option schema file.

<OPTIONCLASSIFICATION>
    <OPTIONITEM-INTEGER key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-INTEGER>
    <OPTIONITEM-REAL key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-REAL>
    <OPTIONITEM-STRING key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-STRING>
    <OPTIONITEM-BOOLEAN key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-BOOLEAN>
    <OPTIONITEM-TEXT key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-TEXT>
    <OPTIONITEM-ENUMERATION key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
        <ENUMERATIONITEM>...</ENUMERATIONITEM>
        ...
    </OPTIONITEM-ENUMERATION>
    <OPTIONITEM-FONTNAME key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-FONTNAME>
    <OPTIONITEM-FILENAME key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-FILENAME>
    <OPTIONITEM-PATHNAME key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-PATHNAME>
    <OPTIONITEM-COLOR key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
    </OPTIONITEM-COLOR>
    <OPTIONITEM-RANGE key="...">
        <CAPTION>...</CAPTION>
        <DESCRIPTION>...</DESCRIPTION>
        <DEFAULTVALUE>...</DEFAULTVALUE>
        <MINVALUE>...</MINVALUE>
        <MAXVALUE>...</MAXVALUE>
        <STEP>...</STEP>
    </OPTIONITEM-RANGE>
     ... 
</OPTIONITEMCLASSIFICATION>

The following example is the part of option schema file for StarUML.

<?xml version="1.0" encoding="UTF-8" ?>
<OPTIONSCHEMA id="ENVIRONMENT">
    <HEADER>
        <CAPTION>Environment</CAPTION>
        <DESCRIPTION> </DESCRIPTION>
    </HEADER>
    <BODY>
        <OPTIONCATEGORY>
            <CAPTION>General</CAPTION>
            <DESCRIPTION>General Configuration is a group of the basic and general option items for the program. This category includes the [General], [Browser], [Collection Editor] and [Web] subcategories.</DESCRIPTION>
            <OPTIONCLASSIFICATION>
                <CAPTION>General</CAPTION>
                <DESCRIPTION></DESCRIPTION>
                <OPTIONITEM-RANGE key="UNDO_LEVEL">
                    <CAPTION>Max. number of undo actions</CAPTION>
                    <DESCRIPTION>Specifies the maximum number of actions for undo and redo.</DESCRIPTION>
                    <DEFAULTVALUE>30</DEFAULTVALUE>
                    <MINVALUE>1</MINVALUE>
                    <MAXVALUE>100</MAXVALUE>
                    <STEP>1</STEP>
                </OPTIONITEM-RANGE>
                <OPTIONITEM-BOOLEAN key="CREATE_BACKUP">
                    <CAPTION>Create backup files</CAPTION>
                    <DESCRIPTION>Specifies whether to create backup files when saving changes.</DESCRIPTION>
                    <DEFAULTVALUE>True</DEFAULTVALUE>
                </OPTIONITEM-BOOLEAN>
            </OPTIONCLASSIFICATION>
        </OPTIONCATEGORY>
    </BODY>
</OPTIONSCHEMA>

Registering Option Schema

To make a option schema to be recognized automatically by StarUML, must place the file in the subdirectory of StarUML module directory(<install-dir>\modules). StarUML searches and reads all option schema files in the module directory and registers them at the program automatically when StarUML is initializing. If option schema file is invalid or it's extension file name is not .opt, StarUML will not read the option schema file and ignore it. It is recommended that make a subdirectory in the StarUML module directory and place the add-in description file in there to avoid modules being out of order.

Note: Delete option schema file from the StarUML module directory(<install-dir>\modules) not to use the option extension any more.

Accessing Option Values

Accessing Option Values with COM Interface

You can access the option values that user changed in option dialog by using COM interface of StarUML.
GetOptionValue() of IStarUMLApplication returns option value depends on SchemaID and Key inputted as variant.
The method usage is as follows.

IStarUMLApplication.GetOptionValue(SchemaID: String, Key: String): Variant

Use the Variant typed return value of GetOptionValue() by casting it according to the type of each option item. You can read the value directly without additional type casting in script languages such as JScript and VBScript.

The following is JScript example that reads "UNDO_LEVEL" option value defined in the StarUML environment option schema and output it to message box.

var app = new ActiveXObject("StarUML.StarUMLApplication");
var undoLevel = app.GetOptionValue("ENVIRONMENT", "UNDO_LEVEL");

WScript.Echo("Max. number of undo actions : " + undoLevel);

Processing change event of option value

StarUML propagates events that occurs in using the program to Add-ins that implement IEventSubscriber interface. If user changes option values in option dialog, Application invokes event handler- NotifyEvent()- of Add-ins that implement IEventSubscriber. If you want to apply option values promptly to the Add-in when user changes the option values, implement IEventSubscriber interface and NotifiyEvent() to read the option values by using IStarUMLApplication.GetOptionValue() method in case of EVK_OPTIONS_APPLIED event. Add-Ins that use script such as VBScript and JSCript cannot apply option values to the Add-in becase they can't implement IEventSubscriber interface.

For the details of event handling, it will be featured in the next section.

Basic Concepts of Event Subscription

An Add-In Object that implements the IEventSubscriber interface can subscribe to various internal events of the StarUML™ application. Whenever an internal event occurs, the StarUML™ application calls the NotifyEvent method of the registered IEventSubscriber type objects.

The class diagram below illustrates the organization of the external API interfaces related to event subscription.

Kinds of Events

As illustrated above, the EventKind enumeration defines the kinds of internal events of the StarUML™ application that can be subscribed by Add-In objects that implement the IEventSubscriber interface. The table below describes each literal of the EventKind enumeration.

Event Kind (literal) Integer Value Event Description
EVK_APPLICATION_ACTIVATE 0 Occurs when the StarUML™ application window is activated.
EVK_APPLICATION_DEACTIVATE 1 Occurs when the StarUML™ application window is deactivated.
EVK_APPLICATION_MINIMIZE 2 Occurs when the StarUML™ application window is minimized.
EVK_APPLICATION_RESTORE 3 Occurs when the minimized StarUML™ application window is restored to the previous size.
EVK_OPTIONS_APPLIED 4 Occurs when an option value is modified.
EVK_PROJECT_OPENED 5 Occurs when a project element is created or a project file is opened.
EVK_PROJECT_SAVED 6 Occurs whenever a project is saved.
EVK_PROJECT_CLOSING 7 Occurs when "Close Project" is selected.
EVK_PROJECT_CLOSED 8 Occurs when a project is closed.
EVK_DOCUMENT_MODIFIED 9 Occurs when a document (project or unit) is modified.
EVK_DOCUMENT_SAVED 10 Occurs when a document (project or unit) is saved.
EVK_UNIT_SEPARATED 11 Occurs when a unit element is separated.
EVK_UNIT_MERGED 12 Occurs when a separated unit element is merged.
EVK_UNIT_OPENED 13 Occurs when a unit is opened.
EVK_SELECTION_CHANGED 14 Occurs when the modeling element selection is changed.
EVK_DIAGRAM_ACTIVATED 15 Occurs when a diagram is opened.
EVK_ELEMENTS_ADDED 16 Occurs whenever a new modeling element is created.
EVK_ELEMENTS_DELETING 17 Occurs when deleting a modeling element.
EVK_ELEMENTS_DELETED 18 Occurs when a modeling element is deleted.
EVK_MODELS_CHANGED 19 Occurs when a model element property value is modified.
EVK_VIEWS_CHANGED 20 Occurs when a view element property value is modified.

Subscribing to Events

In order for an Add-In to subscribe to the StarUML™ application events, it needs to implement the IEventSubscriber interface in addition to the IStarUMLAddIn interface, which is the common interface for all StarUML™ Add-Ins.

The following example shows the class definition of an StarUML™ Add-In object that implements the IStarUMLAddIn interface and the IEventSubscriber interface. This example is written in Delphi Pascal.

type
    AddInExample = class(TComObject, IStarUMLAddIn, IEventSubscriber)
    private
        StarUMLApp: IStarUMLApplication;
        EventPub: IEventPublisher;
    protected
        function InitializeAddIn: HResult; stdcall;
        function FinalizeAddIn: HResult; stdcall;
        function DoMenuAction(ActionID: Integer): HResult; stdcall;
        function NotifyEvent(AEvent: EventKind): HResult; stdcall;
        ...
    public
        procedure Initialize; override;
        destructor Destroy; override;
        ...
    end;

Event Subscription Registration and Removal

In order for an Add-In object, which implements the IEventSubscriber interface, to subscribe to events, the event subscription must be registered. Event subscription registration and removal can be done through the IEventPublisher type object. Reference to the IEventPublisher type object can be obtained through the IStarUMLApplication element. The following Delphi Pascal example shows obtaining reference to IStarUMLApplication and the IEventPublisher type object.

implementation

procedure AddInExample.Initialize;
begin
    inherited;
    StarUMLApp := CreateOleObject('StarUML.StarUMLApplication') as IStarUMLApplication;
    EventPub := StarUMLApp.EventPublisher;
end;

destructor AddInExample.Destroy;
begin
    EventPub := nil;
    StarUMLApp := nil;
    inherited;
end;

The IEventPublisher interface provides the following methods for registration and removal of event subscription. The "ASubscriber" argument for each method represents the actual Add-In object that implements the IEventSubscriber interface.

Method Description
Subscribe(ASubscriber: IEventSubscriber; AEvent: EventKind) Registers subscription to an event specified by the AEvent argument.
SubscribeAll(ASubscriber: IEventSubscriber) Registers subscription to all events.
Unsubscribe(ASubscriber: IEventSubscriber; AEvent: EventKind) Removes subscription to an event specified by the AEvent argument.
UnsubscribeAll(ASubscriber: IEventSubscriber) Removes subscription to all events.

Use the Subscribe method if an Add-In object needs to subscribe to certain events only. For instance, for subscribing to two specific events, call the Subscribe method for each event. Use the SubscribeAll method to subscribe to all events. In general, the Subscribe and SubscribeAll methods are called by the IPlasticAddIn.InitializeAddIn method.

If an Add-In object no longer needs to subscribe to the registered events (e.g. when the object is terminated), all the events registered must be unregistered. Use the Unsubscribe method if the subscription was registered by the Subscribe method, and use the UnsubscribeAll method if the subscription was registered by the SubscribeAll method. In general, the Unsubscribe and SubscribeAll methods are called by the IStarUMLAddIn.FinalizeAddIn method.

The following example shows registration and removal of subscription to the EVK_ELEMENTS_ADDED and EVK_ELEMENTS_DELETED events.

implementation

function AddInExample.InitializeAddIn: HResult;
begin
    EventPub.Subscribe(Self, EVK_ELEMENTS_ADDED);
    EventPub.Subscribe(Self, EVK_ELEMENTS_DELETED);
    ...
    Result := S_OK;
end;

function AddInExample.FinalizeAddIn: HResult;
begin
    EventPub.Unsubscribe(Self, EVK_ELEMENTS_ADDED);
    EventPub.Unsubscribe(Self, EVK_ELEMENTS_DELETED);
    ...
    Result := S_OK;
end;

Acquiring Event Argument

When an event occurs, it is necessary to acquire the related arguments. For instance, when an event related to the creation of a modeling element occurs (EVK_ELEMENTS_ADDED), it is necessary to identify which modeling element is created. The IEventPublisher interface provides the following methods in respect of event arguments.

Method Description
GetEventArgModelCount (): Integer Returns the model element count related to the event.
GetEventArgModelAt(Index: Integer): IModel Returns reference to the (index)th model element related to the event.
GetEventArgViewCount: Integer Returns the view element count related to the event.
GetEventArgViewAt(Index: Integer): IView Returns reference to the (index)th view element related to the event.
GetEventArgDocument: IDocument Returns reference to the document element related to the event.
GetEventArgUnit: IUMLUnitDocument Returns reference to the unit element related to the event.

Processing Events

When a subscribed event occurs, the Add-In needs to execute appropriate processes. Whenever a subscribed event occurs, the StarUML™ application calls the NotifyEvent method of the respective Add-In and passes the event kind as an argument. The event kind is passed as an argument for the NotifyEvent method because it is possible for an Add-In to subscribe to more than one event. Each Add-In needs to implements the NotifyEvent method to arrive at a logic to execute various processes according to the event kinds.

The following example shows implementation of the NotifyEvent method. This example verifies the semantic validity of the element connections when the association element (UMLAssociation) or the generalization element (UMLGeneralization) is created in the StarUML™ application. (This example is a continuation of the examples above. For definition of the Add-In object, see the examples above.)

implementation

function AddInExample.NotifyEvent(AEvent: EventKind): HResult;
var
    M: IModel;
    Assoc: IUMLAssociation;
    Gen: IUMLGeneralization;
    End1, End2: IUMLClassifier;
begin
    if AEvent = EVK_ELEMENTS_ADDED then 
    begin
        if EventPub.GetEventArgModelCount = 1 then 
        begin
            M := EventPub.GetEventArgModelAt(0);

            // Association
            if M.QueryInterface(IUMLAssociation, Assoc) = S_OK then 
            begin
                End1 := Assoc.GetConnectionAt(0).Participant;
                End2 := Assoc.GetConnectionAt(1).Participant
                if End1.IsKindOf('UMLPackage') or End2.IsKindOf('UMLPackage') then
                    ShowMessage('Packages cannot have associations.')
                ...
            end;

          // Generalization
          if M.QueryInterface(IUMLGeneralization, Gen) = S_OK then 
          begin
              if Gen.Child.IsRoot then
                  ShowMessage('Root elements cannot have parent elements.');
              if Gen.Parent.IsLeaf then
                  ShowMessage('Leaf elements cannot have child elements.');
           end;
        end;
    end;

    Result := S_OK;
end;

¡¡