org.nightlabs.base.ui.action.registry.AbstractActionRegistry.java Source code

Java tutorial

Introduction

Here is the source code for org.nightlabs.base.ui.action.registry.AbstractActionRegistry.java

Source

/* *****************************************************************************
 * org.nightlabs.base.ui - NightLabs Eclipse utilities                         *
 * Copyright (C) 2004-2005 NightLabs - http://NightLabs.org                    *
 *                                                                             *
 * This library is free software; you can redistribute it and/or               *
 * modify it under the terms of the GNU Lesser General Public                  *
 * License as published by the Free Software Foundation; either                *
 * version 2.1 of the License, or (at your option) any later version.          *
 *                                                                             *
 * This library is distributed in the hope that it will be useful,             *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of              *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU           *
 * Lesser General Public License for more details.                             *
 *                                                                             *
 * You should have received a copy of the GNU Lesser General Public            *
 * License along with this library; if not, write to the                       *
 *     Free Software Foundation, Inc.,                                         *
 *     51 Franklin St, Fifth Floor,                                            *
 *     Boston, MA  02110-1301  USA                                             *
 *                                                                             *
 * Or get it online :                                                          *
 *     http://www.gnu.org/copyleft/lesser.html                                 *
 *                                                                             *
 *                                                                             *
 ******************************************************************************/
package org.nightlabs.base.ui.action.registry;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.ICoolBarManager;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.SubContributionItem;
import org.eclipse.jface.action.SubContributionManager;
import org.eclipse.jface.action.SubCoolBarManager;
import org.eclipse.jface.action.ToolBarContributionItem;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.widgets.Display;
import org.nightlabs.base.ui.action.IXContributionItem;
import org.nightlabs.eclipse.extension.AbstractEPProcessor;
import org.nightlabs.eclipse.extension.EPProcessorException;

/**
 * This class can be used to easily create an own extension-point for the management of actions.
 * To implement it, you not only need to create a class extending <code>AbstractActionRegistry</code>,
 * but as well define the extension-point in your plugin.xml. You must adhere to the following structure:
 * <ul>
 *  <li>groupMarker (0 - *) {name, path}</li>
 *  <li>separator (0 - *) {name, path}</li>
 *  <li>action (0 - *) {id, name, tooltip, icon, disabledIcon, hoverIcon, menubarPath, toolbarPath, contextmenuPath, visible, visibleInMenubar, visibleInToolbar, visibleInContextmenu}</li>
 *  <li>menu (0 - *) {id, label, path}
 *   <ul>
 *    <li>groupMarker (0 - *) {name}</li>
 *    <li>separator (0 - *) {name}</li>
 *   </ul>
 *  </li>
 * </ul>
 * As you see, the <i>path</i> property of <i>groupMarker</i> and <i>separator</i> is only used
 * when they are defined on the top-level; it is ignored when these elements
 * are declared within a menu.
 * </p>
 * <p>
 * Of course, you can decide to omit some of the properties in the extension point and control them
 * programatically. For example. this is very often the case with the properties visible,
 * visibleInMenubar, visibleInToolbar and visibleInContextmenu.
 * </p>
 * <p>
 * Instead of <i>action</i>, you might want to use another element name. This can easily be done via
 * {@link #getActionElementName()}.
 * </p>
 *
 * @author Marco Schulze - marco at nightlabs dot de
 * @author Daniel Mazurek - daniel at nightlabs dot de
 */
public abstract class AbstractActionRegistry extends AbstractEPProcessor
//implements Cloneable
{
    protected static final String ELEMENT_NAME_SEPARATOR = "separator"; //$NON-NLS-1$
    protected static final String ELEMENT_NAME_GROUP_MARKER = "groupMarker"; //$NON-NLS-1$
    protected static final String ELEMENT_NAME_MENU = "menu"; //$NON-NLS-1$

    protected static final String ATTRIBUTE_NAME_SEPARATOR_NAME = "name"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_SEPARATOR_PATH = "path"; //$NON-NLS-1$

    protected static final String ATTRIBUTE_NAME_GROUP_MARKER_NAME = "name"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_GROUP_MARKER_PATH = "path"; //$NON-NLS-1$

    protected static final String ATTRIBUTE_NAME_MENU_ID = "id"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_MENU_LABEL = "label"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_MENU_PATH = "path"; //$NON-NLS-1$

    protected static final String ATTRIBUTE_NAME_ACTION_ID = "id"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_NAME = "name"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_TOOLTIP = "tooltip"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_ICON = "icon"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_DISABLED_ICON = "disabledIcon"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_HOVER_ICON = "hoverIcon"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_MENUBAR_PATH = "menubarPath"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_TOOLBAR_PATH = "toolbarPath"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_CONTEXTMENU_PATH = "contextmenuPath"; //$NON-NLS-1$

    protected static final String ATTRIBUTE_NAME_ACTION_VISIBLE = "visible"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_VISIBLE_IN_MENUBAR = "visibleInMenubar"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_VISIBLE_IN_TOOLBAR = "visibleInToolbar"; //$NON-NLS-1$
    protected static final String ATTRIBUTE_NAME_ACTION_VISIBLE_IN_CONTEXTMENU = "visibleInContextmenu"; //$NON-NLS-1$

    protected static final String ATTRIBUTE_NAME_ACTION_ACTION_DEFINITION_ID = "actionDefinitionId"; //$NON-NLS-1$

    private String elementNameAction = null;

    /**
     * LOG4J logger used by this class
     */
    private static final Logger logger = Logger.getLogger(AbstractActionRegistry.class);

    /**
     * key: String articleEditActionID<br/>
     * value: ActionDescriptor actionDescriptor
     */
    private Map<String, ActionDescriptor> actionDescriptorsByID = new HashMap<String, ActionDescriptor>();

    /**
     * Contains all ActionDescriptors and MenuDescriptors in the order in which they have been read.
     */
    private List<ItemDescriptor> menuRaw = new ArrayList<ItemDescriptor>();

    private Map<ContributionManagerKind, List<ItemDescriptor>> contributionManagerKind2menuSorted = new HashMap<ContributionManagerKind, List<ItemDescriptor>>();

    /**
     * This method populates the passed <code>menuManager</code> with all visible
     * {@link IAction}s and other {@link IXContributionItem}s (e.g. separators or group-markers).
     * <p>
     * This method is meant to be used in an {@link org.eclipse.ui.IEditorActionBarContributor}
     * or another action-bar-contributor in order to contribute to the main menu or better a
     * sub-menu (which needs then to be created by the contributor before).
     * </p>
     * <p>
     * This method contributes WITHOUT removing items before. If you want to remove items, you must
     * do it manually before calling this method.
     * </p>
     *
     * @param menuManager The menu-manager which is responsible for the menu (or more likely sub-menu).
     * @return Returns the number of visible items (i.e. actions) that have been added (because some
     *      might be invisible and therefore not added).
     */
    public Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> contributeToMenuBar(
            IMenuManager menuManager) {
        return contribute(menuManager, ContributionManagerKind.menuBar);
    }

    /**
     * This method populates the passed <code>menuManager</code> with all visible
     * {@link IAction}s and other {@link IXContributionItem}s (e.g. separators or group-markers).
     * <p>
     * This method is meant to be used in an {@link org.eclipse.ui.IEditorActionBarContributor}
     * or another action-bar-contributor (or wherever you prefer to handle the context menu) in
     * order to contribute to the context-menu.
     * </p>
     * <p>
     * This method contributes WITHOUT removing items before. If you want to remove items, you must
     * do it manually before calling this method.
     * </p>
     *
     * @param menuManager The menu-manager which is responsible for the context-menu (or one of its sub-menus).
     * @return Returns the number of visible items (i.e. actions) that have been added (because some
     *      might be invisible and therefore not added).
     */
    public Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> contributeToContextMenu(
            IMenuManager menuManager) {
        return contribute(menuManager, ContributionManagerKind.contextMenu);
    }

    /**
     * This method populates the passed <code>toolBarManager</code> with all visible
     * {@link IAction}s and other {@link IXContributionItem}s (e.g. separators or group-markers).
     * <p>
     * This method is meant to be used in an {@link org.eclipse.ui.IEditorActionBarContributor}
     * or another action-bar-contributor in order to contribute to the toolbar. Note, that
     * sub-menu-structures are flattened automatically because a toolbar doesn't support sub-menus.
     * </p>
     * <p>
     * This method contributes WITHOUT removing items before. If you want to remove items, you must
     * do it manually before calling this method.
     * </p>
     *
     * @param toolBarManager The tool-bar-manager which is responsible for your tool-bar.
     * @return Returns the number of visible items (i.e. actions) that have been added (because some
     *      might be invisible and therefore not added).
     */
    public Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> contributeToToolBar(
            IToolBarManager toolBarManager) {
        return contribute(toolBarManager, ContributionManagerKind.toolBar);
    }

    private ActionVisibilityDecider actionVisibilityDecider;

    private static boolean useRemoveInsteadOfUnvisibleWorkaround = true;

    public void removeAllFromMenuBar(IMenuManager menuManager) {
        removeAll(menuManager, ContributionManagerKind.menuBar);
    }

    public void removeAllFromContextMenu(IMenuManager menuManager) {
        removeAll(menuManager, ContributionManagerKind.contextMenu);
    }

    protected void removeAll(IContributionManager contributionManager, ContributionManagerKind kind) {
        MenuManager tmpMenu = new MenuManager();
        ActionVisibilityDecider backupActionVisibilityDecider = this.actionVisibilityDecider;
        Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> itemDescriptorsToRemove;
        try {
            this.actionVisibilityDecider = actionVisibilityDeciderAlwaysVisible;
            itemDescriptorsToRemove = contribute(tmpMenu, kind);
        } finally {
            this.actionVisibilityDecider = backupActionVisibilityDecider;
        }

        // We only remove ActionDescriptors (i.e. ContributionItems or Actions) and NO menus,
        // because menus and separators/groupmarkers might be used by someone else.
        List<ItemDescriptor> actionDescriptorsToRemove = itemDescriptorsToRemove.get(ActionDescriptor.class);
        if (actionDescriptorsToRemove != null) {
            for (ItemDescriptor _actionDescriptor : actionDescriptorsToRemove) {
                ActionDescriptor ad = (ActionDescriptor) _actionDescriptor;
                String path = getPath(ad, kind);
                IContributionItem anchor = findUsingPath(contributionManager, path);

                if (anchor != null) {
                    if (anchor instanceof MenuManager) {
                        IContributionItem removedItem = ((MenuManager) anchor).remove(ad.getID());
                        String removed = removedItem != null ? "" : "NOT "; //$NON-NLS-1$ //$NON-NLS-2$
                        logger.debug("ActionDescriptor with id " + ad.getID() + " was " + removed + "removed from " //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
                                + kind + " !"); //$NON-NLS-1$
                    } else {
                        IContributionManager parent = ((ContributionItem) anchor).getParent();
                        if (parent != null) {
                            IContributionItem removedItem = parent.remove(ad.getID());
                            String removed = removedItem != null ? "" : "NOT "; //$NON-NLS-1$ //$NON-NLS-2$
                            logger.debug("ActionDescriptor with id " + ad.getID() + "was" + removed //$NON-NLS-1$//$NON-NLS-2$
                                    + "removed from " + kind + " !"); //$NON-NLS-1$ //$NON-NLS-2$
                        }
                    }
                }
            }
        }
    }

    private ActionVisibilityDecider actionVisibilityDeciderAlwaysVisible = new ActionVisibilityDecider() {
        @Override
        public boolean isVisible(ActionVisibilityContext actionVisibilityContext,
                ActionDescriptor actionDescriptor) {
            return true;
        }
    };

    /**
     * Removes all contributions of this registry from the CoolBar of the
     * given coolBarManager. Ideally this is done by making the contributions
     * invisible, so Eclipse can remember their positions. However
     * this is currently not possible and thats why this method
     * removes the affected contributions from the CoolBar.
     * This behaviour can is configured via {@link #useRemoveInsteadOfUnvisibleWorkaround}.
     *
     * @param coolBarManager The {@link ICoolBarManager} where the contributions of this registry should be removed from.
     */
    public void removeAllFromCoolBar(ICoolBarManager coolBarManager) {
        if (Display.getCurrent() == null)
            throw new IllegalStateException("This method must be called on the UI thread!"); //$NON-NLS-1$

        IContributionManager coolBarContributionManager = coolBarManager;
        if (coolBarManager instanceof SubCoolBarManager)
            coolBarContributionManager = ((SubCoolBarManager) coolBarManager).getParent();

        if (!useRemoveInsteadOfUnvisibleWorkaround)
            ((SubCoolBarManager) coolBarManager).setVisible(false);

        String baseID = this.getClass().getName();
        String orphanageToolbarID = baseID + '.' + ORPHANAGE_TOOLBAR_ID;
        // We use a temporary MenuManager which will be translated into the real
        // coolbar afterwards.
        MenuManager tmpMenu = new MenuManager();
        ActionVisibilityDecider backupActionVisibilityDecider = this.actionVisibilityDecider;
        try {
            this.actionVisibilityDecider = actionVisibilityDeciderAlwaysVisible;
            contribute(tmpMenu, ContributionManagerKind.coolBar);
        } finally {
            this.actionVisibilityDecider = backupActionVisibilityDecider;
        }

        // convert the existing items of the real coolbar-manager into a Map - the new items might
        // already exist because of Eclipse's workspace memory (and then the old ones need to be
        // manipulated - new ones would be ignored because of a bug/feature in the EclipseRCP)
        //      IContributionItem[] coolBarItems = ((SubCoolBarManager)coolBarManager).getParent().getItems();
        IContributionItem[] coolBarItems = coolBarContributionManager.getItems();

        // key: String itemId
        // value: IXContributionItem
        Map<String, IContributionItem> coolBarItemMap = new HashMap<String, IContributionItem>(coolBarItems.length);
        for (int i = 0; i < coolBarItems.length; ++i) {
            IContributionItem coolBarItem = coolBarItems[i];
            coolBarItemMap.put(coolBarItem.getId(), coolBarItem);
            logger.debug("Having " + coolBarItem.getId() + " in CoolBar"); //$NON-NLS-1$ //$NON-NLS-2$
        }

        ToolBarContributionItem orphanageToolBarContributionItem = getToolBarContributionItem(
                coolBarItemMap.get(orphanageToolbarID));
        if (orphanageToolBarContributionItem != null) {
            IContributionItem item = coolBarContributionManager.find(orphanageToolBarContributionItem.getId());
            if (item != null) {
                if (useRemoveInsteadOfUnvisibleWorkaround) {
                    coolBarContributionManager.remove(orphanageToolBarContributionItem.getId());
                } else {
                    orphanageToolBarContributionItem.setVisible(false);
                    item.setVisible(false);
                }
            }
        }

        // Now, we iterate all the "precompiled" items and contribute them to the coolbar
        IContributionItem[] tmpItems = tmpMenu.getItems();
        for (int i = 0; i < tmpItems.length; ++i) {
            IContributionItem tmpItem = tmpItems[i];

            // Test for items that are already in the parent
            if (tmpItem instanceof IMenuManager) {
                IMenuManager tmpSubMenu = (IMenuManager) tmpItem;
                String tmpSubMenuID = baseID + '.' + tmpSubMenu.getId();

                ToolBarContributionItem toolBarContributionItem = getToolBarContributionItem(
                        coolBarItemMap.get(tmpSubMenuID));
                if (toolBarContributionItem != null) {
                    IContributionItem item = coolBarContributionManager.find(toolBarContributionItem.getId());
                    if (item != null) {
                        if (useRemoveInsteadOfUnvisibleWorkaround) {
                            coolBarContributionManager.remove(tmpSubMenuID);
                        } else {
                            toolBarContributionItem.setVisible(false);
                            item.setVisible(false);
                        }
                    }
                }
            }
        }

        try {
            coolBarContributionManager.update(true);
        } catch (Exception x) {
            // https://bugs.eclipse.org/bugs/show_bug.cgi?id=239945
            logger.error("CoolBarManager.update failed: " + x.getLocalizedMessage(), x); //$NON-NLS-1$
        }
    }

    /**
     * @param coolBarManager
     * @return Returns the number of visible items (i.e. actions) that have been added (because some
     *      might be invisible and therefore not added).
     */
    public Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> contributeToCoolBar(
            ICoolBarManager coolBarManager) {
        if (coolBarManager instanceof SubContributionManager)
            ((SubCoolBarManager) coolBarManager).setVisible(true);

        String baseID = this.getClass().getName();
        String orphanageToolbarID = baseID + '.' + ORPHANAGE_TOOLBAR_ID;

        // We use a temporary MenuManager which will be translated into the real
        // coolbar afterwards.
        MenuManager tmpMenu = new MenuManager();
        Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> res = contribute(tmpMenu,
                ContributionManagerKind.coolBar);

        // all contributionItems with these IDs must not be added to the orphanage menu but directly
        // to the coolbar
        // TODO this should be somehow cached
        Set<String> additions_contributionItemIDs = new HashSet<String>();
        additions_contributionItemIDs.add("additions"); // additions itself must not end up in orphanage menu //$NON-NLS-1$
        for (ActionDescriptor actionDescriptor : getActionDescriptors()) {
            if ("additions".equals(actionDescriptor.getToolbarPath())) //$NON-NLS-1$
                additions_contributionItemIDs.add(actionDescriptor.getID());
        }

        // convert the existing items of the real coolbar-manager into a Map - the new items might
        // already exist because of Eclipse's workspace memory (and then the old ones need to be
        // manipulated - new ones would be ignored because of a bug/feature in the EclipseRCP)
        IContributionItem[] coolBarItems = null;
        if (coolBarManager instanceof SubCoolBarManager) {
            coolBarItems = ((SubCoolBarManager) coolBarManager).getParent().getItems();
        } else if (coolBarManager instanceof IContributionManager) {
            coolBarItems = ((IContributionManager) coolBarManager).getItems();
        }
        //      IContributionItem[] coolBarItems = ((SubCoolBarManager)coolBarManager).getParent().getItems();

        // key: String itemId
        // value: IXContributionItem
        Map<String, IContributionItem> coolBarItemMap = new HashMap<String, IContributionItem>(coolBarItems.length);
        for (int i = 0; i < coolBarItems.length; ++i) {
            IContributionItem coolBarItem = coolBarItems[i];
            coolBarItemMap.put(coolBarItem.getId(), coolBarItem);
        }

        // Clean the "orphanage" toolbar which holds all those actions, that are not wrapped by a menu.
        // This toolbar does only exist, if it is needed.
        ToolBarContributionItem orphanageToolBarContributionItem = getToolBarContributionItem(
                coolBarItemMap.get(orphanageToolbarID));
        if (orphanageToolBarContributionItem != null)
            orphanageToolBarContributionItem.getToolBarManager().removeAll();

        // We need to collect all the "orphaned" actions in a menu first and at them after all the other menus.
        MenuManager orphanageMenu = new MenuManager();

        List<IContributionItem> additionsToBeAddedDirectlyToCoolBar = new ArrayList<IContributionItem>();

        // Now, we iterate all the "precompiled" items and contribute them to the coolbar
        IContributionItem[] tmpItems = tmpMenu.getItems();
        for (int i = 0; i < tmpItems.length; ++i) {
            IContributionItem tmpItem = tmpItems[i];

            // Either, we've hit a menu or an "orphaned" item
            if (tmpItem instanceof IMenuManager) {
                IMenuManager tmpSubMenu = (IMenuManager) tmpItem;
                String tmpSubMenuID = baseID + '.' + tmpSubMenu.getId();

                // find the previously existing ToolBarManager or create a new one.
                IToolBarManager toolBarManager;
                ToolBarContributionItem toolBarContributionItem = getToolBarContributionItem(
                        coolBarItemMap.get(tmpSubMenuID));
                if (toolBarContributionItem != null)
                    toolBarManager = toolBarContributionItem.getToolBarManager();
                else {
                    toolBarManager = new ToolBarManager();
                    toolBarContributionItem = new ToolBarContributionItem(toolBarManager, tmpSubMenuID);
                    toolBarContributionItem.setUseChevron(false);
                    coolBarManager.add(toolBarContributionItem);
                }

                toolBarManager.removeAll();
                addFlattenedMenu(toolBarManager, tmpSubMenuID + ".separator", tmpSubMenu); //$NON-NLS-1$
            } else {
                if (additions_contributionItemIDs.contains(tmpItem.getId()))
                    additionsToBeAddedDirectlyToCoolBar.add(tmpItem);
                else
                    orphanageMenu.add(tmpItem);
            }
        } // for (int i = 0; i < tmpItems.length; ++i) {

        // If we have actions without menus, populate a toolbar with them.
        if (!orphanageMenu.isEmpty()) {
            IToolBarManager toolBarManager;
            orphanageToolBarContributionItem = getToolBarContributionItem(coolBarItemMap.get(orphanageToolbarID));
            if (orphanageToolBarContributionItem != null)
                toolBarManager = orphanageToolBarContributionItem.getToolBarManager();
            else {
                toolBarManager = new ToolBarManager();
                orphanageToolBarContributionItem = new ToolBarContributionItem(toolBarManager, orphanageToolbarID);
                orphanageToolBarContributionItem.setUseChevron(false);
                coolBarManager.add(orphanageToolBarContributionItem);
            }

            toolBarManager.removeAll();
            addFlattenedMenu(toolBarManager, orphanageToolbarID + ".separator", orphanageMenu); //$NON-NLS-1$
        }

        // contribute this stuff directly into the CoolBar (not a nested ToolBar).
        for (IContributionItem contributionItem : additionsToBeAddedDirectlyToCoolBar) {
            coolBarManager.add(contributionItem);
        }

        try {
            coolBarManager.update(true);
        } catch (Exception e) {
            // TODO WORKAROUND
            // sometimes at first startup an ArrayIndexOutOfBounds occurs, because
            // CoolBarManager.adjustContributionList removes first an separator and
            // afterwards also the first element, of an now empty list
            // this is an workaround for this bug
            logger.warn("Eclipse bug in coolBarManager.update(true) occured", e); //$NON-NLS-1$
        }

        //      if (coolBarManager instanceof CoolBarManager)
        //         ((CoolBarManager)coolBarManager).refresh();

        return res;
    }

    protected static ToolBarContributionItem getToolBarContributionItem(Object item) {
        if (item == null)
            return null;

        if (item instanceof ToolBarContributionItem)
            return (ToolBarContributionItem) item;
        else if (item instanceof SubContributionItem)
            return (ToolBarContributionItem) ((SubContributionItem) item).getInnerItem();
        else {
            String itemID;
            if (item instanceof IContributionItem)
                itemID = ((IContributionItem) item).getId();
            else
                itemID = "{unknown}"; //$NON-NLS-1$

            throw new IllegalStateException("Item with id=" + itemID //$NON-NLS-1$
                    + " should be an instance of ToolBarContributionItem or Sub, but is of type " //$NON-NLS-1$
                    + item.getClass().getName() + "! Item: " + item); //$NON-NLS-1$
        }
    }

    private static final String ORPHANAGE_TOOLBAR_ID = "orphanage"; //$NON-NLS-1$

    /**
     * @param manager The manager in which to search.
     * @param path A slash ('/') separated path or <code>null</code>. If <code>path</code> is
     *      <code>null</code>, this method will always return <code>null</code>.
     * @return Returns the contribution item specified by path or <code>null</code>, if nothing
     *      could be found.
     */
    protected static IContributionItem findUsingPath(IContributionManager manager, String path) {
        if (path == null)
            return null;

        if (manager instanceof IMenuManager)
            return ((IMenuManager) manager).findUsingPath(path);

        String[] parts = path.split("/"); //$NON-NLS-1$
        IContributionManager mgr = manager;
        for (int i = 0; i < parts.length; ++i) {
            String id = parts[i];
            if (mgr != null) {
                IContributionItem item = mgr.find(id);
                if (i == parts.length - 1)
                    return item;

                if (item instanceof IMenuManager) {
                    StringBuffer sbSubPath = new StringBuffer();
                    for (int n = i + 1; n < parts.length; ++n) {
                        if (sbSubPath.length() != 0)
                            sbSubPath.append('/');
                        sbSubPath.append(parts[n]);
                    }
                    return ((IMenuManager) item).findUsingPath(sbSubPath.toString());
                }

                if (item instanceof IContributionManager)
                    mgr = (IContributionManager) item;
                else
                    return null;
            }
        }

        return null;
    }

    protected static void addFlattenedMenu(IContributionManager dest, String parentID, IMenuManager menu) {
        String id = menu.getId();
        String sepName = (parentID == null || "".equals(parentID)) ? id : (parentID + '.' + id); //$NON-NLS-1$
        dest.add(new Separator(sepName));
        IContributionItem[] items = menu.getItems();
        for (int i = 0; i < items.length; ++i) {
            IContributionItem item = items[i];
            if (item instanceof IMenuManager) {
                String itemId = item.getId();
                String newParentID = (parentID == null || "".equals(parentID)) ? itemId : (parentID + '.' + itemId); //$NON-NLS-1$
                addFlattenedMenu(dest, newParentID, (IMenuManager) item);
            } else
                dest.add(item);
        }
    }

    public AbstractActionRegistry(ActionVisibilityDecider actionVisibilityDecider) {
        this.actionVisibilityDecider = actionVisibilityDecider;
        if (this.actionVisibilityDecider == null)
            this.actionVisibilityDecider = new ActionVisibilityDecider();
    }

    public AbstractActionRegistry() {
        this(null);
    }

    //   /**
    //   * This method contributes WITHOUT removing items before.
    //   *
    //   * @param contributionManager
    //   * @param kind
    //   * @return Returns the number of visible contribution items (i.e. actions) that have been added.
    //   */
    //   protected int contribute(IContributionManager contributionManager, String kind)
    //   {
    //   int visibleContributionItemCount = 0;

    //   List menuSorted;
    //   if (ContributionManagerKind.MENUBAR.equals(kind))
    //   menuSorted = menuSortedForMenubar;
    //   else if (ContributionManagerKind.CONTEXTMENU.equals(kind))
    //   menuSorted = menuSortedForContextmenu;
    //   else if (ContributionManagerKind.TOOLBAR.equals(kind))
    //   menuSorted = menuSortedForToolbar;
    //   else if (ContributionManagerKind.COOLBAR.equals(kind))
    //   menuSorted = menuSortedForToolbar;
    //   else
    //   throw new IllegalArgumentException("kind \"" + kind + "\" invalid!");

    //   boolean firstRun = menuSorted == null;
    //   LinkedList menuRaw = null;
    //   int lastMenuRawSize = 0;
    //   if (firstRun) {
    //   menuRaw = new LinkedList(this.menuRaw);
    //   lastMenuRawSize = menuRaw.size();
    //   menuSorted = new LinkedList();
    //   }

    ////contributionManager.removeAll();

    //   while ((firstRun && !menuRaw.isEmpty()) || !firstRun) {
    //   for (Iterator itTopLevel = (firstRun ? menuRaw : menuSorted).iterator(); itTopLevel.hasNext(); ) {
    //   ItemDescriptor item = (ItemDescriptor) itTopLevel.next();
    //   if (item instanceof ActionDescriptor)
    //   {
    //   ActionDescriptor ad = (ActionDescriptor) item;
    //   String path;
    //   if (ContributionManagerKind.MENUBAR.equals(kind))
    //   path = ad.getMenubarPath();
    //   else if (ContributionManagerKind.CONTEXTMENU.equals(kind))
    //   path = ad.getContextmenuPath();
    //   else if (ContributionManagerKind.TOOLBAR.equals(kind))
    //   path = ad.getToolbarPath();
    //   else if (ContributionManagerKind.COOLBAR.equals(kind))
    //   path = ad.getToolbarPath();
    //   else
    //   throw new IllegalArgumentException("kind \"" + kind + "\" invalid!");

    //   IContributionItem anchor = path == null ? null : findUsingPath(contributionManager, path);
    //   if (anchor != null) {
    //   boolean visible = ad.isVisible();
    //   if (visible) {
    //   if (ContributionManagerKind.MENUBAR.equals(kind))
    //   visible = ad.isVisibleInMenubar();
    //   else if (ContributionManagerKind.CONTEXTMENU.equals(kind))
    //   visible = ad.isVisibleInContextmenu();
    //   else if (ContributionManagerKind.TOOLBAR.equals(kind))
    //   visible = ad.isVisibleInToolbar();
    //   else if (ContributionManagerKind.COOLBAR.equals(kind))
    //   visible = ad.isVisibleInToolbar();
    //   else
    //   throw new IllegalArgumentException("kind \"" + kind + "\" invalid!");
    //   }

    //   if (visible) {
    //   ++visibleContributionItemCount;
    //   if (ad.getAction() != null)
    //   ((ContributionItem)anchor).getParent().insertAfter(anchor.getId(), ad.getAction());
    //   else if (ad.getContributionItem() != null)
    //   ((ContributionItem)anchor).getParent().insertAfter(anchor.getId(), ad.getContributionItem());
    //   }

    //   if (firstRun) {
    //   menuSorted.add(item);
    //   itTopLevel.remove();
    //   }
    //   }
    //   }
    //   else if (item instanceof SeparatorDescriptor) {
    //   SeparatorDescriptor s = (SeparatorDescriptor) item;
    //   String path = s.getPath();
    //   IContributionItem anchor = path == null ? null : findUsingPath(contributionManager, path);
    //   if (path == null || anchor != null) {
    //   Separator separator = new Separator(s.getName());
    //   if (anchor == null)
    //   contributionManager.add(separator);
    //   else
    //   ((ContributionItem)anchor).getParent().insertAfter(anchor.getId(), separator);

    //   if (firstRun) {
    //   menuSorted.add(item);
    //   itTopLevel.remove();
    //   }
    //   }
    //   }
    //   else if (item instanceof GroupMarkerDescriptor) {
    //   GroupMarkerDescriptor gm = (GroupMarkerDescriptor) item;
    //   String path = gm.getPath();
    //   IContributionItem anchor = path == null ? null : findUsingPath(contributionManager, path);
    //   if (path == null || anchor != null) {
    //   GroupMarker groupMarker = new GroupMarker(gm.getName());
    //   if (anchor == null)
    //   contributionManager.add(groupMarker);
    //   else
    //   ((ContributionItem)anchor).getParent().insertAfter(anchor.getId(), groupMarker);

    //   if (firstRun) {
    //   menuSorted.add(item);
    //   itTopLevel.remove();
    //   }
    //   }
    //   }
    //   else if (item instanceof MenuDescriptor) {
    //   MenuDescriptor md = (MenuDescriptor) item;
    //   String path = md.getPath();
    //   IContributionItem anchor = path == null ? null : findUsingPath(contributionManager, path);
    //   if (path == null || anchor != null) {
    //   MenuManager subMenu = new MenuManager(md.getLabel(), md.getId());

    //   for (Iterator itSub = md.getSubItems().iterator(); itSub.hasNext(); ) {
    //   Object obj = itSub.next();
    //   if (obj instanceof SeparatorDescriptor) {
    //   SeparatorDescriptor separator = (SeparatorDescriptor) obj;
    //   subMenu.add(new Separator(separator.getName()));
    //   }
    //   else if (obj instanceof GroupMarkerDescriptor) {
    //   GroupMarkerDescriptor groupMarker = (GroupMarkerDescriptor) obj;
    //   subMenu.add(new GroupMarker(groupMarker.getName()));
    //   }
    //   else
    //   throw new IllegalStateException("SubItem of menu is neither a SeparatorDescriptor nor a GroupMarkerDescriptor but " + obj.getClass().getName());
    //   }

    //   if (anchor == null)
    //   contributionManager.add(subMenu);
    //   else
    //   ((ContributionItem)anchor).getParent().insertAfter(anchor.getId(), subMenu);

    //   if (firstRun) {
    //   menuSorted.add(item);
    //   itTopLevel.remove();
    //   }
    //   }
    //   }
    //   else
    //   throw new IllegalStateException("Item in menuRaw of type " + item.getClass() + " is an instance of an unknown class!");
    //   }

    //   if (firstRun && (lastMenuRawSize == menuRaw.size())) {
    //   if (logger.isDebugEnabled()) {
    //   logger.debug("Could not add the following contributions to the menu (kind " + kind + "):");
    //   for (Iterator it = menuRaw.iterator(); it.hasNext(); ) {
    //   ItemDescriptor item = (ItemDescriptor) it.next();
    //   if (item instanceof ActionDescriptor) {
    //   logger.debug("    Action with id=" + ((ActionDescriptor)item).getAction().getId());
    //   }
    //   else if (item instanceof MenuDescriptor) {
    //   logger.debug("    Menu with id=" + ((MenuDescriptor)item).getId());
    //   }
    //   else if (item instanceof SeparatorDescriptor) {
    //   logger.debug("    Separator with name=" + ((SeparatorDescriptor)item).getName());
    //   }
    //   else if (item instanceof GroupMarkerDescriptor) {
    //   logger.debug("    GroupMarker with name=" + ((GroupMarkerDescriptor)item).getName());
    //   }
    //   else
    //   logger.debug("    " + item);
    //   }
    //   }
    //   break;
    //   }

    //   if (!firstRun)
    //   break;

    //   lastMenuRawSize = menuRaw.size();
    //   } // while ((firstRun && !menuRaw.isEmpty()) || !firstRun) {

    //   if (firstRun) {
    //   if (ContributionManagerKind.MENUBAR.equals(kind))
    //   menuSortedForMenubar = menuSorted;
    //   else if (ContributionManagerKind.CONTEXTMENU.equals(kind))
    //   menuSortedForContextmenu = menuSorted;
    //   else if (ContributionManagerKind.TOOLBAR.equals(kind))
    //   menuSortedForToolbar = menuSorted;
    //   else if (ContributionManagerKind.COOLBAR.equals(kind))
    //   menuSortedForToolbar = menuSorted;
    //   else
    //   throw new IllegalArgumentException("kind \"" + kind + "\" invalid!");
    //   }

    //   // flatten the menus if we're contributing to the toolbar (which doesn't understand sub-menus)
    //   // the coolbar is handled by contributeToCoolBar(...) directly
    //   if (ContributionManagerKind.TOOLBAR.equals(kind)) {
    //   IContributionItem[] items = contributionManager.getItems();
    //   contributionManager.removeAll();
    //   for (int i = 0; i < items.length; ++i) {
    //   IContributionItem item = items[i];
    //   if (item instanceof IMenuManager)
    //   addFlattenedMenu(contributionManager, null, (IMenuManager)item);
    //   else
    //   contributionManager.add(item);
    //   }
    //   }
    //   contributionManager.update(true);
    //   return visibleContributionItemCount;
    //   }

    private static void addVisibleItemDescriptor(
            Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> visibleItemDescriptors,
            Class<? extends ItemDescriptor> c, ItemDescriptor i) {
        List<ItemDescriptor> l = visibleItemDescriptors.get(c);
        if (l == null) {
            l = new ArrayList<ItemDescriptor>();
            visibleItemDescriptors.put(c, l);
        }
        l.add(i);
    }

    //   private static String getPath(ItemDescriptor itemDescriptor, ContributionManagerKind kind)
    //   {
    //      if (itemDescriptor instanceof ActionDescriptor) {
    //         return getPath((ActionDescriptor)itemDescriptor, kind);
    //      }
    //      else if (itemDescriptor instanceof PathItemDescriptor) {
    //         return ((PathItemDescriptor)itemDescriptor).getPath();
    //      }
    //
    //      return null;
    //   }

    private static String getPath(ActionDescriptor actionDescriptor, ContributionManagerKind kind) {
        String path;
        switch (kind) {
        case menuBar:
            path = actionDescriptor.getMenubarPath();
            break;
        case contextMenu:
            path = actionDescriptor.getContextmenuPath();
            break;
        case toolBar:
            path = actionDescriptor.getToolbarPath();
            break;
        case coolBar:
            path = actionDescriptor.getToolbarPath();
            break;
        default:
            throw new IllegalArgumentException("kind \"" + kind + "\" invalid!"); //$NON-NLS-1$ //$NON-NLS-2$
        }
        return path;
    }

    /**
     * This method contributes WITHOUT removing items before.
     *
     * @param contributionManager
     * @param kind
     * @return Returns the number of visible contribution items (i.e. actions) that have been added.
     */
    protected Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> contribute(
            IContributionManager contributionManager, ContributionManagerKind kind) {
        Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> visibleItemDescriptors = new HashMap<Class<? extends ItemDescriptor>, List<ItemDescriptor>>();
        ActionVisibilityContext actionVisibilityContext = new ActionVisibilityContext(kind);
        List<ItemDescriptor> menuSorted = contributionManagerKind2menuSorted.get(kind);

        boolean firstRun = menuSorted == null;
        LinkedList<ItemDescriptor> menuRaw = null;
        int lastMenuRawSize = 0;
        if (firstRun) {
            menuRaw = new LinkedList<ItemDescriptor>(this.menuRaw);
            menuRaw.add(new SeparatorDescriptor("additions")); //$NON-NLS-1$
            lastMenuRawSize = menuRaw.size();
            menuSorted = new LinkedList<ItemDescriptor>();
        }

        //      contributionManager.removeAll();

        while ((firstRun && !menuRaw.isEmpty()) || !firstRun) {
            for (Iterator<?> itTopLevel = (firstRun ? menuRaw : menuSorted).iterator(); itTopLevel.hasNext();) {
                ItemDescriptor item = (ItemDescriptor) itTopLevel.next();
                if (item instanceof ActionDescriptor) {
                    ActionDescriptor ad = (ActionDescriptor) item;

                    String path = getPath(ad, kind);

                    IContributionItem anchor = path == null ? null : findUsingPath(contributionManager, path);
                    if (anchor != null) {
                        if (actionVisibilityDecider.isVisible(actionVisibilityContext, ad)) {
                            addVisibleItemDescriptor(visibleItemDescriptors, ActionDescriptor.class, ad);

                            if (anchor instanceof MenuManager) {
                                if (ad.getAction() != null)
                                    ((MenuManager) anchor).add(ad.getAction());
                                else if (ad.getContributionItem() != null)
                                    ((MenuManager) anchor).add(ad.getContributionItem());
                            } else {
                                if (ad.getAction() != null)
                                    ((ContributionItem) anchor).getParent().insertAfter(anchor.getId(),
                                            ad.getAction());
                                else if (ad.getContributionItem() != null)
                                    ((ContributionItem) anchor).getParent().insertAfter(anchor.getId(),
                                            ad.getContributionItem());
                            }
                        }

                        if (firstRun) {
                            menuSorted.add(item);
                            itTopLevel.remove();
                        }
                    }
                } else if (item instanceof SeparatorDescriptor) {
                    addVisibleItemDescriptor(visibleItemDescriptors, SeparatorDescriptor.class, item);

                    SeparatorDescriptor s = (SeparatorDescriptor) item;
                    String path = s.getPath();
                    IContributionItem anchor = path == null ? null : findUsingPath(contributionManager, path);
                    if (path == null || anchor != null) {
                        Separator separator = new Separator(s.getName());

                        if (anchor == null) {
                            if (contributionManager.find(item.getID()) == null)
                                contributionManager.add(separator);
                        } else {
                            if (((ContributionItem) anchor).getParent().find(item.getID()) == null)
                                ((ContributionItem) anchor).getParent().insertAfter(anchor.getId(), separator);
                        }

                        if (firstRun) {
                            menuSorted.add(item);
                            itTopLevel.remove();
                        }
                    }
                } else if (item instanceof GroupMarkerDescriptor) {
                    addVisibleItemDescriptor(visibleItemDescriptors, GroupMarkerDescriptor.class, item);

                    GroupMarkerDescriptor gm = (GroupMarkerDescriptor) item;
                    String path = gm.getPath();
                    IContributionItem anchor = path == null ? null : findUsingPath(contributionManager, path);
                    if (path == null || anchor != null) {
                        GroupMarker groupMarker = new GroupMarker(gm.getName());

                        if (anchor == null) {
                            if (contributionManager.find(item.getID()) == null)
                                contributionManager.add(groupMarker);
                        } else {
                            if (((ContributionItem) anchor).getParent().find(item.getID()) == null)
                                ((ContributionItem) anchor).getParent().insertAfter(anchor.getId(), groupMarker);
                        }

                        if (firstRun) {
                            menuSorted.add(item);
                            itTopLevel.remove();
                        }
                    }
                } else if (item instanceof MenuDescriptor) {
                    addVisibleItemDescriptor(visibleItemDescriptors, MenuDescriptor.class, item);
                    MenuDescriptor md = (MenuDescriptor) item;
                    String path = md.getPath();

                    // already existing in the contribution-manager?
                    IContributionItem anchor = path == null ? null : findUsingPath(contributionManager, path);
                    if (path == null || anchor != null) {
                        MenuManager subMenu = new MenuManager(md.getLabel(), md.getID());

                        for (Iterator<?> itSub = md.getSubItems().iterator(); itSub.hasNext();) {
                            Object obj = itSub.next();
                            if (obj instanceof SeparatorDescriptor) {
                                SeparatorDescriptor separator = (SeparatorDescriptor) obj;
                                subMenu.add(new Separator(separator.getName()));
                            } else if (obj instanceof GroupMarkerDescriptor) {
                                GroupMarkerDescriptor groupMarker = (GroupMarkerDescriptor) obj;
                                subMenu.add(new GroupMarker(groupMarker.getName()));
                            } else
                                throw new IllegalStateException(
                                        "SubItem of menu is neither a SeparatorDescriptor nor a GroupMarkerDescriptor but " //$NON-NLS-1$
                                                + obj.getClass().getName());
                        }

                        if (anchor == null) {
                            if (contributionManager.find(item.getID()) == null)
                                contributionManager.add(subMenu);
                        } else {
                            if (((ContributionItem) anchor).getParent().find(item.getID()) == null)
                                ((ContributionItem) anchor).getParent().insertAfter(anchor.getId(), subMenu);
                        }

                        if (firstRun) {
                            menuSorted.add(item);
                            itTopLevel.remove();
                        }
                    }
                } else
                    throw new IllegalStateException(
                            "Item in menuRaw of type " + item.getClass() + " is an instance of an unknown class!"); //$NON-NLS-1$ //$NON-NLS-2$
            }

            if (firstRun && (lastMenuRawSize == menuRaw.size())) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not add the following contributions to the menu (kind " + kind + "):"); //$NON-NLS-1$ //$NON-NLS-2$
                    for (Iterator<ItemDescriptor> it = menuRaw.iterator(); it.hasNext();) {
                        ItemDescriptor item = it.next();
                        if (item instanceof ActionDescriptor) {
                            logger.debug("    Action with id=" + ((ActionDescriptor) item).getID()); //$NON-NLS-1$
                        } else if (item instanceof MenuDescriptor) {
                            logger.debug("    Menu with id=" + ((MenuDescriptor) item).getID()); //$NON-NLS-1$
                        } else if (item instanceof SeparatorDescriptor) {
                            logger.debug("    Separator with name=" + ((SeparatorDescriptor) item).getName()); //$NON-NLS-1$
                        } else if (item instanceof GroupMarkerDescriptor) {
                            logger.debug("    GroupMarker with name=" + ((GroupMarkerDescriptor) item).getName()); //$NON-NLS-1$
                        } else
                            logger.debug("    " + item); //$NON-NLS-1$
                    }
                }
                break;
            }

            if (!firstRun)
                break;

            lastMenuRawSize = menuRaw.size();
        } // while ((firstRun && !menuRaw.isEmpty()) || !firstRun) {

        if (firstRun)
            contributionManagerKind2menuSorted.put(kind, menuSorted);

        // flatten the menus if we're contributing to the toolbar (which doesn't understand sub-menus)
        // the coolbar is handled by contributeToCoolBar(...) directly
        if (ContributionManagerKind.toolBar == kind) {
            IContributionItem[] items = contributionManager.getItems();
            contributionManager.removeAll();
            for (int i = 0; i < items.length; ++i) {
                IContributionItem item = items[i];
                if (item instanceof IMenuManager)
                    addFlattenedMenu(contributionManager, null, (IMenuManager) item);
                else
                    contributionManager.add(item);
            }
        }
        contributionManager.update(true);

        debugContributions(visibleItemDescriptors, kind, true);

        return visibleItemDescriptors;
    }

    private void debugContributions(Map<Class<? extends ItemDescriptor>, List<ItemDescriptor>> itemDescriptors,
            ContributionManagerKind kind, boolean addIfTrueRemoveIfFalse) {
        for (Map.Entry<Class<? extends ItemDescriptor>, List<ItemDescriptor>> entry : itemDescriptors.entrySet()) {
            Class<? extends ItemDescriptor> clazz = entry.getKey();
            String descriptorKind = null;
            if (clazz == ActionDescriptor.class) {
                descriptorKind = "ActionDescriptor"; //$NON-NLS-1$
            } else if (clazz == MenuDescriptor.class) {
                descriptorKind = "MenuDescriptor"; //$NON-NLS-1$
            } else if (clazz == GroupMarkerDescriptor.class) {
                descriptorKind = "GroupMarkerDescriptor"; //$NON-NLS-1$
            } else if (clazz == SeparatorDescriptor.class) {
                descriptorKind = "SeparatorDescriptor"; //$NON-NLS-1$
            }

            String contributionKind = addIfTrueRemoveIfFalse ? "added to " : "removed from "; //$NON-NLS-1$ //$NON-NLS-2$
            for (ItemDescriptor itemDescriptor : entry.getValue()) {
                logger.debug(
                        descriptorKind + " " + itemDescriptor.getID() + " has been " + contributionKind + kind); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
    }

    /**
     * You are free to name the action element as you desire. It's a good idea though, to name
     * it somewhat ending on "Action" (e.g. mySpecialAction) - or simply "action".
     * <p>
     * The default implementation of this method returns "action".
     * </p>
     *
     * @return Returns the name of the action element. Default is "action".
     */
    protected String getActionElementName() {
        return "action"; //$NON-NLS-1$
    }

    /**
     * In your extension of <code>AbstractActionRegistry</code>, you must create
     * your actions yourself in order to define what shall be done in the {@link IAction#run()}
     * method.
     * <p>
     * You should not do any initialization here, but use {@link #initAction(IAction, IExtension, IConfigurationElement)}
     * or {@link #initContributionItem(IXContributionItem, IExtension, IConfigurationElement)}
     * instead.
     * </p>
     *
     * @param extension The extension which contributes the action.
     * @param element The current element (which matches {@link #getActionElementName()}).
     * @return Returns a new instance of {@link IAction} or {@link IXContributionItem}.
     * @throws EPProcessorException If the element's attributes are not correct or another problem occurs.
     */
    protected abstract Object createActionOrContributionItem(IExtension extension, IConfigurationElement element)
            throws EPProcessorException;

    /**
     * This method is called after all the general properties of the new action have been set. It is NOT called,
     * if {@link #createActionOrContributionItem(IExtension, IConfigurationElement)} returned an
     * {@link IXContributionItem}. Override it, if you want to initialize your action.
     *
     * @param action The action as it has been created before by {@link #createActionOrContributionItem(IExtension, IConfigurationElement)}.
     * @param extension The extension which contributes the action.
     * @param element The current element (which matches {@link #getActionElementName()}).
     * @throws EPProcessorException If the element's attributes are not correct or another problem occurs.
     */
    protected void initAction(IAction action, IExtension extension, IConfigurationElement element)
            throws EPProcessorException {
    }

    /**
     * This method is called after all the general properties of the new ContributionItem have been set.
     * It is NOT called, if {@link #createActionOrContributionItem(IExtension, IConfigurationElement)}
     * returned an {@link IAction}. Override it, if you want to initialize your contribution item.
     *
     * @param contributionItem The contributionItem as it has been created before by {@link #createActionOrContributionItem(IExtension, IConfigurationElement)}.
     * @param extension The extension which contributes the action.
     * @param element The current element (which matches {@link #getActionElementName()}).
     * @throws EPProcessorException If the element's attributes are not correct or another problem occurs.
     */
    protected void initContributionItem(IXContributionItem contributionItem, IExtension extension,
            IConfigurationElement element) throws EPProcessorException {
    }

    /**
     * Override this method if you need an extended <code>ActionDescriptor</code>. Note, that you
     * cannot initialize it here, but you should then additionally extend the method
     * {@link #initActionDescriptor(ActionDescriptor, IExtension, IConfigurationElement)}.
     *
     * @return Returns a newly created instance of <code>ActionDescriptor</code>.
     */
    protected ActionDescriptor createActionDescriptor() {
        return new ActionDescriptor();
    }

    /**
     * Extend this method to set additional properties of your <code>ActionDescriptor</code>.
     * This method is called, after the basic properties of {@link ActionDescriptor} have already
     * been initialized.
     * <p>
     * Though the basic implementation of this method is empty, it is still a good idea, to
     * call <code>super.initActionDescriptor(...)</code> before doing your
     * own initialization.
     * </p>
     *
     * @param actionDescriptor The <code>ActionDescriptor</code> that was previously created by {@link #createActionDescriptor()}.
     * @param extension The extension that is contributing the current action.
     * @param element The current element (which is matching {@link #getActionElementName()}).
     * @return whether to use this {@link ActionDescriptor}. <code>false</code> indicates that the current
     *      <code>actionDescriptor</code> shall be ignored by this registry instance (it will silently be
     *      thrown away instead of being added to the internal datastructure).
     * @throws EPProcessorException If the element's attributes are not correct or another problem occurs.
     */
    protected boolean initActionDescriptor(ActionDescriptor actionDescriptor, IExtension extension,
            IConfigurationElement element) throws EPProcessorException {
        return true;
    }

    @Override
    public void processElement(IExtension extension, IConfigurationElement element) throws Exception {
        if (elementNameAction == null)
            elementNameAction = getActionElementName();

        String elementName = element.getName();
        if (ELEMENT_NAME_SEPARATOR.equals(elementName)) {
            String name = element.getAttribute(ATTRIBUTE_NAME_SEPARATOR_NAME);
            String path = element.getAttribute(ATTRIBUTE_NAME_SEPARATOR_PATH);
            menuRaw.add(new SeparatorDescriptor(name, path));
        } else if (ELEMENT_NAME_GROUP_MARKER.equals(elementName)) {
            String name = element.getAttribute(ATTRIBUTE_NAME_GROUP_MARKER_NAME);
            String path = element.getAttribute(ATTRIBUTE_NAME_GROUP_MARKER_PATH);
            menuRaw.add(new GroupMarkerDescriptor(name, path));
        } else if (ELEMENT_NAME_MENU.equals(elementName)) {
            String id = element.getAttribute(ATTRIBUTE_NAME_MENU_ID);
            String label = element.getAttribute(ATTRIBUTE_NAME_MENU_LABEL);
            String path = element.getAttribute(ATTRIBUTE_NAME_MENU_PATH);
            if ("".equals(path)) //$NON-NLS-1$
                path = null;

            MenuDescriptor menuDescriptor = new MenuDescriptor(id, label, path);
            menuRaw.add(menuDescriptor);

            //         String separatorName = "";
            //         String groupMarkerName = "";
            IConfigurationElement[] children = element.getChildren();
            for (int i = 0; i < children.length; ++i) {
                IConfigurationElement child = children[i];
                if (ELEMENT_NAME_SEPARATOR.equals(child.getName())) {
                    String name = child.getAttribute(ATTRIBUTE_NAME_SEPARATOR_NAME);
                    if (child.getAttribute(ATTRIBUTE_NAME_SEPARATOR_PATH) != null)
                        logger.warn(
                                "There is a separator's path specified within a menu. This path will be ignored! You should not specify a path when using a separator inside of a menu! plugin=" //$NON-NLS-1$
                                        + extension.getNamespaceIdentifier() + " extension-point=" //$NON-NLS-1$
                                        + getExtensionPointID());
                    menuDescriptor.addSubItem(new SeparatorDescriptor(name));
                } else if (ELEMENT_NAME_GROUP_MARKER.equals(child.getName())) {
                    String name = child.getAttribute(ATTRIBUTE_NAME_GROUP_MARKER_NAME);
                    if (child.getAttribute(ATTRIBUTE_NAME_GROUP_MARKER_PATH) != null)
                        logger.warn(
                                "There is a group-marker's path specified within a menu. This path will be ignored! You should not specify a path when using a group-marker inside of a menu! plugin=" //$NON-NLS-1$
                                        + extension.getNamespaceIdentifier() + " extension-point=" //$NON-NLS-1$
                                        + getExtensionPointID());
                    menuDescriptor.addSubItem(new GroupMarkerDescriptor(name));
                } else
                    throw new IllegalArgumentException(
                            "childElement.name=\"" + child.getName() + "\" of menu \"" + id + "\" is unknown!"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            }
        } else if (elementNameAction.equals(elementName)) {
            String id = element.getAttribute(ATTRIBUTE_NAME_ACTION_ID);
            String name = element.getAttribute(ATTRIBUTE_NAME_ACTION_NAME);
            String tooltip = element.getAttribute(ATTRIBUTE_NAME_ACTION_TOOLTIP);
            String icon = element.getAttribute(ATTRIBUTE_NAME_ACTION_ICON);
            String disabledIcon = element.getAttribute(ATTRIBUTE_NAME_ACTION_DISABLED_ICON);
            String hoverIcon = element.getAttribute(ATTRIBUTE_NAME_ACTION_HOVER_ICON);
            String menubarPath = element.getAttribute(ATTRIBUTE_NAME_ACTION_MENUBAR_PATH);
            String toolbarPath = element.getAttribute(ATTRIBUTE_NAME_ACTION_TOOLBAR_PATH);
            String contextmenuPath = element.getAttribute(ATTRIBUTE_NAME_ACTION_CONTEXTMENU_PATH);
            String visible = element.getAttribute(ATTRIBUTE_NAME_ACTION_VISIBLE);
            String visibleInMenubar = element.getAttribute(ATTRIBUTE_NAME_ACTION_VISIBLE_IN_MENUBAR);
            String visibleInToolbar = element.getAttribute(ATTRIBUTE_NAME_ACTION_VISIBLE_IN_TOOLBAR);
            String visibleInContextmenu = element.getAttribute(ATTRIBUTE_NAME_ACTION_VISIBLE_IN_CONTEXTMENU);

            String actionDefinitionId = element.getAttribute(ATTRIBUTE_NAME_ACTION_ACTION_DEFINITION_ID);

            Object actionOrContribution = createActionOrContributionItem(extension, element);

            IAction action = null;
            IXContributionItem contributionItem = null;
            if (actionOrContribution instanceof IAction) {
                action = (IAction) actionOrContribution;

                action.setId(id);
                action.setText(name);
                action.setToolTipText(tooltip);
                action.setEnabled(false);

                if (icon != null && !"".equals(icon)) //$NON-NLS-1$
                    action.setImageDescriptor(ImageDescriptor
                            .createFromURL(Platform.getBundle(extension.getNamespaceIdentifier()).getEntry(icon)));

                if (disabledIcon != null && !"".equals(disabledIcon)) //$NON-NLS-1$
                    action.setDisabledImageDescriptor(ImageDescriptor.createFromURL(
                            Platform.getBundle(extension.getNamespaceIdentifier()).getEntry(disabledIcon)));

                if (hoverIcon != null && !"".equals(hoverIcon)) //$NON-NLS-1$
                    action.setHoverImageDescriptor(ImageDescriptor.createFromURL(
                            Platform.getBundle(extension.getNamespaceIdentifier()).getEntry(hoverIcon)));

                if (actionDefinitionId != null && !actionDefinitionId.isEmpty())
                    action.setActionDefinitionId(actionDefinitionId);

                initAction(action, extension, element);
            } else if (actionOrContribution instanceof IXContributionItem) {
                contributionItem = (IXContributionItem) actionOrContribution;
                contributionItem.setId(id);
                initContributionItem(contributionItem, extension, element);
                //            if (!id.equals(contributionItem.getId()))
                //            throw new EPProcessorException("Your IContribution, which you created for the ActionDescriptor defined in extension " + extension.getNamespace() + " with id=" + id + " does have the wrong id (\"" + contributionItem.getId() + "\") set! Check your method " + this.getClass().getName() + ".createActionOrContributionItem(...)!");
            } else
                throw new EPProcessorException("Action class defined in extension " //$NON-NLS-1$
                        + extension.getNamespaceIdentifier() + " with id=" + id //$NON-NLS-1$
                        + " does neither implement IAction nor IXContributionItem! It must implement ONE of them!"); //$NON-NLS-1$

            ActionDescriptor actionDescriptor = createActionDescriptor();
            actionDescriptor.init(action, contributionItem, menubarPath, toolbarPath, contextmenuPath,
                    parseBooleanAcceptingNull(visible, true), parseBooleanAcceptingNull(visibleInMenubar, true),
                    parseBooleanAcceptingNull(visibleInToolbar, true),
                    parseBooleanAcceptingNull(visibleInContextmenu, true), id);
            //         actionDescriptor.setGroupMarkerName(groupMarkerName);
            if (initActionDescriptor(actionDescriptor, extension, element)) {
                ActionDescriptor oldActionDescriptor = actionDescriptorsByID.put(id, actionDescriptor);
                if (oldActionDescriptor != null) {
                    menuRaw.remove(oldActionDescriptor);
                }

                menuRaw.add(actionDescriptor);
            }
        } else
            throw new IllegalArgumentException("element.name=\"" + elementName + "\" unknown!"); //$NON-NLS-1$ //$NON-NLS-2$
    }

    protected static boolean parseBooleanAcceptingNull(String s, boolean defaultVal) {
        return s == null || "".equals(s) ? defaultVal : Boolean.parseBoolean(s); //$NON-NLS-1$
    }

    public Collection<ActionDescriptor> getActionDescriptors() {
        return actionDescriptorsByID.values();
    }

    public ActionDescriptor getActionDescriptor(String actionID, boolean throwExceptionIfNotFound) {
        ActionDescriptor descriptor = actionDescriptorsByID.get(actionID);

        if (throwExceptionIfNotFound && descriptor == null)
            throw new IllegalArgumentException("No ActionDescriptor known for actionID=" + actionID); //$NON-NLS-1$

        return descriptor;
    }

    //   @Override
    //   protected Object clone() {
    //      AbstractActionRegistry clone;
    //      try {
    //         clone = (AbstractActionRegistry) super.clone();
    //      } catch (CloneNotSupportedException e) {
    //         throw new RuntimeException(e); // we do implement Cloneable, hence this exception should never be thrown.
    //      }
    //
    //      clone.menuRaw = new ArrayList<ItemDescriptor>(this.menuRaw.size());
    //      for (ItemDescriptor itemDescriptor : this.menuRaw) {
    //
    //      }
    //
    //      // we don't want the IActions to be cloned multiple times, hence we clear the menuSorted and recreate it from menuRaw
    //      clone.contributionManagerKind2menuSorted = new HashMap<ContributionManagerKind, List<ItemDescriptor>>();
    //
    //      return clone;
    //   }
}