com.isencia.passerelle.hmi.HMIBase.java Source code

Java tutorial

Introduction

Here is the source code for com.isencia.passerelle.hmi.HMIBase.java

Source

/*
 * (c) Copyright 2004, iSencia Belgium NV
 * All Rights Reserved.
 *
 * This software is the proprietary information of iSencia Belgium NV.
 * Use is subject to license terms.
 */
package com.isencia.passerelle.hmi;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import ptolemy.actor.CompositeActor;
import ptolemy.actor.ExecutionListener;
import ptolemy.actor.FiringEvent;
import ptolemy.actor.Manager;
import ptolemy.actor.gui.Configuration;
import ptolemy.actor.gui.ModelDirectory;
import ptolemy.actor.gui.PtolemyEffigy;
import ptolemy.data.expr.Parameter;
import ptolemy.kernel.ComponentEntity;
import ptolemy.kernel.Entity;
import ptolemy.kernel.util.Attribute;
import ptolemy.kernel.util.ChangeListener;
import ptolemy.kernel.util.ChangeRequest;
import ptolemy.kernel.util.IllegalActionException;
import ptolemy.kernel.util.NameDuplicationException;

import com.isencia.constants.IPropertyNames;
import com.isencia.passerelle.actor.gui.binding.ParameterToWidgetBinder;
import com.isencia.passerelle.actor.gui.graph.ModelGraphPanel;
import com.isencia.passerelle.core.PasserelleException;
import com.isencia.passerelle.domain.cap.Director;
import com.isencia.passerelle.ext.ErrorCollector;
import com.isencia.passerelle.ext.ExecutionTracer;
import com.isencia.passerelle.ext.FiringEventListener;
import com.isencia.passerelle.ext.impl.DefaultActorErrorControlStrategy;
import com.isencia.passerelle.hmi.action.AskTheUserErrorControlStrategy;
import com.isencia.passerelle.hmi.action.ModelCreator;
import com.isencia.passerelle.hmi.action.ModelDebugStepper;
import com.isencia.passerelle.hmi.action.ModelDebugger;
import com.isencia.passerelle.hmi.action.ModelExecutor;
import com.isencia.passerelle.hmi.action.ModelResumer;
import com.isencia.passerelle.hmi.action.ModelStopper;
import com.isencia.passerelle.hmi.action.ModelSuspender;
import com.isencia.passerelle.hmi.action.SaveAction;
import com.isencia.passerelle.hmi.action.SaveAsAction;
import com.isencia.passerelle.hmi.binding.ParameterChangeListener;
import com.isencia.passerelle.hmi.definition.Model;
import com.isencia.passerelle.hmi.definition.ModelBundle;
import com.isencia.passerelle.hmi.state.StateMachine;
import com.isencia.passerelle.hmi.trace.HMIExecutionTracer;
import com.isencia.passerelle.hmi.trace.TraceDialog;
import com.isencia.passerelle.hmi.trace.TraceVisualizer;
import com.isencia.passerelle.model.util.MoMLParser;
import com.isencia.passerelle.util.ExecutionTracerService;
import com.isencia.util.swing.components.FinderAccessory;

import diva.gui.ExtensionFileFilter;

public abstract class HMIBase implements ChangeListener {
    private final static Log logger = LogFactory.getLog(HMIBase.class);

    public static final String EXECUTION_CONTROL_ATTR_NAME = "_executionControl";

    public static final String USER_TRACER_ATTR_NAME = "_userTracer";

    public static class Mode {
        private Mode() {

        }
    }

    // accept all models and try to automatically build a UI
    public final static Mode GENERIC = new Mode();

    // validate models if they fit within the list
    // of supported model types
    // and show a custom UI for them
    public final static Mode SPECIFIC = new Mode();

    // this is the hmi config info parsed from some cfg file
    // current implementation uses XMLStreamer files
    private ModelBundle hmiModelsDef;

    // this is constructed when a model is selected
    private CompositeActor currentModel;

    private Model currentModelDef;

    protected Map<String, ParameterToWidgetBinder> hmiFields = new HashMap<String, ParameterToWidgetBinder>();

    // this represents the path to be used to open/save the model
    private URL modelURL;

    // this contains the instance of the Passerelle/Ptolemy execution manager
    // while a model is running
    private Manager manager;

    protected Component hookComponent;

    private TraceVisualizer traceComponent;

    private ExecutionTracer executionTracer;

    private final Mode mode;

    // some actions that are shared between menu items and toolbar buttons
    private ModelExecutor modelExecutor;

    private ModelDebugger modelDebugger;

    private ModelSuspender modelSuspender;

    private ModelResumer modelResumer;

    private ModelStopper modelStopper;

    private ModelDebugStepper modelDebugStepper;

    private SaveAction saveAction;
    private SaveAsAction saveAsAction;

    public SaveAction getSaveAction() {
        return saveAction;
    }

    public SaveAsAction getSaveAsAction() {
        return saveAsAction;
    }

    private Action modelCreatorAction;

    private boolean animateModelExecution = false;

    private boolean interactiveErrorControl = false;

    // private EditorGraphController animationController;

    private AnimationEventListener animationEventListener;

    protected ModelGraphPanel graphPanel;

    private Configuration config;
    private final boolean showModelGraph;
    protected PtolemyEffigy graphPanelEffigy;

    public static final String MODELS_URL_STRING = System.getProperty("generic.hmi.modelURL");

    public HMIBase(final Mode mode, final ModelBundle hmiModelsDef, final boolean showModelGraph)
            throws IOException {
        this.mode = mode;
        this.hmiModelsDef = hmiModelsDef;
        this.showModelGraph = showModelGraph;
    }

    public HMIBase(final Mode mode, final String cfgDefPath, final boolean showModelGraph) throws IOException {
        this.mode = mode;
        hmiModelsDef = ModelBundle.parseModelBundleDefFile(cfgDefPath);
        this.showModelGraph = showModelGraph;
    }

    protected Configuration getPtolemyConfiguration() {
        return config;
    }

    public void init() {
        // register exit hook to try to enforce a nice shutdown
        // even with Ctrl-C etc
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            public void run() {
                if (StateMachine.MODEL_EXECUTING.equals(StateMachine.getInstance().getCurrentState())) {
                    if (getCurrentModel() != null && getCurrentModel().getManager() != null) {
                        try {
                            getCurrentModel().getManager().stop();
                        } catch (final Throwable t) {
                            t.printStackTrace();
                            logger.error("Error stopping model " + getCurrentModel().getFullName()
                                    + " during application shutdown", t);
                        }
                    }
                }
            }
        }));
        String configurationFile = null;
        try {
            configurationFile = System.getProperty(IPropertyNames.APP_HOME, IPropertyNames.APP_HOME_DEFAULT)
                    + java.io.File.separatorChar
                    + System.getProperty(IPropertyNames.APP_CFG, IPropertyNames.APP_CFG_DEFAULT)
                    + java.io.File.separatorChar + "passerelle-actor.xml";
            System.out.println("readconfiguration file " + configurationFile);
            logger.debug("readconfiguration file " + configurationFile);
            config = readConfiguration(configurationFile);

        } catch (final Exception e) {
            e.printStackTrace();
            logger.error("readconfiguration failed " + e.toString());
        }

        traceComponent = createTraceComponent();
        hookComponent = initUI(HMIMessages.getString(HMIMessages.TITLE));
        if (executionTracer == null) {
            executionTracer = new HMIExecutionTracer(traceComponent);
        }
        ExecutionTracerService.registerTracer(executionTracer);
    }

    /**
     * Method that can be overridden if execution trace messages need to be
     * displayed in a custom component.
     * 
     * Default implementation uses a TraceDialog, that wraps a TracePanel. The
     * component should implement the TraceVisualizer interface.
     * 
     * @return the chosen TraceVisualizer UI implementation
     */
    protected TraceVisualizer createTraceComponent() {
        final TraceDialog traceDialog = new TraceDialog((JFrame) hookComponent);
        traceDialog.setVisible(false);

        return traceDialog;
    }

    public CompositeActor getCurrentModel() {
        return currentModel;
    }

    public Model getCurrentModelDef() {
        return currentModelDef;
    }

    public URL getModelURL() {
        return modelURL;
    }

    public void setModelURL(final URL modelURL) {
        this.modelURL = modelURL;
    }

    /**
     * Must be implemented to show the general frame of the HMI, without actual
     * forms/fields for specific models in it yet.
     * 
     * @param title
     * @return some component we can use for locating pop-ups etc
     */
    protected abstract Component initUI(String title);

    /**
     * Must fill the form/field contents of the HMI corresponding to what's
     * needed to configure models corresponding to the given key.
     * 
     * @param modelKey
     */
    protected abstract void showModelForm(String modelKey);

    protected abstract void clearModelForms();

    private final Map<URL, CompositeActor> loadedModels = new HashMap<URL, CompositeActor>();

    /**
     * @param _modelURL
     * @throws Exception
     */
    public void loadModel(final URL _modelURL, String modelKey) throws Exception {
        final CompositeActor currentModelTmp = ModelUtils.loadModel(_modelURL);
        loadedModels.put(_modelURL, currentModelTmp);
        modelKey = validateModel(currentModelTmp, modelKey);
        modelURL = _modelURL;
        currentModelDef = hmiModelsDef.getModel(modelKey);
        setCurrentModel(currentModelTmp, modelKey, showModelGraph);
    }

    protected void refreshParamsForm(final URL _modelURL, String modelKey) throws Exception {
        if (loadedModels.containsKey(_modelURL)) {
            final CompositeActor currentModelTmp = loadedModels.get(_modelURL);
            modelKey = validateModel(currentModelTmp, modelKey);
            modelURL = _modelURL;
            currentModelDef = hmiModelsDef.getModel(modelKey);
            setCurrentModel(currentModelTmp, modelKey, false);
        }

    }

    protected void setCurrentModel(final CompositeActor model, final String modelKey,
            final boolean loadGraphPanel) {
        if (currentModel != null) {
            currentModel.removeChangeListener(this);
        }
        currentModel = model;
        hmiFields.clear();
        showModelForm(modelKey);
        if (loadGraphPanel) {
            showModelGraph(modelKey);
        }

        currentModel.addChangeListener(this);
    }

    /**
     * Checks if the given model is compatible with this HMI, based on the HMI
     * configuration definition.
     * 
     * @param currentModelTmp
     * @return
     */
    protected String validateModel(final CompositeActor model, final String modelKey) {
        if (SPECIFIC.equals(mode)) {
            String result = null;
            // now let's check if we support it
            // the model name is used as model type key
            final String _modelkey = model.getName();
            if (modelKey != null && !modelKey.equals(_modelkey)) {
                throw new IllegalArgumentException(
                        "Invalid model key in " + model.getName() + ", expecting " + modelKey);
            } else {
                // check if the _modelkey from the model is support by this HMI
                if (hmiModelsDef.getModel(_modelkey) != null) {
                    // finally, it's a supported model type
                    result = _modelkey;
                }
            }
            if (result == null) {
                throw new IllegalArgumentException("Invalid model for this HMI " + model.getName());
            }

            return result;
        } else {
            return modelKey;
        }
    }

    protected JMenuBar createDefaultMenu() {
        return createDefaultMenu(null, null);
    }

    private boolean showThing(final String thingName, final Set<String> thingsToShow,
            final Set<String> thingsToHide) {
        return (thingsToShow == null || thingsToShow.contains(thingName))
                && (thingsToHide == null || !thingsToHide.contains(thingName));
    }

    /**
     * The code here will be executing before application exit Put your code
     * here. DBA : to be validating
     */
    public void exitApplication() {
    }

    /**
     * Constructs a default menu.
     * <ul>
     * <li>If the menuItems set is null, all default items are created.
     * <li>If the set is not null, only the menu items whose names are in there
     * are shown
     * </ul>
     * For an overview of the names, check HMIMessages.MENU_...
     * 
     * @param menuItemsToShow
     * @param menuItemsToHide
     * @return
     */
    public JMenuBar createDefaultMenu(final Set<String> menuItemsToShow, final Set<String> menuItemsToHide) {
        final JMenuBar menuBar = new JMenuBar();

        final JMenu fileMenu = new JMenu(HMIMessages.getString(HMIMessages.MENU_FILE));
        fileMenu.setMnemonic(HMIMessages.getString(HMIMessages.MENU_FILE + HMIMessages.KEY).charAt(0));

        if (showThing(HMIMessages.MENU_TEMPLATES, menuItemsToShow, menuItemsToHide)) {
            final JMenu templatesSubMenu = new JMenu(HMIMessages.getString(HMIMessages.MENU_TEMPLATES));
            final Iterator itr = hmiModelsDef.getModels().keySet().iterator();

            final List<String> orderedList = new ArrayList<String>();
            while (itr.hasNext()) {
                final String modelKey = (String) itr.next();
                final String labelKey = modelKey; // HMIMessages.getString(modelKey);
                orderedList.add(labelKey);
            }
            Collections.sort(orderedList);

            for (int i = 0; i < orderedList.size(); i++) {
                try {
                    // JMenuItem templateMenuItem = new
                    // JMenuItem(HMIMessages.getString(HMIMessages.MENU_TEMPLATES)
                    // + " " + HMIMessages.getString(modelKey));
                    final JMenuItem templateMenuItem = new JMenuItem(HMIMessages.getString(orderedList.get(i)));
                    templateMenuItem.addActionListener(new TemplateModelOpener(orderedList.get(i)));
                    templatesSubMenu.add(templateMenuItem);
                } catch (final Exception e1) {
                    e1.printStackTrace();
                    logger.error("", e1);
                }
            }
            fileMenu.add(templatesSubMenu);

            StateMachine.getInstance().registerActionForState(StateMachine.READY, HMIMessages.MENU_TEMPLATES,
                    templatesSubMenu);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_TEMPLATES,
                    templatesSubMenu);

        }

        if (showModelGraph) {
            if (showThing(HMIMessages.MENU_NEW, menuItemsToShow, menuItemsToHide)) {
                if (modelCreatorAction == null) {
                    modelCreatorAction = new ModelCreator(this);
                }
                fileMenu.add(modelCreatorAction);
                // final JMenuItem fileNewMenuItem = new JMenuItem(HMIMessages
                // .getString(HMIMessages.MENU_NEW), HMIMessages.getString(
                // HMIMessages.MENU_NEW + HMIMessages.KEY).charAt(0));
                // fileNewMenuItem.setAccelerator(KeyStroke.getKeyStroke(
                // KeyEvent.VK_N, InputEvent.CTRL_MASK));
                // fileNewMenuItem.addActionListener(new ModelCreator());
                // fileMenu.add(fileNewMenuItem);

                // StateMachine.getInstance().registerActionForState(
                // StateMachine.READY, HMIMessages.MENU_NEW, fileNewMenuItem);
                // StateMachine.getInstance().registerActionForState(
                // StateMachine.MODEL_OPEN, HMIMessages.MENU_NEW,
                // fileNewMenuItem);
            }
        }

        if (showThing(HMIMessages.MENU_OPEN, menuItemsToShow, menuItemsToHide)) {
            final JMenuItem fileOpenMenuItem = new JMenuItem(HMIMessages.getString(HMIMessages.MENU_OPEN),
                    HMIMessages.getString(HMIMessages.MENU_OPEN + HMIMessages.KEY).charAt(0));
            fileOpenMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
            fileOpenMenuItem.addActionListener(new ModelOpener());
            fileMenu.add(fileOpenMenuItem);

            StateMachine.getInstance().registerActionForState(StateMachine.READY, HMIMessages.MENU_OPEN,
                    fileOpenMenuItem);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_OPEN,
                    fileOpenMenuItem);
        }

        if (showThing(HMIMessages.MENU_CLOSE, menuItemsToShow, menuItemsToHide)) {
            final JMenuItem fileCloseMenuItem = new JMenuItem(HMIMessages.getString(HMIMessages.MENU_CLOSE),
                    HMIMessages.getString(HMIMessages.MENU_CLOSE + HMIMessages.KEY).charAt(0));
            fileCloseMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK));
            fileCloseMenuItem.addActionListener(new ModelCloser());
            fileMenu.add(fileCloseMenuItem);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_CLOSE,
                    fileCloseMenuItem);
        }
        if (showThing(HMIMessages.MENU_SAVE, menuItemsToShow, menuItemsToHide)) {
            fileMenu.add(new JSeparator());
            if (saveAction == null) {
                saveAction = new SaveAction(this);
            }
            fileMenu.add(saveAction);
            // StateMachine.getInstance().registerActionForState(
            // StateMachine.MODEL_OPEN, HMIMessages.MENU_SAVE, save);
        }
        if (showThing(HMIMessages.MENU_SAVEAS, menuItemsToShow, menuItemsToHide)) {
            if (saveAsAction == null) {
                saveAsAction = new SaveAsAction(this);
            }
            fileMenu.add(saveAsAction);
            // StateMachine.getInstance().registerActionForState(
            // StateMachine.MODEL_OPEN, HMIMessages.MENU_SAVEAS, save);
        }
        if (showThing(HMIMessages.MENU_EXIT, menuItemsToShow, menuItemsToHide)) {
            final JMenuItem exitMenuItem = new JMenuItem(HMIMessages.getString(HMIMessages.MENU_EXIT),
                    HMIMessages.getString(HMIMessages.MENU_EXIT + HMIMessages.KEY).charAt(0));
            exitMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK));
            exitMenuItem.addActionListener(new ActionListener() {
                public void actionPerformed(final ActionEvent e) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Exit action - entry"); //$NON-NLS-1$
                    }

                    logger.info(HMIMessages.getString(HMIMessages.INFO_EXIT));
                    // DBA to be validating...
                    // Application which use HMIBase needs to call exit
                    // treatment before exiting
                    exitApplication();

                    if (logger.isTraceEnabled()) {
                        logger.trace("Exit action - exit"); //$NON-NLS-1$
                    }

                    System.exit(0);
                }
            });
            fileMenu.add(new JSeparator());
            fileMenu.add(exitMenuItem);

            StateMachine.getInstance().registerActionForState(StateMachine.READY, HMIMessages.MENU_EXIT,
                    exitMenuItem);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_EXIT,
                    exitMenuItem);
        }

        final JMenu runMenu = new JMenu(HMIMessages.getString(HMIMessages.MENU_RUN));
        runMenu.setMnemonic(HMIMessages.getString(HMIMessages.MENU_RUN + HMIMessages.KEY).charAt(0));

        if (showThing(HMIMessages.MENU_EXECUTE, menuItemsToShow, menuItemsToHide)) {
            if (modelExecutor == null) {
                modelExecutor = new ModelExecutor(this);
            }
            final JMenuItem runExecuteMenuItem = new JMenuItem(modelExecutor);
            runMenu.add(runExecuteMenuItem);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_EXECUTE,
                    modelExecutor);
        }
        if (showThing(HMIMessages.MENU_STOP, menuItemsToShow, menuItemsToHide)) {
            if (modelStopper == null) {
                modelStopper = new ModelStopper(this);
            }
            final JMenuItem stopExecuteMenuItem = new JMenuItem(modelStopper);
            runMenu.add(stopExecuteMenuItem);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_EXECUTING, HMIMessages.MENU_STOP,
                    modelStopper);
        }
        if (showThing(HMIMessages.MENU_INTERACTIVE_ERRORHANDLING, menuItemsToShow, menuItemsToHide)) {
            final JMenuItem interactiveErrorCtrlMenuItem = new JCheckBoxMenuItem(
                    HMIMessages.getString(HMIMessages.MENU_INTERACTIVE_ERRORHANDLING));
            interactiveErrorCtrlMenuItem.addActionListener(new ActionListener() {
                public void actionPerformed(final ActionEvent e) {
                    interactiveErrorControl = ((JCheckBoxMenuItem) e.getSource()).getState();
                }

            });
            runMenu.add(new JSeparator());
            runMenu.add(interactiveErrorCtrlMenuItem);
        }

        final JMenu graphMenu = new JMenu(HMIMessages.getString(HMIMessages.MENU_GRAPH));
        graphMenu.setMnemonic(HMIMessages.getString(HMIMessages.MENU_GRAPH + HMIMessages.KEY).charAt(0));

        // if (showThing(HMIMessages.MENU_SHOW, menuItemsToShow,
        // menuItemsToHide)) {
        // JMenuItem graphViewMenuItem = new
        // JCheckBoxMenuItem(HMIMessages.getString(HMIMessages.MENU_SHOW));
        // graphMenu.add(graphViewMenuItem);
        //
        // graphViewMenuItem.addActionListener(new ActionListener() {
        // public void actionPerformed(ActionEvent e) {
        // if (logger.isTraceEnabled()) {
        //                  logger.trace("Graph Show action - entry"); //$NON-NLS-1$
        // }
        // boolean showGraph = ((JCheckBoxMenuItem) e.getSource()).getState();
        // if(showGraph) {
        // applyFieldValuesToParameters();
        // clearModelForms();
        // showModelGraph(HMIBase.this.currentModel.getName());
        // } else {
        // clearModelGraphs();
        // showModelForm(HMIBase.this.currentModel.getName());
        // }
        // if (logger.isTraceEnabled()) {
        //                  logger.trace("Graph Show action - exit"); //$NON-NLS-1$
        // }
        // }
        //
        //
        // });
        // }
        if (showThing(HMIMessages.MENU_ANIMATE, menuItemsToShow, menuItemsToHide)) {
            final JMenuItem animateGraphViewMenuItem = new JCheckBoxMenuItem(
                    HMIMessages.getString(HMIMessages.MENU_ANIMATE));
            animateGraphViewMenuItem.addActionListener(new ActionListener() {
                public void actionPerformed(final ActionEvent e) {
                    animateModelExecution = ((JCheckBoxMenuItem) e.getSource()).getState();
                }

            });
            graphMenu.add(new JSeparator());
            graphMenu.add(animateGraphViewMenuItem);
        }

        if (fileMenu.getMenuComponentCount() > 0) {
            menuBar.add(fileMenu);
        }
        if (runMenu.getMenuComponentCount() > 0) {
            menuBar.add(runMenu);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_RUN,
                    runMenu);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_EXECUTING, HMIMessages.MENU_RUN,
                    runMenu);
        }
        if (graphMenu.getMenuComponentCount() > 0) {
            menuBar.add(graphMenu);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_GRAPH,
                    graphMenu);
            StateMachine.getInstance().registerActionForState(StateMachine.MODEL_EXECUTING, HMIMessages.MENU_GRAPH,
                    graphMenu);
        }

        final JMenu monitoringMenu = new JMenu(HMIMessages.getString(HMIMessages.MENU_MONITORING));
        monitoringMenu.setMnemonic(HMIMessages.getString(HMIMessages.MENU_MONITORING + HMIMessages.KEY).charAt(0));
        if (showThing(HMIMessages.MENU_TRACING, menuItemsToShow, menuItemsToHide)) {
            final JMenuItem traceMenuItem = new JMenuItem(HMIMessages.getString(HMIMessages.MENU_TRACING),
                    HMIMessages.getString(HMIMessages.MENU_TRACING + HMIMessages.KEY).charAt(0));
            traceMenuItem.addActionListener(new TraceDialogOpener());
            monitoringMenu.add(traceMenuItem);
        }

        if (monitoringMenu.getMenuComponentCount() > 0) {
            menuBar.add(monitoringMenu);
        }

        StateMachine.getInstance().compile();
        StateMachine.getInstance().transitionTo(StateMachine.READY);
        return menuBar;
    }

    protected void showModelGraph(final String modelKey) {
        try {
            final JFrame gf = new JFrame(HMIMessages.getString(HMIMessages.GRAPH_TITLE));
            gf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            if (graphPanelEffigy == null) {
                graphPanelEffigy = new PtolemyEffigy(getPtolemyConfiguration(), modelKey);
            }
            graphPanel = new ModelGraphPanel(currentModel, graphPanelEffigy);

            // TODO BEWARE : animation now happens in the actor
            // thread and blocks for a second!!
            // graphPanel.setAnimationDelay(1000);
            gf.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosed(final WindowEvent e) {
                    clearModelGraphs();
                }
            });
            gf.getContentPane().add(graphPanel);
            gf.setLocation(400, 300);
            gf.setSize(700, 400);
            gf.setVisible(true);

        } catch (final Throwable t) {
            logger.error(HMIMessages.getString(HMIMessages.ERROR_GENERIC), t);
        }
    }

    protected abstract void clearModelGraphs();

    public JToolBar createDefaultToolbar() {
        final JToolBar toolBar = new JToolBar();

        if (modelExecutor == null) {
            modelExecutor = new ModelExecutor(this);
        }
        if (modelStopper == null) {
            modelStopper = new ModelStopper(this);
        }
        if (modelDebugger == null) {
            modelDebugger = new ModelDebugger(this);
        }
        if (modelDebugStepper == null) {
            modelDebugStepper = new ModelDebugStepper(this);
        }
        if (modelSuspender == null) {
            modelSuspender = new ModelSuspender(this);
        }
        if (modelResumer == null) {
            modelResumer = new ModelResumer(this);
        }
        if (saveAction == null) {
            saveAction = new SaveAction(this);
        }
        // DBA : add tooltip for each button
        createToolbarButton(modelExecutor, toolBar, HMIMessages.getString(HMIMessages.MENU_EXECUTE));
        createToolbarButton(modelSuspender, toolBar, HMIMessages.getString(HMIMessages.MENU_SUSPEND));
        createToolbarButton(modelResumer, toolBar, HMIMessages.getString(HMIMessages.MENU_RESUME));
        createToolbarButton(modelStopper, toolBar, HMIMessages.getString(HMIMessages.MENU_STOP));
        toolBar.addSeparator();
        createToolbarButton(modelDebugger, toolBar, HMIMessages.getString(HMIMessages.MENU_DEBUG));
        createToolbarButton(modelDebugStepper, toolBar, HMIMessages.getString(HMIMessages.MENU_DEBUG_STEP));
        toolBar.addSeparator();
        toolBar.addSeparator();
        createToolbarButton(saveAction, toolBar, HMIMessages.getString(HMIMessages.MENU_SAVE));

        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_EXECUTE,
                modelExecutor);
        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_OPEN, HMIMessages.MENU_DEBUG,
                modelDebugger);
        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_DEBUGGING, HMIMessages.MENU_DEBUG_STEP,
                modelDebugStepper);
        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_DEBUGGING, HMIMessages.MENU_STOP,
                modelStopper);
        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_EXECUTING, HMIMessages.MENU_STOP,
                modelStopper);
        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_EXECUTING_SUSPENDED,
                HMIMessages.MENU_STOP, modelStopper);
        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_EXECUTING, HMIMessages.MENU_SUSPEND,
                modelSuspender);
        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_EXECUTING_SUSPENDED,
                HMIMessages.MENU_RESUME, modelResumer);
        StateMachine.getInstance().registerActionForState(StateMachine.MODEL_DEBUGGING, HMIMessages.MENU_RESUME,
                modelResumer);

        StateMachine.getInstance().compile();
        StateMachine.getInstance().transitionTo(StateMachine.READY);

        return toolBar;
    }

    /**
     * DBA : add tooltip for each button
     * 
     * @return
     */
    private JButton createToolbarButton(final Action action, final JToolBar toolbar, final String tooltip) {
        final JButton b = new JButton(action);
        b.setBorderPainted(false);
        b.setToolTipText(tooltip);
        b.setText(null);
        toolbar.add(b);
        return b;
    }

    /**
     * 
     * @param mappingKey
     * @param uiWidget
     * @param binder
     * @return
     */
    protected Object registerBinding(final String mappingKey, final Object uiWidget,
            final ParameterToWidgetBinder binder) {
        final String paramName = getCurrentModelDef().getFieldMapping().getValueForKey(mappingKey);
        try {
            final Parameter p = ModelUtils.getParameter(getCurrentModel(), paramName);
            p.addValueListener(new ParameterChangeListener(binder));
            binder.setBoundComponent(uiWidget);
            binder.setBoundParameter(p);
            hmiFields.put(mappingKey, binder);
            // now make sure the fields contain the param values from the model
            binder.fillWidgetFromParameter();
        } catch (final Exception e) {
            e.printStackTrace();
        }

        return uiWidget;
    }

    public abstract void setSaved();

    public void saveModelAs(final CompositeActor model, final URL destinationURL)
            throws IOException, IllegalActionException, NameDuplicationException {
        File destinationFile = null;

        try {
            destinationFile = getLocalFileFromURL(destinationURL);
        } catch (final Exception e) {
            // ignore, means that the URL is unknown or a remote model URL
            // this is handled further below
        }
        applyFieldValuesToParameters();
        if (destinationFile != null && (!destinationFile.exists() || destinationFile.canWrite())) {
            BufferedWriter outputWriter = null;
            try {
                outputWriter = new BufferedWriter(new FileWriter(destinationFile));
                // set name of the model with file name
                String newModelName = destinationFile.getName();
                if (newModelName.contains(".moml")) {
                    newModelName = newModelName.replace(".moml", "");
                }
                model.setName(newModelName);
                model.exportMoML(outputWriter);
            } finally {
                if (outputWriter != null) {
                    outputWriter.flush();
                    outputWriter.close();
                    this.setSaved();
                }
            }
        } else {
            throw new IOException("File not writable " + destinationFile);
        }

    }

    public File getLocalFileFromURL(final URL destinationURL) throws IOException {
        File destinationFile = null;
        try {
            destinationFile = new File(destinationURL.toURI());
        } catch (final URISyntaxException e) {
            throw new IOException("Destination URL not a valid local file " + destinationURL);
        }
        return destinationFile;
    }

    /**
     *
     */
    protected void applyFieldValuesToParameters() {
        final Iterator fieldsItr = hmiFields.values().iterator();
        while (fieldsItr.hasNext()) {
            final ParameterToWidgetBinder pwb = (ParameterToWidgetBinder) fieldsItr.next();
            pwb.fillParameterFromWidget();
        }
    }

    public Director getDirector() {
        return (Director) currentModel.getDirector();
    }

    public void launchModel(final ModelExecutor executor) {
        applyFieldValuesToParameters();
        launchModel(getCurrentModel(), executor, false, true);
    }

    public void launchModel(final CompositeActor model, final ModelExecutor executor, final boolean blocking,
            final boolean updateState) {
        try {
            manager = new Manager(model.workspace(), model.getName());
            model.setManager(manager);

            final Director director = (Director) model.getDirector();
            if (interactiveErrorControl) {
                director.setErrorControlStrategy(new AskTheUserErrorControlStrategy(this, executor));
            } else {
                director.setErrorControlStrategy(new DefaultActorErrorControlStrategy());
            }

            /*
             * java.lang.reflect.Method m =
             * director.getClass().getDeclaredMethod("setInteractiveExecution",
             * boolean.class); if(m != null) m.invoke(director, true);
             */
            /*
             * if (interactiveErrorControl) {
             * getDirector().setErrorControlStrategy(new
             * AskTheUserErrorControlStrategy(this, executor)); }
             */
            /* specific for Soleil 
            if (getDirector() instanceof fr.soleil.passerelle.domain.BasicDirector) {
               ((fr.soleil.passerelle.domain.BasicDirector) getDirector())
              .setInteractiveExecution(interactiveErrorControl);
            }
            */
            // we enforce a suspend/resume control
            // TODO investigate if we should be able to support multiple
            // execution controllers,
            // e.g. when one has been configured already for the director...
            final Attribute tmp = director.getAttribute(HMIBase.EXECUTION_CONTROL_ATTR_NAME);
            if (tmp != null) {
                // remove the previous execution controller
                tmp.setContainer(null);
            }
            if (executor != null) {
                executor.createExecutionControlStrategy(director, HMIBase.EXECUTION_CONTROL_ATTR_NAME);
            }

            setModelConfigurationBeforeExecution(model);
            animationEventListener = setModelAnimation(model, animateModelExecution, animationEventListener);

            if (blocking) {
                manager.execute();
            } else {
                manager.startRun();
            }

            if (updateState && executor != null) {
                StateMachine.getInstance().transitionTo(executor.getSuccessState());
            }
        } catch (final Throwable t) {
            t.printStackTrace();
            logger.error(HMIMessages.getString(HMIMessages.ERROR_GENERIC), t);
            PopupUtil.showError(hookComponent, "error.execution.error", t.getMessage());
            StateMachine.getInstance().transitionTo(executor.getErrorState());
        }
    }

    protected Configuration readConfiguration(final String urlSpec) throws Exception {
        logger.debug("read Passerelle Configuration");

        final URL inurl = specToURL(urlSpec);
        final MoMLParser parser = new MoMLParser();
        final Configuration toplevel = (Configuration) parser.parse(inurl, inurl.openStream());
        // If the toplevel model is a configuration containing a directory,
        // then create an effigy for the configuration itself, and put it
        // in the directory.
        final ComponentEntity directoryEntity = toplevel.getEntity("directory");
        if (directoryEntity instanceof ModelDirectory) {
            final PtolemyEffigy effigy = new PtolemyEffigy((ModelDirectory) directoryEntity, toplevel.getName());
            effigy.setModel(toplevel);
            effigy.identifier.setExpression(inurl.toExternalForm());
        }
        return toplevel;
    }

    /**
     * Given the name of a file or a URL, convert it to a URL. This first
     * attempts to do that directly by invoking a URL constructor. If that
     * fails, then it tries to interpret the spec as a file name on the local
     * file system. If that fails, then it tries to interpret the spec as a
     * resource accessible to the classloader, which uses the classpath to find
     * the resource. If that fails, then it throws an exception. The
     * specification can give a file name relative to current working directory,
     * or the directory in which this application is started up.
     * 
     * @param spec
     *            The specification.
     * @exception IOException
     *                If it cannot convert the specification to a URL.
     */
    public static URL specToURL(final String spec) throws IOException {
        try {
            // First argument is null because we are only
            // processing absolute URLs this way. Relative
            // URLs are opened as ordinary files.
            return new URL(null, spec);
        } catch (final MalformedURLException ex) {
            try {
                final File file = new File(spec);
                if (!file.exists()) {
                    throw new MalformedURLException();
                }
                return file.getCanonicalFile().toURL();
            } catch (final MalformedURLException ex2) {
                try {
                    // Try one last thing, using the classpath.
                    // Need a class context, and this is a static method, so...
                    // NOTE: There doesn't seem to be any way to convert
                    // this a canonical name, so if a model is opened this
                    // way, and then later opened as a file, the model
                    // directory will think it has two different files.
                    final Class refClass = Class.forName("ptolemy.kernel.util.NamedObj");
                    final URL inurl = refClass.getClassLoader().getResource(spec);
                    if (inurl == null) {
                        throw new Exception();
                    } else {
                        return inurl;
                    }
                } catch (final Exception exception) {
                    throw new IOException("File not found: " + spec);
                }
            }
        }
    }

    /**
     * Override this method in HMI subclasses if you want to add extra
     * configurations to the model. <br>
     * By default, this sets a ModelExecutionListener that will show a pop-up
     * when a model has finished its execution (possible with error info).
     * 
     * @param model
     */
    protected void setModelConfigurationBeforeExecution(final CompositeActor model) {
        final Director director = (Director) model.getDirector();
        director.removeAllErrorCollectors();
        final ModelExecutionListener executionListener = createExecutionListener();
        director.addErrorCollector(executionListener);
        model.getManager().addExecutionListener(executionListener);
    }

    /**
     * Override this method if you need another kind of execution listener in a
     * specific HMI application
     * 
     * @return
     */
    protected ModelExecutionListener createExecutionListener() {
        return new ModelExecutionListener();
    }

    /**
     * Override this method in HMI subclasses if you want to specify animation
     * behaviour for the model. <br>
     * By default, this uses an AnimationEventListener that will animate the
     * graphical view of the model being executed.
     * 
     * @param model
     * @param animateModelExecution
     *            if true, some kind of animation should be provided
     * @param currentListener
     *            the current animation listener that was set, e.g. for a
     *            previous model execution
     * @return the listener that was effectively set if animation is needed, or
     *         null if no animation is set
     */
    protected AnimationEventListener setModelAnimation(final CompositeActor model,
            final boolean animateModelExecution, AnimationEventListener currentListener) {
        final Director director = (Director) model.getDirector();
        if (animateModelExecution) {
            if (currentListener == null) {
                currentListener = createAnimationListener();
            }
            director.registerFiringEventListener(currentListener);
        } else {
            director.removeFiringEventListener(animationEventListener);
            currentListener = null;
        }
        return currentListener;
    }

    /**
     * Override this method if you need another kind of animation listener in a
     * specific HMI application
     * 
     * @return
     */
    protected AnimationEventListener createAnimationListener() {
        return new AnimationEventListener();
    }

    public void stopModel() {
        try {
            if (manager != null) {
                manager.stop();
                // desactivate toolbar while stopping manager.stop() since is
                // not a
                // blocking method
                // see ModelExecutionListener.executionFinished where the model
                // is really stop
                StateMachine.getInstance().transitionTo(StateMachine.READY);
            }
        } catch (final Throwable t) {
            t.printStackTrace();
            logger.error(HMIMessages.getString(HMIMessages.ERROR_GENERIC), t);
            PopupUtil.showError(hookComponent, HMIMessages.ERROR_GENERIC, t.getMessage());
        }
    }

    /**
     * DBA : suspend execution Need to be validating
     */
    public void suspendModel() {
        try {
            if (manager != null) {
                manager.pause();
            }
            StateMachine.getInstance().transitionTo(StateMachine.MODEL_EXECUTING_SUSPENDED);
        } catch (final Throwable t) {
            t.printStackTrace();
            logger.error(HMIMessages.getString(HMIMessages.ERROR_GENERIC), t);
            PopupUtil.showError(hookComponent, HMIMessages.ERROR_GENERIC, t.getMessage());
        }
    }

    public void setHmiModelsDef(final ModelBundle hmiModelsDef) {
        this.hmiModelsDef = hmiModelsDef;
    }

    /**
     * DBA : suspend execution Need to be validating
     */
    public void resumeModel() {
        try {
            if (manager != null) {
                manager.resume();
            }
            StateMachine.getInstance().transitionTo(StateMachine.MODEL_EXECUTING);
        } catch (final Throwable t) {
            t.printStackTrace();
            logger.error(HMIMessages.getString(HMIMessages.ERROR_GENERIC), t);
            PopupUtil.showError(hookComponent, HMIMessages.ERROR_GENERIC, t.getMessage());
        }
    }

    public void setTraceComponent(final TraceVisualizer traceComponent) {
        this.traceComponent = traceComponent;
    }

    /**
     * Opens a new form for a given model key, i.e. a form containing values
     * from the "template" model file.
     * 
     * @author erwin dl
     */
    protected class TemplateModelOpener implements ActionListener {

        String modelKey;

        public TemplateModelOpener(final String key) {
            modelKey = key;
        }

        public void actionPerformed(final ActionEvent e) {
            try {
                currentModelDef = hmiModelsDef.getModel(modelKey);
                loadModel(currentModelDef.getMomlPath(), null);

                StateMachine.getInstance().transitionTo(StateMachine.MODEL_OPEN);

            } catch (final Exception e1) {
                e1.printStackTrace();
                logger.error("", e1);
                PopupUtil.showError(hookComponent, "error.file.open");
            }
        }

    }

    protected class ModelCloser implements ActionListener {

        public ModelCloser() {
        }

        public void actionPerformed(final ActionEvent e) {
            if (logger.isTraceEnabled()) {
                logger.trace("File Close action - entry"); //$NON-NLS-1$
            }

            clearModelForms();
            clearModelGraphs();
            try {
                MoMLParser.purgeModelRecord(modelURL);
            } catch (final Exception ex) {
                // ignore, just trying to be nice and clean up memory
            }

            StateMachine.getInstance().transitionTo(StateMachine.READY);

            if (logger.isTraceEnabled()) {
                logger.trace("File Close action - exit"); //$NON-NLS-1$
            }
        }
    }

    protected class ModelOpener implements ActionListener {

        public ModelOpener() {
            super();
        }

        public void actionPerformed(final ActionEvent e) {
            if (logger.isTraceEnabled()) {
                logger.trace("File Open action - entry"); //$NON-NLS-1$
            }
            try {
                JFileChooser fileChooser;

                fileChooser = new JFileChooser(new URL(HMIBase.MODELS_URL_STRING).getFile());

                fileChooser.setAccessory(new FinderAccessory(fileChooser));
                fileChooser.addChoosableFileFilter(
                        new ExtensionFileFilter(new String[] { "xml", "moml" }, "Passerelle model files"));
                final int returnVal = fileChooser.showOpenDialog((Component) e.getSource());
                if (returnVal == JFileChooser.APPROVE_OPTION) {
                    modelURL = fileChooser.getSelectedFile().toURL();
                    loadModel(modelURL, null);

                    StateMachine.getInstance().transitionTo(StateMachine.MODEL_OPEN);
                }
            } catch (final Exception e1) {
                logger.error("", e1);
                PopupUtil.showError(hookComponent, "error.file.open");
            }
            if (logger.isTraceEnabled()) {
                logger.trace("File Open action - exit - current file : " + modelURL); //$NON-NLS-1$
            }
        }
    }

    protected class ModelExecutionListener implements ExecutionListener, ErrorCollector {

        private boolean popUpError = true;

        public void acceptError(final PasserelleException e) {
            executionError(null, e);
        }

        public void executionError(final Manager manager, final Throwable throwable) {
            logger.error(HMIMessages.getString("error.execution.error"), throwable);
            if (popUpError) {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        PopupUtil.showError(hookComponent, "error.execution.error", throwable.getMessage());
                        // StateMachine.getInstance().transitionTo(StateMachine.MODEL_OPEN);
                    }
                });
                popUpError = false;
            }
        }

        public void executionFinished(final Manager manager) {
            StateMachine.getInstance().transitionTo(StateMachine.MODEL_OPEN);
            // SwingUtilities.invokeLater(new Runnable() {
            // public void run() {
            // PopupUtil
            // .showInfo(hookComponent, "info.execution.finished");
            // }
            // });
        }

        public void managerStateChanged(final Manager manager) {
            // System.out.println(manager.getState());
            if (manager.getState().equals(Manager.INITIALIZING)) {
                popUpError = true;
            }
        }
    }

    protected class TraceDialogOpener implements ActionListener {
        public void actionPerformed(final ActionEvent e) {
            traceComponent.show();
        }
    }

    protected class AnimationEventListener implements FiringEventListener {
        public void onEvent(final FiringEvent e) {
            if (graphPanel != null) {
                graphPanel.event(e);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("onEvent() : " + ((Entity) e.getActor()).getName() + ":" + e.getType());
            }
        }
    }

    public ModelBundle getHmiModelsDef() {
        return hmiModelsDef;
    }

    public TraceVisualizer getTraceComponent() {
        return traceComponent;
    }

    /**
     * If it's a structural change, we need to adapt the cfg forms. Any
     * graphical views should be model change listeners already, and will
     * probably be the origin of the change anyway...
     */
    public void changeExecuted(final ChangeRequest change) {
        if (change.getDescription() != null) {
            final String[] importantChanges = new String[] { "entity", "property", "deleteEntity", "deleteProperty",
                    "class" };
            boolean shouldRefreshForms = false;
            for (final String changeType : importantChanges) {
                if (change.getDescription().contains(changeType)) {
                    shouldRefreshForms = true;
                    break;
                }
            }
            if (shouldRefreshForms) {
                showModelForm(null);
            }
        }
    }

    public void changeFailed(final ChangeRequest change, final Exception exception) {
        // TODO Auto-generated method stub

    }

}