org.cishell.reference.gui.menumanager.menu.MenuAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.cishell.reference.gui.menumanager.menu.MenuAdapter.java

Source

/* **************************************************************************** 
 * CIShell: Cyberinfrastructure Shell, An Algorithm Integration Framework.
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Apache License v2.0 which accompanies
 * this distribution, and is available at:
 * http://www.apache.org/licenses/LICENSE-2.0.html
 * 
 * Created on Jun 16, 2006 at Indiana University.
 * 
 * Contributors:
 *     Indiana University - 
 * ***************************************************************************/
package org.cishell.reference.gui.menumanager.menu;

import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.cishell.framework.CIShellContext;
import org.cishell.framework.algorithm.AlgorithmFactory;
import org.cishell.framework.algorithm.AlgorithmProperty;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class MenuAdapter implements AlgorithmProperty {
    public static final String DEFAULT_MENU_FILE_NAME = "default_menu.xml";

    // Tags in DEFAULT_MENU_FILE_NAME.
    public static final String TAG_TOP_MENU = "top_menu";
    public static final String TAG_MENU = "menu";
    public static final String TYPE_ATTRIBUTE = "type";
    public static final String NAME_ATTRIBUTE = "name";
    public static final String PID_ATTRIBUTE = "pid";
    public static final String PRESERVED_GROUP = "group";
    public static final String PRESERVED_BREAK = "break";
    public static final String PRESERVED_EXIT = "Exit";
    public static final String PRESERVED_SERVICE_PID = "service.pid";
    public static final String PRESERVED = "preserved";

    public static final String HELP_DESK_EMAIL_ADDRESS = "nwb-helpdesk@googlegroups.com";

    private String toolName;
    private String toolTicketURL;
    private IMenuManager menuManager;
    private Shell shell;
    private BundleContext bundleContext;
    private CIShellContext ciShellContext;
    private Map<String, Action> algorithmsToActions;
    private Map<Action, IMenuManager> actionsToMenuManagers;
    private ContextListener contextListener;
    private IWorkbenchWindow workbenchWindow;
    private LogService logger;
    /*
     * This map holds a pid as a key and the corresponding ServiceReference as a value.
     * It is built when preprocessServiceBundles() is invoked.
     * Then the entries are gradually removed when the pids are specified in
     *  DEFAULT_MENU_FILE_NAME.
     * If any entries are left, in processLeftServiceBundles(), those plug-ins that have specified
     *  the menu_path and label but are not listed in DEFAULT_MENU_FILE_NAME will be added on to
     *  the menu.
     */
    private Map<String, ServiceReference> pidsToServiceReferences;
    /*
     * This is the exactly same copy of pidsToServiceReferences. 
     * Since some plug-ins could display on menu more than once, it provides a map between a pid
     *  and a serviceReference while in pidsToServiceReferences that pid has been removed. 
     */
    private Map<String, ServiceReference> pidsToServiceReferencesCopy;
    private Document documentObjectModel;

    private Runnable updateAction = new Runnable() {
        public void run() {
            MenuAdapter.this.menuManager.updateAll(true);
        }
    };

    private Runnable stopAction = new Runnable() {
        public void run() {
            String[] algorithmKeys = MenuAdapter.this.algorithmsToActions.keySet().toArray(new String[] {});

            for (int ii = 0; ii < algorithmKeys.length; ii++) {
                Action item = MenuAdapter.this.algorithmsToActions.get(algorithmKeys[ii]);
                IMenuManager targetMenu = MenuAdapter.this.actionsToMenuManagers.get(item);

                targetMenu.remove(item.getId());
                MenuAdapter.this.algorithmsToActions.remove(algorithmKeys[ii]);
                MenuAdapter.this.actionsToMenuManagers.remove(item);
            }
        }
    };

    public MenuAdapter(String toolName, String toolTicketURL, IMenuManager menuManager, Shell shell,
            BundleContext bundleContext, CIShellContext ciShellContext, IWorkbenchWindow workbenchWindow) {
        this.toolName = toolName;
        this.toolTicketURL = toolTicketURL;
        this.menuManager = menuManager;
        this.shell = shell;
        this.bundleContext = bundleContext;
        this.ciShellContext = ciShellContext;
        this.workbenchWindow = workbenchWindow;
        this.algorithmsToActions = new HashMap<String, Action>();
        this.actionsToMenuManagers = new HashMap<Action, IMenuManager>();
        this.pidsToServiceReferences = new HashMap<String, ServiceReference>();
        this.pidsToServiceReferencesCopy = new HashMap<String, ServiceReference>();
        this.logger = (LogService) this.ciShellContext.getService(LogService.class.getName());

        /*
        * The intention of this clearShortcuts was to programmatically clear all of the
        *  bound shortcuts, so any found in our own plugins wouldn't have conflicts.
        * Doing this doesn't immediately seem very possible, though there may be some promise in
        *  "redirecting" the actions taken by the shortcuts.
        * (See: http://dev.eclipse.org/newslists/news.eclipse.platform/msg79882.html )
        * As a note, the keyboard shortcuts are actually called accelerator key codes, and the
        *  machinery that makes them work is very deeply ingrained in Eclipse.
        * The difficulties I faced with trying to clear already-bound shortcuts has led me to
        *  suspect three possible things:
        *   We may be "abusing" Eclipse by using it as the foundation for our own applications.
        *   There may be a way to customize/configure (or interface with) the culprit plugin
        *    that's binding the shortcuts before us (org.eclipse.ui.workbench).  This seems to be
        *    highly-likely, and more research on the matter can probably be justified at
        *    some point.
        *   The intended way TO work around the shortcuts already being bound is to redirect the
        *    actions taken by them, as mentioned above.
        * Either way, to get these shortcuts working, I'm just going to use non-standard key
        *  combinations.
        */
        //clearShortcuts();

        /*
         * Appears to add a context listener which updates the menu item whenever a corresponding
         *  change occurs in the bundle (registers, unregisters, etc...).
         */
        String filter = "(" + Constants.OBJECTCLASS + "=" + AlgorithmFactory.class.getName() + ")";
        this.contextListener = new ContextListener();

        try {
            bundleContext.addServiceListener(this.contextListener, filter);
            preprocessServiceBundles();
            String applicationLocation = System.getProperty("osgi.configuration.area");

            /*
             * Comments below refer to problems with earlier versions of this document.
             * Keeping these around for now, as well as the system.out.printlns, until we are sure
             *  that the current fix works.
             */

            /*
             * This is a temporary fix. A bit complex to explain the observation I got so far.
             * On Windows XP, 
             *  app_location =
             *   file:/C:/Documents and Settings/huangb/Desktop/nwb-sept4/nwb/configuration/
             * If I didn't trim "file:/", on some windows machines 
             *  new File(fileFullpath).exists()
             *  will return false, and initializaMenu() will be invoked.
             * When initializaMenu() is invoked, not all required top menus will show up.
             * So either Bruce code or Tim's fix has some problems. Can not append top menu such as 
             *  Tools-->Scheduler if Tools is not specified in the XML.
             * If pass trimmed file path
             *  C:/Documents and Settings/huangb/Desktop/nwb-sept4/nwb/configuration/
             *  to createMenuFromXML, on some machines,
             *  URL = C:/Documents and Settings/huangb/Desktop/nwb-sept4/nwb/configuration/ 
             *  is a bad one, and can not create a document builder instance and the
             *  DOM representation of the XML file.
             * 
             * This piece of code needs to be reviewed and refactored!!!
            */

            /*
             * Better to use System.err, since it prints the stream immediately instead of storing
             *  it in a buffer which might be lost if there is a crash.
             */
            String fileFullPath = applicationLocation + DEFAULT_MENU_FILE_NAME;
            URL configurationDirectoryURL = new URL(fileFullPath);

            try {
                configurationDirectoryURL.getContent();
                //System.out.println(">>>config.ini Exists!");
                createMenuFromXML(fileFullPath);
                processLeftServiceBundles();
            } catch (IOException ioException) {
                ioException.printStackTrace();
                //System.err.println("config.ini does not exist... Reverting to backup plan");
                initializeMenu();
            }

            Display.getDefault().asyncExec(this.updateAction);
        } catch (InvalidSyntaxException invalidSyntaxException) {
            // TODO: Improve this error message.  "Invalid Syntax" is terrible!
            this.logger.log(LogService.LOG_DEBUG, "Invalid Syntax", invalidSyntaxException);
        } catch (Throwable exception) {
            /*
             * TODO: Improve this.
             * Should catch absolutely everything catchable. Will hopefully reveal the error coming
             *  out of the URI constructor.
             * No time to test today, just commiting this for testing later.
             */
            exception.printStackTrace();
        }
    }

    /*
     * This method scans all service bundles. If a bundle specifies  menu_path and label,
     *  get service.pid of this bundle (key), let the service reference as the value, and put
     *  key/value pair  to pidsToServiceReferences for further processing.
     */
    private void preprocessServiceBundles() throws InvalidSyntaxException {
        ServiceReference[] serviceReferences = this.bundleContext
                .getAllServiceReferences(AlgorithmFactory.class.getName(), null);

        if (serviceReferences != null) {
            for (int ii = 0; ii < serviceReferences.length; ii++) {
                String path = (String) serviceReferences[ii].getProperty(MENU_PATH);

                if (path == null) {
                    continue;
                } else {
                    String pid = (String) serviceReferences[ii].getProperty(PRESERVED_SERVICE_PID);
                    this.pidsToServiceReferences.put(pid.toLowerCase().trim(), serviceReferences[ii]);
                    this.pidsToServiceReferencesCopy.put(pid.toLowerCase().trim(), serviceReferences[ii]);
                }
            }
        }
    }

    /*
     * Parse DEFAULT_MENU_FILE_NAME file.
     * For each menu node, get the value of the attribute pid.
     * Check if the pid exists in pidsToServiceReferences.
     * If so, get the action and add to the parent menu.
     * If not, ignore this menu.
     * At the end of each top menu or subgroup menu or before help menu,  add "additions" so that
     *  new algorithms can be added on later. 
     * 
     * What is the reasonable logic?
     * If a plug-in has been specified in the DEFAULT_MENU_FILE_NAME, always use that menu layout
     * If a plug-in specified in the DEFAULT_MENU_FILE_NAME, use the menu_path specified in the
     *  properties file.
     * If a plug-in specifies a label in the properties file, always use it.
     */
    private void createMenuFromXML(String menuFilePath) throws InvalidSyntaxException {
        parseXMLFile(menuFilePath);
        // Get the root elememt.
        Element documentElement = this.documentObjectModel.getDocumentElement();

        // Get a nodelist of the top menu elements.
        NodeList topMenuList = documentElement.getElementsByTagName(TAG_TOP_MENU);

        if ((topMenuList != null) && (topMenuList.getLength() > 0)) {
            for (int ii = 0; ii < topMenuList.getLength(); ii++) {
                Element element = (Element) topMenuList.item(ii);
                processTopMenu(element);
            }
        }
    }

    private void processTopMenu(Element topMenuNode) {
        /*   
         * The File and Help menus are created in ApplicationActionBarAdvisor.java.
         * This function now parses the XML file and appends the new menus/menu items to the
         *  correct group.
         * We first check to see if the menu already exists in our MenuBar.
         * If it does, we modify the already existing menu. If not, then we create a new Menu.
         * 
         * Additional code at: org.cishell.reference.gui.workspace.ApplicationActionBarAdvisor.java
         */

        String topMenuName = topMenuNode.getAttribute(NAME_ATTRIBUTE);
        MenuManager topMenuBar = (MenuManager) this.menuManager.findUsingPath(topMenuName);

        if (topMenuBar == null) {
            topMenuBar = new MenuManager(topMenuName, topMenuName.toLowerCase());
            this.menuManager.appendToGroup(IWorkbenchActionConstants.MB_ADDITIONS, topMenuBar);
        }

        // Second process submenu.
        processSubMenu(topMenuNode, topMenuBar);
    }

    /*
     * Recursively process sub menu and group menu.
     */
    private void processSubMenu(Element menuNode, MenuManager parentMenuBar) {
        NodeList subMenuList = menuNode.getElementsByTagName(TAG_MENU);

        if ((subMenuList != null) && (subMenuList.getLength() > 0)) {
            for (int ii = 0; ii < subMenuList.getLength(); ii++) {
                Element element = (Element) subMenuList.item(ii);

                /*
                 * Only process direct children nodes and drop all grand or grand of grand
                 *  children nodes.
                 * TODO: Why?
                 */
                if (!element.getParentNode().equals(menuNode)) {
                    continue;
                }

                String menuType = element.getAttribute(TYPE_ATTRIBUTE);
                String algorithmPID = element.getAttribute(PID_ATTRIBUTE);

                if (((menuType == null) || (menuType.length() == 0)) && (algorithmPID != null)) {
                    processAMenuNode(element, parentMenuBar);
                } else if (menuType.equalsIgnoreCase(PRESERVED_GROUP)) {
                    String groupName = element.getAttribute(NAME_ATTRIBUTE);
                    MenuManager groupMenuBar = new MenuManager(groupName, groupName.toLowerCase());
                    parentMenuBar.add(groupMenuBar);
                    processSubMenu(element, groupMenuBar);
                } else if (menuType.equalsIgnoreCase(PRESERVED_BREAK)) {
                    /*
                     * It seems that the framework automatically takes care of issues such as
                     *  double separators and a separator at the top or bottom.
                     */
                    parentMenuBar.add(new Separator());
                } else if (menuType.equalsIgnoreCase(PRESERVED)) {
                    String menuName = element.getAttribute(NAME_ATTRIBUTE);
                    if (menuName.equalsIgnoreCase(PRESERVED_EXIT)) {
                        //allow to add more menu before "File/Exit"   
                        if (parentMenuBar.getId().equalsIgnoreCase(IWorkbenchActionConstants.M_FILE)) {
                            parentMenuBar.add(new GroupMarker(START_GROUP));
                            parentMenuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
                            parentMenuBar.add(new GroupMarker(END_GROUP));
                        }
                        IWorkbenchAction exitAction = ActionFactory.QUIT.create(workbenchWindow);
                        parentMenuBar.add(new Separator());
                        parentMenuBar.add(exitAction);
                    }
                }
            }
            //allow to append new submenu(s) at the bottom under each top menu
            //except "File" and "Help"
            if (!parentMenuBar.getId().equalsIgnoreCase(IWorkbenchActionConstants.M_FILE)
                    && !parentMenuBar.getId().equalsIgnoreCase(IWorkbenchActionConstants.M_HELP)) {
                parentMenuBar.add(new GroupMarker(START_GROUP));
                parentMenuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
                parentMenuBar.add(new GroupMarker(END_GROUP));
            }
        }
    }

    /*
     * TODO: Better name?
     * Process a menu (algorithm).   
     */
    private void processAMenuNode(Element menuNode, MenuManager parentMenuBar) {
        String menuName = menuNode.getAttribute(NAME_ATTRIBUTE);
        String pid = menuNode.getAttribute(PID_ATTRIBUTE);

        if ((pid == null) || (pid.length() == 0)) {
            /*
             * TODO: Check if the name is one of the preserved one, and add the default action if
             *  it is?
             */
        } else {
            // Check if the PID has registered in pidsToServiceReferences.
            if (this.pidsToServiceReferencesCopy.containsKey(pid.toLowerCase().trim())) {
                ServiceReference serviceReference = this.pidsToServiceReferencesCopy.get(pid.toLowerCase().trim());
                this.pidsToServiceReferences.remove(pid.toLowerCase().trim());
                AlgorithmAction action = new AlgorithmAction(serviceReference, this.bundleContext,
                        this.ciShellContext);
                String menuLabel = (String) serviceReference.getProperty(LABEL);

                if ((menuName != null) && (menuName.trim().length() > 0)) {
                    // Use the name specified in the XML to overwrite the label.
                    action.setText(menuName);
                    action.setId(getItemID(serviceReference));
                    parentMenuBar.add(action);
                    handleActionAccelerator(action, parentMenuBar, serviceReference);
                } else {
                    if ((menuLabel != null) && (menuLabel.trim().length() > 0)) {
                        action.setText(menuLabel);
                        action.setId(getItemID(serviceReference));
                        parentMenuBar.add(action);
                        handleActionAccelerator(action, parentMenuBar, serviceReference);
                    } else {
                        /*
                         * TODO: This is a problem: No label is specified in the plug-in's
                         *  properties file and no name is specified in the XML file.
                         */
                    }
                }

            } else {
                String algorithmNotFoundFormat = "Oops!  %s tried to place an algorithm with the id '%s' "
                        + "on the menu, but the algorithm could not be found.";
                String algorithmNotFoundMessage = String.format(algorithmNotFoundFormat, this.toolName, pid);
                //            String algorithmNotFoundMessage =
                //               "Oops! Network Workbench tried to place an algorithm with the id '" +
                //               pid +
                //               "' on the menu, but the algorithm could not be found.";
                String contactInformationFormat = "If you see this error, please contact %s, "
                        + "or post a ticket on our bug tracker at: %s .";
                String contactInformationMessage = String.format(contactInformationFormat, HELP_DESK_EMAIL_ADDRESS,
                        this.toolTicketURL);
                //            String contactInformationMessage =
                //               "If you see this error, please contact nwb-helpdesk@googlegroups.com, or " +
                //               "post a ticket on our bug tracker at: " +
                //               "http://cns-trac.slis.indiana.edu/trac/nwb .";
                this.logger.log(LogService.LOG_DEBUG, algorithmNotFoundMessage);
                this.logger.log(LogService.LOG_DEBUG, contactInformationMessage);
            }
        }
    }

    private void parseXMLFile(String menuFilePath) {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setCoalescing(true);

        try {
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();

            this.documentObjectModel = documentBuilder.parse(menuFilePath);

        } catch (ParserConfigurationException parserConfigurationException) {
            parserConfigurationException.printStackTrace();
        } catch (SAXException saxException) {
            saxException.printStackTrace();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    /*
     * Handle some service bundles that have specified the menu_path and label
     * but not specified in the DEFAULT_MENU_FILE_NAME
     */
    private void processLeftServiceBundles() {
        if (!this.pidsToServiceReferences.isEmpty()) {
            for (String key : this.pidsToServiceReferences.keySet()) {
                ServiceReference serviceReference = this.pidsToServiceReferences.get(key);
                makeMenuItem(serviceReference);
            }
            //          Object[] keys = this.pidsToServiceReferences.keySet().toArray();
            //
            //          for (int ii = 0; ii < keys.length; ii++) {
            //             ServiceReference serviceReference =
            //                (ServiceReference)this.pidsToServiceReferences.get((String)keys[ii]);
            //             makeMenuItem(serviceReference);
            //          }          
        }
    }

    private void initializeMenu() throws InvalidSyntaxException {
        ServiceReference[] serviceReferences = this.bundleContext
                .getAllServiceReferences(AlgorithmFactory.class.getName(), null);

        if (serviceReferences != null) {
            for (int ii = 0; ii < serviceReferences.length; ii++) {
                makeMenuItem(serviceReferences[ii]);
            }
        }
    }

    private class ContextListener implements ServiceListener {
        public void serviceChanged(ServiceEvent event) {
            switch (event.getType()) {
            case ServiceEvent.REGISTERED:
                makeMenuItem(event.getServiceReference());

                break;
            case ServiceEvent.UNREGISTERING:
                removeMenuItem(event.getServiceReference());

                break;
            case ServiceEvent.MODIFIED:
                updateMenuItem(event.getServiceReference());

                break;
            }
        }
    }

    private void makeMenuItem(ServiceReference serviceReference) {
        String path = (String) serviceReference.getProperty(MENU_PATH);
        String[] items = null;

        if (path != null) {
            items = path.split("/");
        }

        IMenuManager menu = null;

        if ((items != null) && (items.length > 1)) {
            AlgorithmAction action = new AlgorithmAction(serviceReference, this.bundleContext, this.ciShellContext);
            action.setId(getItemID(serviceReference));

            IMenuManager targetMenu = this.menuManager;
            String group = items[items.length - 1];

            for (int ii = 0; ii < items.length - 1; ii++) {
                menu = targetMenu.findMenuUsingPath(items[ii]);

                if ((menu == null) && (items[ii] != null)) {
                    menu = targetMenu.findMenuUsingPath(items[ii].toLowerCase());
                }

                if (menu == null) {
                    menu = createMenu(items[ii], items[ii]);
                    targetMenu.appendToGroup(ADDITIONS_GROUP, menu);
                }

                targetMenu = menu;
            }

            group = items[items.length - 1];
            IContributionItem groupItem = targetMenu.find(group);

            if (groupItem == null) {
                groupItem = new GroupMarker(group);
                targetMenu.appendToGroup(ADDITIONS_GROUP, groupItem);
            }

            targetMenu.appendToGroup(group, action);
            handleActionAccelerator(action, targetMenu, serviceReference);
            targetMenu.appendToGroup(group, new Separator());
            algorithmsToActions.put(getItemID(serviceReference), action);
            actionsToMenuManagers.put(action, targetMenu);

            Display.getDefault().asyncExec(this.updateAction);
        } else {
            this.logger.log(LogService.LOG_DEBUG,
                    "Bad menu path for Algorithm: " + serviceReference.getProperty(LABEL));
        }
    }

    private String getItemID(ServiceReference serviceReference) {
        return serviceReference.getProperty("PID:" + Constants.SERVICE_PID) + "-SID:"
                + serviceReference.getProperty(Constants.SERVICE_ID);
    }

    private MenuManager createMenu(String name, String id) {
        MenuManager menu = new MenuManager(name, id);
        menu.add(new GroupMarker(START_GROUP));
        menu.add(new GroupMarker(ADDITIONS_GROUP));
        menu.add(new GroupMarker(END_GROUP));

        return menu;
    }

    private void updateMenuItem(ServiceReference serviceReference) {
        Action item = (Action) this.algorithmsToActions.get(getItemID(serviceReference));

        if (item != null) {
            this.logger.log(LogService.LOG_DEBUG, "updateMenuItem for " + getItemID(serviceReference));
            item.setText("" + serviceReference.getProperty(LABEL));
        }
    }

    private void removeMenuItem(ServiceReference serviceReference) {
        String path = (String) serviceReference.getProperty(MENU_PATH);
        final Action item = this.algorithmsToActions.get(getItemID(serviceReference));

        if ((path != null) && (item != null)) {
            int index = path.lastIndexOf('/');

            if (index != -1) {
                path = path.substring(0, index);
                final IMenuManager targetMenu = this.menuManager.findMenuUsingPath(path);

                if (targetMenu != null) {
                    if (!this.shell.isDisposed()) {
                        this.shell.getDisplay().syncExec(new Runnable() {
                            public void run() {
                                targetMenu.remove(item.getId());
                            }
                        });
                    }

                    this.algorithmsToActions.remove(getItemID(serviceReference));
                    this.actionsToMenuManagers.remove(item);
                }
            }
        }
    }

    public void stop() {
        this.bundleContext.removeServiceListener(this.contextListener);

        if (!this.shell.isDisposed()) {
            this.shell.getDisplay().syncExec(this.stopAction);
        }
    }

    //private void clearShortcuts() {
    /*IWorkbench workbench = this.window.getWorkbench();
    IBindingService bindingService =
      (IBindingService)workbench.getService(IBindingService.class);
    Binding[] bindings = bindingService.getBindings();
    IHandlerService handlerService =
      (IHandlerService)workbench.getService(IHandlerService.class);*/
    //getLog().log(LogService.LOG_WARNING, "handlerService: " + handlerService);
    //for (int ii = 0; ii < bindings.length; ii++) {
    // getLog().log(LogService.LOG_INFO, "Binding[" + ii + "]: " + bindings[ii]);
    /*String bindingInfo =
       "\tcontextID: " + bindings[ii].getContextId() + "\n" +
       "\tparameterized command: " + bindings[ii].getParameterizedCommand() + "\n" +
       "\ttrigger sequence: " + bindings[ii].getTriggerSequence().format();
    getLog().log(LogService.LOG_INFO, "Binding:\n" + bindingInfo);*/

    /*KeyBinding(
       bindings[ii].getTriggerSequence(),
       null,
       bindings[ii].getSchemeId(),
       bindings[ii].getContextId(),
       null,
       null,
       null,
       Binding.SYSTEM);*/
    //}
    //}

    private void handleActionAccelerator(Action action, IMenuManager parentMenuBar,
            ServiceReference serviceReference) {
        action.setAccelerator(determineActionAcceleratorKeyCode(serviceReference, action));
    }

    private static int determineActionAcceleratorKeyCode(ServiceReference serviceReference, Action action) {
        String shortcutString = (String) serviceReference.getProperty(SHORTCUT);

        if (shortcutString != null) {
            return Action.convertAccelerator(shortcutString);
        } else {
            return 0;
        }
    }

    /*
     * printElementAttributes takes in a xml document, gets the nodes, then prints the attributes.
     * Copied from Java Tutorial on XML Parsing by Tim Kelley for debugging purposes.
     */
    static void printElementAttributes(Document doc) {
        NodeList nl = doc.getElementsByTagName("*");
        Element e;
        Node n;
        NamedNodeMap nnm;

        String attrname;
        String attrval;
        int i, len;

        len = nl.getLength();

        for (int j = 0; j < len; j++) {
            e = (Element) nl.item(j);
            System.err.println(e.getTagName() + ":");
            nnm = e.getAttributes();

            if (nnm != null) {
                for (i = 0; i < nnm.getLength(); i++) {
                    n = nnm.item(i);
                    attrname = n.getNodeName();
                    attrval = n.getNodeValue();
                    System.err.print(" " + attrname + " = " + attrval);
                }
            }
            System.err.println();
        }
    }
}