org.eclipse.pde.internal.ui.editor.plugin.ExtensionsSection.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.pde.internal.ui.editor.plugin.ExtensionsSection.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2014 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
 *     Peter Friese <peter.friese@gentleware.com> - bug 194529, bug 196867
 *     Sascha Becher <s.becher@qualitype.com> - bug 360894
 *     Alexander Kurtakov <akurtako@redhat.com> - bug 415649
 *     Brian de Alwis (MTI) - bug 429420
 *******************************************************************************/
package org.eclipse.pde.internal.ui.editor.plugin;

import java.util.*;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.IModelChangedEvent;
import org.eclipse.pde.core.plugin.*;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase;
import org.eclipse.pde.internal.core.ischema.*;
import org.eclipse.pde.internal.core.schema.SchemaRegistry;
import org.eclipse.pde.internal.core.text.IDocumentElementNode;
import org.eclipse.pde.internal.core.text.plugin.PluginBaseNode;
import org.eclipse.pde.internal.core.text.plugin.PluginExtensionNode;
import org.eclipse.pde.internal.ui.*;
import org.eclipse.pde.internal.ui.editor.*;
import org.eclipse.pde.internal.ui.editor.actions.*;
import org.eclipse.pde.internal.ui.editor.contentassist.XMLElementProposalComputer;
import org.eclipse.pde.internal.ui.elements.DefaultContentProvider;
import org.eclipse.pde.internal.ui.parts.TreePart;
import org.eclipse.pde.internal.ui.search.ExtensionsPatternFilter;
import org.eclipse.pde.internal.ui.search.PluginSearchActionGroup;
import org.eclipse.pde.internal.ui.util.*;
import org.eclipse.pde.internal.ui.wizards.extension.ExtensionEditorWizard;
import org.eclipse.pde.internal.ui.wizards.extension.NewExtensionWizard;
import org.eclipse.pde.ui.IExtensionEditorWizard;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.BidiUtil;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.ui.progress.WorkbenchJob;

public class ExtensionsSection extends TreeSection implements IPropertyChangeListener {
    private static final int REFRESHJOB_DELAY_TIME = 1200; // milliseconds to wait
    private static final int ACCELERATED_SCROLLING = 15; // lines to skip

    // All constants changed for removal of search button
    private static final int BUTTON_MOVE_DOWN = 4;
    private static final int BUTTON_MOVE_UP = 3;
    private static final int BUTTON_EDIT = 2;
    private static final int BUTTON_REMOVE = 1;
    private static final int BUTTON_ADD = 0;
    private TreeViewer fExtensionTree;
    private Image fExtensionImage;
    private Image fGenericElementImage;
    private FormFilteredTree fFilteredTree;
    private ExtensionsPatternFilter fPatternFilter;
    private SchemaRegistry fSchemaRegistry;
    private Hashtable<String, ArrayList<IConfigurationElement>> fEditorWizards;
    private SortAction fSortAction;
    private CollapseAction fCollapseAction;
    private ToggleExpandStateAction fExpandAction;
    private FilterRelatedExtensionsAction fFilterRelatedAction;
    private boolean fBypassFilterDelay = false;

    /**
     * <code>label, name, class, id, commandId, property, activityId, attribute, value</code>
     * <br>
     * While adding elements to the array at the end is possible without concern, changing 
     * previous elements requires to refactor occurrences with indexed access to the array.
     */
    // TODO common label properties might be configured through preferences
    public static final String[] COMMON_LABEL_ATTRIBUTES = { "label", //$NON-NLS-1$
            "name", "locationURI", "class", "id", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
            "commandId", "property", "activityId", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            "attribute", "value" }; //$NON-NLS-1$ //$NON-NLS-2$

    private static final String[] VALID_IMAGE_TYPES = { "png", "bmp", "ico", "gif", "jpg", "tiff" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
    private static final String MENU_NEW_ID = "NewMenu"; //$NON-NLS-1$

    class ExtensionContentProvider extends DefaultContentProvider implements ITreeContentProvider {
        public Object[] getChildren(Object parent) {
            Object[] children = null;
            if (parent instanceof IPluginBase)
                children = ((IPluginBase) parent).getExtensions();
            else if (parent instanceof IPluginExtension) {
                children = ((IPluginExtension) parent).getChildren();
            } else if (parent instanceof IPluginElement) {
                children = ((IPluginElement) parent).getChildren();
            }
            if (children == null)
                children = new Object[0];
            return children;
        }

        public boolean hasChildren(Object parent) {
            return getChildren(parent).length > 0;
        }

        public Object getParent(Object child) {
            if (child instanceof IPluginExtension) {
                return ((IPluginModelBase) getPage().getModel()).getPluginBase();
            }
            if (child instanceof IPluginObject)
                return ((IPluginObject) child).getParent();
            return null;
        }

        public Object[] getElements(Object parent) {
            return getChildren(parent);
        }
    }

    class ExtensionLabelProvider extends LabelProvider implements IFontProvider {
        @Override
        public String getText(Object obj) {
            return resolveObjectName(obj);
        }

        @Override
        public Image getImage(Object obj) {
            return resolveObjectImage(obj);
        }

        public Font getFont(Object element) {
            if (fFilteredTree.isFiltered() && fPatternFilter.getMatchingLeafs().contains(element)) {
                return JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT);
            }
            return null;
        }
    }

    public ExtensionsSection(PDEFormPage page, Composite parent) {
        super(page, parent, Section.DESCRIPTION, new String[] {
                /*PDEUIMessages.Actions_search_targetplatform,*/PDEUIMessages.ManifestEditor_DetailExtension_new,
                PDEUIMessages.ManifestEditor_DetailExtension_remove,
                PDEUIMessages.ManifestEditor_DetailExtension_edit, PDEUIMessages.ManifestEditor_DetailExtension_up,
                PDEUIMessages.ManifestEditor_DetailExtension_down });
        fHandleDefaultButton = false;
    }

    private static void addItemsForExtensionWithSchema(MenuManager menu, IPluginExtension extension,
            IPluginParent parent) {
        ISchema schema = getSchema(extension);
        // Bug 213457 - look up elements based on the schema in which the parent is found
        ISchemaElement elementInfo = null;
        if (schema.getIncludes().length == 0 || parent == extension) {
            String tagName = (parent == extension ? "extension" : parent.getName()); //$NON-NLS-1$
            elementInfo = schema.findElement(tagName);
        } else {
            Stack<String> stack = new Stack<String>();
            IPluginParent parentParent = parent;
            while (parentParent != extension && parentParent != null) {
                stack.push(parentParent.getName());
                parentParent = (IPluginParent) parentParent.getParent();
            }
            while (!stack.isEmpty()) {
                elementInfo = schema.findElement(stack.pop());
                schema = elementInfo.getSchema();
            }
        }

        if ((elementInfo != null) && (elementInfo.getType() instanceof ISchemaComplexType)
                && (parent instanceof IDocumentElementNode)) {
            // We have a schema complex type.  Either the element has attributes
            // or the element has children.
            // Generate the list of element proposals
            TreeSet<ISchemaElement> elementSet = XMLElementProposalComputer.computeElementProposal(elementInfo,
                    (IDocumentElementNode) parent);

            // Create a corresponding menu entry for each element proposal;
            // first add non-deprecated elements, then add deprecated elements
            for (ISchemaElement element : elementSet) {
                if (!element.isDeprecated()) {
                    Action action = new NewElementAction(element, parent);
                    menu.add(action);
                }
            }
            menu.add(new Separator());
            for (ISchemaElement element : elementSet) {
                if (element.isDeprecated()) {
                    Action action = new NewElementAction(element, parent);
                    menu.add(action);
                }
            }
        }
    }

    /**
     * @param parent
     */
    private static ISchema getSchema(IPluginParent parent) {
        if (parent instanceof IPluginExtension) {
            return getSchema((IPluginExtension) parent);
        } else if (parent instanceof IPluginElement) {
            return getSchema((IPluginElement) parent);
        } else {
            return null;
        }
    }

    private static ISchema getSchema(IPluginExtension extension) {
        String point = extension.getPoint();
        SchemaRegistry registry = PDECore.getDefault().getSchemaRegistry();
        return registry.getSchema(point);
    }

    /**
     * @param element
     */
    static ISchemaElement getSchemaElement(IPluginElement element) {
        ISchema schema = getSchema(element);
        if (schema != null) {
            return schema.findElement(element.getName());
        }
        return null;
    }

    /**
     * @param element
     */
    private static ISchema getSchema(IPluginElement element) {
        IPluginObject parent = element.getParent();
        while (parent != null && !(parent instanceof IPluginExtension)) {
            parent = parent.getParent();
        }
        if (parent != null) {
            return getSchema((IPluginExtension) parent);
        }
        return null;
    }

    @Override
    public void createClient(Section section, FormToolkit toolkit) {
        initializeImages();
        Composite container = createClientContainer(section, 2, toolkit);
        TreePart treePart = getTreePart();
        createViewerPartControl(container, SWT.MULTI | SWT.BORDER, 2, toolkit);

        fExtensionTree = treePart.getTreeViewer();
        fExtensionTree.setContentProvider(new ExtensionContentProvider());
        fExtensionTree.setLabelProvider(new ExtensionLabelProvider());
        toolkit.paintBordersFor(container);
        section.setClient(container);
        section.setDescription(PDEUIMessages.ExtensionsSection_sectionDescExtensionsMaster);
        // See Bug # 160554: Set text before text client
        section.setText(PDEUIMessages.ManifestEditor_DetailExtension_title);
        initialize((IPluginModelBase) getPage().getModel());
        createSectionToolbar(section, toolkit);
        // accelerated tree scrolling enabled
        fFilteredTree.addMouseWheelListener(
                new AcceleratedTreeScrolling(fExtensionTree.getTree(), ACCELERATED_SCROLLING));
        toolkit.paintBordersFor(fFilteredTree.getParent());
        // Create the adapted listener for the filter entry field
        fFilteredTree.createUIListenerEntryFilter(this);
        final Text filterText = fFilteredTree.getFilterControl();
        if (filterText != null) {
            filterText.addModifyListener(new ModifyListener() {
                public void modifyText(ModifyEvent e) {
                    StructuredViewer viewer = getStructuredViewerPart().getViewer();
                    IStructuredSelection ssel = (IStructuredSelection) viewer.getSelection();
                    updateButtons(ssel.size() != 1 ? null : ssel);
                }
            });
        }
    }

    /**
     * @param section
     * @param toolkit
     */
    private void createSectionToolbar(Section section, FormToolkit toolkit) {
        ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
        ToolBar toolbar = toolBarManager.createControl(section);
        final Cursor handCursor = Display.getCurrent().getSystemCursor(SWT.CURSOR_HAND);
        toolbar.setCursor(handCursor);
        // Add sort action to the tool bar
        fSortAction = new SortAction(fExtensionTree, PDEUIMessages.ExtensionsPage_sortAlpha, null, null, this) {
            @Override
            public void run() {
                Object[] expanded = fFilteredTree.getViewer().getVisibleExpandedElements();
                try {
                    fFilteredTree.setRedraw(false);
                    super.run();
                    // bugfix: retain tree expand state after sort action
                    fFilteredTree.getViewer().setExpandedElements(expanded);
                } finally {
                    fFilteredTree.setRedraw(true);
                }
            }
        };
        toolBarManager.add(fSortAction);
        // Add expand selected leafs action to the toolbar
        fExpandAction = new ToggleExpandStateAction(fFilteredTree, fExtensionTree);
        toolBarManager.add(fExpandAction);
        // Add collapse action to the tool bar
        fCollapseAction = new CollapseAction(fExtensionTree, PDEUIMessages.ExtensionsPage_collapseAll);
        toolBarManager.add(fCollapseAction);

        // Create filter action for context menu and global find keybinding
        fFilterRelatedAction = new FilterRelatedExtensionsAction(fExtensionTree, fFilteredTree, this);

        toolBarManager.update(true);

        section.setTextClient(toolbar);
    }

    @Override
    protected void selectionChanged(IStructuredSelection selection) {
        getPage().getPDEEditor().setSelection(selection);
        updateButtons(selection);
        getTreePart().getButton(BUTTON_EDIT).setVisible(isSelectionEditable(selection));
    }

    @Override
    protected void buttonSelected(int index) {
        switch (index) {
        case BUTTON_ADD:
            handleNew();
            break;
        case BUTTON_REMOVE:
            handleDelete();
            break;
        case BUTTON_EDIT:
            handleEdit();
            break;
        case BUTTON_MOVE_UP:
            handleMove(true);
            break;
        case BUTTON_MOVE_DOWN:
            handleMove(false);
            break;
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.ui.forms.AbstractFormPart#dispose()
     */
    @Override
    public void dispose() {
        // Explicitly call the dispose method on the extensions tree
        if (fFilteredTree != null) {
            fFilteredTree.dispose();
        }
        fEditorWizards = null;
        IPluginModelBase model = (IPluginModelBase) getPage().getPDEEditor().getAggregateModel();
        if (model != null)
            model.removeModelChangedListener(this);
        super.dispose();
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.PDESection#doGlobalAction(java.lang.String)
     */
    @Override
    public boolean doGlobalAction(String actionId) {
        if (actionId.equals(ActionFactory.FIND.getId()) && fFilterRelatedAction != null) {
            fFilterRelatedAction.run();
            return true;
        }
        if (!isEditable()) {
            return false;
        }
        if (actionId.equals(ActionFactory.DELETE.getId())) {
            handleDelete();
            return true;
        }
        if (actionId.equals(ActionFactory.CUT.getId())) {
            if (isSingleSelection()) {
                handleDelete();
            }
            return true;
        }
        if (actionId.equals(ActionFactory.PASTE.getId())) {
            if (isSingleSelection()) {
                doPaste();
            }
            return true;
        }
        if (actionId.equals(ActionFactory.SELECT_ALL.getId())) {
            handleSelectAll();
            return true;
        }

        return false;
    }

    @Override
    public boolean setFormInput(Object object) {
        if (object instanceof IPluginExtension || object instanceof IPluginElement) {
            fExtensionTree.setSelection(new StructuredSelection(object), true);
            return true;
        }
        return false;
    }

    @Override
    protected void fillContextMenu(IMenuManager manager) {
        ISelection selection = fExtensionTree.getSelection();
        final IStructuredSelection ssel = (IStructuredSelection) selection;
        if (ssel.size() == 1) {
            Object object = ssel.getFirstElement();
            if (object instanceof IPluginParent) {
                IPluginParent parent = (IPluginParent) object;
                if (parent.getModel().getUnderlyingResource() != null) {
                    boolean removeEnabled = !fFilteredTree.isFiltered() || isRemoveEnabled(ssel);
                    fillContextMenu(getPage(), parent, manager, false, removeEnabled);
                    manager.add(new Separator());
                }
            }
            manager.add(new Separator());
            if (object instanceof IPluginExtension) {
                PluginSearchActionGroup actionGroup = new PluginSearchActionGroup();
                actionGroup.setContext(new ActionContext(selection));
                actionGroup.fillContextMenu(manager);
                manager.add(new Separator());
            }
        } else if (ssel.size() > 1) {
            // Add delete action
            Action delAction = new Action() {
                @Override
                public ImageDescriptor getImageDescriptor() {
                    return PDEPluginImages.DESC_DELETE;
                }

                @Override
                public ImageDescriptor getDisabledImageDescriptor() {
                    return PDEPluginImages.DESC_REMOVE_ATT_DISABLED;
                }

                @Override
                public void run() {
                    handleDelete();
                }
            };
            delAction.setText(PDEUIMessages.ExtensionsSection_Remove);
            manager.add(delAction);
            manager.add(new Separator());
            delAction.setEnabled(isEditable() && isRemoveEnabled(ssel));
        }
        if (ssel.size() > 0) {
            if (ExtensionsFilterUtil.isFilterRelatedEnabled(ssel)) {
                manager.add(fFilterRelatedAction);
            }
        }
        if (fFilteredTree.isFiltered()) {
            // Add action to clear the current filtering
            manager.add(new Action() {
                @Override
                public String getText() {
                    return PDEUIMessages.ShowAllExtensionsAction_label;
                }

                @Override
                public void run() {
                    Text filterText = fFilteredTree.getFilterControl();
                    setBypassFilterDelay(true);
                    filterText.setText(""); //$NON-NLS-1$
                }

            });
        }

        manager.add(new Separator());
        if (ssel.size() < 2) { // only cut things when the selection is one
            getPage().getPDEEditor().getContributor().addClipboardActions(manager);
        }
        getPage().getPDEEditor().getContributor().contextMenuAboutToShow(manager, false);
        this.fFilteredTree.update();
    }

    static IMenuManager fillContextMenu(PDEFormPage page, final IPluginParent parent, IMenuManager manager) {
        return fillContextMenu(page, parent, manager, false);
    }

    static IMenuManager fillContextMenu(PDEFormPage page, final IPluginParent parent, IMenuManager manager,
            boolean addSiblingItems) {
        return fillContextMenu(page, parent, manager, addSiblingItems, true);
    }

    static IMenuManager fillContextMenu(PDEFormPage page, final IPluginParent parent, IMenuManager manager,
            boolean addSiblingItems, boolean fullMenu) {
        MenuManager menu = new MenuManager(PDEUIMessages.Menus_new_label, MENU_NEW_ID);
        IPluginExtension extension = getExtension(parent);
        ISchema schema = getSchema(extension);
        if (schema == null) {
            menu.add(new NewElementAction(null, parent));
        } else {
            addItemsForExtensionWithSchema(menu, extension, parent);
            if (addSiblingItems) {
                IPluginObject parentsParent = parent.getParent();
                if (!(parentsParent instanceof IPluginExtension)) {
                    IPluginParent pparent = (IPluginParent) parentsParent;
                    menu.add(new Separator());
                    addItemsForExtensionWithSchema(menu, extension, pparent);
                }
            }
        }
        manager.add(menu);
        manager.add(new Separator());
        if (fullMenu) {
            Action deleteAction = new Action(PDEUIMessages.ExtensionsSection_Remove) {
                @Override
                public void run() {
                    try {
                        IPluginObject parentsParent = parent.getParent();
                        if (parent instanceof IPluginExtension) {
                            IPluginBase plugin = (IPluginBase) parentsParent;
                            plugin.remove((IPluginExtension) parent);
                        } else {
                            IPluginParent parentElement = (IPluginParent) parent.getParent();
                            parentElement.remove(parent);
                        }
                    } catch (CoreException e) {
                    }
                }

                @Override
                public ImageDescriptor getImageDescriptor() {
                    return PDEPluginImages.DESC_DELETE;
                }

                @Override
                public ImageDescriptor getDisabledImageDescriptor() {
                    return PDEPluginImages.DESC_REMOVE_ATT_DISABLED;
                }
            };
            deleteAction.setEnabled(page.getModel().isEditable());
            manager.add(deleteAction);
        }
        return menu;
    }

    static IPluginExtension getExtension(IPluginParent parent) {
        while (parent != null && !(parent instanceof IPluginExtension)) {
            parent = (IPluginParent) parent.getParent();
        }
        return (IPluginExtension) parent;
    }

    private void handleDelete() {
        IStructuredSelection sel = (IStructuredSelection) fExtensionTree.getSelection();
        if (sel.isEmpty())
            return;
        for (Iterator<?> iter = sel.iterator(); iter.hasNext();) {
            IPluginObject object = (IPluginObject) iter.next();
            try {
                IStructuredSelection newSelection = null;
                boolean sorted = fSortAction != null && fSortAction.isChecked();
                if (object instanceof IPluginElement) {
                    IPluginElement ee = (IPluginElement) object;
                    IPluginParent parent = (IPluginParent) ee.getParent();
                    if (!sorted) {
                        int index = getNewSelectionIndex(parent.getIndexOf(ee), parent.getChildCount());
                        newSelection = index == -1 ? new StructuredSelection(parent)
                                : new StructuredSelection(parent.getChildren()[index]);
                    } else {
                        IPluginObject original[] = parent.getChildren();
                        IPluginObject objects[] = new IPluginObject[original.length];
                        for (int i = 0; i < original.length; i++)
                            objects[i] = original[i];
                        fExtensionTree.getComparator().sort(fExtensionTree, objects);
                        int index = getNewSelectionIndex(getArrayIndex(objects, ee), objects.length);
                        newSelection = index == -1 ? new StructuredSelection(parent)
                                : new StructuredSelection(objects[index]);
                    }
                    parent.remove(ee);
                } else if (object instanceof IPluginExtension) {
                    IPluginExtension extension = (IPluginExtension) object;
                    IPluginBase plugin = extension.getPluginBase();
                    if (!sorted) {
                        int index = getNewSelectionIndex(plugin.getIndexOf(extension),
                                plugin.getExtensions().length);
                        if (index != -1)
                            newSelection = new StructuredSelection(plugin.getExtensions()[index]);
                    } else {
                        IPluginExtension original[] = plugin.getExtensions();
                        IPluginExtension extensions[] = new IPluginExtension[original.length];
                        for (int i = 0; i < original.length; i++)
                            extensions[i] = original[i];
                        fExtensionTree.getComparator().sort(fExtensionTree, extensions);
                        int index = getNewSelectionIndex(getArrayIndex(extensions, extension), extensions.length);
                        if (index != -1)
                            newSelection = new StructuredSelection(extensions[index]);
                    }
                    plugin.remove(extension);
                }
                if (newSelection != null)
                    fExtensionTree.setSelection(newSelection);
            } catch (CoreException e) {
                PDEPlugin.logException(e);
            }
        }
    }

    public FormFilteredTree getFormFilteredTree() {
        return fFilteredTree;
    }

    /**
     * Adds another value to filter text and a preceding separator character if necessary.
     * Empty values as well as <code>true</code> and <code>false</code> are omitted.
     * 
     * @param attributeValue Value to be trimmed and added to the filter text
     * @param clearFilterText When <code>true</code> the filter text is replaced with the attribute value, appended otherwise.
     */
    public void addAttributeToFilter(String attributeValue, boolean clearFilterText) {
        Text filterControl = fFilteredTree.getFilterControl();
        if (filterControl != null && attributeValue != null) {
            String trimmedValue = attributeValue.trim();
            if (trimmedValue.length() > 0 && !ExtensionsFilterUtil.isBoolean(trimmedValue)) {
                if (trimmedValue.startsWith("%")) {//$NON-NLS-1$
                    IPluginModelBase model = getPluginModelBase();
                    trimmedValue = ((model != null) ? model.getResourceString(trimmedValue) : trimmedValue)
                            .replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$
                }
                String filterPattern;
                if (clearFilterText) {
                    filterPattern = trimmedValue;
                } else {
                    filterPattern = filterControl.getText();
                    if (filterPattern.length() > 0 && !filterPattern.endsWith("/")) { //$NON-NLS-1$
                        filterPattern += "/"; //$NON-NLS-1$
                    }
                    filterPattern += trimmedValue;
                }
                if (filterPattern.indexOf('/') != -1) { // quote when
                    filterPattern = "\"" + filterPattern + "\""; //$NON-NLS-1$ //$NON-NLS-2$
                }
                setBypassFilterDelay(true); // force immediate job run
                filterControl.setText(filterPattern);
            }
        }
    }

    private void handleNew() {
        final IProject project = getPage().getPDEEditor().getCommonProject();
        BusyIndicator.showWhile(fExtensionTree.getTree().getDisplay(), new Runnable() {
            public void run() {
                ((ManifestEditor) getPage().getEditor()).ensurePluginContextPresence();
                NewExtensionWizard wizard = new NewExtensionWizard(project, (IPluginModelBase) getPage().getModel(),
                        (ManifestEditor) getPage().getPDEEditor()) {
                    @Override
                    public boolean performFinish() {
                        return super.performFinish();
                    }
                };
                WizardDialog dialog = new WizardDialog(PDEPlugin.getActiveWorkbenchShell(), wizard);
                dialog.create();
                SWTUtil.setDialogSize(dialog, 500, 500);
                dialog.open();
            }
        });
    }

    private void handleEdit(IConfigurationElement element, IStructuredSelection selection) {
        IProject project = getPage().getPDEEditor().getCommonProject();
        IPluginModelBase model = (IPluginModelBase) getPage().getModel();
        try {
            final IExtensionEditorWizard wizard = (IExtensionEditorWizard) element
                    .createExecutableExtension("class"); //$NON-NLS-1$
            wizard.init(project, model, selection);
            BusyIndicator.showWhile(fExtensionTree.getTree().getDisplay(), new Runnable() {
                public void run() {
                    WizardDialog dialog = new WizardDialog(PDEPlugin.getActiveWorkbenchShell(), wizard);
                    dialog.create();
                    SWTUtil.setDialogSize(dialog, 500, 500);
                    dialog.open();
                }
            });
        } catch (CoreException e) {
            PDEPlugin.logException(e);
        }
    }

    private void handleEdit() {
        final IStructuredSelection selection = (IStructuredSelection) fExtensionTree.getSelection();
        ArrayList<?> editorWizards = getEditorWizards(selection);
        if (editorWizards == null)
            return;
        if (editorWizards.size() == 1) {
            // open the wizard directly         
            handleEdit((IConfigurationElement) editorWizards.get(0), selection);
        } else {
            IProject project = getPage().getPDEEditor().getCommonProject();
            IPluginModelBase model = (IPluginModelBase) getPage().getModel();
            final ExtensionEditorWizard wizard = new ExtensionEditorWizard(project, model, selection);
            BusyIndicator.showWhile(fExtensionTree.getTree().getDisplay(), new Runnable() {
                public void run() {
                    WizardDialog dialog = new WizardDialog(PDEPlugin.getActiveWorkbenchShell(), wizard);
                    dialog.create();
                    SWTUtil.setDialogSize(dialog, 500, 500);
                    dialog.open();
                }
            });
        }
    }

    private void handleSelectAll() {
        fExtensionTree.getTree().selectAll();
        updateButtons(fFilteredTree.getViewer().getSelection());
    }

    private ArrayList<?> getEditorWizards(IStructuredSelection selection) {
        if (selection.size() != 1)
            return null;
        Object obj = selection.getFirstElement();
        String pointId = null;
        if (obj instanceof IPluginExtension) {
            pointId = ((IPluginExtension) obj).getPoint();
        } else if (obj instanceof IPluginElement) {
            IPluginObject parent = ((IPluginElement) obj).getParent();
            while (parent != null) {
                if (parent instanceof IPluginExtension) {
                    pointId = ((IPluginExtension) parent).getPoint();
                    break;
                }
                parent = parent.getParent();
            }
        }
        if (pointId == null)
            return null;
        if (fEditorWizards == null)
            loadExtensionWizards();
        return fEditorWizards.get(pointId);
    }

    private void loadExtensionWizards() {
        fEditorWizards = new Hashtable<String, ArrayList<IConfigurationElement>>();
        IConfigurationElement[] elements = Platform.getExtensionRegistry()
                .getConfigurationElementsFor("org.eclipse.pde.ui.newExtension"); //$NON-NLS-1$
        for (int i = 0; i < elements.length; i++) {
            IConfigurationElement element = elements[i];
            if (element.getName().equals("editorWizard")) { //$NON-NLS-1$
                String pointId = element.getAttribute("point"); //$NON-NLS-1$
                if (pointId == null)
                    continue;
                ArrayList<IConfigurationElement> list = fEditorWizards.get(pointId);
                if (list == null) {
                    list = new ArrayList<IConfigurationElement>();
                    fEditorWizards.put(pointId, list);
                }
                list.add(element);
            }
        }
    }

    private boolean isSelectionEditable(IStructuredSelection selection) {
        if (!getPage().getModel().isEditable())
            return false;
        return getEditorWizards(selection) != null;
    }

    public void initialize(IPluginModelBase model) {
        fExtensionTree.setInput(model.getPluginBase());
        selectFirstExtension();
        boolean editable = model.isEditable();
        TreePart treePart = getTreePart();
        treePart.setButtonEnabled(BUTTON_ADD, editable);
        treePart.setButtonEnabled(BUTTON_REMOVE, false);
        treePart.setButtonEnabled(BUTTON_EDIT, false);
        treePart.setButtonEnabled(BUTTON_MOVE_UP, false);
        treePart.setButtonEnabled(BUTTON_MOVE_DOWN, false);
        model.addModelChangedListener(this);
    }

    private void selectFirstExtension() {
        Tree tree = fExtensionTree.getTree();
        TreeItem[] items = tree.getItems();
        if (items.length == 0)
            return;
        TreeItem firstItem = items[0];
        Object obj = firstItem.getData();
        fExtensionTree.setSelection(new StructuredSelection(obj));
    }

    void fireSelection() {
        fExtensionTree.setSelection(fExtensionTree.getSelection());
    }

    public void initializeImages() {
        PDELabelProvider provider = PDEPlugin.getDefault().getLabelProvider();
        fExtensionImage = provider.get(PDEPluginImages.DESC_EXTENSION_OBJ);
        fGenericElementImage = provider.get(PDEPluginImages.DESC_GENERIC_XML_OBJ);
    }

    @Override
    public void refresh() {
        // The model changed but the editor is still open, we should try to retain expansion, selection will be retained on its own
        Object[] expanded = fExtensionTree.getExpandedElements();
        IPluginModelBase model = (IPluginModelBase) getPage().getModel();
        int[] indexPath = getTreeIndexPath(fExtensionTree.getTree());
        try {
            fExtensionTree.getControl().setRedraw(false);
            fExtensionTree.setInput(model.getPluginBase());
            fExtensionTree.setExpandedElements(expanded);

            reportMissingExtensionPointSchemas(model.getPluginBase());
            getManagedForm().fireSelectionChanged(ExtensionsSection.this, fExtensionTree.getSelection());
            super.refresh();

            if (indexPath != null) {
                // fix for Bug 371066
                revealTopItem(fExtensionTree.getTree(), indexPath);
            }
        } finally {
            fExtensionTree.getControl().setRedraw(true);
        }
    }

    private static int[] getTreeIndexPath(Tree tree) {
        int[] indexPath = null;
        if (tree != null) {
            TreeItem item = tree.getTopItem();
            int count = 1;
            while (item != null && (item = item.getParentItem()) != null) {
                count++;
            }
            indexPath = new int[count];
            int index = 0;
            item = tree.getTopItem();
            while (item != null && index < count) {
                TreeItem parent = item.getParentItem();
                if (parent != null) {
                    indexPath[index++] = parent.indexOf(item);
                } else {
                    indexPath[index++] = tree.indexOf(item);
                }
                item = parent;
            }
        }
        return indexPath;
    }

    private static void revealTopItem(Tree tree, int[] indexPath) {
        TreeItem itemFound = null;
        for (int i = indexPath.length - 1; i >= 0; i--) {
            int index = indexPath[i];
            if (itemFound != null) {
                itemFound = (itemFound.getItemCount() > index) ? itemFound.getItem(indexPath[i]) : null;
            } else if (i == indexPath.length - 1) {
                itemFound = (tree.getItemCount() > index) ? tree.getItem(indexPath[i]) : null;
            }
        }
        if (itemFound != null) {
            tree.setTopItem(itemFound);
        }
    }

    @Override
    public void modelChanged(IModelChangedEvent event) {
        if (event.getChangeType() == IModelChangedEvent.WORLD_CHANGED) {
            markStale();
            return;
        }
        Object changeObject = event.getChangedObjects()[0];
        if (changeObject instanceof IPluginBase && event.getChangeType() == IModelChangedEvent.CHANGE
                && event.getChangedProperty().equals(IExtensions.P_EXTENSION_ORDER)) {
            IStructuredSelection sel = (IStructuredSelection) fExtensionTree.getSelection();
            IPluginExtension extension = (IPluginExtension) sel.getFirstElement();
            fExtensionTree.refresh();
            fExtensionTree.setSelection(new StructuredSelection(extension));
            return;
        }
        if (changeObject instanceof IPluginExtension || (changeObject instanceof IPluginElement
                && ((IPluginElement) changeObject).getParent() instanceof IPluginParent)) {
            IPluginObject pobj = (IPluginObject) changeObject;
            IPluginObject parent = changeObject instanceof IPluginExtension
                    ? ((IPluginModelBase) getPage().getModel()).getPluginBase()
                    : pobj.getParent();
            if (event.getChangeType() == IModelChangedEvent.INSERT) {
                // enables adding extensions while tree is filtered
                if (fFilteredTree.isFiltered()) {
                    Object[] inserted = event.getChangedObjects();
                    for (int i = 0; i < inserted.length; i++) {
                        fPatternFilter.addElement(inserted[i]);
                    }
                    if (inserted.length == 1) {
                        fFilteredTree.getViewer().setSelection(new StructuredSelection(inserted[0]));
                    }
                }

                //
                fExtensionTree.refresh(parent);
                if (changeObject instanceof IPluginExtension) {
                    IPluginExtension ext = (IPluginExtension) changeObject;
                    if (ext.getSchema() == null)
                        reportMissingExtensionPointSchema(ext.getPoint());
                }
                fExtensionTree.setSelection(new StructuredSelection(changeObject), true);
                fExtensionTree.getTree().setFocus();
            } else if (event.getChangeType() == IModelChangedEvent.REMOVE) {
                if (changeObject instanceof IPluginExtension) {
                    IPluginExtension ext = (IPluginExtension) changeObject;
                    IPluginExtension[] extensions = ((IPluginBase) parent).getExtensions();
                    boolean found = false;
                    // search if there is at least another extension extending the same point than the one being removed
                    for (int i = 0; i < extensions.length; i++) {
                        String point = extensions[i].getPoint();
                        if (ext.getPoint().equals(point)) {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                        getManagedForm().getMessageManager().removeMessage(ext.getPoint());
                }
                fExtensionTree.remove(pobj);
            } else {
                if (event.getChangedProperty().equals(IPluginParent.P_SIBLING_ORDER)) {
                    IStructuredSelection sel = (IStructuredSelection) fExtensionTree.getSelection();
                    IPluginObject child = (IPluginObject) sel.getFirstElement();
                    fExtensionTree.refresh(child.getParent());
                    fExtensionTree.setSelection(new StructuredSelection(child));
                } else {
                    fExtensionTree.update(changeObject, null);
                }
            }
        }
    }

    private Image resolveObjectImage(Object obj) {
        if (obj instanceof IPluginExtension) {
            return fExtensionImage;
        }
        Image elementImage = fGenericElementImage;
        if (obj instanceof IPluginElement) {
            IPluginElement element = (IPluginElement) obj;
            Image customImage = getCustomImage(element);
            if (customImage == null)
                customImage = PDEPlugin.getDefault().getLabelProvider().getImage(obj);
            if (customImage != null)
                elementImage = customImage;
        }
        return elementImage;
    }

    private static boolean isStorageModel(IPluginObject object) {
        IPluginModelBase modelBase = object.getPluginModel();
        return modelBase.getInstallLocation() == null;
    }

    static Image getCustomImage(IPluginElement element) {
        if (isStorageModel(element))
            return null;
        ISchemaElement elementInfo = getSchemaElement(element);
        if (elementInfo != null && elementInfo.getIconProperty() != null) {
            String iconProperty = elementInfo.getIconProperty();
            IPluginAttribute att = element.getAttribute(iconProperty);
            String iconPath = null;
            if (att != null && att.getValue() != null) {
                iconPath = att.getValue();
            }
            // we have a value from a resource attribute
            if (iconPath != null) {
                String ext = new Path(iconPath).getFileExtension();
                // if the resource targets a folder, the file extension will be null
                if (ext == null)
                    return null;
                boolean valid = false;
                // ensure the resource is an image
                for (int i = 0; i < VALID_IMAGE_TYPES.length; i++) {
                    if (ext.equalsIgnoreCase(VALID_IMAGE_TYPES[i])) {
                        valid = true;
                        break;
                    }
                }
                // if the resource is an image, get the image, otherwise return null
                return valid ? getImageFromPlugin(element, iconPath) : null;
            }
        }
        return null;
    }

    private static Image getImageFromPlugin(IPluginElement element, String iconPathName) {
        // 39283 - ignore icon paths that
        // point at plugin.properties
        if (iconPathName.startsWith("%")) //$NON-NLS-1$
            return null;

        IPluginModelBase model = element.getPluginModel();
        if (model == null)
            return null;

        return PDEPlugin.getDefault().getLabelProvider().getImageFromPlugin(model, iconPathName);
    }

    private String resolveObjectName(Object obj) {
        return resolveObjectName(getSchemaRegistry(), obj);
    }

    private SchemaRegistry getSchemaRegistry() {
        if (fSchemaRegistry == null)
            fSchemaRegistry = PDECore.getDefault().getSchemaRegistry();
        return fSchemaRegistry;
    }

    public static String resolveObjectName(SchemaRegistry schemaRegistry, Object obj) {
        boolean fullNames = PDEPlugin.isFullNameModeEnabled();
        if (obj instanceof IPluginExtension) {
            IPluginExtension extension = (IPluginExtension) obj;
            if (!fullNames) {
                return extension.getPoint();
            }
            if (extension.getName() != null)
                return extension.getTranslatedName();
            ISchema schema = schemaRegistry.getSchema(extension.getPoint());
            // try extension point schema definition
            if (schema != null) {
                // exists
                return schema.getName();
            }
            return extension.getPoint();
        } else if (obj instanceof IPluginElement) {
            IPluginElement element = (IPluginElement) obj;
            String baseName = element.getName();
            String fullName = null;
            ISchemaElement elementInfo = getSchemaElement(element);
            IPluginAttribute labelAtt = null;
            if (elementInfo != null && elementInfo.getLabelProperty() != null) {
                labelAtt = element.getAttribute(elementInfo.getLabelProperty());
            }
            if (labelAtt == null) {
                // try some hard-coded attributes that
                // are used frequently
                for (int i = 0; i < COMMON_LABEL_ATTRIBUTES.length; i++) {
                    labelAtt = element.getAttribute(COMMON_LABEL_ATTRIBUTES[i]);
                    if (labelAtt != null && labelAtt.getValue().length() > 0)
                        break;
                }
                if (labelAtt == null) {
                    // Last try - if there is only one attribute,
                    // use that
                    if (element.getAttributeCount() == 1)
                        labelAtt = element.getAttributes()[0];
                }
            }
            if (labelAtt != null && labelAtt.getValue() != null) {
                fullName = stripShortcuts(labelAtt.getValue());
                if (labelAtt.getName().equals(COMMON_LABEL_ATTRIBUTES[3])) { // remove package from handler class 
                    fullName = fullName.substring(fullName.lastIndexOf('.') + 1, fullName.length());
                }
            }
            fullName = element.getResourceString(fullName);

            if (fullNames)
                return fullName != null ? fullName : baseName;
            if (fullName == null)
                return baseName;
            // Bug 183417 - Bidi3.3: Elements' labels in the extensions page in the fragment manifest characters order is incorrect
            // add RTL zero length character just before the ( and the LTR character just after to ensure:
            // 1. The leading parenthesis takes proper orientation when running in BiDi configuration
            // Assumption: baseName (taken from the schema definition), is only Latin characters and is therefore always displayed LTR
            if (BidiUtil.isBidiPlatform())
                return fullName + " \u200f(\u200e" + baseName + ")"; //$NON-NLS-1$ //$NON-NLS-2$
            return fullName + " (" + baseName + ')'; //$NON-NLS-1$
        }
        if (obj != null) {
            return obj.toString();
        }
        return new String();
    }

    @Override
    public void setFocus() {
        if (fExtensionTree != null)
            fExtensionTree.getTree().setFocus();
    }

    /**
     * Temporarily bypasses default {@link FormFilteredTree#getRefreshJobDelay()} for several actions to immediately start tree 
     * filtering. Only the next job to call <code>getRefreshJobDelay()</code> will be affected and reset this value.
     * 
     * @param bypassFilterDelay <code>true</code> bypasses the refresh job delay by overriding it with <code>0</code> 
     */
    public void setBypassFilterDelay(boolean bypassFilterDelay) {
        fBypassFilterDelay = bypassFilterDelay;
    }

    public static String stripShortcuts(String input) {
        StringBuffer output = new StringBuffer();
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            if (c == '&')
                continue;
            else if (c == '@')
                break;
            output.append(c);
        }
        return output.toString();
    }

    @Override
    public boolean canCopy(ISelection selection) {
        // Partial fix for Bug 360079, enables Ctrl+C in filter text if plugin model is editable
        if (fFilteredTree.getFilterControl().isFocusControl() && !selection.isEmpty()) {
            return true;
        }
        // TODO enable copy also when plug-in model is not editable
        return super.canCopy(selection);
    }

    @Override
    public boolean canPaste(Clipboard clipboard) {
        // Partial fix for Bug 360079, enables Ctrl+V in filter text if plugin model is editable
        if (fFilteredTree.getFilterControl().isFocusControl()) {
            return true;
        }
        // TODO enable paste also when plug-in model is not editable
        return super.canPaste(clipboard);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#canPaste(java.lang.Object, java.lang.Object[])
     */
    @Override
    protected boolean canPaste(Object targetObject, Object[] sourceObjects) {
        // Note: Multi-select in is enabled and this function can support 
        // multiple source object but it needs to be investigated
        // Rule:  Element source objects are always pasted as children of the
        // target object (if allowable)
        // Rule:  Extension source objects are always pasted and are independent
        // of the target object
        // Ensure all the sourceObjects are either extensions or elements
        boolean allExtensions = true;
        boolean allElements = true;
        for (int i = 0; i < sourceObjects.length; i++) {
            if (sourceObjects[i] instanceof IPluginExtension) {
                allElements = false;
            } else if (sourceObjects[i] instanceof IPluginElement) {
                allExtensions = false;
            } else {
                return false;
            }
        }
        // Because of the extension rule, we can paste all extension source
        // objects
        if (allExtensions) {
            return true;
        }
        // Pasting a mixture of elements and extensions is not supported
        // (or wise from the users perspective)
        if (allElements == false) {
            return false;
        }
        // Ensure the target object can have children 
        if ((targetObject instanceof IPluginParent) == false) {
            return false;
        } else if ((targetObject instanceof IDocumentElementNode) == false) {
            return false;
        }
        // Retrieve the schema corresponding to the target object      
        IPluginParent targetParent = (IPluginParent) targetObject;
        ISchema schema = getSchema(targetParent);
        // If there is no schema, then a source object can be pasted as a 
        // child of any target object
        if (schema == null) {
            return true;
        }
        // Determine the element name of the target object
        String tagName = ((IDocumentElementNode) targetParent).getXMLTagName();
        // Retrieve the element schema for the target object
        ISchemaElement schemaElement = schema.findElement(tagName);
        // Ensure we found a schema element and it is a schema complex type
        if (schemaElement == null) {
            // Something is seriously wrong, we have a schema
            return false;
        } else if ((schemaElement.getType() instanceof ISchemaComplexType) == false) {
            // Something is seriously wrong, we are a plugin parent
            return false;
        }
        // We have a schema complex type.  Either the target object has 
        // attributes or the element has children.
        // Generate the list of element proposals
        TreeSet<?> elementSet = XMLElementProposalComputer.computeElementProposal(schemaElement,
                (IDocumentElementNode) targetObject);
        // Determine whether we can paste the source elements as children of
        // the target object
        if (sourceObjects.length > 1) {
            IPluginElement[] sourcePluginElements = new IPluginElement[sourceObjects.length];
            System.arraycopy(sourceObjects, 0, sourcePluginElements, 0, sourceObjects.length);
            return canPasteSourceElements(sourcePluginElements, elementSet);
        }
        return canPasteSourceElement((IPluginElement) sourceObjects[0], elementSet);
    }

    /**
     * @param sourceElements
     * @param targetElementSet
     */
    private boolean canPasteSourceElements(IPluginElement[] sourceElements, TreeSet<?> targetElementSet) {
        // Performance optimisation
        // HashSet of schema elements is not comparable for the source
        // objects (schema elements are transient)
        // Create a new HashSet with element names for comparison      
        HashSet<String> targetElementNameSet = new HashSet<String>();
        Iterator<?> iterator = targetElementSet.iterator();
        while (iterator.hasNext()) {
            targetElementNameSet.add(((ISchemaElement) iterator.next()).getName());
        }
        // Paste will be enabled only if all source objects can be pasted 
        // as children into the target element
        // Limitation:  Multiplicity checks will be compromised because we
        // are pasting multiple elements as a single transaction.  The 
        // multiplicity check is computed on the current static state of the
        // target object with the assumption one new element will be added.
        // Obviously, adding more than one element can invalidate the check
        // due to choice, sequence multiplicity constraints.  Even if source
        // elements that are pasted violate multiplicity constraints the 
        // extensions builder will flag them with errors
        for (int i = 0; i < sourceElements.length; i++) {
            String sourceTagName = sourceElements[i].getName();
            if (targetElementNameSet.contains(sourceTagName) == false) {
                return false;
            }
        }
        return true;
    }

    /**
     * @param sourceElement
     * @param targetElementSet
     */
    private boolean canPasteSourceElement(IPluginElement sourceElement, TreeSet<?> targetElementSet) {
        boolean canPaste = false;
        // Get the source element tag name
        String sourceTagName = sourceElement.getName();
        // Iterate over set of valid element proposals
        Iterator<?> iterator = targetElementSet.iterator();
        while (iterator.hasNext()) {
            // Get the proposal element tag name
            String targetTagName = ((ISchemaElement) iterator.next()).getName();
            // Only a source element that is found within the set of element 
            // proposals can be pasted
            if (sourceTagName.equals(targetTagName)) {
                canPaste = true;
                break;
            }
        }
        return canPaste;
    }

    private IPluginModelBase getPluginModelBase() {
        // Note:  This method will work with fragments as long as a fragment.xml
        // is defined first.  Otherwise, paste will not work out of the box.
        // Get the model
        IPluginModelBase model = (IPluginModelBase) getPage().getModel();
        // Ensure the model is a bundle plug-in model
        if ((model instanceof IBundlePluginModelBase) == false) {
            return null;
        }
        // Get the extension model
        ISharedExtensionsModel extensionModel = ((IBundlePluginModelBase) model).getExtensionsModel();
        // Ensure the extension model is defined
        if ((extensionModel == null) || ((extensionModel instanceof IPluginModelBase) == false)) {
            return null;
        }
        return ((IPluginModelBase) extensionModel);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#doPaste(java.lang.Object, java.lang.Object[])
     */
    @Override
    protected void doPaste(Object targetObject, Object[] sourceObjects) {
        // By default, fragment.xml does not exist until the first extension
        // or extension point is created.  
        // Ensure the file exists before pasting because the model will be 
        // null and the paste will fail if it does not exist
        ((ManifestEditor) getPage().getEditor()).ensurePluginContextPresence();
        // Note:  Multi-select in tree viewer is disabled; but, this function
        // can support multiple source objects
        // Get the model
        IPluginModelBase model = getPluginModelBase();
        // Ensure the model is defined
        if (model == null) {
            return;
        }
        IPluginBase pluginBase = model.getPluginBase();
        try {
            // Paste all source objects into the target object
            for (int i = 0; i < sourceObjects.length; i++) {
                Object sourceObject = sourceObjects[i];

                if ((sourceObject instanceof IPluginExtension) && (pluginBase instanceof IDocumentElementNode)) {
                    // Extension object
                    IDocumentElementNode extension = (IDocumentElementNode) sourceObject;
                    // Adjust all the source object transient field values to
                    // acceptable values
                    extension.reconnect((IDocumentElementNode) pluginBase, model);
                    // Add the extension to the plug-in parent (plug-in)
                    pluginBase.add((IPluginExtension) extension);

                } else if ((sourceObject instanceof IPluginElement) && (targetObject instanceof IPluginParent)
                        && (targetObject instanceof IDocumentElementNode)) {
                    // Element object
                    IDocumentElementNode element = (IDocumentElementNode) sourceObject;
                    // Adjust all the source object transient field values to
                    // acceptable values
                    element.reconnect((IDocumentElementNode) targetObject, model);
                    // Add the element to the plug-in parent (extension or
                    // element)
                    ((IPluginParent) targetObject).add((IPluginElement) element);
                }
            }
        } catch (CoreException e) {
            PDEPlugin.logException(e);
        }
    }

    private void handleMove(boolean up) {
        IStructuredSelection sel = (IStructuredSelection) fExtensionTree.getSelection();
        IPluginObject object = (IPluginObject) sel.getFirstElement();
        if (object instanceof IPluginElement) {
            IPluginParent parent = (IPluginParent) object.getParent();
            IPluginObject[] children = parent.getChildren();
            int index = parent.getIndexOf(object);
            int newIndex = up ? index - 1 : index + 1;
            IPluginObject child2 = children[newIndex];
            try {
                parent.swap(object, child2);
            } catch (CoreException e) {
                PDEPlugin.logException(e);
            }
        } else if (object instanceof IPluginExtension) {
            IPluginExtension extension = (IPluginExtension) object;
            IPluginBase plugin = extension.getPluginBase();
            IPluginExtension[] extensions = plugin.getExtensions();
            int index = plugin.getIndexOf(extension);
            int newIndex = up ? index - 1 : index + 1;
            IPluginExtension e2 = extensions[newIndex];
            try {
                plugin.swap(extension, e2);
            } catch (CoreException e) {
                PDEPlugin.logException(e);
            }
        }
    }

    private void updateButtons(Object item) {
        if (fExpandAction != null) {
            fExpandAction.setEnabled(
                    ToggleExpandStateAction.isExpandable((IStructuredSelection) fExtensionTree.getSelection()));
        }
        if (fFilterRelatedAction != null) {
            boolean filterRelatedEnabled = false;
            if (fExtensionTree != null) {
                filterRelatedEnabled = ExtensionsFilterUtil
                        .isFilterRelatedEnabled((IStructuredSelection) fExtensionTree.getSelection());
            }
            fFilterRelatedAction.setEnabled(filterRelatedEnabled);
        }

        if (getPage().getModel().isEditable() == false)
            return;
        boolean sorted = fSortAction != null && fSortAction.isChecked();
        if (sorted) {
            getTreePart().setButtonEnabled(BUTTON_MOVE_UP, false);
            getTreePart().setButtonEnabled(BUTTON_MOVE_DOWN, false);
            return;
        }
        IStructuredSelection selection = (item instanceof IStructuredSelection) ? (IStructuredSelection) item
                : null;

        boolean filtered = fFilteredTree.isFiltered();
        boolean addEnabled = true;
        boolean removeEnabled = true;
        boolean upEnabled = false;
        boolean downEnabled = false;

        if (filtered) {
            // Fix for bug 194529 and bug 194828
            // Update: adding during filtering enabled by additional filter capability
            addEnabled = true;
            upEnabled = false;
            downEnabled = false;
            removeEnabled = isRemoveEnabled(selection);
        } else {
            if (selection != null && selection.size() == 1) {
                Object selected = selection.getFirstElement();
                if (selected instanceof IPluginElement) {
                    IPluginElement element = (IPluginElement) selected;
                    IPluginParent parent = (IPluginParent) element.getParent();
                    // check up
                    int index = parent.getIndexOf(element);
                    if (index > 0)
                        upEnabled = true;
                    if (index < parent.getChildCount() - 1)
                        downEnabled = true;
                } else if (selected instanceof IPluginExtension) {
                    IPluginExtension extension = (IPluginExtension) selected;
                    IExtensions extensions = (IExtensions) extension.getParent();
                    int index = extensions.getIndexOf(extension);
                    int size = extensions.getExtensions().length;
                    if (index > 0)
                        upEnabled = true;
                    if (index < size - 1)
                        downEnabled = true;
                }
            }
        }
        getTreePart().setButtonEnabled(BUTTON_ADD, addEnabled);
        getTreePart().setButtonEnabled(BUTTON_REMOVE, removeEnabled);
        getTreePart().setButtonEnabled(BUTTON_MOVE_UP, upEnabled);
        getTreePart().setButtonEnabled(BUTTON_MOVE_DOWN, downEnabled);
    }

    /**
     * Since filtering potentially hides children of extensions, removing them when they still have children is intransparent.
     * Needs to be called only when the tree is filtered.
     *  
     * @param selection selection to be tested
     * @return whether removing the selected elements is enabled
     */
    boolean isRemoveEnabled(IStructuredSelection selection) {
        if (selection != null) {
            for (Iterator<?> iterator = selection.iterator(); iterator.hasNext();) {
                Object element = iterator.next();
                if (element instanceof PluginExtensionNode) {
                    return ((PluginExtensionNode) element).getChildCount() == 0;
                }
            }
        }
        return true;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.TreeSection#createTreeViewer(org.eclipse.swt.widgets.Composite, int)
     */
    @Override
    protected TreeViewer createTreeViewer(Composite parent, int style) {
        fPatternFilter = new ExtensionsPatternFilter();
        fFilteredTree = new FormFilteredTree(parent, style, fPatternFilter) {
            @Override
            protected WorkbenchJob doCreateRefreshJob() {
                final WorkbenchJob job = super.doCreateRefreshJob();
                job.addJobChangeListener(new JobChangeAdapter() {
                    private ISelection selection;
                    private boolean aboutToRunPassed = false;

                    @Override
                    public void scheduled(IJobChangeEvent event) {
                        ((ExtensionsPatternFilter) fFilteredTree.getPatternFilter()).clearMatchingLeafs();
                        selection = fExtensionTree.getSelection();
                    }

                    @Override
                    public void aboutToRun(IJobChangeEvent event) {
                        aboutToRunPassed = true;
                    }

                    /* 
                     * Restores selection after tree refresh and expands tree up to matching leafs only
                     */
                    @Override
                    public void done(IJobChangeEvent event) {
                        if (aboutToRunPassed) { // restoring is only required if the job actually ran
                            try {
                                fFilteredTree.setRedraw(false);
                                ExtensionsPatternFilter extensionsPatternFilter = ((ExtensionsPatternFilter) fFilteredTree
                                        .getPatternFilter());
                                fExtensionTree.collapseAll();
                                Object[] leafs = extensionsPatternFilter.getMatchingLeafsAsArray();
                                for (int i = 0; i < leafs.length; i++) {
                                    fExtensionTree.expandToLevel(leafs[i], 0);
                                }
                                if (selection != null && !(selection.isEmpty())) {
                                    fExtensionTree.setSelection(selection, true);
                                }
                            } finally {
                                fFilteredTree.setRedraw(true);
                            }
                        }
                    }
                });
                return job;
            }

            @Override
            protected long getRefreshJobDelay() {
                // Prolonged job delay time is required because of the attribute search being more costly in nature.
                // This can block input to the filter text severely. Thus it shouldn't happen when typing slowly.
                // The delay of 1500ms is bypassed by some actions that use the filter text to initiate searches or clear the text.
                long delay = (fBypassFilterDelay) ? 0 : REFRESHJOB_DELAY_TIME;
                setBypassFilterDelay(false); // reset afterwards
                return delay;
            }

            @Override
            protected void clearText() {
                // bugfix: additional notification with textChanged() would cause a needless 2nd refresh job run
                // which in turn would have a longer delay time than the 1st run.
                setFilterText(""); //$NON-NLS-1$
            }

            @Override
            protected void textChanged() {
                String filterText = getFilterString();
                if (filterText != null && filterText.length() == 0) {
                    // clearing the filter text doesn't require a refresh job delay
                    setBypassFilterDelay(true);
                }
                super.textChanged();
            }
        };
        parent.setData("filtered", Boolean.TRUE); //$NON-NLS-1$
        return fFilteredTree.getViewer();
    }

    public void propertyChange(PropertyChangeEvent event) {
        if (fSortAction.equals(event.getSource()) && IAction.RESULT.equals(event.getProperty())) {
            StructuredViewer viewer = getStructuredViewerPart().getViewer();
            IStructuredSelection ssel = (IStructuredSelection) viewer.getSelection();
            updateButtons(ssel);
        }
    }

    protected void selectExtensionElement(ISelection selection) {
        fExtensionTree.setSelection(selection, true);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#isDragAndDropEnabled()
     */
    @Override
    protected boolean isDragAndDropEnabled() {
        return true;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#canDragMove(java.lang.Object[])
     */
    @Override
    public boolean canDragMove(Object[] sourceObjects) {
        // Validate source objects
        if (validateDragMoveSanity(sourceObjects) == false) {
            return false;
        } else if (fFilteredTree.isFiltered()) {
            return false;
        } else if (isTreeViewerSorted()) {
            return false;
        }
        return true;
    }

    /**
     * @param targetObject
     * @param sourceObjects
     */
    private boolean validateDropMoveSanity(Object targetObject, Object[] sourceObjects) {
        // Validate target object
        if ((targetObject instanceof IPluginParent) == false) {
            return false;
        } else if ((targetObject instanceof IDocumentElementNode) == false) {
            return false;
        }
        // Validate source objects
        if (validateDragMoveSanity(sourceObjects) == false) {
            return false;
        }
        return true;
    }

    /**
     * @param sourceObjects
     */
    private boolean validateDragMoveSanity(Object[] sourceObjects) {
        // Validate source
        if (sourceObjects == null) {
            // No objects
            return false;
        } else if (sourceObjects.length != 1) {
            // Multiple selection not supported
            return false;
        } else if ((sourceObjects[0] instanceof IDocumentElementNode) == false) {
            // Must be the right type
            return false;
        } else if ((sourceObjects[0] instanceof IPluginParent) == false) {
            // Must be the right type
            return false;
        }
        return true;
    }

    /**
     * @param sourcePluginObject
     * @param targetPluginObject
     */
    private boolean validateDropMoveModel(IPluginParent sourcePluginObject, IPluginParent targetPluginObject) {
        // Objects have to be from the same model
        ISharedPluginModel sourceModel = sourcePluginObject.getModel();
        ISharedPluginModel targetModel = targetPluginObject.getModel();
        if (sourceModel.equals(targetModel)) {
            return true;
        }
        return false;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#canDropMove(java.lang.Object, java.lang.Object[], int)
     */
    @Override
    public boolean canDropMove(Object targetObject, Object[] sourceObjects, int targetLocation) {
        // Sanity check
        if (validateDropMoveSanity(targetObject, sourceObjects) == false) {
            return false;
        }
        // Multiple selection not supported
        IPluginParent sourcePluginObject = (IPluginParent) sourceObjects[0];
        IPluginParent targetPluginObject = (IPluginParent) targetObject;
        // Validate model
        if (validateDropMoveModel(sourcePluginObject, targetPluginObject) == false) {
            return false;
        }
        // Validate move
        if (sourcePluginObject instanceof IPluginExtension) {
            IPluginExtension sourceExtensionObject = (IPluginExtension) sourcePluginObject;
            if (targetPluginObject instanceof IPluginExtension) {
                // Source:  Extension
                // Target:  Extension
                IPluginExtension targetExtensionObject = (IPluginExtension) targetPluginObject;
                return canDropMove(targetExtensionObject, sourceExtensionObject, targetLocation);
            } else if (targetPluginObject instanceof IPluginElement) {
                // Source:  Extension
                // Target:  Element
                return false;
            }
        } else if (sourcePluginObject instanceof IPluginElement) {
            IPluginElement sourceElementObject = (IPluginElement) sourcePluginObject;
            if (targetPluginObject instanceof IPluginExtension) {
                // Source:  Element
                // Target:  Extension
                IPluginExtension targetExtensionObject = (IPluginExtension) targetPluginObject;
                return canDropMove(targetExtensionObject, sourceElementObject, targetLocation);
            } else if (targetPluginObject instanceof IPluginElement) {
                // Source:  Element
                // Target:  Element
                IPluginElement targetElementObject = (IPluginElement) targetPluginObject;
                return canDropMove(targetElementObject, sourceElementObject, targetLocation);
            }
        }
        return false;
    }

    /**
     * @param targetElementObject
     * @param sourceElementObject
     * @param targetLocation
     */
    private boolean canDropMove(IPluginElement targetElementObject, IPluginElement sourceElementObject,
            int targetLocation) {

        // Verify that the source is not the parent of the target
        if (validateDropMoveParent(targetElementObject, sourceElementObject) == false) {
            return false;
        }

        if (targetLocation == ViewerDropAdapter.LOCATION_BEFORE) {
            IDocumentElementNode previousNode = ((IDocumentElementNode) targetElementObject).getPreviousSibling();
            if (sourceElementObject.equals(previousNode)) {
                return false;
            }
            IPluginObject targetParentObject = targetElementObject.getParent();
            if ((targetParentObject instanceof IPluginParent) == false) {
                return false;
            }
            // Paste element as a sibling of the other element (before)
            return validateDropMoveSchema((IPluginParent) targetParentObject, sourceElementObject);
        } else if (targetLocation == ViewerDropAdapter.LOCATION_AFTER) {
            IDocumentElementNode nextNode = ((IDocumentElementNode) sourceElementObject).getPreviousSibling();
            if (targetElementObject.equals(nextNode)) {
                return false;
            }
            IPluginObject targetParentObject = targetElementObject.getParent();
            if ((targetParentObject instanceof IPluginParent) == false) {
                return false;
            }
            // Paste element as a sibling of the other element (after)
            return validateDropMoveSchema((IPluginParent) targetParentObject, sourceElementObject);
        } else if (targetLocation == ViewerDropAdapter.LOCATION_ON) {
            IDocumentElementNode targetExtensionNode = (IDocumentElementNode) targetElementObject;
            int childCount = targetExtensionNode.getChildCount();
            if (childCount != 0) {
                IDocumentElementNode lastNode = targetExtensionNode.getChildAt(childCount - 1);
                if (sourceElementObject.equals(lastNode)) {
                    return false;
                }
            }
            // Paste element as the last child of the element
            return validateDropMoveSchema(targetElementObject, sourceElementObject);
        }
        return false;
    }

    /**
     * @param targetElementObject
     * @param sourceElementObject
     */
    private boolean validateDropMoveParent(IPluginElement targetElementObject, IPluginElement sourceElementObject) {

        IPluginObject currentParent = targetElementObject.getParent();
        while (true) {
            if (currentParent == null) {
                return true;
            } else if ((currentParent instanceof IPluginElement) == false) {
                return true;
            } else if (sourceElementObject.equals(currentParent)) {
                return false;
            }
            currentParent = currentParent.getParent();
        }
    }

    /**
     * @param targetExtensionObject
     * @param sourceElementObject
     * @param targetLocation
     */
    private boolean canDropMove(IPluginExtension targetExtensionObject, IPluginElement sourceElementObject,
            int targetLocation) {

        if (targetLocation == ViewerDropAdapter.LOCATION_BEFORE) {
            return false;
        } else if (targetLocation == ViewerDropAdapter.LOCATION_AFTER) {
            return false;
        } else if (targetLocation == ViewerDropAdapter.LOCATION_ON) {
            IDocumentElementNode targetExtensionNode = (IDocumentElementNode) targetExtensionObject;
            int childCount = targetExtensionNode.getChildCount();
            if (childCount != 0) {
                IDocumentElementNode lastNode = targetExtensionNode.getChildAt(childCount - 1);
                if (sourceElementObject.equals(lastNode)) {
                    return false;
                }
            }
            // Paste element as the last child of the extension
            return validateDropMoveSchema(targetExtensionObject, sourceElementObject);
        }
        return false;
    }

    /**
     * @param targetPluginObject
     * @param sourcePluginObject
     */
    private boolean validateDropMoveSchema(IPluginParent targetPluginObject, IPluginParent sourcePluginObject) {
        IDocumentElementNode targetPluginNode = (IDocumentElementNode) targetPluginObject;
        // If the target is the source's parent, then the move is always 
        // valid.  No need to check the schema.  Order does not matter
        if (targetPluginObject.equals(sourcePluginObject.getParent())) {
            return true;
        }
        // Retrieve the schema corresponding to the target object      
        ISchema schema = getSchema(targetPluginObject);
        // If there is no schema, then a source object can be pasted as a 
        // child of any target object
        if (schema == null) {
            return true;
        }
        // Determine the element name of the target object
        String targetNodeTagName = targetPluginNode.getXMLTagName();
        // Retrieve the element schema for the target object
        ISchemaElement schemaElement = schema.findElement(targetNodeTagName);
        // Ensure we found a schema element and it is a schema complex type
        if (schemaElement == null) {
            // Something is seriously wrong, we have a schema
            return false;
        } else if ((schemaElement.getType() instanceof ISchemaComplexType) == false) {
            // Something is seriously wrong, we are a plug-in parent
            return false;
        }
        // We have a schema complex type.  Either the target object has 
        // attributes or the element has children.
        // Generate the list of element proposals
        TreeSet<?> elementSet = XMLElementProposalComputer.computeElementProposal(schemaElement, targetPluginNode);
        // Iterate over set of valid element proposals
        Iterator<?> iterator = elementSet.iterator();
        while (iterator.hasNext()) {
            // Get the proposal element tag name
            String targetTagName = ((ISchemaElement) iterator.next()).getName();
            // Only a source element that is found within the set of element 
            // proposals can be pasted
            String sourceNodeTagName = ((IDocumentElementNode) sourcePluginObject).getXMLTagName();
            if (sourceNodeTagName.equals(targetTagName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param targetExtensionObject
     * @param sourceExtensionObject
     * @param targetLocation
     */
    private boolean canDropMove(IPluginExtension targetExtensionObject, IPluginExtension sourceExtensionObject,
            int targetLocation) {

        if (targetLocation == ViewerDropAdapter.LOCATION_BEFORE) {
            IDocumentElementNode previousNode = ((IDocumentElementNode) targetExtensionObject).getPreviousSibling();
            if (sourceExtensionObject.equals(previousNode)) {
                return false;
            }
            // Paste extension as sibling of extension (before)
            return true;
        } else if (targetLocation == ViewerDropAdapter.LOCATION_AFTER) {
            IDocumentElementNode nextNode = ((IDocumentElementNode) sourceExtensionObject).getPreviousSibling();
            if (targetExtensionObject.equals(nextNode)) {
                return false;
            }
            // Paste extension as sibling of extension (after)
            return true;
        } else if (targetLocation == ViewerDropAdapter.LOCATION_ON) {
            return false;
        }
        return false;
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#doDragRemove(java.lang.Object[])
     */
    @Override
    public void doDragRemove(Object[] sourceObjects) {
        // Validate source objects
        if (validateDragMoveSanity(sourceObjects) == false) {
            return;
        }
        IPluginParent pluginParentObject = (IPluginParent) sourceObjects[0];
        // Remove the object from the model
        try {
            if (pluginParentObject instanceof IPluginExtension) {
                IPluginExtension extension = (IPluginExtension) pluginParentObject;
                IPluginBase pluginBase = pluginParentObject.getPluginBase();
                if (pluginBase != null) {
                    pluginBase.remove(extension);
                }
            } else if (pluginParentObject instanceof IPluginElement) {
                IPluginElement element = (IPluginElement) pluginParentObject;
                IPluginObject object = element.getParent();
                if (object instanceof IPluginParent) {
                    ((IPluginParent) object).remove(element);
                }
            }
            // Applicable for move operations
            // Flush the text edit operations associated with the move operation
            // to the source page
            // Move involves add new cloned object x and remove of original object
            // x 
            // Without flushing, multiple move operations up and down cause the
            // text edit operations to get completely screwed up (e.g. mark-up
            // in wrong position or getting lost)
            // TODO: MP: Undo: What are the implications of this?
            ((PDEFormEditor) getPage().getEditor()).getContextManager().getPrimaryContext().flushEditorInput();
        } catch (CoreException e) {
            PDEPlugin.logException(e);
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#doDropMove(java.lang.Object, java.lang.Object[], int)
     */
    @Override
    public void doDropMove(Object targetObject, Object[] sourceObjects, int targetLocation) {
        // Sanity check
        if (validateDropMoveSanity(targetObject, sourceObjects) == false) {
            Display.getDefault().beep();
            return;
        }
        // Multiple selection not supported
        IPluginParent sourcePluginObject = (IPluginParent) sourceObjects[0];
        IPluginParent targetPluginObject = (IPluginParent) targetObject;
        // Validate move
        try {
            if (sourcePluginObject instanceof IPluginExtension) {
                IPluginExtension sourceExtensionObject = (IPluginExtension) sourcePluginObject;
                if (targetPluginObject instanceof IPluginExtension) {
                    // Source:  Extension
                    // Target:  Extension
                    IPluginExtension targetExtensionObject = (IPluginExtension) targetPluginObject;
                    doDropMove(targetExtensionObject, sourceExtensionObject, targetLocation);
                } else if (targetPluginObject instanceof IPluginElement) {
                    // Source:  Extension
                    // Target:  Element
                    return;
                }
            } else if (sourcePluginObject instanceof IPluginElement) {
                IPluginElement sourceElementObject = (IPluginElement) sourcePluginObject;
                if (targetPluginObject instanceof IPluginExtension) {
                    // Source:  Element
                    // Target:  Extension
                    IPluginExtension targetExtensionObject = (IPluginExtension) targetPluginObject;
                    doDropMove(targetExtensionObject, sourceElementObject, targetLocation);
                } else if (targetPluginObject instanceof IPluginElement) {
                    // Source:  Element
                    // Target:  Element
                    IPluginElement targetElementObject = (IPluginElement) targetPluginObject;
                    doDropMove(targetElementObject, sourceElementObject, targetLocation);
                }
            }
        } catch (CoreException e) {
            PDEPlugin.logException(e);
        }
    }

    /**
     * @param targetExtensionObject
     * @param sourceExtensionObject
     * @param targetLocation
     */
    private void doDropMove(IPluginExtension targetExtensionObject, IPluginExtension sourceExtensionObject,
            int targetLocation) throws CoreException {
        // Get the model
        IPluginModelBase model = getPluginModelBase();
        // Ensure the model is defined
        if (model == null) {
            return;
        }
        // Get the plug-in base
        IPluginBase pluginBase = model.getPluginBase();
        // Ensure the plug-in base is a document node
        if ((pluginBase instanceof IDocumentElementNode) == false) {
            return;
        } else if ((pluginBase instanceof PluginBaseNode) == false) {
            return;
        }
        // Plug-in base node
        IDocumentElementNode pluginBaseNode = (IDocumentElementNode) pluginBase;
        // Source extension node
        IDocumentElementNode sourceExtensionNode = (IDocumentElementNode) sourceExtensionObject;
        // Target extension node
        IDocumentElementNode targetExtensionNode = (IDocumentElementNode) targetExtensionObject;
        // Do drop move
        if (targetLocation == ViewerDropAdapter.LOCATION_BEFORE) {
            // Adjust all the source object transient field values to
            // acceptable values
            sourceExtensionNode.reconnect(pluginBaseNode, model);
            // Get index of target extension
            int index = (pluginBaseNode.indexOf(targetExtensionNode));
            // Ensure the target index was found
            if (index == -1) {
                return;
            }
            // Paste extension as sibling of extension (before)
            ((PluginBaseNode) pluginBaseNode).add(sourceExtensionObject, index);
        } else if (targetLocation == ViewerDropAdapter.LOCATION_AFTER) {
            // Adjust all the source object transient field values to
            // acceptable values
            sourceExtensionNode.reconnect(pluginBaseNode, model);
            // Get index of target extension
            int index = (pluginBaseNode.indexOf(targetExtensionNode));
            // Ensure the target index was found
            if (index == -1) {
                return;
            }
            // Paste extension as sibling of extension (after)
            ((PluginBaseNode) pluginBaseNode).add(sourceExtensionObject, index + 1);
        } else if (targetLocation == ViewerDropAdapter.LOCATION_ON) {
            // NO-OP
        }
    }

    /**
     * @param targetExtensionObject
     * @param sourceElementObject
     * @param targetLocation
     */
    private void doDropMove(IPluginExtension targetExtensionObject, IPluginElement sourceElementObject,
            int targetLocation) throws CoreException {
        // Get the model
        IPluginModelBase model = getPluginModelBase();
        // Ensure the model is defined
        if (model == null) {
            return;
        }
        // Target extension node
        IDocumentElementNode targetExtensionNode = (IDocumentElementNode) targetExtensionObject;
        // Source extension node
        IDocumentElementNode sourceElementNode = (IDocumentElementNode) sourceElementObject;
        // Do drop move
        if (targetLocation == ViewerDropAdapter.LOCATION_BEFORE) {
            // NO-OP
        } else if (targetLocation == ViewerDropAdapter.LOCATION_AFTER) {
            // NO-OP
        } else if (targetLocation == ViewerDropAdapter.LOCATION_ON) {
            // Adjust all the source object transient field values to
            // acceptable values
            sourceElementNode.reconnect(targetExtensionNode, model);
            // Paste element as the last child of the extension
            targetExtensionObject.add(sourceElementObject);
        }
    }

    /**
     * @param targetElementObject
     * @param sourceElementObject
     * @param targetLocation
     */
    private void doDropMove(IPluginElement targetElementObject, IPluginElement sourceElementObject,
            int targetLocation) throws CoreException {
        // Get the model
        IPluginModelBase model = getPluginModelBase();
        // Ensure the model is defined
        if (model == null) {
            return;
        }
        // Target extension node
        IDocumentElementNode targetElementNode = (IDocumentElementNode) targetElementObject;
        // Source extension node
        IDocumentElementNode sourceElementNode = (IDocumentElementNode) sourceElementObject;
        // Do drop move
        if (targetLocation == ViewerDropAdapter.LOCATION_BEFORE) {
            // Get the target's parent
            IPluginObject targetParentObject = targetElementObject.getParent();
            if ((targetParentObject instanceof IPluginParent) == false) {
                return;
            } else if ((targetParentObject instanceof IDocumentElementNode) == false) {
                return;
            }
            IDocumentElementNode targetParentNode = (IDocumentElementNode) targetParentObject;
            // Adjust all the source object transient field values to
            // acceptable values
            sourceElementNode.reconnect(targetParentNode, model);
            // Get index of target element
            int index = (targetParentNode.indexOf(targetElementNode));
            // Ensure the target index was found
            if (index == -1) {
                return;
            }
            // Paste element as a sibling of the other element (before)
            ((IPluginParent) targetParentObject).add(index, sourceElementObject);
        } else if (targetLocation == ViewerDropAdapter.LOCATION_AFTER) {
            // Get the target's parent
            IPluginObject targetParentObject = targetElementObject.getParent();
            if ((targetParentObject instanceof IPluginParent) == false) {
                return;
            } else if ((targetParentObject instanceof IDocumentElementNode) == false) {
                return;
            }
            IDocumentElementNode targetParentNode = (IDocumentElementNode) targetParentObject;
            // Adjust all the source object transient field values to
            // acceptable values
            sourceElementNode.reconnect(targetParentNode, model);
            // Get index of target element
            int index = (targetParentNode.indexOf(targetElementNode));
            // Ensure the target index was found
            if (index == -1) {
                return;
            }
            // Paste element as a sibling of the other element (after)
            ((IPluginParent) targetParentObject).add(index + 1, sourceElementObject);
        } else if (targetLocation == ViewerDropAdapter.LOCATION_ON) {
            // Adjust all the source object transient field values to
            // acceptable values
            sourceElementNode.reconnect(targetElementNode, model);
            // Paste element as the last child of the element
            targetElementObject.add(sourceElementObject);
        }

    }

    private boolean isTreeViewerSorted() {
        if (fSortAction == null) {
            return false;
        }
        return fSortAction.isChecked();
    }

    private boolean isSingleSelection() {
        IStructuredSelection selection = (IStructuredSelection) fExtensionTree.getSelection();
        return selection.size() == 1;
    }

    private void reportMissingExtensionPointSchemas(IPluginBase pluginBase) {
        IPluginExtension[] extensions = pluginBase.getExtensions();
        for (int i = 0; i < extensions.length; i++) {
            IPluginExtension ext = extensions[i];
            if (ext.getSchema() == null)
                reportMissingExtensionPointSchema(ext.getPoint());
        }
    }

    private void reportMissingExtensionPointSchema(String point) {
        getManagedForm().getMessageManager().addMessage(point,
                NLS.bind(PDEUIMessages.ManifestEditor_DetailExtension_missingExtPointSchema, point), null,
                IMessageProvider.WARNING);
    }
}