Skip to content

开发文档

开发指南

开发说明

本指南介绍了 Magicdraw 的 OpenJavaAPI 或 Cameo Suite 产品(如 Cameo Systems Modeler),并提供了有关如何实现自定义插件、向菜单或工具栏添加操作、更改 UML 模型元素以及创建新模式的说明。虽然 Magicdraw 主要用于示例中,但其他建模工具(因为它们基于 Magicdraw 建模平台,如 Magic System of Systems Architect)执行相同的操作。

程序核心代码和使用的库在打包在 jar 文件中,这些文件位于<建模工具安装目录>\lib\**(及其子目录中)。

其中,部分插件还公开了其 API。插件库可以位于<建模工具安装目录中>\plugins\<plugin_name>

本指南的开发,基于 Magic2022R2 版本,JDK11.Magic19.0 暂未测试,Magic21 全线理论支持

API 范围说明

该代码可分为以下三个 API 作用域组:

  • OpenAPI-供公众使用的代码,通过构建和版本稳定。
  • InternalAPI-仅用于内部 NoMagic 使用的代码,可能会通过构建和版本进行更改,没有任何限制。
  • NotAPI-不是 API 代码,它可能会在每次构建中更改。
注解接口范围范围适用于
@OpenApiAllOpenAPI带注释的类型生命和所有声明的成员、构造函数。
@OpenApiOpenAPI仅带批注的类型声明。每个声明的成员、构造函数都使用 @OpenApi 或 @InternalApi 注释显式注释(请参阅下表)。
@InternalApiInternalAPI带注释的类型声明和所有声明的成员、构造函数。
@NotApiNotAPI带注释的类型声明和所有声明的成员、构造函数。

成员的注释,@OpenApi 类型的构造函数声明:

|注解|接口范围|范围适用于| |@OpenApi|OpenAPI|已注释的声明成员或构造函数。| |@InternalApi|InternalAPI|已注释的声明成员或构造函数。|

只应使用 OpenAPI 作用域中的代码来扩展建模工具。所有内部 API 和 NotAPI 作用域代码都使用 @Deprecated 注释进行注释。这样,无需其他配置即可查看所有现代 IDE 中的非法 API 使用情况。

插件开发

插件工作原理

在每次启动时,建模工具都会扫描插件目录,并在那里搜索子目录:

如果子目录包含插件描述符文件(plugin.xml),则插件的管理器将读取描述符文件。

如果满足描述符文件中指定的要求,则插件的管理器将加载指定的类(指定的插件类必须来自于 com.nomagic.magicdraw.plugins.Plugin.class)。

然后调用类的init()方法。

init()可以使用 actions 体系结构添加 GUI 组件,或者执行其他活动并从该方法返回。

init()方法只有在isSupported()返回为 true 时才调用。

下图说明了插件的工作原理。

编写开发测试样例

参见快速开始

其他说明

plugin.xml 文件参数

文件包含以下属性

  • plugin
  • requires
  • api
  • required-plugin
  • runtime
  • library
plugin
属性描述
id插件 ID 应该是唯一的。它用于通过插件的管理器内部和其他插件的要求来识别插件。例如,my.first.plugin.0
name插件名称。没有应用于此属性的严格规则。例如,示例插件。
version插件版本。如果未定义内部版本,则该版本可用于检查其他插件依赖项。
internalVersion插件内部版本。它用于检查其他插件依赖关系。
provider-name插件提供程序名称,即公司或作者名称。例如:No Magic
class完全限定的类名。该类必须派生自 com.nomagic.magicdraw.plugins。一个插件,存储在插件运行时库中。此类将由插件的管理器加载和初始化。例如,myplugin.MyPlugin
ownClassloader可选;默认值为 false。指示是由使用自己的插件的类加载器(与其他插件分开)。所有程序插件都由一个类装入器加载。如果存在无法由同一类装入器装入的插件(例如,由于插件库版本或其他版本中的冲突),则必须将其描述符定义为使用自己的类装入器。
class-lookup可选;可能的值为 LocalFirst、GlobalFirst,默认值为 GlobalFirst。指定“父”类装入器的优先级(如果插件使用 ownClassloader)。LocalFirst 强制从插件类装入器加载类,既是建模工具核心类路径中存在此类。如果在插件中,您希望使用核心中使用的相同库的不同版本,则应使用此选项。
Nested elements
属性描述
requires插件所需的建模工具 API 版本。插件及其插件所需的版本。
runtime插件所需的运行库
api
属性描述
versionAPI 所需的版本。如 1.0。
required-plugin
属性描述
id所需插件的 ID。例如,my.first.plugin.0
name所需插件的名称。
internalVersion所需插件的内部版本。如果未定义,则使用“版本”来检查所需的插件是否合适。
version所需插件的版本。如果未定义,则任何所需的插件版本都是合适的。例如:1.1。
optional此属性的默认值为 false。如果值为 false(或跳过属性),则仅当加载了所需的插件时,才会加载插件。如果 value 为 true,则在加载所需插件或未安装所需插件时将加载插件。

plugin class 说明

com.nomagic.magicdraw.plugins.Plugin 是所有的建模工具插件的抽象类。用户编写的插件必须从此类扩展。每个插件都有通过插件管理器的描述符。插件类有三种特殊方法。

  • init(),在插件启动时调用。插件必须重写此方法,并实现自己的功能。
  • close(),在插件退出时调用。如果插件已准备好退出,则插件必须重写此方法并返回值 true。在其他情况下,它应该返回值 false,则取消程序退出。
  • isSupported(),插件初始化之前调用,如果此方法返回 false,则插件不会初始化。isSupported() 可用于检查插件是否可以启动,例如,在此操作系统上。

使用 com.nomagic.magicdraw.plugins.PluginUtils 获取对已加载插件的引用。如果在插件之间具有依赖关系并希望获取对其他插件对象的引用,这将非常有用。

com.nomagic.magicdraw.plugins.PluginDescriptor 是一个类,它提供从 plugin.xml 文件加载到插件的信息。使用 com.nomagic.magicdraw.plugins.Plugin.getDescriptor() 获取描述符。

图:插件加载过程

其他的信息参考 JavaDoc 文档即可。

包依赖导入

参见快速开始

增加新功能接口

Magic 允许添加新功能以及通过 GUI 调用它的方式。在这种情况下,所有菜单命令和按钮都被视为操作。

不同调用说明

以下列举 Magic 在程序页面上,不同的调用按钮(方法)。

英文原名中文名称图片说明
Main menu主菜单
Main toolbar主工具栏
Diagram palette图调色板
Diagram toolbar图表工具栏
Browser shortcut menu浏览器快捷菜单
Diagram shortcut menu关系图快捷菜单![](./images/action-diagram-shortcut-menu.png
Smart manipulator toolbar智能机械手工具栏
Keyboard shortcuts键盘快捷键此操作不能在 GUI 中表示。创建一个新操作并分配一个键盘快捷键来调用该操作。

样例说明

以下样例存放于

  • <建模工具安装目录>\openapi\example\actiontypes
  • <建模工具安装目录>\openapi\example\simpleconfigurators
  • <建模工具安装目录>\openapi\example\simpleconfigurators

创建新的操作类

建模工具中使用的所有操作都必须继承自 com.nomagic.magicdraw.actions.MDAction 类。

Magic 中,已包含了以下三个继承自 MDAction 的类。

  • com.nomagic.magicdraw.ui.browser.actions.DefaultBrowserAction-一个动作类,用于浏览器菜单的动作。它允许访问某些浏览器树和节点,建议用于对选定的浏览器节点执行某些操作。
  • com.nomagic.magicdraw.ui.actions.DefaultDiagramAction-用于图表操作的操作类。它允许访问某些图元素,建议用于对所选图元素执行某些操作。
  • com.nomagicdraw.actions.PropertyAction-用于更改某些元素或应用程序属性的操作。它可用于更改用户定义的属性。

同时,还必须重写这些类中的actionPerformed()方法。

例 1 简单案例
java
class SimpleAction extends MDAction {
    public SimpleAction(String id, String title) {
        super(id, title, null, null);
    }

    @Override
    public void actionPerformed(@CheckForNull ActionEvent actionEvent) {
        JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(), "This is:" + getName());
    }
}
例 2 浏览器树样例
java
class SimpleAction extends DefaultBrowserAction {
    public SimpleAction(String id, String title) {
        super(id, title, null, null);
    }

    @Override
    public void actionPerformed(@CheckForNull ActionEvent actionEvent) {
        Collection<? extends BaseElement> selectedBaseElements = getSelectedBaseElements();
        StringJoiner sj = new StringJoiner(", ");
        if (selectedBaseElements != null) {
            selectedBaseElements.stream().map(BaseElement::getHumanName).forEach(sj::add);
        }
        JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(), "选中了: " + sj.toString());
    }
}

指定操作属性

英文名称中文名称功能描述
ID用户名ID 是用于标识操作的唯一字符串。如果操作 ID 设置为 null,则会生成一个新 ID。操作名称可用作 ID。所有 Magicdraw(或 No Magic Inc. 开发的其他建模工具)默认操作 ID 都在 com.nomagic.magicdraw.actions.ActionsID 类中定义。可以使用这些常量来访问操作。不要将其用作新操作 ID。
Name名字操作名称在所有 GUI 元素中都可见。
Shortcut and mnemonic快捷方式和助记符每个操作都可以有一个指定的键盘快捷键。可以从“环境选项”对话框中自定义快捷方式(请参阅“Magicdraw 用户手册”中的“环境选项”一节)。操作必须分配有 ID(不为空)。在其他情况下,重新启动应用程序后无法还原快捷方式。
Icon图标每个操作都可以有一个小图标和大图标。小图标被描述为 javax.swing.Actions.SMALL_ICON,可以在菜单项中使用。工具栏按钮中使用大图标。工具栏的操作必须具有大图标,否则它将显示为具有操作名称的按钮。
Description描述显示为工具提示文本的操作说明。

注:图标属性中,添加图标为

java
// setting an icon. On the toolbar, the button with an icon looks better than with the text.
action.setLargeIcon( new ImageIcon(getClass().getResource("main_toolbar_icon.gif") ) );

启用/禁用

  • 将操作添加到预定义中,这样随着操作的进行,会自己启动和关闭功能。但启动和禁用状态是预定义的,无法更改。
java
MDAction action = new MDAction("Example", "Example", KeyEvent.VK_E, ActionsGroups.PROJECT_OPENED_RELATED);
  • 该操作实现 updateState() 方法。
java
public void updateState() {
    // action will be enabled only if there are some selected nodes.
    Tree tree = getTree();
    if (tree != null) {
        setEnabled(tree.getSelectedNode() != null);
    }
}

在操作类中添加操作步骤(配置器)

每个操作都必须添加到 com.nomagic.actions.ActionsCategory 类中。ActionsCategory 是一个操作组,表示为一个大菜单或子菜单嵌套。

类别被添加到 com.nomagic.ActionsManager 类中,该类是某种操作容器。

下面说明举例说明:

ActionsManagerCategoryAction
菜单菜单栏菜单菜单项
工具栏总工具栏一个工具条按钮
快捷菜单快捷菜单子菜单菜单项

操作管理器(ActionsManager)中的操作由许多配置器配置。配置器负责将操作添加或删除到严格定义的位置和其他操作之间的位置。

其中,有三种配置器

  • com.nomagic.actions.AMConfigurator - 一种用于通用的配置器。它用于菜单、工具栏、浏览器和图表的快捷方式。
  • com.nomagic.magicdraw.actions.BrowserContextAMConfigurator - 用于为浏览器快捷方式(弹出式)菜单配置管理器的配置器。它可以访问浏览器树和节点。
  • com.nomagic.magicdraw.actions.DiagramContextAMConfigurator - 用于在图表中配置快捷菜单的配置器。它可以访问关系图、选定的关系图元素以及请求快捷菜单的元素。

创建并配置主菜单和所有工具栏的操作管理器(ActionsManager)后,只能禁用这些操作,以后不能删除这些操作。

例 1:将某些操作添加到浏览器的快捷菜单中
java
final DefaultBrowserAction browserAction = ...;
BrowserContextAMConfigurator brCfg = new BrowserContextAMConfigurator() {
    // Implement configuration.
    // Add or remove some actions in ActionsManager.
    // A tree is passed as an argument, provides ability to access nodes.
    @Override
    public void configure(ActionsManager mngr, Tree tree) {
        // Actions must be added into some category.
        // So create the new one, or add an action into the existing category.
        MDActionsCategory category = new MDActionsCategory("", "");
        category.addAction(browserAction);
        // Add a category into the manager.
        // A category isn't displayed in a shortcut menu.
        mngr.addCategory(category);
    }

    @Override
    public int getPriority() {
        return AMConfigurator.MEDIUM_PRIORITY;
    }
};
例 2:创建新项目后,将一些操作添加到主菜单中
java
// Create an action.
final MDAction someAction = null;
AMConfigurator conf = new AMConfigurator() {
    @Override
    public void configure(ActionsManager mngr) {
        // Searching for an action after which an insertion should be done.
        NMAction found = mngr.getActionFor(ActionsID.NEW_PROJECT);

        // The action found, inserting.
        if (found != null) {
            // find the category of the "New Project" action.
            ActionsCategory category = (ActionsCategory) mngr.getActionParent(found);

            // Get all actions from this category (menu).
            List actionsInCategory = category.getActions();

            // Add the action after the "New Project" action.
            int indexOfFound = actionsInCategory.indexOf(found);
            actionsInCategory.add(indexOfFound + 1, someAction);
            
            // Set all actions.
            category.setActions(actionsInCategory);
        }
    }

    @Override
    public int getPriority() {
        return AMConfigurator.MEDIUM_PRIORITY;
    }
};

注册配置器

所有配置器都注册在 com.nomagic.actions.ActionsConfiguratorsManager 类中。

ActionsConfiguratorsManager 允许向建模工具预定义的每个配置添加或删除许多配置器。

所有可用配置均可通过以下方式访问:

java
ActionsConfiguratorsManager.getInstance().add<configuration_name>Configurator(configurator);
例:为 CONTAINMENT_BROWSER_CONTEXT 配置添加新的配置器
java
// See previous examples, how to create a configurator for browser actions.
// Add the configurator into ActionsConfiguratorsManager.
ActionsConfiguratorsManager.getInstance().addContainmentBrowserContextConfigurator(brcfg);

相关类层次关系图

图标类说明

使用 com.nomagic.magicdraw.icons.IconsFactory 加载图标。

从版本 18.2 开始,建模工具支持 HiDPI/Retina 监视器,并动态选择要加载的图标 - 更低或更高的分辨率。所有这些逻辑都封装在 com.nomagic.ui.ScalableImageIcon 中添加了其他缓存,并确保来自同一 URL 的图标将只加载一次。

HiDPI/视网膜兼容性需要两个图像 - 用于正常分辨率和高分辨率。对高分辨率图像使用 SVG 格式。将具有相同名称(但扩展名不同)的两个图标放在同一位置。使用普通图标 URL 加载图标,但 SVG 图标将加载到高分辨率监视器上。

图标类:

描述
com.nomagic.ui.ResizableIconjavax.swing.Icon 的扩展版本。它可以在绘画过程中缩放(调整大小)。
com.nomagic.ui.ResizableIconAdapter一个简单的 javax.swing.Iconcom.nomagic.ui.ResizableIcon 的适配器。
com.nomagic.ui.SquareIcon将另一个图标放入给定大小的正方形中的图标。
com.nomagic.ui.ScalableImageIconHiDPI(Retina) 友好的 java.swing.ImageIcon 实现。此类通过当前屏幕 dpi 和/或系统中定义的比例因子(视网膜等)动态选择正确的图像。如果需要缩放,并且具有相同名称的 SVG 图标在给定位置可用,则将使用 SVG 图标而不是位图图标。仅当在同一位置提供两个图标时,动态选择才有效。例如,/com/product/icons/a.png/com/product/icons/a.svgScalableImageIcon(Product.class,"/com/product/icons/a.png") 将在常规 dpi 监视器上加载 a.png,但在 HiDPI 监视器上加载 a.svg。请记住,SVG 图标大小可以是任意大小。SVG 图标将根据 a.png 图标的大小和比例因子调整大小。例如,如果 a.png 的大小为 16x16,比例因子为 2,则加载的 SVG 图标将根据需要调整为 32x32。如果未提供 SVG 图标,则将加载并缩放原始图标,但使用具有缩放功能的 HiDPI。

程序类:

描述
com.nomagic.magicdraw.icons.IconsFactory一个实用程序类,用于从 MagicDraw 图标包或其他位置加载图标。
com.nomagic.ui.IconUtilities用于使用图表的 utility 类。

高级图标类:

描述
com.nomagic.ui.AlphaCompositeIcon使用 Alpha 复合体绘制环绕的图标。
com.nomagic.ui.BorderIcon用于在环绕的图标周围添加边框的图标。
com.nomagic.ui.DoubleIcon一个图标,用于在彼此的顶部绘制两个包装的图标。
com.nomagic.ui.RetinaImageIcon视网膜友好型图像图标实现。图标(以及此图标返回的图像)比包装的图标本身小两倍。包装图标用于绘画。在绘画时,图形按视网膜比例因子缩小,并绘制包装的图标。
com.nomagic.ui.DoubleSizeImageIcon图标组合了另外两个图标,并根据图形缩放选择要绘制的图标。
com.nomagic.ui.ResizableIconImageIcon一个图像图标,它包装其他图标并为包装图标提供图像。provided 图像是 HiDPI/视网膜友好的。在 Mac 上,返回了一个支持多分辨率的 java.awt.Image(sun.awt.image.MultiResolutionToolkitImage) 的特殊实例。

持久类运行

对于持久的操作,需要显示进度并运行异步代码。

有两个选项可用于执行这些操作:

  • 前台任务 - 使用 com.nomagic.task.RunnableWithProgresscom.nomagic.magicdraw.ui.MagicDrawProgressStatusRunner
  • 后台任务 - 使用 com.nomagic.magicdraw.task.BackgroundTaskRunner

您可以在<建模工具安装目录>\openapi\examples\progressstatus中找到代码示例。

项目类接口

Project concept

com.nomagic.magicdraw.core.Project 标识所有项目特定数据的存储,如 UML 模型,所有图表,项目特定选项等。

可以同时打开多个项目,但只有一个项目可以在 UI 中处于活动状态。

一个项目的数据不能引用其他打开项目中的数据。可以通过以下方式访问活动(当前项目):

java
Project project = Application.getInstance().getProject();

此外,该项目可供任何其他 com.nomagic.magicdraw.uml.BaseElement(UML Element 或 Presentation Element) 访问。

java
Project project = Project.getProject(element);

该项目保留了对根模型的引用,也包含对所有关系图的引用。

项目始终具有一个主根模型,并且可能具有来自已用项目的零个或多个附加模型。附加的模型仅在从 TWC 中可用。

本地存储的项目或项目只有一个主根模型。

可以通过以下方式访问主根模型:

java
Package model = project.getPrimaryModel();

可以通过以下方式访问所有根模型:

java
List<Package> models = project.getModels();

项目描述符 Project descriptor

com.nomagic.magicdraw.core.project.ProjectDescriptor 表示一个项目(或一个使用的项目)作为存储和加载的资源。

同一个项目可以有多个项目描述符,有两种类型的项目描述符:

  • A local project descriptor.它代表一个本地普通项目。可以为项目或文件对象创建描述符。
  • A remote project descriptor.它表示存储在服务器中的项目。

服务器项目同时具有两个描述符(本地和远程),因为它可以保存在本地。

每个项目描述符都提供以下属性:

  • URI.该位置是一个特定的 java.lang.String 值,该值保存访问项目所需的所有信息。
  • 表示字符串。用于用户界面(GUI)中的项目标识的字符串。
  • 远程标志。远程标志项目描述符是否表示服务器项目。

com.nomagic.magicdraw.core.project.ProjectDescriptorsFactory 可以创建一个合适的 ProjectDescriptor 对象,com.nomagic.magicdraw.esi.EsiUtils 帮助找到现有的 Teamwork Cloud 项目描述符。

项目管理

项目管理器

样例在<程序安装目录>\openapi\examples\projects

com.nomagic.magicdraw.core.project.ProjectsManager 类负责项目的包含和管理。

使用下面的代码访问项目管理器

java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();

ProjectsManager 是 com.nomagic.magicdraw.core.Project 的创建、关闭、保存、加载和激活的 API。

一个项目群可以有多个打开的项目,但只能有一个项目处于活动状态。

例如:

java
// Gets all the projects
List<Project> projects = projectsManager.getProjects();

// Gets an active project
Project activeProject = projectsManager.getActiveProject();

同时,活动项目也可以直接从 com.nomagic.magicdraw.core.Application 访问:

java
// Gets an active project
Project project = Application.getInstance().getProject();

项目的加载和保存

通过使用 com.nomagic.magicdraw.core.project.ProjectsManager 类中的两种方法保存和加载项目。

  • saveProject(ProjectDescriptor, boolean)
  • loadProject(ProjectDescriptor, boolean)

“保存 save”和“加载 load”为本地概念。twc 服务器中,表示为“提交 commit”和“更新 update”。

如果未指定项目,则无法使用描述符保存项目。如果未指定文件,则无法加载项目。会抛出java.lang.IllegalStateException异常。

静默模式(The silent mode)意味着在保存或加载过程中不会对用户输入使用 GUI 中断,例如,在提交服务器项目时没有“提交项目”对话框,或者在保存新项目时没有“保存”对话框(项目将保存到上次使用的目录中)。

保存参与者

使用 com.nomagic.magicdraw.core.SaveParticipant 插入到 save/commit 操作中。

使用 com.nomagic.magicdraw.core.Application.addSaveParticipant(SaveParticipant) 注册它。

实现 SaveParticipant.isReadyForSave(Project, ProjectDescriptor),您可以“禁用”保存/提交操作,直到满足某些条件。

例 1:保存活动项目
java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
// An active project
Project project = projectsManager.getActiveProject();

// Get a project descriptor
ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.getDescriptorForProject(project);

// Save a project
projectsManager.saveProject(projectDescriptor, true);
例 2:从本地文件加载项目

如果项目的文件名已知,则可以直接加载项目:

java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
File file = new File(projectFilePath);
// Create a project descriptor
ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI());
projectsManager.loadProject(projectDescriptor, true);
例 3:导入另一个项目文件

如果项目的文件名已知,则可以导入项目:

java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
File file = new File(projectFilePath);
// Create a project descriptor
ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI());
projectsManager.importProject(projectDescriptor);
例 4:加载服务器项目

如果项目的限定名称已知,则可以加载项目:

TeamworkUtils.getRemoteProjectDescriptorByQualifiedName方法已不存在。

java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
// Create a project descriptor
ProjectDescriptor projectDescriptor = TeamworkUtils.getRemoteProjectDescriptorByQualifiedName(remoteProjectQualifiedName);
if (projectDescriptor != null) {
    projectsManager.loadProject(projectDescriptor, true);
}

使用项目管理

com.nomagic.magicdraw.core.project.ProjectsManager 提供了使用的项目管理(项目使用、导出、导入、重新加载和包共享)方法。

例 1:导出已使用的本地项目

工程包的集合可以导出为已使用的项目。

java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
File file = new File(moduleFilePath);
ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI());
// Export a collection of packages as a used project
projectsManager.exportModule(project, packages, "My used local project", projectDescriptor);
例 2:导出已使用的服务器项目

com.nomagic.magicdraw.teamwork.application.TeamworkUtils 类用于导出使用的服务端项目。

TeamworkUtils 类已不存在。

java
TeamworkUtils.exportTeamworkModule(project, packages, "My used remote project", remoteProjectQualifiedName);
例 3:使用项目

本地和服务器项目的用法是相似的 - 必须使用适当的项目描述符,但在使用本地项目之前应更新模块目录项目选项:

java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
File file = new File(moduleFilePath);
// update project options only when using local project
String moduleDir = file.getParent();
ProjectOptions projectOptions = project.getOptions();
List<String> directories = projectOptions.getModulesDirectories(true);
if (!directories.contains(moduleDir)) {
    projectOptions.addModuleDirectory(moduleDir);
}
ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI());
projectsManager.useModule(project, projectDescriptor);
例 4:导入已使用的项目

本地和服务器项目导入没有区别,必须只使用适当的项目描述符:

java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
File file = new File(moduleFilePath);
ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI());
projectsManager.importModule(project, projectDescriptor);
例 5:重新加载已使用的项目

本地项目和服务器项目重新加载没有区别。必须只使用适当的项目描述符:

java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
File file = new File(moduleFilePath);
ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.createProjectDescriptor(file.toURI());
projectsManager.reloadModule(project, projectDescriptor);
例 6:包共享
java
ProjectsManager projectsManager = Application.getInstance().getProjectsManager();
SessionManager.getInstance().createSession(project, "Create shared package");
// Create a package to share
Package aPackage = project.getElementsFactory().createPackageInstance();
aPackage.setOwner(project.getModel());
aPackage.setName("myShare");
SessionManager.getInstance().closeSession(project);

// Share a package
projectsManager.sharePackage(project, Arrays.asList(aPackage), "my used project");

// Save a project
ProjectDescriptor projectDescriptor = ProjectDescriptorsFactory.getDescriptorForProject(project);
projectsManager.saveProject(projectDescriptor, true);
例 7:编辑项目中的已用项目
java
final String moduleFileName = new File(moduleFilePath).getName();
// Find a used project directly used by the project(the 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 the used project editable(the read-write accessibility mode)
        ModulesService.setReadOnlyOnTask(moduleUsages, false);
    }
    // Get the first shared package of the used project
    final Collection<Package> sharedPackages = ProjectUtilities.getSharedPackages(attachedProject);
    final Package aPackage = sharedPackages.iterator().next();
    // Create a class in the used project
    SessionManager.getInstance().createSession(project, "Create use case in used project");
    final Class aCase = project.getElementsFactory().createClassInstance();
    aCase.setOwner(aPackage);
    aCase.setName("myClass");
    SessionManager.getInstance().closeSession(project);
    // save the used project
    Application.getInstance().getProjectsManager().saveModule(project, attachedProject, true, false);
    if (readOnly) {
        // Make a used project not editable(the read-only accessibility mode)
        ModulesService.setReadOnlyOnTask(moduleUsages, true);
    }
}

高级管理

使用 com.nomagic.magicdraw.core.ProjectUtilities 实用程序类来检索更多与项目分解相关的信息。

使用 com.nomagic.magicdraw.core.modules.ModulesServiceProjectsManager 类来管理附加的项目。

使用 TWC 的项目

使用 com.nomagic.magicdraw.esi.EsiUtils 实用程序类来处理 Teamwork Cloud 中的项目。

检查 JavaDoc Api 文档了解更多详情。

样例存在于<建模工具安装目录>\openapi\examples\teamworkcloud

项目结构分解

项目结构分解为几个部分 - 一个主要项目和附加项目。

主项目部件属于项目本身。

附加的项目部件来自“已使用的项目”(模块)。

使用的项目可以使用其他项目,因此附加项目具有深层结构。

使用 com.nomagic.magicdraw.core.ProjectUtilities 实用程序类来检索更多与项目分解相关的信息。

使用 com.nomagic.magicdraw.core.modules.ModulesServiceProjectsManager 类来管理附加的项目。

项目选项

每个项目都有与项目相关的选项。这些选项与项目数据一起存储。选项使用 com.nomagic.magicdraw.properties.Propertycom.nomagic.magicdraw.properties.PropertyManager 类进行组织。

项目选项分为三类:

  1. 在“项目选项” UI 窗口中可见
  2. 在“项目选项” UI 窗口中不可见
  3. 在“项目选项” UI 窗口中的“个人”不可见

个人选项特定于服务器项目中的每个用户。这意味着,如果将同一选项标记为“个人”,则每个用户可能具有相同的选项的不同值。个人选项被视为本地项目中的常规选项。

检索项目选项

使用com.nomagic.magicdraw.core.Project.getOptions()

检索项目选项值

下面的示例演示如何访问项目选项的值:

java
// retrieving a visible in UI option
Property property = project.getOptions().getProperty(ProjectOptions.PROJECT_GENERAL_PROPERTIES, "TEST_PROPERTY_ID");
// retrieving an invisible in UI option
Property property1 = project.getOptions().getProperty(ProjectOptions.PROJECT_INVISIBLE_PROPERTIES, "TEST_PROPERTY_ID");
// retrieving a personal invisible in UI option
Property property2 = project.getOptions().getProperty(ProjectOptions.PERSONAL_INVISIBLE_PROPERTIES, "TEST_PROPERTY_ID");

// retrieving an option value
if (property != null) {
    Object value = property.getValue();
}
添加自定义项目选项

配置器用于定义新的项目选项。

下面的示例演示如何在插件的init()方法中添加项目选项的配置器,以便为每个项目提供附加项目选项“Test Property”。

java
ProjectOptions.addConfigurator(new ProjectOptionsConfigurator() {
    @Override
    public void configure(ProjectOptions projectOptions) {
        Property property = projectOptions.getProperty(ProjectOptions.PROJECT_GENERAL_PROPERTIES, "TEST_PROPERTY_ID");
        if (property == null) {
            // Create a property, if it does not exist
            property = new StringProperty("TEST_PROPERTY_ID", "description");
            // Group
            property.setGroup("MY_GROUP");
            // The custom resource provider
            property.setResourceProvider(new PropertyResourceProvider() {
                @Override
                public String getString(@CheckForNull String string, @CheckForNull Property property) {
                    if ("TEST_PROPERTY_ID".equals(string)) {
                        // Translate ID
                        return "Test Property";
                    }
                    if ("TEST_PROPERTY_ID_DESCRIPTION".equals(string)) {
                        // Translate a description
                        return "Test Property in My Group";
                    }
                    if ("MY_GROUP".equals(string)) {
                        // Translate a group
                        return "My Group";
                    }
                    return string;
                }
            });
            
            // Add a property
            projectOptions.addProperty(ProjectOptions.PROJECT_GENERAL_PROPERTIES, property);
        }
    }
});

项目生命周期事件

使用 com.nomagic.magicdraw.core.project.ProjectsManager.addProjectListener(ProjectEventListener)

注册 com.nomagic.magicdraw.core.project.ProjectEventListener 侦听器将在各种与项目周期相关的操作上被调用。

建议在 com.nomagic.magicdraw.plugins.Plugin.init() 方法中注册一个侦听器。

保存参与者 Save participant

使用 com.nomagic.magicdraw.core.SaveParticipant 插入到 save/commit 操作中。

使用 com.nomagic.magicdraw.core.Application.addSaveParticipant(SaveParticipant) 注册它。

UML 模型接口

总述

转自官网描述:

在由 No Magic Inc. 开发的建模工具创建的项目中,UML 模型是 OMG UML 2.5.1 元模型的实现。在本文档或 JavaDoc 中,我们没有提供所有 UML 元模型元素及其属性的非常详细的描述。我们的 UML 实现涵盖了整个 UML 规范,并通过引入一些额外的元素(如图表)对其进行了扩展。您可以在对象管理组官方网站上的 UML 2.5.1 上部结构规范中找到这些信息,网址为 https://www.omg.org/spec/UML/

您应该使用 com.nomagic.uml2.ext.magicdraw 包中的 UML 模型接口。

DANGER

切勿在代码中使用 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.impl.ElementImpl 等实现类!

所有模型类的基本接口是 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element,它实现了 com.nomagic.magicdraw.uml.BaseElement 接口。

所有结构派生自 OMG-UML 2.5.1 上部结构规范中的元素。

UML 规范中描述的所有属性和引用都已实现,并且可通过 setter 和 getter 进行访问。

DANGER

UML 模型对于读取是线程安全的,但 UML 模型的写入(修改)仍然不是线程安全的

会话管理

Session management

com.nomagic.magicdraw.openapi.uml.SessionManager 是用于编辑模型 Elements 的单例管理器。

对模型元素的所有修改都应该在createSession(Project, java.lang.String)closeSession(Project)方法调用之间执行。

要编辑一些 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element,必须创建与此管理器的会话。编辑模型元素后,必须关闭会话。之后,所有更改都将应用于模型,并在命令历史记录(用于撤销/重做)中注册为一个具有会话的命令。只能有一个会话处于活动状态。

以下代码可用于访问、检查和创建会话:

java
// access a singleton instance by using getInstance()
// only one session can be active, so check this.
if (!SessionManager.getInstance().isSessionCreated(project)) {
    // create a new session
    SessionManager.getInstance().createSession(project, "Edit");
}

如果其他会话已经创建但尚未关闭,则createSession(Project, java.lang.String) 方法将引发 java.lang.IllegalStateException 运行时异常。

检查元素编辑权限

在执行模型更改之前,请检查模型修改权限。模型中的某些元素可以是只读的,但 API 不会阻止修改这些元素。

在一下情况下,该元素是只读的:

  • 元素属于只读使用的项目(模块)
  • 元素未锁定,无法在 Teamwork 云环境中进行编辑
  • 在某些特定的建模案例中添加了一些其他限制

检查编辑权限

使用com.nomagic.magicdraw.uml.BaseElement.isEditable()检查 Element 的属性是否可以更改。

如果修改了基元元素(例如名称)或修改了引用(例如属性类型),则应检查编辑权限。

如果要修改某些包含属性(如 Element.ownedElement),请检查添加权限。

检查添加权限

使用BaseElement.canAddChild()检查是否可以将子项添加到元素中。

使用BaseElement.canAdd(com.nomagic.magicdraw.uml.BaseElement)检查父级是否可以拥有给定元素,而父级是否有权拥有。例如,如果您尝试将操作添加到包中,则此方法将失败。

检查删除权限

使用BaseElement.isEditable()检查是否可以从模型中删除(释放)元素。

检查移动权限

移动权限类似于添加权限,但会检查更多条件。

插入自定义权限处理程序

使用 com.nomagic.magicdraw.uml.permissions.ElementPermissionsManager.addPermissionsHandler(ElementPermissions) 注册自定义元素权限处理程序。使用此 API,您可以处理将权限放在某些特定元素顶部的自定义规则。

访问和修改模型元素属性

可以使用简单的 setter 和 getter 访问元素属性。例如 NamedElement.name 属性:

java
NamedElement el = ...;
String name = el.getName();
el.setName("new name");

容器属性

No Magic Inc. 开发的建模工具使用模型的复合结构。

每个模型元素都是一个容器,包含自己的子元素,并了解自己的父元素。

可以使用com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element.getOwner()方法访问模型元素父元素。

可以使用Element.getOwnedElement()方法接收拥有的子项。不同类型的子级存储在单独的容器属性中。

可以通过 UML 规范中描述的名称访问这些容器属性。getOwnedElement()方法从所有内部容器属性中收集所有子级。

容器属性的修改和迭代非常简单,使用 java.util.Collection 接口。修改容器属性时,将自动触发属性更改事件。

容器实现 UML 元模型规范中的子集和联合约束。这说明了修改一个容器如何影响其他容器。确保您了解 UML 元模型中的子集和联合。如果要将一些内部 Element 添加到联合集合中,则需要将其添加到联合的特定子集中。

某些容器是只读的。对于大多数派生的 UML 元模型属性都是如此。

某些派生属性是可编辑的。例如,Element.ownedElement 是可编辑的。

设置一个 UML 元关联属性值就足够了,并且还将设置一个相反的属性。

例如,可以通过两种方式将类添加到包中:

java
Class myClass = ...;
Package myPackage = ...;
myClass.setOwner(myPackage);

java
myPackage.getOwnedElement().add(myClass);

访问容器属性中的元素

下面的示例演示如何检索模型元素的子元素:

java
Element el = ...;
for (Element element : el.getOwnedElement()) {
    // work with element
}

修改容器中的元素

使用标准的 java.util.Collectionjava.util.List 方法:

java
modelElement.get<SomeContainer>().add(child);
modelElement.get<SomeContainer>().remove(child);

可导航的相反引用

标准 UML 元模型在“成对”的元素之间定义了一个引用 - 对于每个主要的可导航引用,都有一个相反的引用。大多数相反的引用是不可导航的(在相反的元类中未公开)。

例如,TypedElement 按引用类型引用它的 Type,但 Type 不会公开由它键入的 TypedElements。这种方法在语义上是有效的,但使模型遍历具有挑战性。

UML 元模型实现不遵循此限制,并公开所有相反的引用。

一些相反的引用具有由 UML 规范定义的“友好”名称。

例如,NamedElement.clientDependency 相反的引用名称是 Dependency.client

其他相反的引用名称是使用模式 _<primaryReferenceDefiningMetaclassName>Of<PrimaryReferenceName> 构造的。即使最新版本的 UML 规范为这些相反的引用提供了不同的名称,也可以使用这种命名模式。

例如,在 TypedElement 中定义了一个主引用类型。相反的引用在“类型具有名称_typedElementOfType”中定义。

为每个相反的参考提供了 Setter 和 Getter。例如

Primary reference Element.getAppliedStereotype(), 
opposite Stereotype.get_stereotypedElement()

Primary reference TypedElement.getType(), 
opposite Type.get_typedElementOfType()

创建新的模型元素

Creating new model elements

样例存在于<建模工具安装>\openapi\examples\accessors中找到代码示例

使用 com.nomagic.uml2.impl.ElementsFactory 类来创建模型元素。

要创建模型元素,必须创建 com.nomagic.magicdraw.openapi.uml.SessionManager 会话。

create<model 元素类型>Instance()方法创建一个新的模型元素实例。上图仅显示了所有可用创建 <...>Instance() 方法。

UML 模型中的所有更改都将被注册,并且在会话关闭时,将添加到命令历史记录中。

java
Project project = ...;
ElementsFactory f = project.getElementsFactory();
SessionManager.getInstance().createSession(project, "Create a package");
Package packageA = f.createPackageInstance();
// add created package into a root of the project
packageA.setOwner(project.getPrimaryModel());
// ...
// apply changes and add a command into the command history
SessionManager.getInstance().closeSession(project);

创建新的关系对象

Creating new relationship objects

如果模型元素实现以下接口之一,则该元素是一种关系:

  • com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Relationship
  • com.nomagic.uml2.ext.magicdraw.activities.mdbasicativities.ActivityEdge
  • com.nomagic.uml2.ext.magicdraw.statemachines.mdbehaviorstatemachines.Transition
  • com.nomagic.uml2.ext.magicdraw.activities.mdextrastructuredactivities.ExceptionHandler
  • com.nomagic.uml2.ext.magicdraw.compositestructures.mdinternalstructures.Connector

要检查模型元素是否是关系,请调用com.nomagic.uml2.ext.jmi.helpers/CoreHelper.isRelationship(Element)方法。

为了获取关系的供应商和客户端元素,请使用 CoreHelper 类getSupplierElement(Element)getClientElement(Element)方法。

使用 CoreHelper 类setSupplierElement(Element, Element)setClientElement(Element, Element)方法来设置关系的供应商和客户端元素。

创建新关系

  • 创建新的关系模型元素。
  • 通过使用 CoreHelper 类setSupplierElement(Element, Element)setClientElement(Element, Element) 方法设置客户端和供应商模型元素。
  • 通过使用 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element.setOwner(Element)方法将新关系添加到某个父级中。

编辑模型元素

要编辑某些 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element,必须创建与 com.nomagic.magicdraw.openapi.uml.SessionManager 的会话。

UML 模型中的所有更改都将被注册,并且在会话关闭时,将添加到命令历史记录中。

java
Project project = ...;
SessionManager.getInstance().createSession(project, "Edit class A");
if (classA.isEditable()) {
    classA.setName(newName);
}
SessionManager.getInstance().closeSession(project);

需要检查元素编辑权限来确保修改后的元素不是只读的。

添加、移动、删除模型元素

样例路径:<建模工具安装目录>\openapi\examples\hierarchyremover

有两种方法可以添加/移动/删除模型元素:

  • 调用 com.nomagic.magicdraw.openapi.uml.ModelElementsManager 类中方法
  • 在元素上直接调用函数

ModelElementsManager 是单例实用程序类,它在执行函数之前检查 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element 编辑权限和会话是否存在。此外,它还执行检查,是否可以将元素添加到父级。如果未使用 ModelElementsManager,程序员必须在代码中显式执行这些检查。

添加和移动函数与大多数函数类似 - 移动函数执行更多检查,例如如果移动 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.NamedElement,则检查名称冲突。一般规则是对新创建的 Element 使用添加函数,对模型中已存在的元素使用移动函数。重构模型元素页中介绍了更高级的模型重构函数。

使用 ModelElementsManager 向所有者添加元素

要将新的模型元素添加到模型中,请使用 ModelElementsManager 提供的addElement(Element, Element)方法。

java
Project project = ...;
Class classA = ...;
Package packageA = ...;
// Create a new session
SessionManager.getInstance().createSession(project, "Add class into package");
try {
    // add a class into a package
    ModelElementsManager.getInstance().addElement(classA, packageA);
} catch(ReadOnlyElementException e) {

}
// apply changes and add a command into the command history.
SessionManager.getInstance().closeSession(project);

如果给定的模型元素无法添加到给定的父元素中,则会抛出 java.lang.IllegalArgumentException。例如无法将 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Operation 添加到 com.nomagic.uml2.ext.magicdraw.classes mdkernel.Package 中,或者无法将操作添加到未锁定的编辑 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class(在服务器项目中)中。如果元素或父元素为 null,则 java.lang.IllegalArgumentException 也会被抛出。如果给定的元素不可编辑(只读),则抛出 com.nomagic.magicdraw.openapi.uml.ReadOnlyElementException

使用直接函数向所有者添加元素

java
Element parent = ...;
Class classA = ...;
Project project = ...;
// create a new session
SessionManager.getInstance().createSession(project, "Add class into parent");
if (parent.canAdd(classA)) {
    classA.setOwner(parent);
}
// apply changes and add a command into the command history.
SessionManager.getInstance().closeSession(project);

使用模型元素管理器移动元素

要移动现有的模型元素,请使用 ModelElementsManager 提供的moveElement(Element, Element)方法。

java
Project project = ...;
Class classA = ...;
Package packageA = ...;
// create new session
SessionManager.getInstance().createSession(project, "Add class into package");
try {
    // add a class into a package
    ModelElementsManager.getInstance().moveElement(classA, packageA);
} catch (ReadOnlyElementException e) {
    
}
// apply changes and add a command into the command history
SessionManager.getInstance().closeSession(project);

如果给定的模型元素无法移动到给定的父元素中,则会抛出 java.lang.IllegalArgumentException。例如,操作不能移动到包中,或者操作不能移动到未锁定以进行编辑类(在服务器项目中)。如果元素或父元素为 null,则 java.lang.IllegalArgumentException 也会被抛出。如果给定的元素不可编辑(只读),则抛出 com.nomagic.maigcdraw.openapi.uml.ReadOnlyElementException

使用直接函数移动元素

java
Element parent = ...;
Class classA = ...;
Project project = ...;
// create new session
SessionManager.getInstance().createSession(project, "Add class into parent");
if (ModelHelper.canMoveChildInto(parent, classA)) {
    classA.setOwner(parent);
}
// apply changes and add a command into the command history
SessionManager.getInstance().closeSession(project);

使用模型元素管理器删除元素

java
Class classA = ...;
Project project = ...;
// create a new session
SessionManager.getInstance().createSession(project, "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(project);

使用直接调用删除元素

java
Class classA = ...;
Project project = ...;
// create a new session
SessionManager.getInstance().createSession(project, "Remove class");
if (classA.isEditable()) {
    classA.dispose();
}
// apply changes and add a command into the command history
SessionManager.getInstance().closeSession(project);

重构模型元素

示例存在于<建模工具安装目录>\openapi\examples\refactor中。

重构模型中的元素,请使用 com.nomagic.magicdraw.uml.Refactoring 类。

例 1:将元素转换为接口
java
Element elementToConvert = ...;
Project project = ...;
SessionManager sessionManager = SessionManager.getInstance();
sessionManager.createSession(project, "Convert");

// Converts the element to an interface.
ConvertElementInfo info = new ConvertElementInfo(Interface.class);

// Preserves the old element ID for the new element.
info.setPreserveElementID(true);
Element conversionTarget = Refactoring.Converting.convert(elementToConvert, info);
sessionManager.closeSession(project);
例 2:用另一个元素替换一个元素
java
Element element = ...;
Element elementToReplace = ...;
Project project = ...;
SessionManager sessionManager = SessionManager.getInstance();
sessionManager.createSession(project, "Replace");

ConvertElementInfo info = new ConvertElementInfo(elementToReplace.getClassType());
info.setConvertOnlyIncomingReferences(true);

Refactoring.Replacing.replace(element, elementToReplace, info);

sessionManager.closeSession(project);
数据提取重构

使用 com.nomagic.maigcdraw.uml.Refactoring.Extracting 类为要提取的符号创建提取管理器。通过更改 com.nomagic.magicdraw.uml.refactor.extract.ExtractSourcecom.nomagic.magicdraw.uml.refactor.extract.ExtractTarget,配置数据提取重构。使用com.nomagic.magicdraw.uml.refactor.extract.ExtractManager.extract()调用重构。通过检查 ExtractSource 和 ExtractTarget 来查看重构结果。

java
ExtractManager extractManager = Refactoring.Extracting.createExtractManager(symbols);
if (extractManager != null) {
    Project project = ...;
    // A session has to be started before refactoring.
    SessionManager sessionManager = SessionManager.getInstance();
    sessionManager.createSession(project, "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.
    Package package1 = project.getElementsFactory().createPackageInstance();
    package1.setOwner(project.getPrimaryModel());

    // Set the namespace to which the extract result should go.
    extractTarget.setTargetNamespace(package1);

    // Choose target diagram type from allowed diagram types if the default type does not suite.
    List<String> 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(project);

    // 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();
}
例 4:反向关系重构

关系反向重构可以使用com.nomagic.magicdraw.uml.Refactoring.RelationReversing.reverseRelationDirection(Element)方法完成。

java
// We have an arbitrary element or symbol which represents a relationship/
Element element = ...;
Project project = ...;
// Relationship reversing should be wrapped with session create/close calls.

SessionManager sessionManager = SessionManager.getInstance();
sessionManager.createSession(project, "Reverse relation");

// Reverse the relationship.
Refactoring.RelationReversing.reverseRelationDirection(element);

// Close the session.
sessionManager.closeSession(project);
例 5:移动具有与其他所有者的连接关系的元素

使用com.nomagic.magicdraw.uml.Refactoring.Moving.moveElementsWithRelation(java.util.Collection, Element)com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element 和所有连接的 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Relationship(s) 移动到新的所有者。

复制元素和符号

示例:<程序安装目录>\openapi\examples\copypaste中找到代码示例

可以将模型图元和符号复制到同一项目中的其他位置或复制到另一个项目。这必须在同一会话中完成。

制作副本有两种模式:

  • 深度复制(使用新数据)
  • 浅层复制(使用重用的数据)
例 1:复制元素
java
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.
Project project = ...;
SessionManager sessionManager = SessionManager.getInstance();
sessionManager.createSession(project, "Clone");

// A 3rd parameter indicates whether an element name uniqueness should be preserved in the parent.
CopyPasting.copyPasteElement(element, parent, true);

sessionManager.closeSession(project);
例 2:复制多个元素和符号
java
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.
Project project = ...;
SessionManager sessionManager = SessionManager.getInstance();
sessionManager.createSession(project, "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(project);

元素标识

两种方法提供了 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element 的标识符。根据具体情况,可以使用这两种方法。本节介绍两者之间的区别:

  1. com.nomagic.uml2.ext.magicdraw.base.ModelObject.getLocalID()
  2. com.nomagic.magicdraw.foundation.MDObject.getID()

返回的标识符的含义取决于项目类型。

这两个标识符都可用于使用方法com.nomagic.magicdraw.core.Project.getElementByID(java.lang.String)检索元素。请注意,如果通过提供的本地 ID 在项目中存在多个元素,则返回其中一个元素。

本地项目

这两种方法都返回相同的标识符,该标识符在项目范围内是永久性且唯一的。在大多数情况下,此 ID 在不同项目中是唯一的,除非您通过复制文件或使用“另存为”命令创建项目的副本。

例:

  1. 如果项目 A 在项目 B 和 C 中使用,则项目 A 的元素在项目 B 和 C 中具有相同的标识符。
  2. 如果一个项目 A 被复制为一个项目 A1,而一个项目 B 使用项目 A,那么使用项目 A1 也是不允许的,因为 ID 在项目 B 的范围内必须保持唯一。

TWC 项目

如果将本地项目添加到 Teamwork Cloud(反之亦然),则getLocalID()将始终返回相同的标识符值。请注意,具有相同本地 ID 的多个元素可能存在于单个 TWC 项目范围内。

getID()返回 UUID,它在项目和整个 TWC 服务器存储库的范围内都是唯一的。

Teamwork Cloud 导入(克隆)已用项目的内容。这意味着在项目内创建元素的副本。虽然getLocalID()为不同的副本返回相同的值,但getID()将为每个副本(项目用法)返回一个唯一的值。

例:如果一个项目 A 被复制为一个项目 A1,而一个 TWC 项目 B 同时使用 A 和 A1,getLocalID()为 A 和 A1 的相应元素返回相同的值,但getID()为相同的元素返回不同的标识符。

按名称或类型查找元素

使用 com.nomagic.magicdraw.uml.Finder 按名称或类型(元类型) 查找项目中的元素。此实用程序类提供了多个内部查找器,用于执行不同类型的搜索操作。

java
Project project = ...;
// Find a Model element under the project primary model with a name "MyModel".It is not a recursive search.
Model model = Finder.byName().find(project.getPrimaryModel(), Model.class, "MyModel");
// Find a first Model element under project primary model with a name "MyModel".It is a recursive search.
Model model2 = Finder.byNameRecursively().find(project.getPrimaryModel(), Model.class, "MyModel");
// Find all Model elements under project primary model with a name "MyModel".It is a recursive search.
Collection<Model> models = Finder.byNameAllRecursively().find(project.getPrimaryModel(), Model.class, "MyModel");

查找或创建分类器

一个特殊的使用程序类可用于按元类型,简单或限定的名称搜索或创建分类器 - com.nomagic.magicdraw.uml.ClassifierFinder

模型遍历及访客模式

下面是如何从 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element 收集所有子项并使用简单的循环避免递归的示例:

java
List<Element> children = new ArrayList<>();
Element current = ...;
children.add(current);
// if the current element has children, list will be increased.
for (int i = 0; i < children.size(); i++) {
    current = children.get(i);
    // add all children into end of this list, so it emulates a recursion.
    children.addAll(current.getOwnedElement());
}

可以配合 com.nomagic.magicdraw.uml.Finder 的更高级的收集方法:

  • byScope()
  • byType()
  • byTypeRecursively()

模型遍历

样例:<程序安装目录>\openapi\examples\statistics

com.nomagic.uml2.impl.ModelHierarchyVisitor 是访问者模式的增强实现,也是 com.nomagic.uml2.impl.ModelVisitor 的子类,用于访问模型元素。

每次访问<element_metatype>方法都会为超级类型调用访问<element_metatype>方法。

模型元素访问...方法具有 com.nomagic.uml2.ext.jmi.reflect.VisitorContext 类型的附加上下文参数。访问者上下文用于跟踪哪些超类型访问<element_metatype>方法已经访问过(以避免多次访问,因为某些模型元素具有多个继承)。开放 API 用户不应使用访问者上下文。所有跟踪都是在内部实现中完成的。

例如,您可以将一些代码放入 com.nomagic.uml2.impl.ModelHierarchyVisitor.visitClassifier(Classifier) 中,它将为 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Classifier 的所有子类执行。

java
ModelHierarchyVisitor myVisitor = new ModelHierarchyVisitor() {
    @Override
    public void visitClassifier(Classifier element, VisitorContext context) {
        // this is my classifier
    }
};
Project project = ...;
Package root = project.getPrimaryModel();
Iterator<Element> it = root.getOwnedElement().iterator();
while (it.hasNext()) {
    it.next().accept(myVisitor);
}

游客

样例:<程序安装目录>\openapi\examples\statics

访客设计模式在 UML 模型结构中实现。每个 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element 都有accept(AbstractVisitor)方法来访问它。

com.nomagic.uml2.impl.ModelVisitor 具有针对所有类型的模型元素的visit<element_metatype>方法。当您使用大型元素集合并需要执行特定于每种类型的 Element 的操作(例如,保存/加载、复制/粘贴或特定属性设置)时,这非常有用。

只需从 com.nomagic.magicdraw.uml.InheritanceVisitor 派生你的类,并覆盖一些访问...方法和调用accept(AbstractVisitor)为元素。

java
ModelVisitor myVisitor = new ModelVisitor() {
    // override some visit methods...
    @Override
    public void visitClass(Class aClass, VisitorContext visitorContext) {
        // this is my UML class
    }
};
Project project = ...;
Package root = project.getPrimaryModel();
Iterator<Element> it = root.getOwnedElement().iterator();
while (it.hasNext()) {
    it.next().accept(myVisitor);
}

检索有关元素的元信息

每个元素都由它的“元类”标识。Element metaclass 被映射到 java 接口(例如 com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class)。

使用com.nomagic.magicdraw.uml.BaseElement.getClassType()检索元素元类。

使用 com.nomagic.magicdraw.uml.ClassTypes 来管理元类 - 手机所有可用的元类,手机派生的等等。

创建文本元素表示

使用 com.nomagic.magicdraw.uml.RepresentationTextColor 创建 Element 的文本表示。此实用程序类用于在各种 UI(树、列表等)中创建文本元素表示形式。

java
Element element = ...;
// create a preformatted text for any kind of element
String text = RepresentationTextCreator.getRepresentedText(element);

// create a text with special options for com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Property
Property property = ...;
String propertyString = RepresentationTextCreator.createPropertyText(property, true, true, true, true, true, false, false, false, true, false);

提供自定义表示法文本

您可以覆盖特定元素的默认自定义文本创建。

使用 RepresentationTextCreator 类方法 addProvider(RepresentationTextCreator.RepresentationTextProvider)注册自定义表示文本提供程序。

检索元素图标

使用 com.nomagic.magicdraw.uml.ElementIcon 实用程序类来检索元素图标。它还提供了按元素元类型,构造性等检索图标的方法。

java
Element element = ...;
// get element icon
ResizableIcon icon = ElementIcon.getIcon(element);

// get UML Package icon
ResizableIcon packageIcon = ElementIcon.getIconByClassType(Package.class);

高级实用程序功能

使用 com.nomagic.uml2.ext.jmi.helpers.ModelHelper 获取各种与 UML 模型相关的高级函数。

它提供了实用程序方法

  • 一些核心功能
    • 使用元素的文档(注释)
    • 获取/设置客户和供应商关系结束
    • 手机元素的连接关系
    • 与协会合作 - 获得成员结束,改变可导航性
    • 所有权相关功能 - 寻找有效的所有者,检查所有权等
  • 使用值规范
    • 创建价值规范
    • 将值设置为值规范
  • 使用分类器
    • 收集派生的分类器,检查合法继承
    • 收集继承的成员
    • 使用重新定义的元素
    • 检查分类器兼容性
    • 使用操作参数,比较操作签名
  • 使用实例规格
    • 创建插槽,初始化插槽值
    • 使用链接

使用 com.nomagic.uml2.ext.jmi.helpers.InformationFlowHelper for Advanced Information Flows 相关函数

使用 com.nomagic.uml2.ext.jmi.helpers.InteractionHelper for Interaction domain 相关的高级函数

使用 com.nomagic.uml2.ext.jmi.helpers.StateMachineHelper for State Machine 相关的高级函数

使用 com.nomagic.uml2.ext.jmi.helpers.UseCaseHelper for Use Case 相关的高级函数

使用 com.nomagic.magicdraw.uml.ConnectorsCollector收集连接的 Connector 元素

事件/活动

图表接口

日志接口

使用 apache 的 log4j 包,来完成 log 的写入。

以下举例日志输入

java
import org.apache.log4j.Logger;
java
Logger logger = Logger.getLogger("标签");
logger.info("info 的日志");
logger.error("error 的日志")