RapidMiner Studio之Action源码分析

     Action负责响应按钮的动作,GUI启动后,每个按钮都有相应的action进行响应,下面先说明Action的继承关系:

     ResourceAction --> ConditionalAction --> AbstractAction  这三个类都是抽象类 , AbstractAction实现了Action, Cloneable, Serializable接口,Action接口的继承关系

为Action --> ActionListener --> EventListener。

     EventListener是标记接口,所有的事件监听接口都要继承EventListener。

     ActionListener继承自EventListener接口,ActionListener用于接收操作事件的侦听器接口,对处理操作事件感兴趣的类可以实现此接口,而使用该类创建的对象可使用组件的 addActionListener 方法向该组件注册,在发生操作事件时,调用该对象的 actionPerformed 方法。Action是对ActionListener的扩展,具体可参考api,这里不做复述了。AbstractAction提供了Action接口的默认实现,用户只需要实现actionPerformed 方法即可。

     下面重点分析ConditionalAction和ResourceAction二个抽象类:

     ConditionalAction的作用:Action在某些特定条件下需要启用或者禁用,这些条件可能是强制性的、不允许的、或者是不相干的,所有新创建的ConditionalActions将被添加到一个集合中,并且如果这些条件的前提已经发生改变的情况下,这些Action的状态也要自动进行检查。

public abstract class ConditionalAction extends AbstractAction {

    private static final long serialVersionUID = -3581066203343247846L;
/*所有的ConditionalAction对象,这里保持的是ConditionalAction对象的弱引用,以便GC能及时回收内存*/
private static final List<WeakReference<ConditionalAction>> ALL_ACTIONS = new LinkedList<WeakReference<ConditionalAction>>(); /* 定义了可能的状态 */ public static final int DISALLOWED = -1; public static final int DONT_CARE = 0; public static final int MANDATORY = 1; /*定义了可能的条件*/ public static final int OPERATOR_SELECTED = 0; public static final int OPERATOR_CHAIN_SELECTED = 1; public static final int ROOT_SELECTED = 2; public static final int SIBLINGS_EXIST = 3; public static final int PROCESS_STOPPED = 4; public static final int PROCESS_PAUSED = 5; public static final int PROCESS_RUNNING = 6; public static final int PARENT_ENABLED = 7; public static final int EXECUTION_UNIT_SELECTED = 8; public static final int EDIT_IN_PROGRESS = 9; public static final int PROCESS_IS_ON_REMOTE_REPOSITORY = 10; public static final int PROCESS_SAVED = 11; public static final int PROCESS_RENDERER_IS_VISIBLE = 12; public static final int PROCESS_RENDERER_HAS_UNDO_STEPS = 13; public static final int PROCESS_RENDERER_HAS_REDO_STEPS = 14;
/*这里的conditions的值是上面三种状态中的一种,数组的大小为15,每一种状态在这个数组中的位置由上面的condition定义*/
public static final int NUMBER_OF_CONDITIONS = 15; private final int[] conditions = new int[NUMBER_OF_CONDITIONS]; private boolean isDisabledDueToFocusLost; public ConditionalAction(String name) { this(name, null); } public ConditionalAction(String name, Icon icon) { super(name, icon); conditions[EDIT_IN_PROGRESS] = DISALLOWED;

/*是否启用EDT,关于EDT,可以参考这篇博文:深入浅出Swing事件分发线程 */
if (SwingUtilities.isEventDispatchThread()) { ALL_ACTIONS.add(new WeakReference<ConditionalAction>(this)); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ALL_ACTIONS.add(new WeakReference<ConditionalAction>(ConditionalAction.this)); } }); } } /** * @param index * one out of OPERATOR_SELECTED, OPERATOR_CHAIN_SELECTED, * ROOT_SELECTED, CLIPBOARD_FILLED, and PROCESS_RUNNING * @param condition * one out of DISALLOWED, DONT_CARE, and MANDATORY */ public void setCondition(int index, int condition) { conditions[index] = condition; } /** Updates all actions. 传入的states参数,其长度也是NUMBER_OF_CONDITIONS定义的值
* 且下标是由上面定义的15个condition
*/ public static void updateAll(boolean[] states) { Iterator<WeakReference<ConditionalAction>> i = ALL_ACTIONS.iterator(); while (i.hasNext()) { WeakReference<ConditionalAction> ref = i.next(); ConditionalAction c = ref.get(); if (c == null) { i.remove(); } else { c.update(states); } } } /** * Updates an action given the set of states that can be true or false. * States refer to OPERATOR_SELECTED... An action is enabled iff for all * states the condition is MANDATORY and state is true or DISALLOWED and * state is false. If for all states the condition is DONT_CARE, the * enabling status of the action is not touched. */ protected void update(boolean[] state) { // if this action is disabled due to a focus loss never change its enabled state here if (isDisabledDueToFocusLost) { return; } boolean ok = true; boolean ignore = true;
for (int i = 0; i < conditions.length; i++) {
if (conditions[i] != DONT_CARE) { ignore = false; if (((conditions[i] == MANDATORY) && (state[i] == false)) || ((conditions[i] == DISALLOWED) && (state[i] == true))) { ok = false; break; } } } if (!ignore) { setEnabled(ok); } } public boolean isDisabledDueToFocusLost() { return isDisabledDueToFocusLost; } /** * 如果这个参数设置为true , 则无论条件如何变化,该action都不会去响应事件 */ public void setDisabledDueToFocusLost(boolean isDisabledDueToFocusLost) { this.isDisabledDueToFocusLost = isDisabledDueToFocusLost; } }

     ResourceAction创建这样一个action,其配置从properties文件读取,这些文件是GUI Resource Bundles的一部分,具体可查看RapidMiner Studio之GUI多语言支持,一个ResourceAction需要指定一个key,用来获取以下五个值:

  1. gui.action.<specifier>.label = Which will be the caption
  2. gui.action.<specifier>.icon = The icon of this action. For examples used in menus or buttons
  3. gui.action.<specifier>.acc = The accelerator key used for menu entries
  4. gui.action.<specifier>.tip = Which will be the tool tip
  5. gui.action.<specifier>.mne = Which will give you access to the mnemonics key. Please make it the same case as in the label

     ResourceAction类中主要有二个方法,一个是构造器,主要是获取properties文件中的值,再调用putValue方法设置相关属性的值;另外一个是为JComponent设置action的方法addToActionMap。

ResourceAction的构造方法

public ResourceAction(boolean smallIcon, String i18nKey, Object ... i18nArgs) {
/*调用父类构造器中,设置Action.NAME,并设置conditions[EDIT_IN_PROGRESS] = DISALLOWED;*/
super((i18nArgs == null) || (i18nArgs.length == 0) ? getMessage(i18nKey+".label") : MessageFormat.format(getMessage(i18nKey+".label"), i18nArgs)); this.key = i18nKey; String mne = getMessageOrNull(i18nKey + ".mne"); if (mne != null && mne.length() > 0) { String name = (String)getValue(NAME); if (name != null && name.length() > 0 && (name.indexOf(mne.charAt(0)) == -1) && (name.indexOf(Character.toLowerCase(mne.charAt(0))) == -1)) { LogService.getRoot().log(Level.FINE, "com.rapidminer.gui.tools.ResourceAction.key_not_found", new Object[] {mne, i18nKey, name}); } mne = mne.toUpperCase(); putValue(MNEMONIC_KEY, (int)mne.charAt(0)); } String tip = getMessageOrNull(i18nKey + ".tip"); if (tip != null) { putValue(SHORT_DESCRIPTION, (i18nArgs == null) || (i18nArgs.length == 0) ? tip : MessageFormat.format(tip, i18nArgs)); } this.iconName = getMessageOrNull(i18nKey + ".icon"); if (getIconName() != null) { ImageIcon small = SwingTools.createIcon("16/"+getIconName()); ImageIcon large = SwingTools.createIcon("24/"+getIconName()); putValue(LARGE_ICON_KEY, smallIcon ? (small != null ? small : large) : large); putValue(SMALL_ICON, small != null ? small : large); } String acc = getMessageOrNull(i18nKey + ".acc"); if (acc != null) {
/*设置快捷键*/ KeyStroke stroke
= KeyStroke.getKeyStroke(acc); putValue(ACCELERATOR_KEY, stroke); } putValue("rm_id", i18nKey); }

     addToActionMap方法:

public void addToActionMap(int condition, boolean disableOnFocusLost, boolean initiallyDisabled, String actionKey, JComponent... components) {
        for (JComponent comp : components) {
            if (comp == null) {
                throw new IllegalArgumentException("components must not be null!");
            }

            KeyStroke keyStroke = (KeyStroke)getValue(ACCELERATOR_KEY);
            if (keyStroke != null) {
                actionKey = actionKey == null ? key : actionKey;
//键盘事件与actionKey关联 comp.getInputMap(condition).put(keyStroke, actionKey);
//actionKey与action事件响应(this)关联 comp.getActionMap().put(actionKey,
this); } else { LogService.getRoot().log(Level.FINE, "com.rapidminer.gui.tools.ResourceAction.add_action_key_error", key); } if (disableOnFocusLost) {
//处理获取或者失去焦点事件 comp.addFocusListener(
new FocusListener() { @Override public void focusLost(FocusEvent e) { if (!e.isTemporary()) { // 不管条件如何,失去焦点时,action将不响应 focus lost here means disable it no matter the conditions ResourceAction.this.setEnabled(false); ResourceAction.super.setDisabledDueToFocusLost(true); } } @Override public void focusGained(FocusEvent e) { if (!e.isTemporary()) { //重新获取焦点,如果条件满足,则action将响应 focus gained here means enable it if conditions are fulfilled ResourceAction.super.setDisabledDueToFocusLost(false); RapidMinerGUI.getMainFrame().getActions().enableActions(); } } });
//处于初始化阶段,禁用该action
if (initiallyDisabled) { if (SwingUtilities.isEventDispatchThread()) { super.setDisabledDueToFocusLost(true); setEnabled(false); } else { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ResourceAction.super.setDisabledDueToFocusLost(true); ResourceAction.this.setEnabled(false); } }); } } } } }

 

    总结一下,rapidminer提供的二个抽象action类完成了以下功能:

    ConditionalAction是根据条件自动判断该action是否要禁用或者启用;

    ResourceAction是在ConditionalAction基础上添加了资源文件读取的功能,用来设置标题、快捷键、提示等信息。

    至于action是如何工作的,请查看RapidMiner Studio之Process源码分析 。

   

posted @ 2015-07-14 15:21  haizhun  阅读(782)  评论(0编辑  收藏  举报