org.eclipse.wst.sse.ui.internal.ExtendedEditorActionBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wst.sse.ui.internal.ExtendedEditorActionBuilder.java

Source

/*******************************************************************************
 * Copyright (c) 2001, 2007 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     
 *******************************************************************************/
package org.eclipse.wst.sse.ui.internal;

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

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.swt.graphics.Point;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IKeyBindingService;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.part.MultiPageEditorSite;
import org.eclipse.ui.texteditor.IUpdate;
import org.eclipse.wst.sse.ui.internal.extension.ActionDescriptor;
import org.eclipse.wst.sse.ui.internal.extension.IExtendedEditorActionProxyForDelayLoading;
import org.eclipse.wst.sse.ui.internal.extension.RegistryReader;
import org.eclipse.wst.sse.ui.internal.provisional.extensions.ISourceEditingTextTools;

/**
 * This class reads the registry for extensions that plug into 'editorActions'
 * extension point.
 */

public class ExtendedEditorActionBuilder extends RegistryReader {

    public class ExtendedContributor implements IExtendedContributor, IMenuListener {
        private IExtendedSimpleEditor activeExtendedEditor = null;

        private List cache;
        private Map map = new HashMap();
        private IMenuManager menuBar = null;

        private Set menus = new HashSet();

        public ExtendedContributor(List cache) {
            this.cache = cache;
        }

        private IExtendedSimpleEditor computeExtendedEditor(final IEditorPart editor) {
            IExtendedSimpleEditor simpleEditor = null;
            if (editor instanceof IExtendedSimpleEditor) {
                simpleEditor = (IExtendedSimpleEditor) editor;
            }
            if (editor != null && simpleEditor == null) {
                final ISourceEditingTextTools tools = (ISourceEditingTextTools) editor
                        .getAdapter(ISourceEditingTextTools.class);
                if (tools != null) {
                    simpleEditor = new IExtendedSimpleEditor() {
                        public int getCaretPosition() {
                            return tools.getCaretOffset();
                        }

                        public IDocument getDocument() {
                            return tools.getDocument();
                        }

                        public IEditorPart getEditorPart() {
                            return tools.getEditorPart();
                        }

                        public Point getSelectionRange() {
                            ITextSelection selection = tools.getSelection();
                            return new Point(selection.getOffset(), selection.getOffset() + selection.getLength());
                        }

                    };
                }
            }
            return simpleEditor;
        }

        public void contributeToMenu(IMenuManager menu) {
            menuBar = menu;
            long time0 = System.currentTimeMillis();
            for (int i = 0; i < cache.size(); i++) {
                Object obj = cache.get(i);
                if (obj instanceof IConfigurationElement) {
                    IConfigurationElement menuElement = (IConfigurationElement) obj;
                    if ((menuElement.getName()).equals(TAG_MENU)) {
                        contributeMenu(menuElement, menu, true);
                        if (debugMenu)
                            System.out.println(getClass().getName() + "#contributeToMenu() added: " //$NON-NLS-1$
                                    + menuElement.getAttribute(ATT_ID));
                    }
                } else if (obj instanceof ActionDescriptor) {
                    try {
                        ActionDescriptor ad = (ActionDescriptor) obj;
                        IMenuManager mm = contributeMenuAction(ad, menu, true, false);
                        if (mm != null) {
                            map.put(ad.getContributionItem(), mm);
                            mm.addMenuListener(this);
                            menus.add(mm);
                            if (debugMenu)
                                System.out
                                        .println(getClass().getName() + "#contributeToMenu() added: " + ad.getId()); //$NON-NLS-1$
                        }
                    } catch (Exception e) {
                        Logger.logException("contributing to menu", e); //$NON-NLS-1$
                    }
                }
            }
            if (debugContributeTime)
                System.out.println(getClass().getName() + "#contributeToMenu(): ran in " //$NON-NLS-1$
                        + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$
        }

        public void contributeToPopupMenu(IMenuManager menu) {
            long time0 = System.currentTimeMillis();
            for (int i = 0; i < cache.size(); i++) {
                Object obj = cache.get(i);
                if (obj instanceof IConfigurationElement) {
                    IConfigurationElement menuElement = (IConfigurationElement) obj;
                    if ((menuElement.getName()).equals(TAG_POPUPMENU)) {
                        contributeMenu(menuElement, menu, true);
                    }
                } else if (obj instanceof ActionDescriptor) {
                    try {
                        ActionDescriptor ad = (ActionDescriptor) obj;
                        IAction a = ad.getAction();
                        if (a instanceof IExtendedEditorAction) {
                            // uncaught exceptions could cause the menu to not
                            // be shown
                            try {
                                if (((ad.getPopupMenuPath() != null) || (ad.getPopupMenuGroup() != null))
                                        && (a instanceof IExtendedEditorActionProxyForDelayLoading)) {
                                    ((IExtendedEditorActionProxyForDelayLoading) a).realize();
                                }

                                IExtendedEditorAction eea = (IExtendedEditorAction) a;
                                eea.setActiveExtendedEditor(activeExtendedEditor);
                                eea.update();
                                if (eea.isVisible()) {
                                    IMenuManager parent = contributeMenuAction(ad, menu, true, true);
                                    if (debugPopup && parent != null)
                                        System.out.println(getClass().getName() + "#contributeToPopupMenu() added: " //$NON-NLS-1$
                                                + ad.getId());
                                }
                            } catch (Exception e) {
                                Logger.logException(e);
                            }

                        } else {
                            IMenuManager parent = contributeMenuAction(ad, menu, true, true);
                            if (debugPopup && parent != null)
                                System.out.println(
                                        getClass().getName() + "#contributeToPopupMenu() added: " + ad.getId()); //$NON-NLS-1$
                        }
                    } catch (Exception e) {
                        Logger.logException("contributing to popup", e); //$NON-NLS-1$
                    }
                }
            }
            if (debugContributeTime)
                System.out.println(getClass().getName() + "#contributeToPopupMenu(): ran in " //$NON-NLS-1$
                        + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$
        }

        public void contributeToStatusLine(IStatusLineManager manager) {
            // nothing from here
        }

        public void contributeToToolBar(IToolBarManager manager) {
            long time0 = System.currentTimeMillis();
            for (int i = 0; i < cache.size(); i++) {
                Object obj = cache.get(i);
                if (obj instanceof ActionDescriptor) {
                    try {
                        ActionDescriptor ad = (ActionDescriptor) obj;
                        IAction a = ad.getAction();
                        if (a instanceof IExtendedEditorAction) {
                            if (((ad.getToolbarPath() != null) || (ad.getToolbarGroup() != null))
                                    && (a instanceof IExtendedEditorActionProxyForDelayLoading)) {
                                ((IExtendedEditorActionProxyForDelayLoading) a).realize();
                            }
                            IExtendedEditorAction eea = (IExtendedEditorAction) a;
                            eea.setActiveExtendedEditor(activeExtendedEditor);
                            eea.update();
                            if (eea.isVisible()) {
                                boolean contributed = contributeToolbarAction(ad, manager, true);
                                if (debugToolbar && contributed)
                                    System.out.println(
                                            getClass().getName() + "#contributeToToolBar() added: " + ad.getId()); //$NON-NLS-1$
                            } else {
                                if (debugToolbar)
                                    System.out.println(getClass().getName() + "#contributeToToolBar(): [skipped] " //$NON-NLS-1$
                                            + ad.getId());
                            }
                        } else {
                            boolean contributed = contributeToolbarAction(ad, manager, true);
                            if (debugToolbar && contributed)
                                System.out.println(
                                        getClass().getName() + "#contributeToToolBar() added: " + ad.getId()); //$NON-NLS-1$
                        }
                    } catch (Exception e) {
                        Logger.logException("contributing to toolbar", e); //$NON-NLS-1$
                    }
                }
            }
            if (debugContributeTime)
                System.out.println(getClass().getName() + "#contributeToToolBar(): ran in " //$NON-NLS-1$
                        + (System.currentTimeMillis() - time0) + "ms"); //$NON-NLS-1$
        }

        public void dispose() {
            Iterator it = menus.iterator();
            while (it.hasNext()) {
                Object o = it.next();
                if (o instanceof IMenuManager) {
                    ((IMenuManager) o).removeMenuListener(this);
                }
            }
        }

        public void init(IActionBars bars, IWorkbenchPage page) {
            // nothing from here
        }

        public void menuAboutToShow(IMenuManager menu) {
            // slows down the menu and interferes with it for other editors;
            // optimize on visibility
            IEditorSite site = null;
            boolean activeEditorIsVisible = false;

            if (activeExtendedEditor != null && activeExtendedEditor.getEditorPart() != null)
                site = activeExtendedEditor.getEditorPart().getEditorSite();
            if (site == null)
                return;

            // Eclipse bug 48784 - [MPE] ClassCast exception Workbench page
            // isPartVisiable for MultiPageSite
            if (site instanceof MultiPageEditorSite) {
                Object multiPageEditor = ((MultiPageEditorSite) site).getMultiPageEditor();
                activeEditorIsVisible = multiPageEditor.equals(site.getPage().getActiveEditor())
                        || multiPageEditor.equals(site.getPage().getActivePart());
            } else {
                activeEditorIsVisible = site.getWorkbenchWindow().getPartService().getActivePart()
                        .equals(activeExtendedEditor.getEditorPart());
            }
            // due to a delay class loading, don't return now
            //         if (!activeEditorIsVisible)
            //            return;

            IContributionItem[] items = menu.getItems();
            if (items == null || items.length == 0)
                return;

            for (int i = 0; i < items.length; ++i) {
                // add menu listener to submenu
                if (items[i] instanceof IMenuManager) {
                    ((IMenuManager) items[i]).addMenuListener(this);
                    menus.add(items[i]);
                }
            }

            Set keys = map.keySet();
            Iterator it = keys.iterator();
            boolean needActionContributionItemUpdate = false;
            while (it.hasNext()) {
                IContributionItem item = (IContributionItem) it.next();
                IMenuManager mm = (IMenuManager) map.get(item);
                if (menu.getId() != null && menu.getId().equals(mm.getId())
                        && item instanceof ActionContributionItem) {
                    try {
                        IAction action = ((ActionContributionItem) item).getAction();

                        if (action instanceof IExtendedEditorActionProxyForDelayLoading) {
                            IExtendedEditorActionProxyForDelayLoading eea = (IExtendedEditorActionProxyForDelayLoading) action;
                            if (eea.isBundleActive() == true && eea.isRealized() == false) {
                                eea.realize();
                                needActionContributionItemUpdate = true;
                            }
                        }

                        if (activeEditorIsVisible || needActionContributionItemUpdate) {
                            if (action instanceof IUpdate) {
                                ((IUpdate) action).update();
                            }
                        }

                        if (activeEditorIsVisible || needActionContributionItemUpdate) {
                            boolean visible = true;
                            if (action instanceof IExtendedEditorAction) {
                                visible = ((IExtendedEditorAction) action).isVisible();
                            }
                            item.setVisible(visible);
                        }

                        if (needActionContributionItemUpdate) {
                            ((ActionContributionItem) item).update();
                        }

                    } catch (Exception e) {
                        Logger.logException("updating actions", e); //$NON-NLS-1$
                    }
                }
            }
            if (activeEditorIsVisible || needActionContributionItemUpdate) {
                if (needActionContributionItemUpdate) {
                    // the action is realized so that need to update the menu w/
                    // force set to true
                    menu.update(true);
                } else {
                    menu.update(false);
                }
            }
        }

        public void setActiveEditor(IEditorPart editor) {
            activeExtendedEditor = computeExtendedEditor(editor);
            IKeyBindingService svc = (editor != null) ? editor.getEditorSite().getKeyBindingService() : null;
            for (int i = 0; i < cache.size(); i++) {
                Object obj = cache.get(i);
                if (obj instanceof ActionDescriptor) {
                    ActionDescriptor ad = (ActionDescriptor) obj;
                    try {
                        IAction action = ad.getAction();
                        if (action instanceof IExtendedEditorAction) {
                            ((IExtendedEditorAction) action).setActiveExtendedEditor(activeExtendedEditor);
                            ((IExtendedEditorAction) action).update();
                            // update visibility right now so that the menu
                            // will show/hide properly
                            if (!((IExtendedEditorAction) action).isVisible() && ad.getContributionItem() != null)
                                ad.getContributionItem().setVisible(false);
                            if (svc != null && action.getActionDefinitionId() != null) {
                                svc.registerAction(action);
                            }
                        }
                    } catch (Exception e) {
                        Logger.logException("setting active editor on actions", e); //$NON-NLS-1$
                    }
                }
            }

            if (menuBar != null && editor != null) {
                // Class clz = editor.getClass();
                // while (clz != null) {
                // if (clz.getName().equals(targetID)) {
                // contributeToMenu(menuBar);
                // break;
                // }
                // clz = clz.getSuperclass();
                // }
                if (targetIDs.contains(editor.getEditorSite().getId())) {
                    contributeToMenu(menuBar);
                }
            }

            updateToolbarActions();
        }

        public void updateToolbarActions() {
            for (int i = 0; i < cache.size(); i++) {
                Object obj = cache.get(i);
                if (obj instanceof ActionDescriptor) {
                    try {
                        ActionDescriptor ad = (ActionDescriptor) obj;
                        if (ad.getToolbarPath() != null) {
                            IAction action = ad.getAction();
                            if (action instanceof IUpdate) {
                                ((IUpdate) action).update();
                            }
                        }
                    } catch (Exception e) {
                        Logger.logException("updating toolbar actions", e); //$NON-NLS-1$
                    }
                }
            }
        }
    }

    public static final String ATT_ID = "id"; //$NON-NLS-1$
    public static final String ATT_LABEL = "label"; //$NON-NLS-1$
    public static final String ATT_NAME = "name"; //$NON-NLS-1$
    public static final String ATT_PATH = "path"; //$NON-NLS-1$

    public static final String ATT_TARGET_ID = "targetID"; //$NON-NLS-1$
    protected final static boolean debugContributeTime = "true".equalsIgnoreCase( //$NON-NLS-1$
            Platform.getDebugOption("org.eclipse.wst.sse.ui/extendededitoractionbuilder/contributetime")); //$NON-NLS-1$

    protected final static boolean debugMenu = "true".equalsIgnoreCase( //$NON-NLS-1$
            Platform.getDebugOption("org.eclipse.wst.sse.ui/extendededitoractionbuilder/debugmenu")); //$NON-NLS-1$;
    protected final static boolean debugPopup = "true".equalsIgnoreCase( //$NON-NLS-1$
            Platform.getDebugOption("org.eclipse.wst.sse.ui/extendededitoractionbuilder/debugpopup")); //$NON-NLS-1$;
    protected final static boolean debugReadTime = "true".equalsIgnoreCase( //$NON-NLS-1$
            Platform.getDebugOption("org.eclipse.wst.sse.ui/extendededitoractionbuilder/readtime")); //$NON-NLS-1$
    protected final static boolean debugToolbar = "true".equalsIgnoreCase( //$NON-NLS-1$
            Platform.getDebugOption("org.eclipse.wst.sse.ui/extendededitoractionbuilder/debugtoolbar")); //$NON-NLS-1$;

    private static final String EXTENDED_EDITOR = "extendedEditor"; //$NON-NLS-1$

    public static final String PL_EXTENDED_EDITOR_ACTIONS = "extendedEditorActions"; //$NON-NLS-1$

    public static final String PLUGIN_ID = "org.eclipse.wst.sse.ui"; //$NON-NLS-1$
    public static final String TAG_ACTION = "action"; //$NON-NLS-1$

    public static final String TAG_CONTRIBUTION_TYPE = "editorContribution"; //$NON-NLS-1$

    public static final String TAG_MENU = "menu"; //$NON-NLS-1$
    public static final String TAG_POPUPMENU = "popupmenu"; //$NON-NLS-1$
    public static final String TAG_RULERMENU = "rulermenu"; //$NON-NLS-1$
    public static final String TAG_SEPARATOR = "separator"; //$NON-NLS-1$

    protected List readingCache;

    protected String targetContributionTag;
    protected List targetIDs;

    /**
     * The constructor.
     */
    public ExtendedEditorActionBuilder() {
        super();
    }

    /**
     * Creates a menu from the information in the menu configuration element
     * and adds it into the provided menu manager. If 'appendIfMissing' is
     * true, and menu path slot is not found, it will be created and menu will
     * be added into it. Otherwise, add operation will fail.
     */
    protected void contributeMenu(IConfigurationElement menuElement, IMenuManager mng, boolean appendIfMissing) {
        // Get config data.
        String id = menuElement.getAttribute(ATT_ID);
        String label = menuElement.getAttribute(ATT_LABEL);
        String path = menuElement.getAttribute(ATT_PATH);
        if (label == null) {
            Logger.log(Logger.ERROR, "Invalid Menu Extension (label == null): " + id); //$NON-NLS-1$
            return;
        }

        // Calculate menu path and group.
        String group = null;
        if (path != null) {
            int loc = path.lastIndexOf('/');
            if (loc != -1) {
                group = path.substring(loc + 1);
                path = path.substring(0, loc);
            } else {
                // assume that path represents a slot
                // so actual path portion should be null
                group = path;
                path = null;
            }
        }

        // Find parent menu.
        IMenuManager parent = mng;
        if (path != null) {
            parent = mng.findMenuUsingPath(path);
            if (parent == null) {
                // Logger.log("Invalid Menu Extension (Path is invalid): " +
                // id);//$NON-NLS-1$
                return;
            }
            // IMenuManager.findMenuUsingPath() returns invisible menu item if
            // the manager can't find
            // the specified path and create new MenuManager for it.
            // I don't know this is a specification or bug.
            // Anyway, to ensure the menu can be visible, setVisible(true)
            // needs to be called.
            parent.setVisible(true);
        }

        // Find reference group.
        if (group == null)
            group = IWorkbenchActionConstants.MB_ADDITIONS;
        IContributionItem sep = parent.find(group);
        if (sep == null) {
            if (appendIfMissing)
                parent.add(new Separator(group));
            else {
                Logger.log(Logger.ERROR, "Invalid Menu Extension (Group is invalid): " + id); //$NON-NLS-1$
                return;
            }
        }

        // If the menu does not exist create it.
        IMenuManager newMenu = parent.findMenuUsingPath(id);
        if (newMenu == null)
            newMenu = new MenuManager(label, id);

        // Create separators.
        IConfigurationElement[] children = menuElement.getChildren(TAG_SEPARATOR);
        for (int i = 0; i < children.length; i++) {
            contributeSeparator(newMenu, children[i]);
        }

        // Add new menu
        try {
            parent.insertAfter(group, newMenu);
        } catch (IllegalArgumentException e) {
            Logger.log(Logger.ERROR, "Invalid Menu Extension (Group is missing): " + id); //$NON-NLS-1$
        }
    }

    /**
     * Contributes action from action descriptor into the provided menu
     * manager.
     */
    protected IMenuManager contributeMenuAction(ActionDescriptor ad, IMenuManager menu, boolean appendIfMissing,
            boolean popupmenu) {
        if (ad.getContributionItem() == null || ad.getAction() == null)
            return null;

        // Get config data.
        String mpath = popupmenu ? ad.getPopupMenuPath() : ad.getMenuPath();
        String mgroup = popupmenu ? ad.getPopupMenuGroup() : ad.getMenuGroup();
        if (mpath == null && mgroup == null)
            return null;

        // Find parent menu.
        IMenuManager parent = menu;
        if (mpath != null) {
            parent = parent.findMenuUsingPath(mpath);
            if (parent == null) {
                // Logger.log("Invalid Menu Extension (Path is invalid): " +
                // ad.getId()); //$NON-NLS-1$
                return null;
            }
            // IMenuManager.findMenuUsingPath() returns invisible menu item if
            // the manager can't find
            // the specified path and create new MenuManager for it.
            // I don't know this is a specification or bug.
            // Anyway, to ensure the menu can be visible, setVisible(true)
            // needs to be called.
            parent.setVisible(true);
        }

        // First remove existing menu item
        IContributionItem item = parent.find(ad.getId());
        if (item != null) {
            parent.remove(ad.getId());
        }

        // Find reference group.
        if (mgroup == null)
            mgroup = IWorkbenchActionConstants.MB_ADDITIONS;
        IContributionItem sep = parent.find(mgroup);
        if (sep == null) {
            if (appendIfMissing)
                parent.add(sep = new Separator(mgroup));
            else {
                Logger.log(Logger.ERROR, "Invalid Menu Extension (Group is invalid): " + ad.getId()); //$NON-NLS-1$
                return null;
            }
        }

        // Add action.
        try {
            if (popupmenu) {
                // Context menu need a newly created contribution item
                if (sep != null && sep.isGroupMarker())
                    parent.appendToGroup(sep.getId(), ad.getAction());
                else
                    parent.insertAfter(mgroup, ad.getAction());
            } else {
                // Normal menu need to add existing contribution item to
                // remove it from menu listener
                if (sep != null && sep.isGroupMarker())
                    parent.appendToGroup(sep.getId(), ad.getContributionItem());
                else
                    parent.insertAfter(mgroup, ad.getContributionItem());
            }
        } catch (IllegalArgumentException e) {
            Logger.log(Logger.ERROR, "Invalid Menu Extension (Group is missing): " + ad.getId()); //$NON-NLS-1$
            parent = null;
        }

        return parent;
    }

    /**
     * Creates a named menu separator from the information in the
     * configuration element. If the separator already exists do not create a
     * second.
     */
    protected boolean contributeSeparator(IMenuManager menu, IConfigurationElement element) {
        String id = element.getAttribute(ATT_NAME);
        if (id == null || id.length() <= 0)
            return false;
        IContributionItem sep = menu.find(id);
        if (sep != null)
            return false;
        menu.add(new Separator(id));
        return true;
    }

    /**
     * Contributes action from the action descriptor into the provided tool
     * bar manager.
     */
    protected boolean contributeToolbarAction(ActionDescriptor ad, IToolBarManager toolbar,
            boolean appendIfMissing) {
        if (ad.getContributionItem() == null || ad.getAction() == null)
            return false;

        // Get config data.
        String tpath = ad.getToolbarPath();
        String tgroup = ad.getToolbarGroup();
        if (tpath == null && tgroup == null)
            return false;

        // First remove existing toolbar item
        IContributionItem item = toolbar.find(ad.getId());
        if (item != null) {
            toolbar.remove(ad.getId());
        }

        // Find reference group.
        if (tgroup == null)
            tgroup = IWorkbenchActionConstants.MB_ADDITIONS;
        IContributionItem sep = toolbar.find(tgroup);
        if (sep == null) {
            if (appendIfMissing)
                toolbar.add(new Separator(tgroup));
            else {
                Logger.log(Logger.ERROR, "Invalid Toolbar Extension (Group is invalid): " + ad.getId()); //$NON-NLS-1$
                return false;
            }
        }

        // Add action to tool bar.
        try {
            if (sep != null && sep.isGroupMarker())
                toolbar.appendToGroup(sep.getId(), ad.getAction());
            else
                toolbar.insertAfter(tgroup, ad.getAction());
        } catch (IllegalArgumentException e) {
            Logger.log(Logger.ERROR, "Invalid Toolbar Extension (Group is missing): " + ad.getId()); //$NON-NLS-1$
            return false;
        }
        return true;
    }

    /**
     * This factory method returns a new ActionDescriptor for the
     * configuration element. It should be implemented by subclasses.
     */
    protected ActionDescriptor createActionDescriptor(IConfigurationElement element) {
        ActionDescriptor ad = null;
        try {
            ad = new ActionDescriptor(element);
            // these cases like "class not found" are handled
            // at lower level, so no action if formed. In that
            // case, we also don't want to form an action descriptor.
            if ((ad != null) && (ad.getAction() == null)) {
                ad = null;
            }
        } catch (Exception e) {
            Logger.traceException(EXTENDED_EDITOR, e);
            ad = null;
        }
        return ad;
    }

    /**
     * Returns the name of the part ID attribute that is expected in the
     * target extension.
     */
    protected String getTargetID(IConfigurationElement element) {
        String value = element.getAttribute(ATT_TARGET_ID);
        return value != null ? value : "???"; //$NON-NLS-1$
    }

    /**
     * Reads editor contributor if specified directly in the 'editor'
     * extension point, and all external contributions for this editor's ID
     * registered in 'editorActions' extension point.
     */
    public IExtendedContributor readActionExtensions(String editorId) {
        return readActionExtensions(new String[] { editorId });
    }

    /**
     * Reads editor contributor if specified directly in the 'editor'
     * extension point, and all external contributions for this editor's ID
     * registered in 'editorActions' extension point.
     */
    public IExtendedContributor readActionExtensions(String[] ids) {
        long time0 = System.currentTimeMillis();
        ExtendedContributor ext = null;
        readContributions(ids, TAG_CONTRIBUTION_TYPE, PL_EXTENDED_EDITOR_ACTIONS);
        if (debugReadTime) {
            String idlist = ""; //$NON-NLS-1$
            if (ids.length > 0) {
                for (int i = 0; i < ids.length; i++) {
                    idlist += ids[i];
                    if (i < ids.length - 1)
                        idlist += ","; //$NON-NLS-1$
                }
            }
            System.out.println(getClass().getName() + "#readActionExtensions(" + idlist + "): read in " //$NON-NLS-1$//$NON-NLS-2$
                    + (System.currentTimeMillis() - time0) + "ms [" //$NON-NLS-1$
                    + (readingCache != null ? readingCache.size() : 0) + " contributions]"); //$NON-NLS-1$
        }
        if (readingCache != null) {
            ext = new ExtendedContributor(readingCache);
            readingCache = null;
        }
        return ext;
    }

    /**
     * Reads the contributions from the registry for the provided workbench
     * part and the provided extension point IDs.
     */
    protected void readContributions(String[] ids, String tag, String extensionPoint) {
        readingCache = null;
        targetIDs = Arrays.asList(ids);
        targetContributionTag = tag;
        IExtensionRegistry registry = Platform.getExtensionRegistry();
        readRegistry(registry, PLUGIN_ID, extensionPoint);
    }

    /**
     * Implements abstract method to handle the provided XML element in the
     * registry.
     */
    protected boolean readElement(IConfigurationElement element) {
        String tag = element.getName();
        if (tag.equals(targetContributionTag)) {
            String id = getTargetID(element);
            if (id == null || !targetIDs.contains(id)) {
                // This is not of interest to us - don't go deeper
                return true;
            }
        } else if (tag.equals(TAG_MENU)) {
            if (readingCache == null)
                readingCache = new ArrayList();
            readingCache.add(element);
            return true; // just cache the element - don't go into it
        } else if (tag.equals(TAG_POPUPMENU)) {
            if (readingCache == null)
                readingCache = new ArrayList();
            readingCache.add(element);
            return true; // just cache the element - don't go into it
        } else if (tag.equals(TAG_RULERMENU)) {
            if (readingCache == null)
                readingCache = new ArrayList();
            readingCache.add(element);
            return true; // just cache the element - don't go into it
        } else if (tag.equals(TAG_ACTION)) {
            if (readingCache == null)
                readingCache = new ArrayList();
            ActionDescriptor ad = createActionDescriptor(element);
            if (ad != null)
                readingCache.add(ad);
            return true; // just cache the action - don't go into
        } else {
            return false;
        }

        readElementChildren(element);
        return true;
    }
}