MagicDraw OpenAPI UserGuide
MagicDraw OpenAPI UserGuide
All material contained herein is considered proprietary information owned by No Magic, Inc. and is not to be shared, copied, or reproduced by any means. All information copyright 2003-2012 by No Magic, Inc. All Rights Reserved.
CONTENTS
0
9
INTRODUCTION PLUG-INS 11
19
22
22
Step 1: Create Java Eclipse Project for MagicDraw Plug-in 22 Step 2: Create Plug-in Main Class 23 Step 3: Prepare Plug-in Descriptor File 23 Step 4: Start MagicDraw From Eclipse Environment 24 Step 5: Run MagicDraw Tests Cases from Eclipse Environment
25
27
Removed Deprecated Methods 28 Libraries jars Changes 28 Package Name Change for Build-in Plug-ins
28
29
32
CONTENTS
UML MODEL IMPLEMENTATION USING EMF 35
35
36
UML Model Implementation Using EMF 35 UML Model Implementation in MagicDraw 17.0 Details
UML Model Implementation in MagicDraw 17.0.1 Details
DISTRIBUTING RESOURCES
37
How to Distribute Resources 37 Creating Required Files and Folders Structure 37 Resource Manager Descriptor File 43
JYTHON SCRIPTING
Creating Script 46
46
Step 1: Create Directory 46 Step 2: Write Script Descriptor 46 Step 3: Write Script Code 47 Variables Passed to Script 47
Jython 48
Actions Hierarchy 56
Predefined Actions Configurations 57 Selecting Elements via Element Selection Dialog 57
UML MODEL
Project 58
Root Model
58
59
59
62
SessionManager 62 ModelElementsManager 63 Creating New Model Elements 63 Editing Model Elements 63 Adding New Model Elements or Moving them to Another Parent Removing Model Elements 64 Refactoring Model Elements 65 Creating Diagram 67 Creating New Relationship Objects 67
4
64
CONTENTS
Copying Elements and Symbols Hyperlinks 70 68
68
PRESENTATION ELEMENTS
Presentation Element 72
Using set and sSet 73
72
76
77
SYMBOLS RENDERING
Registering Provider 79
79
DIAGRAM EVENTS
84
84
PATTERNS
85
85
Target Concept
CONTENTS
CREATING USE CASE SCENARIO PROPERTIES 92 94
95
91
DIAGRAM TYPES
PROJECTS MANAGEMENT
ProjectsManager 99 ProjectDescriptor 100
Project Management 100 Module Management 101
99
103
PROJECT OPTIONS
105
Adding Own Project Options 105 Retrieving Project Option Value 106
ENVIRONMENT OPTIONS
107
107
EVENT SUPPORT
108
108
108
109
110
SELECTIONS MANAGEMENT
Selection in Diagrams 113 Selection in Model Browser 113
113
115 116
116
Implementing calculateLocalMetricValue(ModelElement target) 116 Implementing acceptModelElement(BaseElement element) 116 Constructor 117
118
119
Copyright 2003-2012 No Magic, Inc. .
CONTENTS
MyMetricsPlugin Class MyMetric Class 119 119
121
122
122
125
126
128
Basic Concepts 128 Validation Rule Developers Roadmap 129 Create OCL2.0 Validation Rule 129 Binary Validation Rule 130 Create Binary Validation Rule - Case A 131 Create Binary Validation Rule - Case B 131 Create Binary Validation Rule - Case C 132 Create Binary Validation Rule - Case D 133 Binary Validation Rule in Plugin 134 How to Provide a Solution for a Problem Found During Validation?
134
GENERIC NUMBERING
135
135
TEAMWORK
141 142
CODE ENGINEERING
Code Engineering Set
142
146
146
Copyright 2003-2012 No Magic, Inc. .
CONTENTS
Understanding Oracle DDL Template Structure 146 Customizing Template 147 Utility Class 148 Example 153
154
156
156
157
INTRODUCTION
This document describes MagicDraw Open Java API and provides instructions how to write your own plug-ins, create actions in the menus and toolbars, change UML model elements, and create new patterns. The following chapters are included in this document:
"Plug-Ins" on page 11 "Developing Plug-Ins Using IDE" on page 22 "Plugins Migration to MagicDraw 15.0 and Later Open API" on page 27 "Plugins Migration to MagicDraw 17.0.1 and later Open API" on page 29 "MagicDraw UML 17.0.1 file format changes" on page 32 "UML Model Implementation Using EMF" on page 35 "Distributing Resources" on page 37 "Jython Scripting" on page 46 "Adding New Functionality" on page 49 "UML Model" on page 58 "Presentation Elements" on page 72 "Symbols Rendering" on page 79 "Diagram events" on page 84 "Patterns" on page 85 "Creating Use Case Scenario" on page 91 "Properties" on page 92 "Diagram Types" on page 94 "Projects Management" on page 99 "Project Options" on page 105 "Environment Options" on page 107 "Event support" on page 108 "Selections Management" on page 113 "Creating Images" on page 115 "Creating Metrics" on page 116 "Managing Generic Tables" on page 122 "Configuring Element Specification" on page 125 "Custom diagram painters" on page 126 "Annotations" on page 127 "Validation" on page 128 "Generic Numbering" on page 135 "Teamwork" on page 141 "Code Engineering" on page 142 "Oracle DDL generation and customization" on page 146 "Running MagicDraw in batch mode" on page 154 "Creating Magic Draw Test Cases" on page 156
9 Copyright 2003-2012 No Magic, Inc.
INTRODUCTION
In the generated JavaDoc you will find detailed descriptions of classes, their attributes, and operations. JavaDoc is located in <MagicDraw installation directory>\openapi\docs. All MagicDraw OpenAPI classes are packaged in these jar files:
<MagicDraw installation directory>\lib\md_api.jar <MagicDraw installation directory>\lib\md_common_api.jar MagicDraw plugin's classes are
packed in the concrete plugin's jar file(s). (E.g. classes of Model Transformation plugin - "<MagicDraw installation directory> plugins\com.nomagic.magicdraw.modeltransformations\modeltransformations_api.jar") Do not forget to add all jar files recursively (except md_commontw.jar and md_commontw_api.jar) from <MagicDraw installation directory>/lib directory into your (IDE) classpath and make sure the patch.jar is the first in the classpath. We provide a set of plug-ins samples in <MagicDraw installation directory>\openapi\examples directory. Examples sometimes is the best way to find out how to use some Open API.
10
PLUG-INS
Plug-ins are the only one way to change functionality of MagicDraw. The main purpose of plug-in architecture is to add new functionality to MagicDraw although there is a limited ability to remove existing functionality using plug-ins. Plug-in must contain the following resources:
Directory. Compiled java files, packaged into jar file. Plug-in descriptor file. Optional files used by plug-in.
Typically plug-in creates some GUI components allowing user to use plug-in functionality. Generally this is not necessary because plug-in can listen for some changes in project and activate itself on desired changes.
(specified plug-in class must be derived from com.nomagic.magicdraw.plugins.Plugin class). Then method init() of loaded class is called. init() method can add GUI components using actions architecture or do other activities and return from the method. init() method is called only if isSupported() returns true.
11
PLUG-INS
Writing Plug-ins
Plugins Manager
Plugin
Check directory [no descriptor] [descriptor exists] Read descriptor file [requirement does not meet] Check requirements [requirements meets] Load plugin class calling init() method
Writing Plug-ins
With this example, we will create a plug-in that displays a message on MagicDraw startup. To create plug-in, you need to write a plug-in descriptor.
12
PLUG-INS
Writing Plug-ins
return true; } public boolean isSupported() { //plugin can check here for specific conditions //if false is returned plugin is not loaded. return true; } }
This plug-in shows one message when it is initialized, and another message when it is closed.
Compiled code must be packed to a jar file. To create the jar file, use a jar command in the plugins directory:
jar -cf myplugin\myplugin.jar myplugin\*.class
For detailed information about plug-in descriptor, see Plug-in Descriptor on page 14.
13
PLUG-INS
Testing Plug-in
Testing Plug-in
1. Restart MagicDraw. On startup message should appear:
2. Then close MagicDraw (File menu -> Exit). Another message should appear:
Another way to check plug-in is to look at md.log file. This file is located in the <User home
directory>\.magicdraw\<version> directory. Also all plugins and their status are listed in the MagicDraw EnvironmentOptions Plugins tab. After startup this file end should contain such information:
LOAD PLUGINS: com.nomagic.magicdraw.plugins.PluginDescriptor@edf730[ id = my.first.plugin, name = My First Plugin, provider = Coder, version = 1.0, class = myplugin.MyPlugin, requires api = 1.0, runtime = [Ljava.net.URL;@ff94b1]
INIT PLUGINS:
com.nomagic.magicdraw.plugins.PluginDescriptor@edf730[ id = my.first.plugin, name = My First Plugin, provider = Coder, version = 1.0, class = myplugin.MyPlugin, requires api = 1.0, runtime = [Ljava.net.URL;@ff94b1]
TIP!
Looking at file is the best way to find problems when plug-in does not work.
Detail Information
Plug-in Descriptor
Plug-in descriptor is a file written in XML and named plugin.xml. Each descriptor contains properties of one plug-in. Descriptor file should contain one plugin element definition. The plugin.xml file consists of the following elements:
14
PLUG-INS
Detail Information
plugin
Attribute id name version internalVersion provider-name class Description Plug-in ID should be unique. Used to identify plug-in by plug-ins manager internals and by requirements of other plug-ins. For example, my.first.plugin.0. A plug-in name. No strict rules applied to this attribute. For example, Example plugin. A plug-in version. The version can be used to check other plug-ins dependencies if internalVersion is not defined. A plug-in internal version. It is used to check other plug-ins dependencies. A plug-in provider name, that is a company or author name. For example: "No Magic". A full qualified class name. The class must be derived from com.nomagic.magicdraw.plugins. A plug-in and stored in plug-in runtime library. This class will be loaded and initialized by the plug-ins manager. For example, myplugin.MyPlugin. Optional; default value - false. Indicates if to use a plug-in own (separate from other plugins) classloader. All MagicDraw plugins are loaded by the one classloader. If there are plugins that cannot be loaded by the same classloader (for example, because of conflicts in plugin libraries versions or or other), their descriptors must define to use own classloaders.
class-lookup
ownClassloader
Optional; possible values - LocalFirst, GlobalFirst default value - GlobalFirst. Specifies the priority of parent class loader, if plugin is using ownClassloader. LocalFirst forces to load classes from the plugin class loader even if such classes exist in the MagicDraw core class path. This option should be used if you want to use in your plugin different versions of some libraries used in core.
Nested elements Element name requires runtime Description A MagicDraw API version required by plug-in. Plug-ins and their versions required by plug-in. Runtime libraries needed by a plug-in.
requires
Nested elements Element name api required-plugin Description A required MagicDraw API version. Required plug-in(s) to run a plug-in.
api
Attribute version Description A required MagicDraw API version. For example, 1.0.
15
PLUG-INS
Detail Information
required-plugin
Attribute id name internalVersion version Description ID of a required plug-in. For example, my.first.plugin.0. A name of a required plug-in. An internal version of a required plugin. If it is not defined, the version is used to check if the required plugin is suitable. A version of a required plug-in. If it is not defined, any required plugin version is suitable. For example: 1.1.
runtime
Nested elements Element name library Description A runtime library for a running plug-in.
library
Attribute name Description A name of the required library. For example, "patterns.jar".
help
Attribute name Description A name of a compressed JavaHelp file (JAR file). TIP! Adobe RoboHelp provides support for the JavaHelp format and automatically creates all Java-based Help features and the HTML-based features such as a HTML content and hypertext links. path A relative path to the JavaHelp file.
Plug-in Classes
16
PLUG-INS
Detail Information
Plugin is the base abstract class for any MagicDraw plug-in. User written plug-in must be extended from this class. Every plug-in has its own descriptor set by plug-ins manager. Plug-in has two special methods:
public abstract void init() method is called on MagicDraw startup. Plug-in must override this
override this method and return to true if plug-in is ready to shutdown or false in other case. If plug-in returns false, MagicDraw shutdown will be canceled.
public abstract boolean isSupported() method is called before plug-in init. If this method returns
false, plugin will not be initialized. isSupported() may be used to check if plugin can be started for example on this OS. PluginDescriptor is the class that provides information loaded from plugin.xml file (plug-in descriptor) to plugin. Detail information about these classes can be found in javadoc.
17
PLUG-INS
Detail Information
Plugins Manager
MagicDraw Shutdown 7*[for all plugins]: 8: result := close() 9[!result]: cancel shutdown
Optional property class-lookup controls how classes are loaded if plugin has its own classloader. If value of this property is LocalFirst, class is loaded from plugin classpath even if such class is already loaded in global MagicDraw core class loader. This property is important if you want to use different version of the same classes (libraries) used in MagicDraw core.
18
PLUG-INS
directory). For example if MagicDraw is installed in c:\MagicDrawUML plug-in directory will be in c:\MagicDrawUML\plugins. On Unix systems, plug-ins manager additionally uses special directory in user home directory ~\.magicdraw\<version>\plugins for plug-in loading. For example, for user Bob MagicDraw version 10.0 will use directory \home\bob\.magicdraw\10.0\plugins for searching plug-in. Even on Unix systems global plug-in directory is used also. This allows to have global and user plug-ins.
Another issue on Unix systems, is related to permissions of a user to write. If MagicDraw is
installed by root, user will not be allowed to write in a global plug-in directory if a user has not such permissions.
19
PLUG-INS
This plug-in will be required for projects if project contains module my_profile_filename. Plug-in name and version will be saved into projects XMI file.
tions.eclipse.rcp.actions.MDEclipseActionWrapper class. TIP! This is an example of an Eclipse bundle class that connects to a MagicDraw command by its id CUSTOM_ACTION_ID: public class MyCustomActionWrapper extends MDEclipseActionWrapper { public MyCustomActionWrapper() { super(CUSTOM_ACTION_ID); } } NOTE: This is a general way of connecting to an Eclipse command (org.eclipse.ui.actions.ActionDelegate) that should to be placed on the Eclipse main menu.
20
PLUG-INS
2. Create a descriptor (plugin.xml file) for the Eclipse bundle to place the command on the Eclipse
main menu. TIP! This is an example of a descriptor that should place the command with id CUSTOM_ACTION_ID under Diagrams > Diagram Wizards menu: <!-- Define command and attach to category --> <extension point="org.eclipse.ui.commands"> <command id="MyCustomActionWrapper.cmd" name= "Command name" categoryId="MagicDraw"/>
<!-- Attach command (action) to menu --> <extension point="org.eclipse.ui.actionSets"> <actionSet id="Custom action set" label="Custom action label" visible="false"> <action class= "org.my.path.MyCustomActionWrapper" label= "Custom action label" id= "Custom_action_id" menubarPath= "Diagrams/Diagram Wizards/group" definitionId="MyCustomActionWrapper.cmd"/> </actionSet> </extension> <!-- Attach to specific MagicDraw view and editor part --> <extension name="Diagram or View Active" point="org.eclipse.ui.actionSetPartAssociations"> <actionSetPartAssociation targetID="Custom_Action_Part"> <part id="com.nomagic.magicdraw.integrations.eclipse.rcp.editors. DiagramEditor"/> <part id="CONTAINMENT_TREE"/> <part id="INHERITANCE_TREE"/> <part id="DIAGRAMS_TREE"/> <part id="EXTENSIONS_TREE"/> <part id="SEARCH_RESULTS_TREE"/> <part id="DOCUMENTATION"/> <part id="PROPERTIES"/> <part id="MESSAGES_WINDOW"/> </actionSetPartAssociation> </extension>
NOTE: This is a general way of adding a command to the Eclipse main menu.
3. Pack the command class with the Eclipse bundle descriptor to an Eclipse bundle (.jar file) and
The new command will appear on the Eclipse main menu. <MagicDraw installation directory>/plugins/eclipse/plugins contains MagicDraw plugins that have commands to appear on the Eclipse main menu. Their plugin.xml files can be used as examples too.
21
22
Figure 2 -- Eclipse Java Project build path with specified MagicDraw libraries
For more information how to create path variables, see at http://help.eclipse.org/indigo/topic/ org.eclipse.platform.doc.user/concepts/cpathvars.htm.
If the created MagicDraw plug-in uses external libraries which conflict with the same MagicDraw libraries, the ownClassload property value should be set as true in the plug-in descriptor as shown in the following example:
23
<?xml version="1.0" encoding="UTF-8"?> <plugin id="com.nomagic.magicdraw.emfuml2xmi.v3" name="Eclipse UML2 (v3.x) XMI Export/Import" version="1.0" ownClassloader="true" provider-name="No Magic" class="com.nomagic.magicdraw.emfuml2xmi.v3.EmfUml2XmiPlugin"> </plugin>
Figure 3 -- Eclipse Run Configuration settings for starting MagicDraw from Eclipse
Moreover, the heap size configuration and the MagicDraw installation directory should be provided as Java Virtual Machine (JVM) arguments in the Arguments tab of the Run Configuration dialog (see Figure 4 on page 25). MagicDraw output can be redirected to Eclipse Console by specifying the -verbose key as the MagicDraw program argument. Some more MagicDraw environment properties can be added. For instance, if MagicDraw needs to be loaded with custom plug-ins only, the custom directory for MagicDraw plug-ins can be specified as the md.plugins.dir property value:
-Dmd.plugins.dir=C:\development\plugins
MagicDraw can be also started in the debug mode as a regular Java program. To use the standard Eclipse Debug feature, on the Eclipse menu, click Run > Debug. In this case, the MagicDraw plug-in code can be
24 Copyright 2003-2012 No Magic, Inc. .
Figure 4 -- MagicDraw heap size and installation directory configuration as JVM arguments
25
26
We left the same package name for merged interfaces like in previous API version com.nomagic.uml2.ext.magicdraw.**. You do not need to make any changes in your code if you was using interfaces from this layer. Otherwise you need simple change import statement in your java source files. For example - import com.nomagic.uml2.omg.kernel.Element to import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element. UML metamodel interfaces uses Java 5 generics, so it is much easier to understand types of various collections in metamodel.
27 Copyright 2003-2012 No Magic, Inc.
Substitution
Project.getProject(BaseElement) MDDialogParentProvider.getProvider(). getDialogParent() MDDialogParentProvider.getProvider(). setDialogParent(Frame) ElementListProperty. setSelectableRestrictedElements(Colle ction)
28
The core change is an introduction of IProject - IPrimaryProject and IAttachedProject. IPrimaryProject is a single instance in the scope of a MagicDraw project. IPrimaryProject can have attached (used) any number of IAttachedProject (modules), while IAttachedProject itself can attach other modules (IAttachedProject). The earlier than MagicDraw 17.0.1 project structure API made deprecated in version 17.0.1. The deprecated classes from the com.nomagic.magicdraw.core.modules package are: Committable IProject ModuleDescriptor
29 Copyright 2003-2012 No Magic, Inc.
30
ProjectEventListener
The following methods have been added: projectActivatedFromGUI (Project) projectCreated (Project) projectOpenedFromGUI (Project) projectPreActivated (Project) projectPreClosed (Project) projectPreDeActivated (Project) projectPreReplaced (Project,Project) projectPreSaved (Project,boolean) projectPreClosedFinal (Project) An empty implementation of these methods must be added to the custom ProjectEventListener implementation. The code change is not required, if ProjectEventListenerAdapter is extended.
31
fdd8862ed8af.
Project structure information - com.nomagic.ci.metamodel.project. Shared and not shared uml model information:
com.nomagic.magicdraw.uml_model.shared_model, com.nomagic.magicdraw.uml_model.model
Proxies information backup of other external resources. Starts with proxy. Project options ends with
32
33
<genFeatures createChild="false" ecoreFeature="ecore:EAttribute diagram.ecore#//DiagramContentsDescriptor/usedElements"/> <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference diagram.ecore#//DiagramContentsDescriptor/ binaryObject"/> <genFeatures createChild="false" ecoreFeature="ecore:EAttribute diagram.ecore#//DiagramContentsDescriptor/contentHash"/> </genClasses> </genPackages> </genmodel:GenModel>
34
U M L M O D E L I M P L E M E N TA T I O N U S I N G EMF
UML Model Implementation Using EMF
Starting from MagicDraw version 17.0, UML model is implemented using Eclipse Modeling Framework (EMF). A MagicDraw UML model is an EMF model. All UML model classes implement the org.eclipse.emf.ecore.EObject interface. The MagicDraw UML model can be accessed and changed using EMF API. For example:
// get project model Model model = project.getModel(); // create session SessionManager.getInstance().createSession("create class"); // get name attribute EAttribute element_name = UMLPackage.eINSTANCE.getNamedElement_Name(); // get name value (same as model.getName()) Object name = model.eGet(element_name); System.out.println("name = " + name); // change name value (same as model.setName(name + "_1");) model.eSet(element_name, name + "_1"); Class aClass = UMLFactory.eINSTANCE.createClass(); // get packaged element collection Collection collection = (Collection) model.eGet(UMLPackage.eINSTANCE.getPackage_PackagedElement()); // add new class (same result as model.getPackagedElement().add(aClass)) collection.add(aClass); // close session SessionManager.getInstance().closeSession();
Model elements can be created using the EMF UML factory com.nomagic.uml2.ext.magicdraw.metadata.UMLFactory: For example,
Component component = UMLFactory.eINSTANCE.createComponent();
Element will be created in the repository of the active project. UML model elements are not contained by any EMF resource.
35
UML model elements can be created using the EMF factory com.nomagic.uml2.ext.magicdraw.metadata.UMLFactory. For example,
Component component = UMLFactory.eINSTANCE.createComponent();
In MagicDraw 17.0.1, an element created using the EMF factory will be placed in the repository that does not belong to any project. The created object will be automatically added into the projects repository after it will be added into the container that is in the projects repository. All UML model elements are directly or indirectly contained by EMF resources. Resource of an element can be retrieved in the standard way:
Element element; org.eclipse.emf.ecore.resource.Resource resource = element.eResource();
Each MagicDraw project has the one EMF resource for private data (all not shared data) and can have the one resource for shared data (if it has shared data). Project resources can be accessed in the following way:
// get primary project of the active project IPrimaryProject project = Application.getInstance().getProject().getPrimaryProject(); // get UML model project feature UMLModelProjectFeature feature = project.getFeature(UMLModelProjectFeature.class); // get private resource // NOTE: this method will return shared resource for attached project/module Resource privateResource = feature.getUMLModelResource(); //do something with elements directly contained in the resource EList<EObject> contents = privateResource.getContents(); for (EObject element : contents) { // do something with the element }
36
DISTRIBUTING RESOURCES
In this section, you will find information about how to share your created resources with other users. The easiest way to distribute data is to store resources to one zip archive and import the files to MagicDraw with the MagicDraw Resource Manager. For more information about Resource Manager, see MagicDraw UserManual.pdf. You can distribute the following types of resources:
Custom Diagrams Profiles Templates Samples and Documentation Plug-ins.
You can distribute one resource type or you may distribute the whole resources set.
Resource Builder, click Help > Resource/Plugin Manager. In the Resource Builder, select the package containing all needed files, add it to the recourse file, and specify several tags. The resource file you have created can be distributed to your team members and installed using the Resource/Plugin Manager.
Distribute manually. Manual distribution consists of the following steps:
Create required files. For information about what files are required for each type of resource, see Creating Required Files and Folders Structure on page 37. descriptor file, see Resource Manager Descriptor File on page 43.
2 Create Resource descriptor file. For more information about the Resource 3 Archive created files to a zip file. The zip file should include the required files, as
well as folders that have structure matching the structure of MagicDraw. For information about folders structure for each type of resource and to see the general view of the file structure, see Figure 11, Structure of directories and files that could be distributed through the Resource Manager, on page 43.
4 Import your prepared data to MagicDraw through the Resource Manager. For
more information about the MagicDraw Resource Manager, see MagicDraw UserManual.pdf. NOTE MagicDraw Resource Manager supports zip archives only!
37
DISTRIBUTING RESOURCES
For each resource files, there should be a created folders structure, which should match the folders structure of the MagicDraw installation folder. To distribute resources, you must create a resource manager descriptor file, which is described in the section Resource Manager Descriptor File on page 43.
Figure 6 -- Folders and files structure required for custom diagram distribution
For more information about creating new diagram types, see the section Diagram Types on page 94 or see the UML Profiling and DSL UserGuide.pdf custom diagrams creation.
Distributing Profiles
Files for profile distribution are as follows:
CustomProfile.xml.zip (required) CustomProfile.htm
38
DISTRIBUTING RESOURCES
You may choose any name for these files.
For more information about working with Profiles see MagicDraw UserManual.pdf and UML Profiling and DSL UserGuide.pdf.
Distributing Templates
Files for template distribution are as follows:
CustomTemplate.xml.zip (required) CustomTemplate.html
39
DISTRIBUTING RESOURCES
description.html.
40
DISTRIBUTING RESOURCES
Distributing Samples and Documentation
You can distribute your created samples and documentation and import into MagicDraw with the Resource Manager.
Figure 9 -- Folders and files structure required for samples and documentation distribution
Distributing Plug-ins
Required files for plug-in distribution are as follows:
plugins.xml - Plug-in description. For more information about plugin.xml file, see Plug-in
41
DISTRIBUTING RESOURCES
The plug-in term may include all resources that could be distributed. Such as custom diagrams, profiles, templates, samples, and others. See the general structure of the resources that could be distributed with the Resource Manager in the image below.
42
DISTRIBUTING RESOURCES
Figure 11 -- Structure of directories and files that could be distributed through the Resource Manager
43
DISTRIBUTING RESOURCES
MDR_<type>_<plugin name>_<plugin id>_descriptor.xml For example:
MDR_Plugin_CustomPlugin_3001_descriptor.xml.l
NOTE
<edition>Enterprise</edition> <edition>Architect</edition> <edition>Standard</edition> <edition>Professional Java</edition> <edition>Professional C++</edition> <edition>Professional C#</edition> <edition>Professional ArcStyler</edition> <edition>Reader</edition> <edition>OptimalJ</edition> <installation> <file from="data/defaults/data/diagrams/CustomDiagram/descriptor.xml" to="data/defaults/data/diagrams/CustomDiagram/descriptor.xml" /> <file from="profiles/CustomProfile.xml.zip" to="profiles/CustomProfile.xml.zip" /> <file from="templates/CustomTemplate.xml.zip" to="templates/CustomTemplate.xml.zip" /> <file from="samples/CustomPluginSample.mdzip" to="samples/CustomPluginSample.mdzip" /> <file from="manual/CustomPluginManual.pdf" to="manual/CustomPluginManual.pdf" /> <file from="plugins/customPlugin/*.*" to="plugins/customPlugin/*.*" /> <file from="data/resourcemanager/MDR_Plugin_CustomPlugin_3001_descriptor.xml" to="data/resourcemanager/MDR_Plugin_CustomPlugin_3001_descriptor.xml" />
44
DISTRIBUTING RESOURCES
</installation> </resourceDescriptor>
See the terms used in the sample description in the table below: Element id name Description Unique plug-in id. id is used to form a descriptor file name. To prevent duplicates use a number starting from 3000. Plug-in name. Name is used to form a descriptor file name. Plug-in name must be unique between all MagicDraw resources. Type may be one of the following types: Custom Diagram, Plugin, Profile, Sample, Template.
type
version internal version internal is an invisible resource number. This version number is not visible to the user and may be used for an internal count. version internal may only be a number. version human Human readable version number of the resource. This version number is visible to users. version human number may use numbers and/or words. edition installation Supported MagicDraw editions. installation includes files, which will be copied from the custom plug-in archive to the MagicDraw folder. IMPORTANT! Do not use "*.*" ! If file name includes *.*, when uninstalling the plug-in all defined files will be removed. For example if "samples/*.*" is defined, then uninstalling the resource will remove all files from the samples folder.
45
JYTHON SCRIPTING
MagicDraw allows you to access open API using Jython scripting language. MagicDraw on every startup checks for scripts in plugins/com.nomagic.magicdraw.jpython/scripts/. If there are subdirectories each of them are scanned for the script.xml file. This file provides information about script in this directory. File is similar to plug-in descriptor described in plug-ins section (See Plug-in Descriptor on page 14.). If script.xml contains valid information, script file specified in script.xml is executed. Script file should contain valid Jython 2.5.3 script. NOTE For writing scripts the user should have basics of Jython programming language and be familiar with MagicDraw open API.
Creating Script
In the following example we will create script which shows a message on MagicDraw startup. To create script you need:
46
JYTHON SCRIPTING
Creating Script In the table below, you will find the detailed script.xml file structure. Element script Description Attributes Name id name version providername script-file requires-api Description Scrip ID, should be unique. Used to identify script. Example: my.first.script.0 Script name. No strict rules applied to this attribute. Example: Example script Script version. Allows numbers separated with one dot value. Examples: 1.0, 0.1 Script provider name. Company or author name. Example: "No Magic" Relative path to script file. This file will be executed. Example:main.py MagicDraw API version required by script. Example:1.0
After saving files, restart MagicDraw. On MagicDraw startup message dialog should appear.
47
JYTHON SCRIPTING
Jython
PythonPluginDescriptor + getID() : S tring + getName() : S tring + getPluginDirectory() : File + getProvider() : S tring + getRequiresAPI() : S tring + getS criptFileName() : S tring + getVersion() : S tring + setID( id : S tring ) : void + setName( name : S tring ) : void + setPluginDirectory( directory : File ) : void + setProvider( provider : S tring ) : void + setRequiresAPI( version : S tring ) : void + setS criptFileName( aS criptFile : S tring ) : void + setVersion( version : S tring ) : void + toS tring() : S tring
Script can retrieve script directory and other necessary information from pluginDescriptor variable. There is no need to change any other fields for this variable
Jython
Jython is an implementation of the high-level, dynamic, and object-oriented language Python which is seamlessly integrated with the Java platform. Using Jython you may access all java API and MagicDraw open API. This allows to avoid compilation and to get the same results without java code. Using scripting you may do everything that you can achieve using java plug-in, and even more: you may change your code without recompiling and restarting an application. More information about Jython you can find at http://www.jython.org Information about python language you can find at http://www.python.org/
48
Invoking Actions
Actions can be invoked from: Main menu
Main toolbar
Diagram toolbar
49
Keyboard shortcuts
Action cannot be represented in GUI. Create a new action and assign some keyboard shortcut for invoking it.
browser tree and nodes. Recommended to use for performing some actions with the selected browser nodes.
DefaultDiagramAction action class for diagram action. Enables to access some diagram
elements. Recommended to use when for performing some actions with the selected diagram elements.
PropertyAction action for changing some element or application property. Can be used for
changing properties defined by user. You must override at least actionPerformed() method and implement in it what this actions is going to do.
50
51
Icon
Every action can have a small and large icon. Small icon is described as javax.swing.Action.SMALL_ICON and can be used in menu items. Large icon is used in toolbar buttons. Action for toolbar must have a large icon, otherwise it will be displayed as a button with an action name.
// setting icon. Button with icon looks better than // with text in toolbar. action.setLargeIcon( new ImageIcon( getClass().getResource("main_toolbar_icon.gif") ) );
Description
Actions can be added into one of predefined actions groups (see Actions groups below). All actions of one group will be disabled/enabled together. Conditions for groups enabling/disabling and status updating are predefined and cannot be changed. Example: MDAction action = new MDAction("Example","Example",KeyEvent.VK_E, ActionsGroups.PROJECT_OPENED_RELATED);
52
Here you may describe all conditions when an action must be enabled and when disabled. Example of updateState() method for some browser context menu action: public void updateState() { //action will be enabled only if there are some selected nodes. setEnabled(getTree().getSelectedNode() != null ); } If action is not added to any group, updateState() method for all such actions will be invoked after executing any command and after closing/opening project or window. When some actions need to refresh their state, all actions without group can be updated manually:
ActionsStateUpdater.updateActionsState();
Actions in ActionsManagers are configured by many Configurators. Configurator is responsible for adding or removing action into some strictly defined place and position between other actions. There are three types of configurators:
AMConfigurator
The configurator for general purpose. Used for menus, toolbars, browser, and diagrams shortcuts.
BrowserContexAMConfigurator
Configurator for configuring managers for browser context (popup) menu. Can access browser tree and nodes.
DiagramContextAMConfigurator
This configurator for configuring context menus in a diagram. Can access diagram, selected diagram elements and element that requests context menu. ActionsManagers for the main menu and all toolbars are created and configured once, so later actions can be only disabled but not removed. Context menus are created on every invoking, so ActionsManagers are created and configured every time and actions can be added or removed every time.
53
BrowserContextAMConfigurator brCfg = new BrowserContextAMConfigurator() { // implement configuration. // Add or remove some actions in ActionsManager. // tree is passed as argument, provides ability to access nodes. public void configure(ActionsManager mngr, Tree browser) { // actions must be added into some category. // so create the new one, or add action into existing category. MDActionsCategory category = new MDActionsCategory("", ""); category.addAction(browserAction); // add category into manager. // Category isnt displayed in context menu. mngr.addCategory(category); } /** * Returns priority of this configurator. * All configurators are sorted by priority before configuration. * This is very important if one configurator expects actions from * other configurators. * In such case configurator must have lower priority than others. * @return priority of this configurator. * @see AMConfigurator.HIGH_PRIORITY * @see AMConfigurator.MEDIUM_PRIORITY * @see AMConfigurator.LOW_PRIORITY */ public int getPriority() { return AMConfigurator.MEDIUM_PRIORITY; } };
Example 2: add some action into main menu, after creating a new project
// create some action. final MDAction someAction = AMConfigurator conf = new AMConfigurator() { public void configure(ActionsManager mngr) { // searching for action after which insert should be done. NMAction found= mngr.getActionFor(ActionsID.NEW_PROJECT); // action found, inserting if( found != null ) { // find category of "New Project" action. ActionsCategory category = (ActionsCategory)mngr.getActionParent(found);
// get all actions from this category (menu). List actionsInCategory = category.getActions(); //add action after "New Project" action. int indexOfFound = actionsInCategory.indexOf(found); actionsInCategory.add(indexOfFound+1, someAction); // set all actions. category.setActions(actionsInCategory); } } public int getPriority()
54
{ return AMConfigurator.MEDIUM_PRIORITY; } }
55
Actions Hierarchy
AbstractAction (javax.swing)
NMAction (com.nomagic.actions)
ActionsCategory (com.nomagic.actions)
MDActionsCategory (com.nomagic.magicdraw.actions)
MDAction (com.nomagic.magicdraw.actions)
DefaultBrowserAction (com.nomagic.magicdraw.ui.browser.actions)
DefaultDiagramAction (com.nomagic.magicdraw.ui.actions)
PropertyAction (com.nomagic.magicdraw.actions)
56
57
UML MODEL
The MagicDraw UML model is an implementation of the OMG UML 2.4.1 metamodel. We do not provide a very detail description of all UML metamodel elements and their properties in this documentation or javadoc. You can find all this information in the UML 2.4.1 superstructure specification on the Object Management Group official Web site at http://www.omg.org/spec/UML/. You should use UML model interfaces from the com.nomagic.uml2.ext.magicdraw package. The base interface of all model classes is Element, implements the BaseElement inteface.
BaseElement (com.nomagic.magicdraw.uml)
ElementImpl (com.nomagic.magicdraw.uml)
Element (com.nomagic.uml2.ext.magicdraw.classes.mdkernel)
PresentationElement (com.nomagic.magicdraw.uml.symbols)
All structure derived from the Element you can see in OMG-UML 2.4.1 Superstructure Specification. All attributes described in the UML specification are implemented and accessible through setters and getters.
Project
Application (com.nomagic.magicdraw.core)
The Project represents the main storage of all project data like the main Package (Model) and all diagrams. Multiple projects can be opened at the same time. Active (current) project can be accessible in the following way:
58 Copyright 2003-2012 No Magic, Inc.
UML MODEL
The project keeps references to a root Model, also has references to all diagrams.
Root Model
The whole model of one project is contained in a Model instance accessible in the following way:
Model root = Application.getInstance().getProject().getModel();
Container Properties
MagicDraw uses a composite structure of the model. Every model element is a container and contains its own children and knows about its own parent. A model element parent can be accessed with the Element.getOwner() method. Owned children can be received with the Element.getOwnedElement() method. Different types of children are stored in separate container properties. You can access these container properties by names that are described in the UML specification. The getOwnedElement() method collects all children from all inner container properties. Container properties modification and iteration is straightforward using the java.util.Collection interface. Property change events are fired automatically when container properties are modified.
59
UML MODEL
Containers implement subsets and unions constraints from the UML metamodel specification. This explains how the modification of one container can affect other containers. Make sure you understand subsets and unions in the UML metamodel. Some containers are read-only. This is true for all DERIVED UML metamodel properties. For example, Element.getOwnedElement() is read-only. If you want to add some inner Element, you need to add it into a subset of ownedElement, for example, for Package.getOwnedPackageMember(). It is enough to set one UML meta-association property value and an opposite property will be set too. For example, adding a Class into a Package can be done in two ways:
Class myclass = ; Package myPackage ; myClass.setOwner(myPackage);
or
myPackage.getOwnedPackageMember().add(myClass);
NOTE
The get<property name>() method call for an empty container property instantiates an empty collection. This leads to the increased memory usage. So, before iterating, check if a container property is not empty with a method has <property name>().
60
UML MODEL
Visitors
Every Element has the accept() method for visiting it in Visitors (for more details about this mechanism, see the Visitor pattern.)
Visitor +visitCollaboration( element : Collaboration, context : VisitorContext ) : void +visitBehavioralFeature( element : BehavioralFeature, context : VisitorContext ) : void +visitElement( element : Element, context : VisitorContext ) : void +visitComment( element : Comment, context : VisitorContext ) : void +visitClass( element : Class, context : VisitorContext ) : void +visitAssociation( element : Association, context : VisitorContext ) : void +visitDiagram( element : Diagram, context : VisitorContext ) : void +visitCallAction( element : CallAction, context : VisitorContext ) : void +visitAction( element : Action, context : VisitorContext ) : void +visitInclude( element : Include, context : VisitorContext ) : void +visitAttributeView( o : AttributeView ) : void +visitClassView( o : ClassView ) : void +visitActorView( o : ActorView ) : void +visitClassifierView( o : ClassifierView ) : void +visitAssociationTextBoxView( o : AssociationTextBoxView ) : void +visitDiagramShape( o : DiagramShape ) : void +visitActionView( o : ActionView ) : void
...
The Visitor has the visit.. method for all types of model elements and presentation elements. This is very useful when you are working with a large collection of ModelElements and need to perform actions, specific for every type of Element (for example, save/load, copy/paste, or a specific properties setting). Just derive your class from InheritanceVisitor and override some visit.. methods. Example of how to visit a group of ModelElements:
Visitor myVisitor = new Visitor() { // override some visit methods ... }; Model root = Application.getInstance().getProject().getModel(); Iterator it = root.getOwnedElement().iterator(); while (it.hasNext()) { ((Element)it.next()).accept(myVisitor); }
InheritanceVisitor
The InheritanceVisitor is an enhanced implementation of the Visitor pattern and a subclass of Visitor, used for visiting model elements and presentation elements. Every visit.. method calls a visit.. method for a super type. For example, you can put some code into visitClassifier() and it will be executed for all subclasses of a Classifier.
61
UML MODEL
Changing UML Model Model elements visit methods have an additional context parameter of the VisitorContext type. The visitor context is used to track what super type visit methods were already visited (to avoid multiple visits because some model elements have a multiple inheritance). Open API users should not work with the visitor context. All tracking is done in InheritanceVisitor and Visitor classes.
The SessionManager is the singleton manager used for editing model Elements. All modifications to model elements should be performed between createSession(sessionName) and closeSession() method calls. To edit some Element, a session with this manager must be created. After editing a model element, a session must be closed. After that, all changes will be applied to the model and registered in the command history (for undo/redo) as one command with a session name. Only one session can be active. The following code can be used for accessing, checking, and creating the session:
// access a singleton instance by using getInstance() // only one session can be active, so check this. if (!SessionManager.getInstance().isSessionCreated()) { // create a new session. SessionManager.getInstance().createSession();
} If other session is already created and not closed, the createSession method throws the IllegalStateException runtime exception.
62
UML MODEL
Changing UML Model
ModelElementsManager
ModelElementsManager <<getter>>+getInstance() : ModelElementsManager +addElement( element : Element, parent : Element ) : void +moveElement( element : Element, parent : Element ) : void +removeElement( element : Element ) : void +createDiagram( type : String, parent : Namespace ) : Diagram
...
The ModelElementsManager is the singleton utility class for adding and removing a model or moving them to other parents. Also, it helps to create different types of diagrams. This manager can be used only if some session was created with the SessionManager. The ModelElementsManager performs additional checks before the modification, if an element is not read-only. Also, check if an element can be added to a parent is performed. If the ModelElementsManager is not used, a programmer must perform these checks in the code explicitly.
63
UML MODEL
Changing UML Model
// apply changes and add a command into the command history. SessionManager.getInstance().closeSession();
It is the programmer responsibility to ensure that the modified model element is not read-only. To check, if the model element is read-only, use the Element.isEditable() method.
If a given model element cannot be added into a given parent, IllegalArgumentException is thrown. For example, an Operation cannot be added into a Package or an Operation cannot be added into not a locked for editing Class (in the server project). If an element or parent is null, IllegalArgumentException also is thrown. If given element is not editable (read-only), ReadOnlyElementException is thrown. An alternative way to add a new model element without using the ModelElementsManager:
Element parent = ; ElementsFactory f = Application.getInstance().getProject().getElementsFactory(); Class classA = f.createClassInstance(); // create a new session SessionManager.getInstance().createSession("Add class into parent"); if (parent.canAdd(classA)) { classA.setOwner(parent); } // apply changes and add a command into the command history. SessionManager.getInstance().closeSession();
64
UML MODEL
Changing UML Model
// create a new session SessionManager.getInstance().createSession("Remove class"); try { // remove a class ModelElementsManager.getInstance().removeElement(classA); } catch (ReadOnlyElementException e) { } // apply changes and add a command into the command history. SessionManager.getInstance().closeSession();
An alternative way to remove the model element without using the ModelElementsManager:
Class classA = ; // create a new session SessionManager.getInstance().createSession("Remove class"); if (classA.isEditable())) { classA.dispose(); } // apply changes and add a command into the command history. SessionManager.getInstance().closeSession();
65
UML MODEL
Changing UML Model NEW! Example 3: Extract refactoring Use the Refactoring.Extracting class to create the extract manager for a symbol which you want to extract. Configure the extract refactoring by changing ExtractSource and ExtractTarget. Invoke the refactoring with RefactorManager.refactor(). Review refactoring results by inspecting ExtractSource and ExtractTarget.
// Creates an extract refactor manager. ExtractManager extractManager = Refactoring.Extracting.createExtractManager(symbols); if (extractManager != null) { // A session has to be started before refactoring. SessionManager sessionManager = SessionManager.getInstance(); sessionManager.createSession("Extract Refactor Symbols"); // We may control the extract refactor result by modifying extract target. ExtractTarget extractTarget = extractManager.getExtractTarget(); // Create a namespace to which we are going to refactor. Project project = Project.getProject(symbols[0]); Package package1 = project.getElementsFactory().createPackageInstance(); package1.setOwner(project.getModel()); // Set the namespace to which the extract result will go. extractTarget.setTargetNamespace(package1); // Choose target diagram type from allowed diagram types if the default type does not suite. List allowedTargetDiagramTypes = extractTarget.getAllowedTargetDiagramTypes(); extractTarget.setTargetDiagramType(allowedTargetDiagramTypes.get(0)); // Modify reference names which link the extract refactor source to the target. List<? extends ExtractReference> references = extractTarget.getReferences(); for (int i = 0; i < references.size(); i++) { ExtractReference reference = references.get(i); reference.setName(Integer.toString(i)); } // We may control the extract refactor source by modifying the extract source. ExtractSource extractSource = extractManager.getExtractSource(); extractSource.setElementName("sourceElementName"); // Perform actual refactoring. extractManager.extract(); sessionManager.closeSession(); // The element which was created in the source during refactoring. Element sourceElement = extractSource.getElement(); // The element which was created in the target during refactoring. Element targetElement = extractTarget.getElement(); // The diagram which was created in the target during refactoring. DiagramPresentationElement targetDiagram = extractTarget.getDiagram(); }
66
UML MODEL
Changing UML Model NEW! Example 4: Reverse relationship refactoring Relation reverse refactoring can be done using the Refactoring.Reversing.reverseRelationDirection(baseElement) method.
// We have an arbitrary element or symbol which represents a relationship. BaseElement baseElement = ...; // Relationship reversing should be wrapped with session create/close calls. SessionManager sessionManager = SessionManager.getInstance(); sessionManager.createSession("Reverse relation"); // Reverse the relationship. Refactoring.RelationReversing.reverseRelationDirection(baseElement); // Close the session. sessionManager.closeSession();
Creating Diagram
An example how to create and add to a parent element:
Project project = Application.getInstance().getProject(); Namespace parent = project.getModel(); // create a new session SessionManager.getInstance().createSession("Create and add diagram"); try { //a class diagram is created and added to a parent model element Diagram diagram = ModelElementsManager.getInstance(). createDiagram(DiagramTypeConstants.UML_CLASS_DIAGRAM, parent); //open a diagram project.getDiagram(diagram).open(); } catch (ReadOnlyElementException e) { } // apply changes and add a command into the command history. SessionManager.getInstance().closeSession();
67
UML MODEL
Helper.setClientElement() methods.
3. Add a new relationship into some parent by using ModelElementsManager.addElement().
Example 1: Copying an element Element element = ...; // An element to copy/paste. Element parent = ...; // A parent to which the element has to be pasted: either the same project or another project.
SessionManager sessionManager = SessionManager.getInstance(); sessionManager.createSession("Clone"); // A 3rd parameter indicates whether an element name uniqueness should be preserved in the parent. CopyPasting.copyPasteElement(element, parent, true); sessionManager.closeSession();
Example 2: Copying multiple elements and symbols List elements = ...; // Elements to copy/paste. List views = ...; // Symbols to copy/paste. Element parent = ...; // A parent to which elements should be pasted: either the same project or another project.
BaseElement symbolParent = ...; // A parent to which symbols should be pasted. SessionManager sessionManager = SessionManager.getInstance(); sessionManager.createSession("Clone"); // A 4th parameter indicates whether deep or shallow copy is applied. // A 5th parameter indicates whether an element name uniqueness should be preserved in the parent. List baseElements = CopyPasting.copyPasteElements(views, parent, symbolParent, true, true); sessionManager.closeSession();
68
UML MODEL
StereotypesHelper hase a lot methods for working with stereotypes. Example: Creating stereotype, applying to element and then setting tag ElementsFactory elementsFactory = project.getElementsFactory();
// create a profile Profile profile = elementsFactory.createProfileInstance(); profile.setName("myProfile"); ModelElementsManager.getInstance().addElement(profile, project.getModel()); // get a metaclass "Class" com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class metaClass = StereotypesHelper.getMetaClassByName(project, "Class"); // create a stereotype, stereotypes will be applicable to classes Stereotype stereotype = StereotypesHelper.createStereotype(profile, "myStereotype", Arrays.asList(metaClass)); // create a tag definition Property property = elementsFactory.createPropertyInstance(); ModelElementsManager.getInstance().addElement(property, stereotype); // a tag name property.setName("myTag"); // the tag type is String Classifier typeString = ModelHelper.findDataTypeFor(project, "String"); property.setType(typeString); if (StereotypesHelper.canApplyStereotype(element, stereotype)) { // apply stereotype StereotypesHelper.addStereotype(element, stereotype); // set a tag value StereotypesHelper.setStereotypePropertyValue(element, stereotype, "myTag", "myTagValue"); }
Example: Retrieving tag values // find a profile Profile profile = StereotypesHelper.getProfile(project, "myProfile"); // find a stereotype Stereotype stereotype = StereotypesHelper.getStereotype(project, "myStereotype", profile); // get a stereotyped elements List stereotypedElements = StereotypesHelper.getExtendedElements(stereotype); for (int i = stereotypedElements.size() - 1; i >= 0; --i) { // a stereotyped element Element element = (Element) stereotypedElements.get(i); if (stereotype.hasOwnedAttribute()) { // get tags stereotype attributes List<Property> attributes = stereotype.getOwnedAttribute(); for (int j = 0; j < attributes.size(); ++j) { Property tagDef = attributes.get(j); // get a tag value List value = StereotypesHelper.getStereotypePropertyValue( element, stereotype, tagDef.getName()); for (int k = 0; k < value.size(); ++k) { // a tag value Object tagValue = (Object) value.get(k); } } }
69 Copyright 2003-2012 No Magic, Inc. .
UML MODEL
Hyperlinks
Hyperlinks are implemented using stereotypes and tag values. The HyperlinkOwner stereotype has the following its properties (tags):
hyperlinkText - all simple hyperlinks hyperlinkTextActive - an active simple hyperlink hyperlinkModel - all hyperlinks to a model element hyperlinkModelActive - an active hyperlink to a model element
Example: Retrieving hyperlinks // get a stereotype Stereotype stereotype = StereotypesHelper.getStereotype( Project.getProject(element), "HyperlinkOwner"); // get hyperlinked elements List modelValues = StereotypesHelper.getStereotypePropertyValue(element, stereotype, "hyperlinkModel"); for (int i = modelValues.size() - 1; i >= 0; --i) { Element linkedElement = (Element) modelValues.get(i); }
// an active hyperlink Object activeLinkedElement = StereotypesHelper.getStereotypePropertyFirst( element, stereotype, "hyperlinkModelActive"); // get hyperlinked elements List textValues = StereotypesHelper.getStereotypePropertyValue(element, stereotype, "hyperlinkText"); for (int i = textValues.size() - 1; i >= 0; --i) { String link = (String) textValues.get(i); } // an active hyperlink Object activeLink = StereotypesHelper.getStereotypePropertyFirst(element, stereotype, "hyperlinkTextActive");
70
UML MODEL
// add an active hyperlink (only one hyperlink can be active) if (modelActive) { StereotypesHelper.setStereotypePropertyValue(element, stereotype, "hyperlinkModelActive", activeLinkedElement, false); } else { StereotypesHelper.setStereotypePropertyValue(element, stereotype, "hyperlinkTextActive", activeHttpLink, false); }
71
P R E S E N TA T I O N E L E M E N TS
Presentation Element
UML semantic defines only UML metamodel. MagicDraw application has its own structure of classes for UML elements representation in the diagram. Base class of this structure is PresentationElement. A presentation element is a textual or graphical presentation of one or more model elements. In the metamodel, a PresentationElement is the BaseElement that presents a set of model elements to a user. It is the base for all metaclasses used for presentation. All other metaclasses with this purpose are indirect subclasses of PresentationElement. Current version of MagicDraw Open API provides just a basic structure of presentation elements.
Every presentation element can have children. For example DiagramPresentationElement has a collection of inner presentation elements. PresentationElement of some Package can have a collection of presentation elements for inner Package elements. Use PresentationElementsManager to create/modify/delete inner presentation elements of the diagram. Current version PresentationElement API allows you to:
Access element bounds PresentationElement.getBounds method). Access children of the element (PresentationElement.getPresentationElements method). Access properties of the element (PresentationElement.getProperty and
PresentationElement.getPropertyManager methods). The sample of properties would be Suppress Operations property for class presentation element, Autosize property for any ShapeElement.
Access model Element of presentation element (PresentationElement.getElement method).
72
PRESENTATION ELEMENTS
Diagram Presentation Element
Access presentation elements of model Element (Project.getSymbolElementMap and
SymbolElementMap.getAllPresentationElements)
Select/unselect or access selection state of the presentation element.
A subclass of presentation elements PathConnector provides information about connected paths to the presentation element. To get a collection of connected paths to the presentation element, use method PathConnector.getConnectedPathElements().
setters" when you edit a group of properties. All changes will be applied and dependent elements will be notified on session close. The same rules are for add, sAddPresentationElement and remove, sRemovePresentationElement accessories. The same naming rules are used in all MagicDraw.
To get a DiagramPresentationElement of the diagram, use Project.getDiagram. To get a collection of inner presentation elements, use
PresentationElement.getPresentationElements method. This method will return only direct children of the diagram.
To open a diagram in the MagicDraw UI, call DiagramPresentationElement.open. To create/modify/delete inner presentation elements of the diagram, use
PresentationElementsManager.
To layout diagram, use: DiagramPresentationElement.layout
NOTE: make sure diagram is opened and the session is closed before doing layout.
73
PRESENTATION ELEMENTS
Shapes
Shapes
Shapes are presentation elements created for such model elements as classes, packages, models, subsystems and others.
Paths
Paths are presentation elements created for such model elements as Relationships. Path has the following attributes:
Supplier the presentation element of Relationship supplier. Use method
74
PRESENTATION ELEMENTS
Presentation Elements Manager PresentationElementsManager can be used only in already loaded and active project. In other cases, results can be unpredictable.
PresentationElementsManager is implemented as a singleton. Use method PresentationElementsManager.getInstance to get a shared instance of this manager.
The diagram is not passed into createPathElement method. A new path element is created in the same diagram as client or supplier presentation elements.
75
PRESENTATION ELEMENTS
Presentation Elements Manager
Every shape element has a preferred size. Shape size cannot be smaller than the preferred size. If you will try to set smaller bounds, these bounds will be increased to the preferred size. If shape has Autosize property set to TRUE, bounds will be reduced to the preferred size.
76
PRESENTATION ELEMENTS
Notification of Presentation Element Draw The order of points in the list must be from the supplier to client connection point. The list may or may not include the client and supplier connection points. At first the given points list will be adopted for the current path style (Rectilinear, Bezier. or Oblique) and only then applied for the path.
All related presentation elements - all children and connected paths - will be removed too.
PresentationElement.useParentProperties.
To get all properties of this element, use method PresentationElement.getPropertyManager. To get property of this element with given ID, use method PresentationElement.getProperty. To change element properties, use method
PresentationElementsManager.setPresentationElementProperties. The given PropertyManager can have only few elements properties (for example just properties you want to change). The following code snippet shows how to change element properties:
ShapeElement element = ; PropertyManager properties = new PropertyManager(); properties.addProperty(new BooleanProperty(PropertyID.AUTOSIZE, true)); SessionsManager.getInstance().createSession(Test); PresentationsElementsManager.getInstance().setPresentationElementProperties(elemen t, properties); SessionsManager.getInstance().closeSession();
The properties must be new instances. You cannot do something like this:
ShapeElement element = ; PropertyManager properties = element.getPropertyManager(). properties.getProperty(PropertyID.AUTOSIZE).setValue(new Boolean(true); SessionsManager.getInstance().createSession(Test); PresentationsElementsManager.getInstance().setPresentationElementProperties(elemen t, properties); SessionsManager.getInstance().closeSession();
PRESENTATION ELEMENTS
Displaying Related Symbols
// element removal listener final PropertyChangeListener removeListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (ExtendedPropertyNames.REMOVE.equals(evt.getPropertyName())) { // symbol removed } } }; // element drawing listener SymbolDrawListener symbolDrawListener = new SymbolDrawListener() { public void symbolAdded(DiagramPresentationElement diagram, PresentationElement symbol, String actionID) { // sybmbol added // register listener symbol.addPropertyChangeListener(removeListener); } }; // register draw listener SymbolDrawNotification symbolDrawNotification = SymbolDrawNotification.getSymbolDrawNotification(project); symbolDrawNotification.addSymbolDrawListener(symbolDrawListener);
Example: Displaying related generalizations and interface realizations SessionManager sessionManager = SessionManager.getInstance(); sessionManager.createSession("Display related");
Set linkTypes = new HashSet(); linkTypes.add(new LinkType(Generalization.class)); linkTypes.add(new LinkType(InterfaceRealization.class)); DisplayRelatedSymbolsInfo info = new DisplayRelatedSymbolsInfo(linkTypes); info.setDepthLimited(true); info.setDepthLimit(3); PresentationElement view = ...; // A symbol for which you need to invoke the displaying related symbols action. DisplayRelatedSymbols.displayRelatedSymbols(view, info); sessionManager.closeSession();
78
SYMBOLS RENDERING
The custom symbol rendering API allows modifying the default symbol view. This API includes:
1. Renderer provider API, which allows to provide a custom renderer for the specific symbol. 2. Renderers, which can modify the default symbol appearance.
In this chapter, we will review, how to register the specific symbol views provider and give the example covering the custom renderers for the package, slots, and dependency link symbols. TIP! You can find the code examples in <MagicDraw installation directory>\openapi\examples\symbolrendering folder.
The main method of the PresentationElementRendererProvider is getRenderer(presentationElement). This method provides a specific renderer for the given presentationElement (Symbol) or null.
Registering Provider
To make your provider start working, you must register it into the PresentationElementRendererManager. To add your provider into the custom providers list, use the following method:
PresentationElementRendererManager.getInstance().addProvider(new RendererProvider());
79
SYMBOLS RENDERING
Custom Renderers Sample
Figure 13 -- Abstract class of the Presentation Element Renderer Class, by default extended with specific renderers for path symbols (PathRenderer) and for shape symbols (ShapeRenderer)
The dependency link is a blue thicker line with custom line ends.
SYMBOLS RENDERING
Custom Renderers Sample
The custom slot renderer (used in instance specification symbol) - the slot values are rounded:
class SlotRenderer extends ShapeRenderer { public String getText(PresentationElement presentationElement, PresentationElementTextEnum textEnum) { if (PresentationElementTextEnum.NAME.equals(textEnum)) { // the slot text is shown as name Element element = presentationElement.getElement(); if (element instanceof Slot) { Slot slot = (Slot) element; if (slot.hasValue()) { String string = ""; List<ValueSpecification> values = slot.getValue(); for (ValueSpecification value : values) { if (value instanceof LiteralString) { LiteralString literalString = (LiteralString) value; if (string.length() > 0) { string += "; "; } String literalValue = literalString.getValue(); try { // round value double doubleValue = Double.parseDouble(literalValue); double rounded = Math.round(doubleValue * 100) / 100; literalValue = Double.toString(rounded); } catch (NumberFormatException e) { } string += literalValue; } } return slot.getDefiningFeature().getName() + "=" + string; } } } return super.getText(presentationElement, textEnum); } }
The custom dependency link renderer - the dependency link is a blue thicker line with custom line ends:
class DependencyRenderer extends PathRenderer { private PathEndRenderer mClientEndRenderer; DependencyRenderer()
81 Copyright 2003-2012 No Magic, Inc. .
SYMBOLS RENDERING
Custom Renderers Sample
{ // custom client end renderer - use filled circle at the end mClientEndRenderer = new PathEndRenderer(PathEndAdornment.CIRCLE, PathEndAdornmentModifier.FILLED); } public Color getColor(PresentationElement presentationElement, PresentationElementColorEnum colorEnum) { if (PresentationElementColorEnum.LINE.equals(colorEnum)) { // use blue color for line return Color.BLUE; } return super.getColor(presentationElement, colorEnum); } protected PathEndRenderer getClientEndRenderer(PathElement pathElement) { // use custom end renderer return mClientEndRenderer; } public int getLineWidth(PresentationElement presentationElement) { // line with is 2 return 2; } protected void drawPathAdornment(Graphics g, PathElement pathElement) { super.drawPathAdornment(g, pathElement); // draw circle at the middle of the dependency line Color background = Color.WHITE; Property property = pathElement.getDiagramPresentationElement() .getProperty(PropertyID.DIAGRAM_BACKGROUND_COLOR); if (property != null) { Object value = property.getValue(); if (value instanceof Color) { background = (Color) value; } } Point middlePoint = pathElement.getMiddlePoint(); int diameter = 10; int radius = diameter / 2; int x = middlePoint.x - radius; int y = middlePoint.y - radius; Color color = g.getColor(); g.setColor(background); g.fillOval(x, y, diameter, diameter); g.setColor(color); g.drawOval(x, y, diameter, diameter); } }
82
SYMBOLS RENDERING
Custom Renderers Sample Step 1. Creating the RendererProvider class
/** * Custom renderers provider. */ class RendererProvider implements PresentationElementRendererProvider { private SlotRenderer mSlotRenderer; private DependencyRenderer mDependencyRenderer; private PackageRenderer mPackageRenderer; RendererProvider() { mSlotRenderer = new SlotRenderer(); mDependencyRenderer = new DependencyRenderer(); mPackageRenderer = new PackageRenderer(); } public PresentationElementRenderer getRenderer(PresentationElement presentationElement) { if (presentationElement instanceof SlotView || presentationElement instanceof SlotListElementView) { // slot renderer return mSlotRenderer; } if (presentationElement instanceof DependencyView) { // dependency renderer return mDependencyRenderer; } if (presentationElement instanceof PackageView) { // package renderer return mPackageRenderer; } return null; } };
83
D I A G R A M E V E N TS
Diagram Listener Adapter
MagicDraw provides an API to listen to all diagram changes in a single adapter that works in the following order:
1. Receives events from all opened diagrams. 2. Delegates these events to your own listener.
NOTE
To listen to diagram change events, you need to create the DiagramListenerAdapter object, i.e., pass the property change listener as a delegator, which will receive all events. To create the DiagramListenerAdapter object, call:
new DiagramListenerAdapter(propertyChangeListener)
The DiagramListenerAdapter object is registered for a project on its creation. To start using the adapter, install it. Uninstall it when it is no longer necessary, i.e., you do not need to receive any events about diagram changes anymore. Example: Listening to symbol creation / removal DiagramListenerAdapter adapter = new DiagramListenerAdapter(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { String propertyName = evt.getPropertyName(); if (ExtendedPropertyNames.VIEW_ADDED.equals(propertyName) || ExtendedPropertyNames.VIEW_REMOVED.equals(propertyName)) { // added / removed symbol PresentationElement presentationElement = (PresentationElement) evt.getNewValue(); } } });
adapter.install(project); // When the adapter is no longer needed, uninstall it. adapter.uninstall(project);
84
PATTERNS
MagicDraw provides API for applying some design pattern for the selected classifier (patterns target). Pattern can modify a target classifier or even the whole model. It also can create presentation elements in the target diagram. Patterns functionality is implemented in a separate MagicDraw plug-in. ID of this plug-in is com.nomagic.magicdraw.plugins.impl.patterns. Usually specific patterns must be added into MagicDraw application as separate plugins.
Target Concept
Patterns target encapsulates information about classifier you want to apply pattern for.
Target owns:
classifier classifiers presentation element diagram of presentation element
Target is passed to patterns method AbstractPattern.applyPattern. It is also accessible from patterns properties AbstractPatternProperties.
Using PatternHelper
Open API provides a helper class PatternHelper with useful methods for patterns. For more details about methods of this class, see javadoc.
Abstract Pattern
Every implementation of specific pattern must provide:
Pattern properties Pattern panels Pattern implementation
85
PATTERNS
Target Concept This information is encapsulated in PatternInfo class.
PatternInfo is used only for pattern registration in the PatternsManager. Other usages are internal and do not impact specific patterns. Every pattern must provide main properties and optionally can have extended properties. Main properties are used for user input from the first pattern wizard page. Extended properties may be used for storing user input from other wizard pages.
Specific pattern must provide implementation of this abstract class and override AbstractPatternProperties.configurePropertyManager method. If specific pattern has some extended properties, it must override AbstractPatternProperties .configureExtendedPropertyManager method. Both these methods must configure some property manager add or remove properties from it. Every pattern must provide one or more panels for the patterns wizard. First wizard page is always used for displaying main pattern properties. Other pages are optional and may be pattern specific. AbstractPanelContainer class is used for providing the following information:
Specific pattern must provide pattern implementation class. This class must extend AbstractPattern and implement AbstractPattern.applyPattern and AbstractPattern.getCategorizedName methods.
86
PATTERNS
MyPattern does not have extended properties, so we do not override configureExtendedPropertyManager method. Names of MagicDraw properties can be translated into other languages, so they are not hard coded inside the properties. To get property name from property ID, PropertyResourceProvider is used. We will write a simple PropertyResourceProvider for our patterns properties.
package com.nomagic.magicdraw.plugins.impl.examples.mypatterns; import com.nomagic.magicdraw.properties.PropertyResourceProvider; public class MyResourceProvider implements PropertyResourceProvider { /** * Instance of this provider. */ private static MyResourceProvider mInstance; /** * Returns shared instance of this provider. * @return shared instance. */ public static MyResourceProvider getInstance() { if(mInstance == null) {
87 Copyright 2003-2012 No Magic, Inc. .
PATTERNS
mInstance = new MyResourceProvider(); } return mInstance; } /** * Returns resource for given key. * @param key a resource key. * @return the resource for given key. */ public String getString(String key) { if(key.equals(MyPatternProperties.METHOD_NAME)) { return "Method Name"; } else if(key.equals(MyPatternProperties.TARGET_CLASS)) { return "Target Class"; } return null; } }
public class MyPattern extends AbstractPattern { /** * Returns the categorized name of the pattern. * @return name of the pattern {"Common", "MyPattern"}. */ public String[] getCategorizedName() { return new String[]{"Common", "My Pattern"}; } /** * Applies design pattern to the target, using properties, passed as an argument. * @param target Target, the pattern is applied for. * @param prop the pattern properties.
88
PATTERNS
*/ public void applyPattern(Target target, AbstractPatternProperties prop) throws ReadOnlyElementException { PropertyManager propManager = prop.getPropertyManager(); String methodName = propManager.getProperty(MyPatternProperties.METHOD_NAME).getValue().toString(); ElementsFactory ef = Application.getInstance().getProject().getElementsFactory(); Operation op = ef.createOperationInstance(); op.setName(methodName); PatternHelper.addDistinctOperation(target.getTargetClassifier(), op); } }
Add method into a classifier. Description.html file must be in the same package as MyPattern class.
PATTERNS
Pattern must be registered in the PatternsManager with PatternsManager .addPattern method. Also we need to provide plugin.xml for this plug-in.
<?xml version="1.0" encoding="UTF-8"?> <plugin id="mypatterns.MyPattern" name="My Pattern" version="1.0" provider-name="No Magic" class="com.nomagic.magicdraw.plugins.impl.examples.mypatterns.MyPatternPlugin"> <requires> <api version="1.0"/> <required-plugin id="com.nomagic.magicdraw.plugins.impl.patterns"/> </requires> <runtime> <library name="mypatterns.jar"/> </runtime> </plugin>
Compile all classes and bundle them into mypatterns.jar file. Also add Description.html into this jar file. Create subfolder mypatterns in the <MagicDraw install>/plugins directory and copy mypatterns.jar and plugin.xml into it. For more details how to deploy plug-in, see Plugins chapter.
90
See an example of how to create the use case scenario with the basic, alternative, and exceptional flows. After the use case scenario is created, the activity diagram is opened. In the activity diagram, the use case scenario is represented as an action flow.
// Creates a use case scenario. Scenario scenario = ScenarioManager.createScenario(useCase); // Sets the scenario name. scenario.setName("Extract money from ATM."); // Adds a basic flow step. FlowStep flowStep1 = scenario.addFlowStep(); // Sets a name for the basic flow step. flowStep1.setName("Insert card"); FlowStep flowStep2 = scenario.addFlowStep(); flowStep2.setName("Enter pin"); FlowStep flowStep3 = scenario.addFlowStep(); flowStep3.setName("Good bye"); // Adds an alternative condition for the basic flow step. AlternativeCondition condition = scenario.addAlternativeCondition(flowStep2); // Sets a condition guard. condition.setIfCondition("Pin correct"); // Sets a name for the alternative flow step. FlowStep flowStep = condition.getAlternativeFlowSteps().get(0); flowStep.setName("Extract money"); // Adds an exception type to the basic flow step. ExceptionType exceptionType = scenario.addExceptionType(flowStep2); // Sets a name for the exception type. exceptionType.setName("Card expired"); // Sets a name for the exceptional flow step. FlowStep exceptionalFlowStep = exceptionType.getExceptionalFlowSteps().get(0); exceptionalFlowStep.setName("Inform customer about expired card"); sessionManager.closeSession(); // Opens and layouts the activity diagram. ScenarioManager.displayScenario(scenario, true, true, "Open ATM Scenario");
For more information about the use case scenario, see Use Case Scenario in MagicDraw UserManual.pdf.
91
PROPERTIES
MagicDraw Open API provides a set of classes used as properties for diagrams symbols and design patterns.
Every property has two major attributes ID and value. Property has value of type java.lang.Object. Every specific property has value of specific type. For example value of BooleanProperty is java.lang.Boolean, value of String property is java.lang.String. Property ID identifies specific property in the properties set. Every property has specific editor for editing value of the property. For example BooleanProperty is edited with javax.swing.JCheckBox component, ChoiceProperty with javax.swing.JComboBox, StringProperty with javax.swing.JTextField. You may provide your own PropertyEditor for some specific property editing. In order to do this you need to override Property.createEditor method.
You must set some PropertyResourceProvider to your property instance in order to display normal name, not id of the property in the MagicDraw UI. PropertyResourceProvider has just one method PropertyResourceProvider.getString(key), where key is id of your property.
92
PROPERTIES
The collections of properties are grouped by PropertyManagers. Every PropertyManager has name and list of properties. It can return property by id, properties with the same value, properties whose values are different.
For more details about every specific kind of property see javadoc.
93
D I A G R A M TY P E S
There are two groups of the diagrams in MagicDraw creatable and not creatable. Only the diagrams of a creatable type can be created (instantiated). A not creatable diagram serves as the base for other types of diagrams. MagicDraw has 17 predefined types of diagrams (13 creatable and 4 not). Creatable diagrams are:
Class Use Case Object Communication Sequence State Machine Protocol State Machine Activity Composite Structure Component Deployment Package Profile
Communication and Sequence diagrams are subdiagrams of the Interaction diagram. Activity, Interaction, State Machine, and Protocol State Machine diagrams are subdiagrams of the Behavior diagram.
94
D I A G R A M TYP E S
Diagram Types Hierarchy
The only way to add a new diagram type in MagicDraw is to extend one of the already existing diagram types and register it. This mechanism is described below.
diagram,
getSingularDiagramTypeHumanName() method returns diagram type human name. getPluralDiagramTypeHumanName() method returns diagram type human name in plural
form.
getSuperType() this method must return a super type (super diagram) of this diagram type. isCreatable() returns flag indicating if this diagram type will be creatable.
95
D I A G R A M TYP E S
getLargeIcon() returns a large icon for this type of the diagram (see "Adding New
Functionality").
getSmallIconURL() returns a small icon URL for this type of the diagram (see "Adding New
Functionality").
getDiagramActions() returns manager of actions used in the diagram. getDiagramToolbarConfigurator() returns action manager configurator which configures
described diagram context menu actions (see "Adding New Functionality"). Example 1: example diagram descriptor (see OpenAPI examples for the full source code)
/** * Descriptor of specific diagram. */ public class SpecificDiagramDescriptor extends DiagramDescriptor { public static final String SPECIFIC_DIAGRAM = "Specific Diagram"; /** * Let this diagram type be a sub type of class diagram type. */ public String getSuperType() { return DiagramType.UML_CLASS_DIAGRAM; } /** * This is creatable diagram. */ public boolean isCreatable() { return true; } /** * Actions used in this diagram. */ public MDActionsManager getDiagramActions() { return SpecificDiagramActions.ACTIONS; } /** * Configurator for diagram toolbar. */ public AMConfigurator getDiagramToolbarConfigurator() { return new SpecificDiagramToolbarConfigurator(); } /** * Configurator for diagram shortcuts. */ public AMConfigurator getDiagramShortcutsConfigurator() { return new ClassDiagramShortcutsConfigurator(); } /** * Configurator for diagram context menu.
96 Copyright 2003-2012 No Magic, Inc. .
D I A G R A M TYP E S
*/ public DiagramContextAMConfigurator getDiagramContextConfigurator() { return new BaseDiagramContextAMConfigurator(); } /** * Id of the diagram type. */ public String getDiagramTypeId() { return SPECIFIC_DIAGRAM; } /** * Diagram type human name. */ public String getSingularDiagramTypeHumanName() { return "Specific Diagram"; } /** * Diagram type human name in plural form. */ public String getPluralDiagramTypeHumanName() { return "Specific Diagrams"; } /** * Large icon for diagram. */ public ImageIcon getLargeIcon() { return new ImageIconProxy(new VectorImageIconControler(getClass(), "icons/specificdiagram.svg", VectorImageIconControler.SVG)); } /** * URL to small icon for diagram. */ public URL getSmallIconURL() { return getClass().getResource("icons/specificdiagram.svg"); } }
D I A G R A M TYP E S
/** * Return true always, * because this plugin does not have any close specific actions. */ public boolean close() { return true; } /** * Return true always, * because this plugin does not * have any specific suportability conditions. */ public boolean isSupported() { return true; }
98
P R O J E C TS MANAGEMENT
ProjectsManager
A project is the fundamental MagicDraw data structure. Project includes all UML model and diagrams. ProjectsManager class is responsible for containment and management of projects.
Application (com.nomagic.magicdraw.core)
ProjectsManager (com.nomagic.magicdraw.core)
* Project (com.nomagic.magicdraw.core)
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
ProjectsManager (com.nomagic.magicdraw.core)
...
+closeProject() : void +createProject() : Project +getActiveProject() : Project +getProjects() : List +isProjectActive( project : Project ) : boolean +loadProject( descriptor : ProjectDescriptor, silent : boolean ) : void +saveProject( descriptor : ProjectDescriptor, silent : boolean ) : void +setActiveProject( project : Project ) : void
...
ProjectsManager provides API for Project creating, closing, saving, loading, and activating. MagicDraw can have multiple opened projects, but only one project can be active.
//gets all projects List projects = projectsManager.getProjects(); //gets active project Project activeProject = projectsManager.getActiveProject();
99
P R O J E C TS M A N A G E M E N T
ProjectDescriptor
ProjectDescriptor
ProjectDescriptor represents a project (and module) as a resource for storing and loading. The same project can have multiple ProjectDescriptors.
Represents a local ordinary project. A descriptor can be created for a project or file object.
Remote project descriptor
Represents a project stored in the Teamwork Server. NOTE. Teamwork Project has both descriptors (local and remote) because it can be saved locally.
Location is some specific String value that holds all information that is needed to access a project.
Representation String
Project Management
Projects are saved and loaded by using two methods in ProjectsManager class.
saveProject(ProjectDescriptor descriptor, boolean silent) loadProject(ProjectDescriptor descriptor, boolean silent)
NOTES
Save and Load means Commit and Update for the Teamwork Project. A project cannot be saved using descriptor, if project isnt specified and a project cannot be loaded, if file isnt specified. In such cases IllegalStateException is thrown.
Silent mode means that during save or load process no GUI interruptions for user input will be used, e.g. no Commit Project dialog box while committing a teamwork project or no Save dialog box while saving a new project (project will be saved into the last used directory). Example: Saving active project
ProjectsManager projectsManager = Application.getInstance().getProjectsManager(); // active project Project project = projectsManager.getActiveProject(); // get project descriptor
100
P R O J E C TS M A N A G E M E N T
ProjectDescriptor
Example: Loading project from file Project can be loaded, if projects file name is known:
ProjectsManager projectsManager = Application.getInstance().getProjectsManager(); File file = new File(projectFilePath); // create project descriptor ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI()); projectsManager.loadProject(projectDescriptor, true);
Example: Importing another MagicDraw project file Project can be imported, if project's file name is known:
ProjectsManager projectsManager = Application.getInstance().getProjectsManager(); File file = new File(projectFilePath); // create project descriptor ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI()); projectsManager.importProject(projectDescriptor);
Example: Loading teamwork project Project can be loaded, if projects qualified name is known:
ProjectsManager projectsManager = Application.getInstance().getProjectsManager(); // create project descriptor ProjectDescriptor projectDescriptor = TeamworkUtils .getRemoteProjectDescriptorByQualifiedName(remoteProjectQualifiedName); if (projectDescriptor != null) { projectsManager.loadProject(projectDescriptor, true); }
Module Management
ProjectsManager also provides module management (module usage, export, import, reload, and package sharing) methods. Example: Exporting local module Collection of project packages can be exported as module.
ProjectsManager projectsManager = Application.getInstance().getProjectsManager(); File file = new File(moduleFilePath); ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI()); // export collection of packages as module projectsManager.exportModule(project, packages, "My local module", projectDescriptor);
101
P R O J E C TS M A N A G E M E N T
ProjectDescriptor Example: Exporting teamwork module TeamworkUtils class is used to export teamwork module.
TeamworkUtils.exportTeamworkModule(project, packages, "My remote module", remoteProjectQualifiedName);
Example: Using module Local and teamwork module usage does not differs. Just appropriate project descriptor must be used:
ProjectsManager projectsManager = Application.getInstance().getProjectsManager(); File file = new File(moduleFilePath); ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI()); // use module projectsManager.useModule(project, projectDescriptor);
Example: Importing module Local and teamwork module import does not differs. Just appropriate project descriptor must be used:
ProjectsManager projectsManager = Application.getInstance().getProjectsManager(); File file = new File(moduleFilePath); ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI()); projectsManager.importModule(project, projectDescriptor);
Example: Reloading module Local and teamwork module reload does not differs. Just appropriate project descriptor must be used:
ProjectsManager projectsManager = Application.getInstance().getProjectsManager(); File file = new File(moduleFilePath); ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI()); projectsManager.reloadModule(project, projectDescriptor);
102
P R O J E C TS M A N A G E M E N T
Merging and Differencing Example: Editing module within project
final String moduleFileName = new File(moduleFilePath).getName(); // find module (attached project) directly used by project (primary project) final IAttachedProject attachedProject = ProjectUtilities.findAttachedProjectByName(project, moduleFileName, false); if (attachedProject != null) { final ModuleUsage moduleUsage = new ModuleUsage(project.getPrimaryProject(), attachedProject); final Set<ModuleUsage> moduleUsages = Collections.singleton(moduleUsage); final boolean readOnly = attachedProject.isReadOnly(); if (readOnly) { // make module editable (read-write accessibility mode) ModulesService.setReadOnlyOnTask(moduleUsages, false); } // get first shared module package final Collection<Package> sharedPackages = ProjectUtilities.getSharedPackages(attachedProject); final Package aPackage = sharedPackages.iterator().next(); // create class in module SessionManager.getInstance().createSession("Create use case in module"); final Class aCase = project.getElementsFactory().createClassInstance(); aCase.setOwner(aPackage); aCase.setName("myClass"); SessionManager.getInstance().closeSession(); // save module Application.getInstance().getProjectsManager().saveModule(project, attachedProject, true, false); if (readOnly) { // make module not editable (read-only accessibility mode) ModulesService.setReadOnlyOnTask(moduleUsages, true); } }
ects are loaded (if they are not loaded yet) for comparing.
2. Accepting / rejecting changes. All target changes that do not conflict source changes are
applied (source changes conflicting with target changes are rejected). A 2-way merge produces source changes only. 3. Applying accepted changes. Changes are applied on the ancestor project (target and ancestor projects are the same in a 2-way merge). There are API for standard and advanced project merging. Standard API encapsulates all three steps into a single call, while advanced merge API allows for accessing the project differences and controlling what changes are accepted or rejected. See the sample API usage code in the <MagicDraw install>\openapi\examples\merge folder. The following figure shows the domain model of the merging.
103
P R O J E C TS M A N A G E M E N T
Merging and Differencing
104
PROJECT OPTIONS
MagicDraw UML provides the API for adding the custom project options. These options are project related and saved together with project data. In this chapter, we will describe how to add a new property and how to retrieve its value.
105
PROJECT OPTIONS
Retrieving Project Option Value
106
ENVIRONMENT OPTIONS
Adding Custom Environment Options
Application-related options are referred to as environment options. They are saved in the global.opt file that is located in <user home directory>\.magicdraw\<version number>\data. You can add custom environment options for MagicDraw. To add your own environment options
1. Extend the AbstractPropertyOptionsGroup class. 2. Add the extending class to application environment options.
Example: Adding custom environment options class MyOptionsGroup extends AbstractPropertyOptionsGroup { ... }
Application application = Application.getInstance(); EnvironmentOptions options = application.getEnvironmentOptions(); options.addGroup(new MyOptionsGroup());
107
EVENT SUPPORT
MagicDraw UML provides the API for listening to the events, while changing a model. There is a possibility either to get an event immediately after the property has been changed, or get the event about all the changes in the transaction, after this transaction has been executed. There are four different listener registration types:
Whole repository listener. You will get events about the changes in all the elements. Specific element listener. You will get events about the changes in any property of this
element.
Elements specific property listener. You will get events about the changes of this property. Specific element type listener. You will get events about the changes in all the elements of
the specific type. The transaction listener is notified, when all the changes within the transaction are done and the transaction is closed. TIP! You can find the code examples in <MagicDraw installation directory>\\openapi\examples\events.
108
EVENT SUPPORT
The events names related with model creation/ deletion are listed in the com.nomagic.uml2.ext.jmi.UML2MetamodelConstants class. An example of the elements name change event: PropertyChangeEvent.getPropertyName() == PropertyNames.NAME; PropertyChangeEvent.getNewValue() will be new name of element; PropertyChangeEvent.getOldValue() will be name of element before change. An example of the new element creation event: PropertyChangeEvent.getPropertyName() == UML2MetamodelConstants.INSTANCE_CREATED; PropertyChangeEvent.getNewValue() will be new created element; PropertyChangeEvent.getOldValue() will be null.
the elements.
If the listener is registered at the specific element, it receives events about the changes in any
about the changes in all the elements of the specific type. The PropertyChangeListener should be registered to receive these events. There are several different ways to register a listener: To listen for the whole repository, use:
project.getRepositoryListenerRegistry().addPropertyChangeListener(listener, (RefObject)null);
This listener will get all the property change events from any element. To listen for any delete operation in the repository and get notified before the delete action is performed, use:
project.getRepositoryListenerRegistry().addPropertyChangeListener(listener, UML2MetamodelConstants.BEFORE_DELETE);
This listener will be notified, when any model element is set to be deleted. To listen for any property changes of the specific element, use:
element.addPropertyChangeListener(listener);
This listener will be notified when any element property is changed. To listen for any property changes of the specific element type, use:
project.getSmartEventSupport().registerConfig(aClass, configs, listener);
109
EVENT SUPPORT
This listener will be notified, when any property of any element in the project with this type is changed. If configs is NULL, the listener will get all property change events. NOTE EventSupport could be disabled from event firing To check if it is enabled, use: project.getRepository().getEventSupport().isEnableEv entFiring() To stop/ start event firing, use: project.getRepository().getEventSupport().setEnableE ventFiring(...)
The listener contains transactionCommited() method, which provides a collection of all PropertyChangeEvents that were executed in a transaction.
110
EVENT SUPPORT
Event Listening Sample
Listener for listening to the specific property (NAME) of the specific element:
element.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (PropertyNames.NAME.equals(evt.getPropertyName())) { // name is Changed } } });
Listener for listening the specific property (NAME) of the specific element type (Classifier): // create smartListenerConfig for Name property SmartListenerConfig cfg = new SmartListenerConfig(PropertyNames.NAME); List<SmartListenerConfig> configs = Collections.singletonList(cfg);
// create listener which will get notified of events. PropertyChangeListener listener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { // evt.getPropertyName() is Changed } }; // register everything into projects event support. project.getSmartEventSupport().registerConfig(Classifier.class, configs,listener);
TransactionCommitListener
This example shows, how to create a transaction commit listener: Create a custom transaction commit listener: public class MyTransactionListener implements TransactionCommitListener { public Runnable transactionCommited(final Collection<PropertyChangeEvent> events) { return new Runnable() { public void run() { for (PropertyChangeEvent event : events) { if (UML2MetamodelConstants.INSTANCE_CREATED.equals(event.getPropertyName())) { Object source = event.getSource(); if (source instanceof Property) { Property property = (Property) source; Element owner = property.getOwner(); if (owner instanceof Classifier) {
111 Copyright 2003-2012 No Magic, Inc. .
EVENT SUPPORT
Event Listening Sample
Classifier propertyOwner = (Classifier) owner; propertyOwner .setName("Contains (" + propertyOwner.getAttribute().size() + ") attributes"); } // additionally for this Property we register listener to listen for any property changes in this Element properties. property.addPropertyChangeListener(new DerivedValuePropertyChangeListener()); } } } } }; } }
This transaction commit listener checks, if new property is created in classifier and updates classifiers (property owner) name. All changes are done in the same session. Register the custom transaction commit listener into the project
MyTransactionListener transactionListener = new MyTransactionListener(); TransactionManager transactionManager = project.getRepository().getTransactionManager(); transactionManager.addTransactionCommitListener(transactionListener);
112
SELECTIONS MANAGEMENT
Selection in Diagrams
Every PresentationElement can access selected elements or change their own selection status.
PresentationElement +getSelected() : List +isSelected() : boolean +setAllSelected( select : boolean ) : void +setSelected( select : boolean ) : void +setSelected( elements : List ) : void
...
Selection events Selection changes fire PropertyChangeEvent. To listen selection change events, PropertyChangeListener must be registered to Project:
Project prj = Application.getInstance().getProject(); prj.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName(). equals(Project.SELECTION_CHANGED)) { // was selected List old = (List)evt.getOldValue(); //now selected List news = (List)evt.getNewValue(); //do something } } });
113
SELECTIONS MANAGEMENT
Browser +getActiveTree() : BrowserTabTree +getContainmentTree() : ContainmentTree +getDiagramsTree() : DiagramsTree +getExtensionsTree() : ExtensionsTree +getInheritanceTree() : InheritanceTree +getSearchResultsTree() : SearchResultsTree
...
Every tree is based on Swing JTree and all manipulations can be done by using API provided by Swing:
JTree tree = activeTree.getTree();
114
CREATING IMAGES
MagicDraw Open API provides class com.nomagic.magicdraw.export.image.ImageExporter for creating images from whole diagram or just part of it.
Image formats are predefined as constants in ImageExporter. To create image from the whole diagram, use method export(DiagramPresentationElement, int, File). To create image from selected symbols in the diagram, use method export(DiagramPresentationElement, int, File, boolean).
115
CREATING METRICS
Creating New Metric
In order to add new metrics you need to extend the Metric class and implement the following methods:
- MetricResult calculateLocalMetricValue(ModelElement target); - boolean acceptModelElement(BaseElement element);
In calculateMetricValue you should put the code that will calculate the local value for your metric. In the example it is calculating the number of stereotypes of the elements. This value can be used while calculating other modes than Local mode.
While implementing acceptModelElement(BaseElement element) method, you have to check the type of elements you want your metrics to be calculated for. However you should restrict it to types of elements that can be shown in metrics results windows. Elements that can be displayed are Class, Interfaces, Diagrams, Packages, or Model packages. You may check if the element is eligible to be show, using the method acceptElement(BaseElement element) from MetricsManager class. MetricsManager class also has a acceptDiagramElement (BaseElement element) that verifies if the given element is a diagram element. Metric class:
116
CREATING METRICS
Creating New Metric
Constructor
The next example will create a metric with the given data about the metrics and put it into the Other group.
public MyMetric() { super("My Metric", "MM", MetricType.OTHER, "MY_METRIC", MetricValue.INTEGER_VALUE); setDescription("This is my metric."); }
You have also to provide a constructor for your metrics that must call the following super constructor:
public Metric(String name, String abbreviation, MetricType type, String id, metricValueType) int
You will have to provide unique name, abbreviation, and ID for your metric. The value return type can be integer or real, the constants are on MetricValue class. There are already some predefined types in MetricType class that can be used. MetricType class:
117
CREATING METRICS
Adding New Metrics to MagicDraw
MetricType +BASIC_TYPE : M etricType +M ODEL_SIZE : MetricType +RELATIONSHIPS : MetricType +DIAGRAM S: M etricType +CLASS_EM PLOYMENT : M etricType +INHERITANCE : MetricType +COHESION : MetricType +COMPLEXITY : MetricType +M OOD : MetricType +OTHER : MetricType +REQUIREM ENTS : MetricType
...
+M etricType( type : String, description : String ) +getDescription() : String +setDescription( description : String ) : void +getType() : String +setType( type : String ) : void
...
In your plugin class you must use then the MetricsManager addMetric(Metric metric) method to add the new metric to MagicDraw. Now your metric will be available for using as any predefined metric.
MetricsManager <<getter>>+getInstance() : MetricsManager +acceptElement( element : BaseElement ) : boolean +acceptDiagramElement( element : BaseElement ) : boolean +removeMetricsSuite( metricsSuite : MetricsSuite ) : void +removeMetric( metric : Metric ) : void +removeMetric( id : String ) : void +addMetricsSuite( metricsSuite : MetricsSuite ) : void +addMetric( metric : Metric ) : void +addMetrics( metrics : Collection ) : void <<getter>>+getMetricsSuite( name : String ) : MetricsSuite <<getter>>+getMetricsSuiteByID( id : String ) : MetricsSuite +contains( metricsSuiteID : String ) : boolean
...
118
CREATING METRICS
Full Example Source Code
MyMetricsPlugin Class
public class MyMetricsPlugin extends Plugin { public void init() { MyMetric myMetric = new MyMetric(); MetricsManager.getInstance().addMetric(myMetric); } public boolean close() { return true; } public boolean isSupported() { return true; } }
MyMetric Class
public class MyMetric extends Metric { public MyMetric() { super("My Metric", "MM", MetricType.OTHER, "MY_METRIC", MetricValue.INTEGER_VALUE); setDescription("This is my metric. This metric will calculate the number of stereotypes for classes."); } protected MetricResult calculateLocalMetricValue(BaseElement target) { Collection stereotypes = StereotypesHelper. getStereotypes((Element) target); MetricResult result = new MetricResult(MetricResult.create(stereotypes.size())); return result;
119 Copyright 2003-2012 No Magic, Inc. .
CREATING METRICS
Full Example Source Code
120
CREATING METRICS
Metric Framework Structure
Me tric s Suite < <c o ns tructo r> > +Me trics S u ite () < <g e tte r> >+ is S e le cted ( m e tric : Me tric ) : b oo lea n + se le ctMetric( m etric : Metric ) : vo id + un s ele c tMe tric ( m e tric : Me tric ) : void + se le ctMetric( id : S trin g ) : void + un s ele c tMe tric ( id : String ) : void + ad d Me tric( m e tric : Me tric ) : vo id + rem ove Me tric ( id : Strin g ) : vo id + rem ove Me tric ( m e tric : Me tric ) : vo id < <g e tte r> >+ ge tD es c rip tion () : S trin g < <s e tte r> >+ se tD es c rip tion ( d e sc riptio n : S trin g ) : vo id < <g e tte r> >+ ge tN am e() : S tring < <s e tte r> >+ se tN am e( n am e : Strin g ) : vo id < <g e tte r> >+ ge tMetrics () : C olle ctio n + ad d Me trics ( m e trics : Co llec tion , ad d Clo ne : bo o le a n ) : vo id + rem ove AllMetrics () : vo id + up d ate ( so urce Me tric s Su ite : Me trics Su ite, u pd a teN am e : b oo lea < <g e tte r> >+ ge tMetricB yNa m e ( na m e : String ) : Metric < <g e tte r> >+ ge tMetricB yAb b re viatio n( a bb revia tio n : S trin g ) : Me tric < <g e tte r> >+ ge tMetricB yID( id : Strin g ) : Me tric < <g e tte r> >+ is D irty() : b o ole an < <s e tte r> >+ se tD irty( is Dirty : b o ole an ) : vo id < <g e tte r> >+ ge tS ele cte d Me trics () : Co llection < <g e tte r> >+ ge tId() : String < <s e tte r> >+ se tId( id : S trin g ) : vo id
...
Me tr ic s Ca lcula tor < <c o ns tru ctor> >+ Metrics C alc ula tor( m e tric s Info rm a tio n : Metrics In form atio n ) + ca lcu la te () : Metrics R es u lts + ca lcu la te ( ele m e n t : B as e Ele m e nt, m e tric : Me tric ) : Me tric Re s ult
...
M etr ics Infor m a tion < <c o ns tru ctor> >+ Metrics In fo rm a tion () + ca lcu la te Metrics () : vo id < <g ette r>> + ge tMe tric s Su ite() : Me trics S u ite < <s ette r>> + se tMe tric s Su ite( m e trics Su ite : Metrics S uite ) : void < <g ette r>> + ge tMe tric s Re s ults () : Me trics Re s ults < <g ette r>> + ge tMe tric s () : Co llec tion + co n ta in s ( ele m e n t : B as e Ele m e nt ) : b o ole a n
...
Metric <<constructor>>+Metric( name : String, abbreviation : String, type : MetricType, id : String, metricValueType : int ) +calculate( target : BaseElement, calculator : MetricsCalculator ) : MetricResult MetricType #calculateLocalMetricValue( target : BaseElement ) : M etricResult +acceptModelElement( element : BaseElement ) : boolean +BASIC_TYPE : MetricType +round( number : float ) : int +M ODEL_SIZE : MetricType +addProperty( property ) : void +RELATIONSHIPS : MetricType +addProperties( properties : Collection ) : void +DIAGRAMS : MetricType +removeProperties( properties : Collection ) : void +CLASS_EMPLOYMENT : M etricType +removeProperty( property ) : void +INHERITANCE : MetricType <<getter>>+getMetricsProperties() : MetricsProperties +COHESION : MetricType <<setter>>+setMetricsProperties( metricProperties : MetricsProperties ) : void +COMPLEXITY : MetricType <<getter>>+getAbbreviation() : String OOD : MetricType -mType +M <<setter>>+setAbbreviation( abbreviation : String ) : void +OTHER : MetricType <<getter>>+getDescription() : String +REQUIREMENTS : MetricType <<setter>>+setDescription( description : String ) : void ... <<getter>>+getName() : String <<constructor>>+MetricType( type : String, description : String ) <<setter>>+setName( name : String ) : void <<getter>>+getDescription() : String <<getter>>+getType() : MetricType <<setter>>+setDescription( description : String ) : void <<getter>>-getPropertyIntegerValue( property : String ) : int <<getter>>+getType() : String <<getter>>-getPropertyFloatValue( property : String ) : float <<setter>>+setType( type : String ) : void <<getter>>+isPackage( element : BaseElement ) : boolean ... <<getter>>+getID() : String <<setter>>+setID( id : String ) : void <<getter>>-getPackageForElement( element : BaseElement ) : BaseElement <<getter>>+getMetricValueType() : int <<setter>>+setMetricValueType( metricValueType : int ) : void
...
MetricResult <<constructor>>+MetricResult( value : MetricValue ) +addValue( value : MetricValue ) : void <<getter>>+getValue() : MetricValue <<setter>>+setValue( value : MetricValue ) : void <<setter>>+setValue( value : int ) : void <<setter>>+setValue( value : float ) : void <<setter>>+setMinValue( value : MetricValue, value2 : MetricValue ) : void <<setter>>+setMaxValue( value : MetricValue, value2 : MetricValue ) : void <<getter>>+isDifferentFromZero() : boolean <<getter>>+getValueAsFloat() : float <<getter>>+getValueAsInteger() : int +hasValidResult() : boolean +create( value : float ) : MetricValue +create( value : int ) : M etricValue <<setter>>+setCalculatedElements( calculatedElements : int ) : void <<getter>>+getCalculatedElements() : int
...
121
MANAGING GENERIC TA B L E S
Generic Table Open API Usage
A generic table is a diagram with a specific stereotype. It is used to represent the element properties in a table form. It can represent different element types in one table and show all their properties. Table rows represent elements, and columns represent element properties. For more information about generic tables and their managing, see Generic Table in MagicDraw UserManual.pdf. MagicDraw Open API provides the com.nomagic.generictable.GenericTableManager class for creating and manipulating generic tables.
This API is used for creating a generic table, adding, editing, and removing table columns and rows, as well as getting cell values. TIP! Find the Generic Table Manager Example Plugin in <MagicDraw_installation_directory>\openapi\examples\generictablemanager.
M AN A G I N G G E N E R I C TA B L E S
Generic Table Open API Usage
} tableElementTypes.addAll(set);
Adding/getting columns
To add columns to the generic table, use the addColumnsById(Diagram, List<String>) method. To get all possible columns from the generic table, use the getPossibleColumnIDs(Element) method. To get set columns for the generic table, use the List<String> getColumnIds(Diagram) method.
List<String> columnIDs = GenericTableManager.getPossibleColumnIDs(elementType);
How to get columnIDs, please look at the GenericTableManagerAction class in the GenericTableManagerExamplePlugin.java file. The file can be found in <MagicDraw_installation_directory>\openapi\examples\generictablemanager.
The element you want to add must be of the same type as one of generic table filter types.
123
M AN A G I N G G E N E R I C TA B L E S
Generic Table Open API Usage
124
CONFIGURING ELEMENT SP E C I F I C A T I O N
Adding Configuration
Open API provides a way to configure Elements' specification windows. With your own configurator you can create new Nodes or remove already existing Nodes. Nodes are items of Tree visible on the left side in every Specification window.
com.nomagic.magicdraw.ui.dialogs.specifications.SpecificationDialogManager class should be used for registering specification dialog configurator ISpecificationNodeConfigurator.
125
CUSTOM DIAGRAM PA IN TE R S
MagicDraw API provides a way to add your own custom diagram painters for painting some additional stuff on the diagram canvas. A good sample would be some highlighting in the diagram.
Painter can be added only into opened diagram's DiagramSurface. BTW, only opened diagram has DiagramSurface. Closed diagram will return null.
Application.getInstance().addProjectEventListener(new ProjectEventListenerAdapter() { public void projectOpened(Project project) { project.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if(evt.getPropertyName().equals(Project.DIAGRAM_OPENED)) { DiagramPresentationElement diagram = Application.getInstance().getProject().getActiveDiagram(); diagram.getDiagramSurface().addPainter(new DiagramSurfacePainter() { public void paint(Graphics g, DiagramPresentationElement diagram) { g.setColor(Color.BLUE); List symbols = diagram.getPresentationElements(); for (int i = 0; i < symbols.size(); i++) { PresentationElement o = (PresentationElement) symbols.get(i); if( o instanceof ShapeElement) { Rectangle bounds = o.getBounds(); bounds.grow(5,5); ((Graphics2D)g).draw(bounds); } } } }); } } }); } });
Full running sample is provided in Open API examples with name CustomDiagramPainter.
126
A N N O TA T I O N S
Using MagicDraw API you can add an annotation to any base element (model element or symbol in a diagram). Annotations are shown in the Containment tree and in the diagrams. Annotation has the following properties:
severity - like error, warning, info, debug, fatal. kind - string representing annotation short name. text - string representing annotation text. target - target base element. actions - optional list of actions. They are shown in a diagram on a symbols smart manipulator.
To add or remove annotations, use com.nomagic.magicdraw.annotation.AnnotationManager. IMPORTANT! TIP! Do not forget to call AnnotationManager.update(), when you are done with adding or removing in order to refresh MagicDraw UI. Find the sample plugin named annotations in MagicDraw Open API examples directory.
127
VA LI D A T IO N
Basic Concepts
Validation rules and validation suites specify what will be validating and how. They also specify how problem found by validation rule can be solved. Validation rule is a constraint with applied stereotype UML Standard Profile::Validation Profile::validationRule. Validation rules can validate model elements and non model elements (e.g. presentation elements) as well. UML metaclass specified as a constrained element defines that a validation rule validates elements of the specified metaclass. Stereotype specified as a constrained element specifies that a validation rule validates elements that have the stereotype applied. Classifier specified as a constrained element specifies that a validation rule validates instances of the specified classifier. Validation rules implementation can be OCL2.0 based or binary. Binary validation rules can be implemented in Java. OCL2.0 based validation rules are described using OCL2.0 language. Validation rule can be global or local. Global validation rules will be executed only once per validation session. Local validation rules will executed for each model element. Validation rule will be treated as global if:
it is a OCL2.0 based validation rule, OCL2.0 specification is valid and OCL2.0 specification
does not use self variable (explicitly or implicitly by using only a property of constrained element).
it is binary based, has specified implementation class name and it has no specified constrained
elements. Local validation rule will be executed on each model element. Validation suite is a package with stereotype UML Standard Profile::Validation Profile::validationSuite. Validation suite organizes several validation rules into a unit that can be used for performing validation. Validation rules can be added or imported into validation suite. Active validation suite is a package with stereotype UML Standard Profile::Validation Profile::activeValidationSuite. Active validation rules can be checked constantly or on model elements change. OCL2.0 or Binary validation rules can be used in the active validation. We suggest to prefer binary, because they give better performance. Annotation defines validation result. It contains information about what is incorrect, severity of the problem, and possible actions that can be used to solve the problem.
128
VALI DA T IO N
129
VALI DA T IO N
Binary Validation Rule OCL2.0 validation rule can be used in an active validation suite. In this case validation rule will be executed on any change of constrained elements that are from the validation scope. Executing of the validation rule might be triggered even if no properties important to the validation rule actually changed and this can slow down active validating speed. In order to avoid degradation of performance you can create a binary validation rule that uses OCL2.0 expression to validate model elements but also provides additional information for the MagicDraw that allows to optimize triggering of executing after model elements change. E.g. we want to check whether all classes have names. This can be done by creating OCL2.0 validation rule and specifying OLC2.0 expression:
name <>
The problem is that such a validation rule actually is interested in a class property name value change but it will be executed on every property value of a class change. How we can fix this problem and inform MagicDraw to execute the validation rule only on class property name change:
1. Create a constraint. 2. Set stereotype UML Standard Profile::Validation Profile::validationRule for the validation rule. 3. Set severity level, error message, and abbreviation. 4. Specify constrained element(s). 5. Specify specification language OCL2.0 6. Enter OCL2.0 expression as a body of specification 7. Create a Java class that extends com.nomagic.magicdraw.validation.DefaultValidation-
RuleImpl class
8. Override method public Map<Class<? extends Element>, Collection<SmartListenerConfig>>
getListenerConfigurations()
public Map<Class<? extends Element>, Collection<SmartListenerConfig>> getListenerConfigurations() { Map<Class<? extends Element>, Collection<SmartListenerConfig>> configs = new HashMap<Class<? extends Element>, Collection<SmartListenerConfig>>(); Collection<SmartListenerConfig> configsForClass = new ArrayList<SmartListenerConfig>(); configsForClass.add(SmartListenerConfig.NAME_CONFIG); configs.put(com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class.class, configsForClass); return configs; }
9. Specify the validation rules implementation property value - qualified name of the class 10. Add/import the created validation rule to a validation suite
See the MyOCLBasedValidationRuleImpl.java example in the <MagicDraw_install_dir>\openapi\examples\validation directory. NOTE The implementation class must be in the classpath of the MagicDraw application or if the implementation class is in a plugin then the plugins classloader must be registered to the validation system (see "Binary Validation Rule in Plugin.").
130
VALI DA T IO N
1. Create a constraint 2. Set stereotype UML Standard Profile::Validation Profile::validationRule for the validation rule 3. Set severity level, error message, and abbreviation. 4. Specify constrained element(s) 5. Specify specification language binary 6. Create Java implementation class (see all possible cases below) 7. Compile Java code, resulting in one or several classes. 8. Ensure that MagicDraw can find & load these classes. You can place them in the MagicDraw
classpath or use the MagicDraw plugins mechanism. The same rules as writing the code for open API are used here. These steps are common to all possible binary validation cases and will not be repeated in each case description. See the MyBinaryValidationRuleImpl.java example in the <MagicDraw_install_dir>\openapi\examples\validation directory. NOTE The implementation class must be in the classpath of the MagicDraw application or if the implementation class is in a plugin then the plugins classloader must be registered to the validation system (see "Binary Validation Rule in Plugin.").
131
VALI DA T IO N
Implementation of this method should return a map of classes derived from com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element class to collection(s) of com.nomagic.uml2.ext.jmi.smartlistener.SmartListenerConfig objects. Another way would be to extend com.nomagic.magicdraw.validation.DefaultValidationRuleImpl class and override the following method:
Map<Class<? extends Element>, Collection<SmartListenerConfig>> getListenerConfigurations()
NOTES
In the Constraint Specification dialog box, Constrained Element property should be specified. Constrained element is element for which validation rule is created.
Validation rule that is interested in model class and interface names changes example
public Map<Class<? extends Element>, Collection<SmartListenerConfig>> getListenerConfigurations() { Map<Class<? extends Element>, Collection<SmartListenerConfig>> configMap = new HashMap<Class<? extends Element>, Collection<SmartListenerConfig>>(); Collection<SmartListenerConfig> configs = new ArrayList<SmartListenerConfig>(); configs.add(SmartListenerConfig.NAME_CONFIG); configMap.put(com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class.class, configs); configMap.put(com.nomagic.uml2.ext.magicdraw.classes.mdinterfaces.Interface.class, configs); return configMap;}
NOTE
In the Constraint Specification dialog box, the Constrained Element property should be Class and Interface from the UML Standard Profile.
Validation rule that is interested in Activity parameters and Activity parameter node changes example:
public Map<Class<? extends Element>, Collection<SmartListenerConfig>> getListenerConfigurations() { Map<Class<? extends Element>, Collection<SmartListenerConfig>> configMap = new HashMap<Class<? extends Element>, Collection<SmartListenerConfig>>();
132
VALI DA T IO N
Collection<SmartListenerConfig> configs = new ArrayList<SmartListenerConfig>(); SmartListenerConfig parameterConfig = new SmartListenerConfig(); Collection<String> parameterPropertiesList = new ArrayList<String>(); parameterPropertiesList.add(PropertyNames.DIRECTION); parameterPropertiesList.add(PropertyNames.TYPE); parameterPropertiesList.add(PropertyNames.OWNED_TYPE); parameterConfig.listenToNested(PropertyNames.OWNED_PARAMETER).listenTo(parameterPr opertiesList); SmartListenerConfig cfg = new SmartListenerConfig(); Collection<String> argumentCftList = new ArrayList<String>(); argumentCftList.add(PropertyNames.PARAMETER); argumentCftList.add(PropertyNames.TYPE); argumentCftList.add(PropertyNames.OWNED_TYPE); cfg.listenTo(argumentCftList); SmartListenerConfig argumentConfig = new SmartListenerConfig(); argumentConfig.listenTo(PropertyNames.NODE, cfg); configs.add(parameterConfig); configs.add(argumentConfig); configMap.put(Activity.class, configs); return configMap; }
VALI DA T IO N
Compile such code into Java bytecode and locate it where MagicDraw can load it (classpath, plugin), and then you can use it for validation:
1. Create the validation rule in our model. 2. Select DataType metaclass as the constrained element of the rule. 3. Select Binary language for the expression of the rule. 4. Specify com.nomagic.magicdraw.validation.TestRule.testMeta as the body of the
expression of the rule. Run the validation suite with included validation rule. The method will be invoked for each datatype model element, found in the scope of the validation run. For each element, where this method returns false, an entry will be placed in the validation results table. For more information about writing Java code, which navigates through the model and accesses various data from the model, see other sections of current user guide.
134
GENERIC NUMBERING
Generic Numbering Open API Usage
Overview
The generic numbering open API provides two interfaces of different complexity. They allow a user to modify how the numbering process executes.
The IJavaNumberPart interface produces a single part of a number generated by the
framework. It does not interfere with the execution of the framework, but simply provides a little piece of information included in the numbering.
The INumberingAction allows for generating complete numbers for sets of elements. All sorting
and numbering is delegated to the implementing class. The framework will only register the generated numbers internally Classes that implement these interfaces can be set as the expression value of an Expression NumberPart. (see Relevant values in MagicDraw UMLProfiling&DSL UserGuide.pdf). These classes have to be specified with the complete package and class name, for example, com.nomagic.magicdraw.autoid.sample.SquareNumberPart.
IJavaNumberPart Interface
The provided NumberParts for Numeric and Character implement numbering based on the natural ordering of integers or the alphabetic ordering of strings. Examples for this would be P1.1, P1.2, P1.3 or P1.A, P1.B, P1.C, etc. What if we wanted to create numbers of the following kind: P1.1, P1.4, P1.9, P1.16, etc with all intermediate numbers omitted? The IJavaNumberPart interface provides a way to create a slice for a generated number that does not follow these natural sequences.
The parameter that this method receives, namely lastNumberPart, is the value that this method generated as a result in the previous call. If the method has not been called before this lastNumberPart value equals an empty string. NOTE This method should adhere to the contract that equal inputs must produce equal outputs. If, for example, the input is ABC and the method computes a value of XYZ then the return value for ABC must always be XYZ.
The return value of this method will be incorporated by the Numbering Framework to create a complete number together with all other NumberParts that are part of the same NumberingScheme.
135
GENERIC NUMBERING
Generic Numbering Open API Usage Example Let's assume the SquareScheme is used as target for UML Activities. According to this NumberingScheme, the first part of a number will be taken from a numbered parent (in this case, the Package sample as shown bellow), then a dot is inserted and the final slice is generated by the SquareNumberPart expression.
We have created a few Activities inside the Package named sample. The owning Package is also customized and has a generated number P1. If we number the activities with this NumberingScheme, the SquareNumberPart class, being set as part3 of type expression, will create the following sequence: 1, 4, 9, 16, 25... These pieces are then added to the results from the other NumberParts, resulting in the Activities being numbered as shown in the following figure.
Figure 19 -- Numbering
The source code for this simple implementation should be self-explaining. It will simply try to transform the string value of lastNumberPart into an integer value called square. Subsequently, the following mathematical operation is performed and returned nextSquare = ( square + 1 ) 2
public class SquareNumberPart implements IJavaNumberPart { private final int initialValue = 1; @Override public String generateNextId(String lastNumberPart) { int nextSquare = initialValue; try { if ("".equals(lastNumberPart)) { lastNumberPart = "0"; } int square = Integer.valueOf(lastNumberPart); int number = (int) Math.sqrt(square) + 1; nextSquare = (int) Math.pow(number, 2); } catch (NumberFormatException ex) { // already set nextSquare as initialValue } return Integer.toString(nextSquare); } }
136 Copyright 2003-2012 No Magic, Inc. .
GENERIC NUMBERING
Generic Numbering Open API Usage
INumberingAction Interface
What if we want to customize the numbering of elements even further? Let's say we need to number uml:Activities depending on their getStructuredNode()value. By implementing the INumberingAction interface, it is possible to create numbering that follow arbitrary conditions, this comes of course at the cost of increased complexity. The implementation must calculate the values for the numbers all by itself. There are two parts to be explained that pertain to this interface. The actual interface that provides access to customized numbering and an immutable support class which encapsulates the values as defined in the AutoNumber and the associated NumberingScheme objects.
The INumberingAction Interface. The interface has one method, which is invoked whenever a new Element is created or when Create or Renumber buttons are clicked in the Element Numbering Dialog. This applies of course only if the Element type equals the type for which the AutoNumber has been customized. So at the time when a new element is created, this method receives a List with a single entry. On the other hand when the user navigates the Element Numbering dialog, the method receives all elements that are currently listed in the right partition of the dialog. This implies that all elements have the same owner. Since this will give access to the complete Model Tree, it is possible to collect necessary information from other parts of the model and incorporate it. This method must return a mapping of each element together with the value that was generated for it. There is no need to number every element that the method receives. The Numbering Framework will register the generated values internally and set the property value for each element. The NumberingInfo support object. This is an immutable Java Bean object which provides the implementer of the INumberingAction interface with all the information as defined in the AutoNumber and NumberingScheme objects:
getName() returns the name of the AutoNumber. isDefaultNumber() returns the AutoNumber.defaultNumber value. getScheme() returns the AutoNumber.numberingScheme value, which is the
be numbered.
isDisplayedProperty() returns true, if the value of the above getProperty() is
displayed in the GUI. An element type can have more than one numbered Property, but only one can be displayed on symbols or in the Element Tree.
137
GENERIC NUMBERING
Generic Numbering Open API Usage
getPrefix() returns the prefix as modified by the user in the Element Numbering dialog,
which can be different from the AutoNumber.getPrefix() value (see the following figure).
getSeparator() returns the separator as modified by the user in the Element Numbering
dialog. In the implemented Numbering Framework, this value will override all the NumberParts of type separator (see the following figure).
Figure 21 -- Modified Separator and Prefix values (fragment of Element NUmbering dialog)
For more information on how to create Numbering customizations with AutoNumber, NumberingScheme, and NumberPart elements, see Working with Generic Numbering Mechanism in MagicDraw UserManual.pdf.
Constraints
A NumberingScheme that references an expression which implements this Interface, can have one and only one NumberPart. Namely a NumberPart that references an INumberingAction. Example #1 We shall now proceed to show a Java class that generates numbers for UML Activities depending on their getStructuredNode() value. Since this class implements the INumberingAction interface, it should be set as the expression in the only NumberPart of this NumberingScheme.
We have created a few Activities inside a sample Package. With the creation of each new Activity, an Id is generated and assigned to it. We then add some StructuredActivityNodes into two of the given Activities. Now, in order to assign Numbers to these Activities that includes the number of StructuredActivityNodes children, we
138
GENERIC NUMBERING
Generic Numbering Open API Usage have to open the Element Numbering dialog and renumber the Activities (see Element Numbering dialog in MagicDraw UserManual.pdf). This will generate new Numbers that reflect the ownership relation.
Figure 23 -- Activities and Structural Nodes before and after renumbering using Element Numbering dialog
Lets have a look at the source code. This is a very simple example, which differentiates between receiving one or many elements, and calculates the values accordingly. This class can be found in com.nomagic.magicdraw.autoid.sample.
public class ActivityNodeNumbering implements INumberingAction { @Override public Map<Element, String> generateIds(List<Element> elements, NumberingInfo nInfo) { Map<Element, String> idMap = new HashMap<Element, String>(); if (! elements.isEmpty()) { // the NumberingInfo object contains the values set in the customization String baseId = nInfo.getPrefix() + nInfo.getSeparator(); // when creating an Activity, this will be called with a single element if (elements.size() == 1) { Element e = elements.iterator().next(); Class<?>[] types = new Class<?>[]{Activity.class}; Collection<Activity> acts = ModelHelper.getElementsOfType( e.getOwner(), types, false, true); String id = baseId + (acts.size()); idMap.put(e, id); // when renumbering in the dialog this will be called with multiple elements } else { int counter = 1; // we sort so that renumber will get the same order // normally this would ask for a custom comparator class // but it is left out for brevity's sake Collections.sort(elements); for (Element e : elements) { if (e instanceof Activity) { Activity act = (Activity) e; String id = baseId + counter; counter ++; Collection<StructuredActivityNode> nodes = act.getStructuredNode(); // we simply attach the suffix '$node_' and the node count if (nodes != null && !nodes.isEmpty()) { id += "$node_" + nodes.size();
139
GENERIC NUMBERING
Generic Numbering Open API Usage
} idMap.put(e, id); } } } } // returning the mapping of elements and ids, so that the framework can register them return idMap; } }
Example #2 A more complicated example can be found the OpenAPI packages com.nomagic.magicdraw.autoid and com.nomagic.magicdraw.autoid.custo. It shows an implementation that creates numbers for elements connected through UML Relationships. The UML diagram shows the DirectedAssociationNumbering class, which is customized to number element types that are connected by DirectedAssociation links. The interested Reader should consult the source code, in order to learn how such an algorithm has been implemented.
140
TE A M W O R K
MagicDraw API provides Teamwork Server accessing methods. Code sniplet:
// check logged user if (!user.equals(TeamworkUtils.getLoggedUserName())) { // login to teamwork if (!TeamworkUtils.login(server, -1, user, password)) { // login failed return; } } // load teamwork project ProjectDescriptor projectDescriptor = TeamworkUtils .getRemoteProjectDescriptorByQualifiedName(projectName); ProjectsManager projectsManager = Application.getInstance(). getProjectsManager(); projectsManager.loadProject(projectDescriptor, true); Project project = Application.getInstance().getProject(); Model model = project.getModel(); // get locked by user Collection userLockedElements = TeamworkUtils. getLockedElement(project, user); if (!userLockedElements.contains(model)) { // model is not locked by user, get all locked Collection allLockedElements = TeamworkUtils. getLockedElement(project, null); if (!allLockedElements.contains(model)) { // model is not locked - lock TeamworkUtils.lockElement(project, model, false); } } SessionManager.getInstance().createSession("Rename Model"); // change name model.setName("MyModel"); SessionManager.getInstance().closeSession(); // unlcok and commit (because do not discard) TeamworkUtils.unlockElement(project, model, false, false); projectsManager.closeProject(); // logout TeamworkUtils.logout();
141
CODE ENGINEERING
Since MagicDraw 16.0 you can perform Code Engineering using OpenAPI. Code engineering allows generation of source code from specific model, and source code reversing into model elements. MagicDraw provides API for:
Code engineering sets creation for particular programming languages. Automatic component creation for every class involved in forward engineering and every file
Destination of the code reverse operation output can be any model package. In this chapter we will review how to manage code engineering and how to do reverse and forward engineering.
model.
Working directory. Represents a code engineering directory where generated/reversed files
are located.
Forward Engineering
To perform code generation, elements should be added to the CodeEngineeringSet object. Use the following
method to add a list of model elements to the code engineering set:
addElementsToCodeEngineeringSet(List<BaseElement> modelElements)
NOTE
Model elements should be in the working package, otherwise model element will not be added to code engineering set. Working package is set when creating code engineering set.
142
CODE ENGINEERING
Managing Code Engineering Sets
Reverse Engineering
Source code files are required to perform the Reverse Engineering. There are 2 methods available for adding files into code engineering set:
1. addFilesToCodeEngineeringSet(List<File> file);
This method adds all specific source code files to code engineering set, starting from given directory. NOTE Source code files should be in working directory, in order to have successful reverse.
CodeEngineeringConstants class.
Name of the code engineering set. Project, where code engineering set will be added. Working package. Working directory.
CodeEngineeringManager.createCodeEngineeringSet(language,dialect, name, project, workingPackage, workingDirectory); Language and dialect can be selected from the CodeEngineeringConstants class. Dialect is required for DDL and C++ languages. NOTES
null workingPackage means, that code engineering working
CodeEngineeringManager.removeCodeEngineeringSet(codeEngineeringSet);
143
CODE ENGINEERING
CodeEngineeringManager.getAllCodeEngineeringSets(project);
To perform generation of CodeEngineeringSet object use the following method:
CodeEngineeringManager.generate(codeEngineerigSet);
To perform reverse engineering use the following method:
CodeEngineeringManager.reverse(codeEngineeringSet, showOptionsDialog);
CodeEngineeringManager.setJavaClasspath( project, arrayOfclasspaths ) NOTE: setting a new classpath will override the old one.
To get applied classpaths for java code engineering sets, use:
CodeEngineeringManager.getJavaClasspath( project )
To resolve collection generics when reversing java code, use:
CodeEngineeringManager.setResolveCollectionGenerics(project,true)
CODE ENGINEERING
// create new element ElementsFactory ef = project.getElementsFactory(); Class classA = ef.createClassInstance(); classA.setName("ClassA"); classA.setOwner(project.getModel()); List<BaseElement> modelsForSample = new ArrayList<BaseElement>(); modelsForSample.add(classA); javaGenerationSet.addElementsToCodeEngineeringSet(modelsForSample);
145
(VTL) syntax. The VTL syntax is used to manipulate the context variables for generating text script. The template file can be changed in the CG Properties Editor dialog box (in the Oracle DDL set shortcut menu, choose the Properties command). The default Oracle DDL template is stored in <MagicDraw install folder>\ data\DB_engineering\Oracle_template folder.
Context variables. Context variables are MagicDraw models (retrieved from OpenAPI), code
engineering set information, and user-defined variables. To identify Oracle object check applied Stereotypes and Tagged Values.
O R A C L E D D L G E N E R A T I O N AN D C U S T O M I Z A T I O N
Customizing Template
#set ( $sqlrestriction = false) #set ( $sqlrestriction = $ oracleHelper.getFirstPropertyValueFromProfile( $data,$VIEW_STEREOTYPE, $QUERY_RESTICTION ) ) #set ( $sql = false) #set ( $sql = $ oracleHelper.getFirstPropertyValueFromProfile( $data,$VIEW_STEREOTYPE, $QUERY_TAG )) #set ( $columns = false) #set ( $columns = $ oracleHelper.getViewColumnList( $data ) ) #if ($columns) #set ( $columns = " ($columns)" ) #else #set ( $columns = $nospace ) #end #writeDocumentation ( $data $nospace ) #writeLine( "$ oracleHelper.getCreate($data)#if ( $ oracleHelper.getPropertiesListFromDefaultProfile($data, $VIEW_STEREOTYPE, $FORCE_TAG).size()>0 )#if( $force ) FORCE#else NO FORCE#end#end VIEW $ oracleHelper.getQualifiedName( $data )$columns AS" $nospace) #if ( $sql )#writeLine( $sql $tab )#end#writeText( ${sqlrestriction} ${space} ${nospace})${semicolon} #end
Model element with stereotype view is passed to macro function, and then all element information is retrieved by $oracleHelper utility class. Oracle View stereotype has 3 properties. These tagged values store information about force, query restriction and query statements.
Method $oracleHelper.getFirstPropertyValueFromProfile( $data,$VIEW_STEREOTYPE, $QUERY_RESTICTION) gets query restriction statement from element $data.
Customizing Template
To change generation of the particular Oracle Object, add new functionality to its macro in velocity template. Helper utility class will assist in retrieving model information. Read the next chapter to get familiar with available methods. We suggest to make a back up of default template. Default template is stored in <MagicDraw install folder>\ data\DB_engineering\Oracle_template folder. The template file can be changed in the CG Properties Editor dialog
box (in the Oracle DDL set shortcut menu, choose the Properties command).
When generating DDL, objects are passed from code engineering set to velocity engine. Object, that are passed to Oracle DDL velocity engine: Object name
$CESList $dropRequired
Object type
List<NamedEleme nt> boolean
Description List of model elements that are added to code engineering set. Flag to notify if drop objects is required. This is a drop setting option which generates Drop statement. This is helper class, for retrieving information from model elements. Use this class to check element for applied stereotypes, get tagged values and elements owned information. Generation Option to generate bracket in new line. Language option for documentation generation.
$oracleHelper
Java.lang.Class
$newLineBracket $genDocumentation
boolean boolean
147
O R A C L E D D L G E N E R A T I O N AN D C U S T O M I Z A T I O N
Utility Class
Utility Class
This utility class helps to retrieve information from MagicDraw model elements. Use these commands to get particular information:
$oracleHelper.hasStereotype($element, $stereotypeName)
Returns true if given element has applied given stereotype. Name Parameters
$element
Type
Description
com.nomagic.uml2 Element to check. .ext.magicdraw.c lasses.mdkernel. Element java.lang.String Stereotype name to be checked. boolean
$stereotypeName
Return
$oracleHelper.getBooleanValueFromDefaultProfile($element,
$stereotypeName, $propertyName) Given an Element, Stereotype name and Tag name, returns tag value as Boolean from Oracle profile. Name Parameters
$element
Type
Description
com.nomagic.uml2 Element to check. .ext.magicdraw.c lasses.mdkernel. Element java.lang.String Stereotype name (from default
$stereotypeName $propertyName
retrieved. Return
boolean
$oracleHelper.getPropertiesListFromDefaultProfile($element,
$stereotypeName,
$propertyName)
Returns list of given property values, that exists on given element with applied stereotype from Oracle profile. Name Parameters
$element
Type
Description
com.nomagic.uml2 Element to be tested. .ext.magicdraw.c lasses.mdkernel. Element java.lang.String Stereotype name which should be
$stereotypeName $propertyName
applied.
java.lang.String Property (Tag) name to get values
from. Return
java.util.List
148
O R A C L E D D L G E N E R A T I O N AN D C U S T O M I Z A T I O N
Utility Class
$oracleHelper.getFirstPropertyValueFromProfile($element,
$stereotypeName, $propertyName) Returns First given Tag property value from given element, which has applied given stereotype. Name Parameters
$element
Type
Description
com.nomagic.uml2 Element to be tested. .ext.magicdraw.c lasses.mdkernel. Element java.lang.String Stereotype name, that should be
$stereotypeName $propertyName
applied to element.
java.lang.String Property name where to check for
values. Return
java.lang.String First property value in String
representation.
$oracleHelper.getDefaultValueAsBoolean($property)
Type
Description
com.nomagic.uml2 Boolean Property to be check for .ext.magicdraw.c default value. lasses.mdkernel. Property boolean
Return
$oracleHelper.getFirtPropertyValueFromGivenProfile($element,
$profileName, $stereotypeName, $propertyName) Returns first given tag property value from given element, which has applied given stereotype from profile. Name Parameters
$element
Type
Description
com.nomagic.uml2 Element to be tested. .ext.magicdraw.c lasses.mdkernel. Element java.lang.String Profile name where stereotype
exists.
java.lang.String Stereotype name. java.lang.String Property (Tag) name, which value
$oracleHelper.getPropertiesListFromProfile($element, $profileName,
$stereotypeName, $propertyName) Returns list of given property values, that exists on given element with applied stereotype from given profile. Name Type Description
149
O R A C L E D D L G E N E R A T I O N AN D C U S T O M I Z A T I O N
Utility Class
Parameters
$element
com.nomagic.uml2 Element to be tested. .ext.magicdraw.c lasses.mdkernel. Element java.lang.String Profile name where stereotype
exists.
java.lang.String Stereotype name. java.lang.String Property (Tag) name, which value
$oracleHelper.getStringValue($object)
Type
Description
Return
$oracleHelper.isDataType($element)
Type
Description
Return
$oracleHelper.getType($type, $modifier)
Type
Description
com.nomagic.uml2 Type of element. .ext.magicdraw.c lasses.mdkernel. Type java.lang.String Modifier of Type. java.lang.String Type definition for Oracle DDL.
$modifier
Return
$oracleHelper.getTypeModifier($element)
Type
Description
com.nomagic.uml2 Element to be tested. .ext.magicdraw.c lasses.mdkernel. Element java.lang.String Type modifier description.
Return
$oracleHelper.getParameters($operation)
150
O R A C L E D D L G E N E R A T I O N AN D C U S T O M I Z A T I O N
Utility Class Returns list of operation parameters. Name Parameters
$element
Type
Description
Return
$oracleHelper.getColumnConstraint($column)
Type
Description
com.nomagic.uml2 Column to check for constraint. .ext.magicdraw.c lasses.mdkernel. Property java.lang.String Constraint definition.
Return
$oracleHelper.getCreate($element)
Type
Description
com.nomagic.uml2 Element to be tested. .ext.magicdraw.c lasses.mdkernel. Element Java.lang.String description - CREATE or CREATE
Return
OR REPLACE.
$oracleHelper.getReturnParameter($operation, $createIfNeeded)
Type
Description
$createIfNeeded
Return
$oracleHelper.getIndexNameDefinition($index)
Type
Description
com.nomagic.uml2 BehavioralFeature as a oracle .ext.magicdraw.. index to be checked for name. classes.mdkernel .BehavioralFeatu re
151
O R A C L E D D L G E N E R A T I O N AN D C U S T O M I Z A T I O N
Utility Class
Return
$oracleHelper.getTableConstraintDefinition($dependency)
Type
Description
com.nomagic.uml2 Dependency to be tested. .ext.magicdraw.. classes.ddepende ncies.Dependency java.lang.String Definition of table constraint.
Return
$oracleHelper.isObjectPackage($element)
Type
Description
Return
$oracleHelper.isPackageDatabase($package)
Type
Description
Return
$oracleHelper.isPublic($element)
Type
Description
Return
$oracleHelper.reverseList($list)
Type
Java.util.List Java.util.List
$oracleHelper.getRefName($element)
Returns reference name description for element with "Ref:Element" tag. Name Type Description
152
O R A C L E D D L G E N E R A T I O N AN D C U S T O M I Z A T I O N
Example
Parameter
$element
com.nomagic.uml2 Element to be tested. .ext.magicdraw.. classes.mdkernel .Element java.lang.String Reference element name.
Return
Example
In this sample we will extend current Oracle View DROP statement. In the Default template we have Oracle view drop function. In this sample the simple macro is presented and it generates the following text:
DROP VIEW view_name;
We will add a new tag to identify the "CASCADE CONSTRAINTS" clause in the DROP VIEW statement. The following script will be generated:
DROP VIEW view_name CASCADE CONSTRAINTS;
There are three steps to do this: Step 1. Creating a new stereotype and tags We need to create a new stereotype with the Boolean property, or extend the default View Stereotype with a new property. Name a new property "cascade_clause". Apply the stereotype to the View object and set value to true. We added a new tag to the view stereotype in the default profile. Step 2. Changing the template file Add the following lines to the template file:
#set($cascadeOption =false) ##this line is required to for setting new variable to false state. #set($cascadeOption = $oracleHelper.getBooleanValueFromDefaultProfile( $data,$VIEW_STEREOTYPE, "cascade_clause") )
This line retrieves boolean value from the given tag in given stereotype "$view_stereotype" and sets it to a newly created $cascadeOption value. Then add a new checking clause into the drop statement. See the final method bellow:
#macro (dropView $data ) ## sets value to new variable #set($cascadeOption = $oracleHelper.getBooleanValueFromDefaultProfile($data,$VIEW_STEREOTYPE, "cascade_clause") ) #writeLine( "DROP VIEW $utils.getQualifiedName( $data )#if ($cascadeOption) CASCADE CONSTRAINTS#end${semicolon}" $nospace) #end
153
NOTE!
MagicDraw application cannot be run on headless device even in batch mode. Graphical environment is required.
MagicDraw provides API for running it in the batch mode. For this you need to extend the following class:
com.nomagic.magicdraw.commandline.CommandLine
Code sniplet:
public class ExportDiagramImages extends CommandLine { public static void main(String[] args) { // launch MagicDraw new ExportDiagramImages().launch(args); } @Override protected byte execute() { File projectFile = ...; //open some project ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(projectFile.toURI()); Application.getInstance().getProjectsManager().loadProject(projectDescriptor , true); // project is opened and now you can work with your project return 0; } }
Full working sample is provided in Open API examples with name ImageGenerator. It takes a project file, destination directory as arguments and generates images for all diagrams.
NOTE!
Do not forget to add all *.jar files recursively (except md_commontw.jar and md_commontw_api.jar) from <MagicDraw installation directory>/lib directory into the classpath. Make sure the patch.jar is the first in the classpath.
154
LOGGING
The Log4j logger is used in MagicDraw. "<MagicDraw installation directory>/data/debug.properties" is the configuration file. NOTE! The "<MagicDraw installation directory>/data/test.properties" file is use as configuration file for MagicDraw test case.
The following sample presents the content of the configuration file. It configures the logger to print INFO category messages to a console with a specific pattern.
log4j.appender.SO=org.apache.log4j.ConsoleAppender log4j.appender.SO.layout=org.apache.log4j.PatternLayout log4j.appender.SO.layout.ConversionPattern=%d [%t] %-5p %c - %m%n log4j.rootCategory=INFO,SO
155
156
MagicDrawTestCase also provides methods for opening, saving, and closing MagicDraw projects. It also performs the memory leak test after the test case has been completed and after the MagicDraw project has been closed. The memory leak test for the whole test case can be disabled using the setMemoryTestReady() method, while the setSkipMemoryTest() method can be used to disable the memory leaks test on closing the MagicDraw project. The list of required MagicDraw plug-ins for the test case can be configured by overriding the getRequiredPlugins() method of MagicDrawTestCase. The overridden method implementation should return the list of required plug-ins IDs. Textual information about required MagicDraw plug-ins and their loading status can be obtained using getRequiredPluginsDescription() and isRequiredPluginsLoaded() methods. Textual information about the test process can be logged using the Log4j logger. The test case logger for the TEST category can be accessed using the getLog() method of MagicDrawTestCase. More information about the Log4j logger configuration and usage can be found at http://logging.apache.org/log4j/1.2/manual.html.
157
The model elements comparison can be configured using the ModelComparator interface. Developers may exclude some model elements from the comparison by providing the custom ModelComparatorFilter interface implementation. Please note that the default implementation of ProjectComparator does not compare the diagram information table, root model comment, and elements from modules. The default implementation of DiagramComparator compares diagrams for changes by analyzing a location and size of the symbols presented in diagrams. The graphical differences of compared diagrams can be saved in the graphical PNG file using the saveDiffToImageFiles() method of the DiagramComparator interface. Developers may create their own comparators for the custom diagrams and model elements comparison by implementing ModelComparator and DiagramComparator interfaces. The custom Model and Diagram comparators implementation can be set to ProjectComparator using appropriate setter methods.
The MagicDraw installation directory should be also specified using the install.root system property which can be passed to JVM as a runtime argument as following:
-Dinstall.root=path_to_MagicDraw_installation_directory
If MagicDraw installation is set up to use the floating license, the following server properties should be passed to JVM as runtime arguments:
-DFL_SERVER_ADDRESS=flexnet_license_server_address -DFL_SERVER_PORT=license_server_port -DFL_EDITION=magicdraw_edition
Test cases may use external resources such as configuration files and MagicDraw projects during tests. It is recommended to keep external test resources apart from test case implementation class files. The MagicDraw test framework provides methods for retrieving test resources from the one specific resource directory. The resource directory for test cases can be configured by specifying the tests.resources system property of JVM. The tests resource property can be passed to JVM as a runtime argument as follows:
-Dtests.resources=path_to_resources_directory
The Log4j logger using in MagicDraw test cases can be configured by specifying the test.properties file which should be placed in <MagicDraw Installation directory>/data. MagicDraw is started in "silent" mode when running tests - user interference requiring dialogs are not shown, instead the warning "WARN GENERAL - TRYING TO DISPLAY "<dialog title>" DIALOG IN SILENT MODE" is logged. The stack trace is logged too if special system property is set:
-DprintSilentDialogStackTrace=true
159