【ProM编程3】如何创建一个复杂的Plug-in

本节在前两节的基础上,介绍如何创建一个复杂的Plug-in。

目录

1.本节介绍

2.基于非静态方法的插件

3.重载插件

3.1  可配置的输入

 3.2 多类型输入

4.总结


1.本节介绍

        到目前为止,本文介绍了简单的插件,其中所有的计算都是通过静态方法执行的。在本节中,我们将介绍两种更复杂的插件类型,即基于非静态方法的插件和重载插件

2.基于非静态方法的插件

        与基于静态方法的插件类似,您可以基于非静态方法定义插件。考虑以下代码, 这与早期的helloWorld示例非常相似,但现在helloworld方法不是静态的.

package org.processmining.plugins.gettingstarted;

import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;

public class HelloWorld9 {
        @Plugin(
                name = "My 5th Hello World Plug-in", 
                parameterLabels = {}, 
                returnLabels = { "Hello world string" }, 
                returnTypes = { String.class }, 
                userAccessible = true, 
                help = "Produces the string: 'Hello world'"
        )
        @UITopiaVariant(
                affiliation = "My company", 
                author = "My name", 
                email = "My e-mail address"
        )
        public String helloWorld(PluginContext context) {
                return "Hello World";
        }
}

        在ProM中使用此插件时,您会发现与静态版本在行为上没有任何区别。然而,存在一个重要的概念差异。如果用@Plugin注释注释的方法不是静态的,就像这里的情况一样,那么当被要求执行这个插件时,框架将调用该方法,就像在框架中的某个地方调用了以下代码一样:

HelloWorld9 hw9 = new HelloWorld9();
hw9.helloWorld(context);

        这里,首先创建类HelloWorld9的一个实例,并为此实例调用方法helloWorld。这意味着在方法主体中,类的所有私有成员都是可访问和实例化的。请注意,使用非静态方法作为插件需要存在默认构造函数,即不带参数的构造函数

3.重载插件

        到目前为止引入的基于方法的插件允许定义已知输入类型的插件。然而,在实践中,许多插件允许应用于多种类型的输入。例如,考虑一个构建Petri网可达性图的插件。这样的插件接受Petri网【a plug-in accepts Petri nets】,但也接受重置网(具有重置弧的Petri网)【Reset nets (Petri nets with reset arcs)】、约束网(具有约束弧的Petri网)【Inhibitor nets (Petri nets with inhibitor arcs)】和重置/约束网【Reset/Inhibitor nets】。

         此外,有时插件只需要m个参数中的n个。例如,考虑使用一个插件来打开一个文件。它的输入是文件名(可以是String、URI、File或FileInputStream类型)。然而,这个插件也可以在没有输入的情况下执行,在这种情况下,会向用户显示一个对话框,以提供指向要打开的文件的指针。但是请注意,只有在GUI中执行插件时,才能显示这样的对话框。

        过载的插件允许上面提到的场景。在本节中,我们将介绍带有变体的插件的概念,这就是这些场景的实现方式。在显示具有多种类型的参数之前,我们首先介绍了可选参数的概念。

3.1  可配置的输入

考虑以下代码:

package org.processmining.plugins.gettingstarted;

import javax.swing.JOptionPane;

import org.processmining.contexts.uitopia.UIPluginContext;
import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;
import org.processmining.framework.plugin.annotations.PluginVariant;

@Plugin(
        name = "My Overloaded Hello World Plugin", 
        parameterLabels = { "First string", "Number", "Second string" }, 
        returnLabels = { "Hello world string" }, 
        returnTypes = { String.class }, 
        userAccessible = true, 
        help = "The plugin produces 'hello' concatenated with at least one world. If no world is given, it is requested from the user, provided that a GUI exists."
)
public class HelloWorld10 {

        private String getWorld(UIPluginContext context) {
                // Ask the user for his world
                String w = JOptionPane.showInputDialog(null, "What's the name of your world?",
                                "Enter your world", JOptionPane.QUESTION_MESSAGE);
                // change your result label
                context.getFutureResult(0).setLabel("Hello " + w + " string");
                return w;
        }

        @PluginVariant(variantLabel = "My original hello world", requiredParameterLabels = {})
        @UITopiaVariant(uiLabel = "My original hello world", affiliation = "My company", author = "My name", email = "My e-mail address")
        public String helloWorld(PluginContext context) {
                return "Hello World";
        }

        @PluginVariant(variantLabel = "My Hello unknown", requiredParameterLabels = {})
        @UITopiaVariant(uiLabel = "My Hello unknown", affiliation = "My company", author = "My name", email = "My e-mail address")
        public String helloUnknown(UIPluginContext context) {
                return "Hello " + getWorld(context);
        }

        @PluginVariant(variantLabel = "My Combine worlds", requiredParameterLabels = { 0, 1, 2 })
        @UITopiaVariant(uiLabel = "My Combine worlds", affiliation = "My company", author = "My name", email = "My e-mail address")
        public Object helloWorlds(PluginContext context, String first, Integer number, String second) {
                String s = first;
                for (int i = 0; i < number; i++) {
                        s += "," + second;
                }
                return s;
        }

        @PluginVariant(variantLabel = "My Combine unknowns", requiredParameterLabels = { 0, 1 })
        @UITopiaVariant(uiLabel = "My Combine unknowns", affiliation = "My company", author = "My name", email = "My e-mail address")
        public Object helloWorlds(UIPluginContext context, String first, Integer number) {
                // return the combined string, after asking for the world
                return helloWorlds(context, first, number, getWorld(context));
        }
}

        在本例中,@Plugin注释不再用于方法,而是用于类。这向框架发出信号,表明这是一个重载的插件,由几个变体组成。这意味着应该至少有一个方法带有@PluginVariant注释。在这种情况下有四个。
        这个类的@Plugin注释与我们之前使用过的方法相同。因此,这里必须指定参数的标签,以及所有变体的返回类型。因此,所有变体都必须返回相同类型的对象,即在本例中它们都应该返回一个String。复杂的返回类型是允许的,只要它们对于所有变体都是相同的。
        此示例代码提供的插件的四个变体由一个标签和该变体所需参数的列表定义。此外,它们需要执行不同的上下文。在下表中,对四种变体进行了简要概述:

 3.2 多类型输入

        在前面的例子中,插件变体都使用了相同类型的参数的不同组合,即“Combine”未知变量需要标记为“First string”和“Number”的参数,在方法定义中,这两个参数的类型分别为“string”和“Integer”。然而,这并不是框架强加的要求,如以下代码所示:

package org.processmining.plugins.gettingstarted;

import javax.swing.JOptionPane;

import org.processmining.contexts.uitopia.UIPluginContext;
import org.processmining.contexts.uitopia.annotations.UITopiaVariant;
import org.processmining.framework.plugin.PluginContext;
import org.processmining.framework.plugin.annotations.Plugin;
import org.processmining.framework.plugin.annotations.PluginVariant;

@Plugin(
        name = "My Overloaded Hello Many Worlds", 
        parameterLabels = { "First string", "Large Number", "Second string" }, 
        returnLabels = { "Hello world string" }, 
        returnTypes = { String.class }, 
        userAccessible = true, 
        help = "The plugin produces 'hello' concatenated with at least one world. If no world is given, it is requested from the user, provided that a GUI exists."
)
public class HelloWorld11 {

        private String getWorld(UIPluginContext context) {
                // Ask the user for his world
                String w = JOptionPane.showInputDialog(null, "What's the name of your world?",
                                "Enter your world", JOptionPane.QUESTION_MESSAGE);
                // change your result label
                context.getFutureResult(0).setLabel("Hello " + w + " string");
                return w;
        }

        @PluginVariant(variantLabel = "My Combine many worlds", requiredParameterLabels = { 0, 1, 2 })
        @UITopiaVariant(uiLabel = "My Combine many worlds", affiliation = "My company", author = "My name", email = "My e-mail address")
        public Object helloWorlds(PluginContext context, String first, Long number, String second) {
                String s = first;
                for (int i = 0; i < number; i++) {
                        s += "," + second;
                }
                return s;
        }

        @PluginVariant(variantLabel = "My Combine few unknowns", requiredParameterLabels = { 0, 1 })
        @UITopiaVariant(uiLabel = "My Combine few unknowns", affiliation = "My company", author = "My name", email = "My e-mail address")
        public Object helloWorlds(UIPluginContext context, String first, Integer number) {
                // return the combined string, after asking for the world
                return helloWorlds(context, first, Long.valueOf(number), getWorld(context));
        }

        @PluginVariant(variantLabel = "My Combine many unknowns", requiredParameterLabels = { 0, 1 })
        @UITopiaVariant(uiLabel = "My Combine many unknowns", affiliation = "My company", author = "My name", email = "My e-mail address")
        public Object helloWorlds(UIPluginContext context, String first, Long number) {
                // return the combined string, after asking for the world
                return helloWorlds(context, first, number, getWorld(context));
        }
}

4.总结

        通过使用插件变体,可以定义复杂的插件来处理不同类型的参数。然而,有几点需要记住:
(1) 如果在确定上下文中不能执行任何变体,则该插件被框架忽略,
(2) 用@PluginVariant注释注释的插件变体的方法必须全部返回类的@Plugin注释中指定类型的对象。在执行插件之前,框架不会检查这一点,在这种情况下,如果返回类型不匹配,就会抛出异常。
(3) 插件变体既可以是静态的,也可以是非静态的
(4) 使用过多的插件变体可能会令人困惑(但有时是必要的)。通常,如果存在许多变体,那么将实际逻辑的实现推迟到(a)私有方法,并保持用@PluginVariant注释注释的方法代码尽可能干净,这是一种很好的编程实践。
(5) 插件变体可以在超类中定义,也就是说,可以定义一个包含许多变体的抽象类,这些变体都调用一个抽象保护方法,该方法在子类中实现。然而,@Plugin注释应该只在子类级别上使用,否则超类和子类都被视为插件。作为这种构造的一个例子,我们指的是以下各项的组合:
        org.processmining.plugins.abstractplugins.AbstractImportPlugin,它定义了打开由不同对象指定的文件的变体,但本身不是一个插件,以及
        org.processmining.plugins.etrinet.tpn.TpnImport,它实现了AbstractImportPlugin的抽象方法,并定义了@Plugin注释。
(6) 用@PluginVariant注释注释的类的方法也可以用@Plugin注释注释。在这种情况下,这些方法被框架视为单独的插件。然而,这种构造是不鼓励的,因为它会导致复杂的代码。
(7) @PluginVariant注释由子类继承。因此,任何用@Plugin注释注释的类都可以由另一个用@Plugine注释的类扩展。超类的变体也存在于子类中。这要求继承关系中的任何两个插件的结果类型都相同。同样,只有在执行了插件之后才会检查这一点。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北冥有鱼zsp

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值