org.eclipse.ui.internal.EditorManager.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ui.internal.EditorManager.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2010 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
 *     Nikolay Botev - bug 240651
 *******************************************************************************/
package org.eclipse.ui.internal;

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

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler;
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.internal.provisional.action.ICoolBarManager2;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.util.SafeRunnable;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.ActiveShellExpression;
import org.eclipse.ui.IEditorActionBarContributor;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorMatchingStrategy;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.IPersistableEditor;
import org.eclipse.ui.IPersistableElement;
import org.eclipse.ui.ISaveablePart;
import org.eclipse.ui.ISaveablePart2;
import org.eclipse.ui.ISaveablesLifecycleListener;
import org.eclipse.ui.ISaveablesSource;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPart3;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.Saveable;
import org.eclipse.ui.dialogs.ListSelectionDialog;
import org.eclipse.ui.handlers.IHandlerActivation;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.internal.StartupThreading.StartupRunnable;
import org.eclipse.ui.internal.dialogs.EventLoopProgressMonitor;
import org.eclipse.ui.internal.misc.StatusUtil;
import org.eclipse.ui.internal.misc.UIStats;
import org.eclipse.ui.internal.part.NullEditorInput;
import org.eclipse.ui.internal.registry.EditorDescriptor;
import org.eclipse.ui.internal.registry.EditorRegistry;
import org.eclipse.ui.internal.tweaklets.TabBehaviour;
import org.eclipse.ui.internal.tweaklets.Tweaklets;
import org.eclipse.ui.internal.util.Util;
import org.eclipse.ui.model.WorkbenchPartLabelProvider;
import org.eclipse.ui.part.AbstractMultiEditor;
import org.eclipse.ui.part.MultiEditor;
import org.eclipse.ui.part.MultiEditorInput;
import org.eclipse.ui.statushandlers.StatusManager;

/**
 * Manage a group of element editors. Prevent the creation of two editors on the
 * same element.
 * 
 * 06/12/00 - DS - Given the ambiguous editor input type, the manager delegates
 * a number of responsibilities to the editor itself.
 * 
 * <ol>
 * <li>The editor should determine its own title.</li>
 * <li>The editor should listen to resource deltas and close itself if the
 * input is deleted. It may also choose to stay open if the editor has dirty
 * state.</li>
 * <li>The editor should persist its own state plus editor input.</li>
 * </ol>
 */
public class EditorManager implements IExtensionChangeHandler {
    EditorAreaHelper editorPresentation;

    WorkbenchWindow window;

    WorkbenchPage page;

    private Map actionCache = new HashMap();

    private static final String PIN_EDITOR_KEY = "PIN_EDITOR"; //$NON-NLS-1$

    private static final String PIN_EDITOR = "ovr16/pinned_ovr.gif"; //$NON-NLS-1$

    // When the user removes or adds the close editors automatically preference
    // the icon should be removed or added accordingly
    private IPropertyChangeListener editorPropChangeListnener = null;

    // Handler for the pin editor keyboard shortcut
    private IHandlerActivation pinEditorHandlerActivation = null;

    static final String RESOURCES_TO_SAVE_MESSAGE = WorkbenchMessages.get().EditorManager_saveResourcesMessage;

    static final String SAVE_RESOURCES_TITLE = WorkbenchMessages.get().EditorManager_saveResourcesTitle;

    /**
     * EditorManager constructor comment.
     */
    public EditorManager(WorkbenchWindow window, WorkbenchPage workbenchPage, EditorAreaHelper pres) {
        Assert.isNotNull(window);
        Assert.isNotNull(workbenchPage);
        Assert.isNotNull(pres);
        this.window = window;
        this.page = workbenchPage;
        this.editorPresentation = pres;

        page.getExtensionTracker().registerHandler(this, null);
    }

    /**
     * Check to determine if the editor resources are no longer needed removes
     * property change listener for editors removes pin editor keyboard shortcut
     * handler disposes cached images and clears the cached images hash table
     */
    void checkDeleteEditorResources() {
        // get the current number of editors
        IEditorReference[] editors = page.getEditorReferences();
        // If there are no editors
        if (editors.length == 0) {
            if (editorPropChangeListnener != null) {
                // remove property change listener for editors
                IPreferenceStore prefStore = WorkbenchPlugin.getDefault().getPreferenceStore();
                prefStore.removePropertyChangeListener(editorPropChangeListnener);
                editorPropChangeListnener = null;
            }
            if (pinEditorHandlerActivation != null) {
                // remove pin editor keyboard shortcut handler
                final IHandlerService handlerService = (IHandlerService) window.getWorkbench()
                        .getService(IHandlerService.class);
                handlerService.deactivateHandler(pinEditorHandlerActivation);
                pinEditorHandlerActivation = null;
            }
        }
    }

    /**
     * Check to determine if the property change listener for editors should be
     * created
     */
    void checkCreateEditorPropListener() {
        if (editorPropChangeListnener == null) {
            // Add a property change listener for closing editors automatically
            // preference
            // Add or remove the pin icon accordingly
            editorPropChangeListnener = new IPropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent event) {
                    if (event.getProperty().equals(IPreferenceConstants.REUSE_EDITORS_BOOLEAN)) {
                        IEditorReference[] editors = getEditors();
                        for (int i = 0; i < editors.length; i++) {
                            ((EditorReference) editors[i]).pinStatusUpdated();
                        }
                    }
                }
            };
            WorkbenchPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(editorPropChangeListnener);
        }
    }

    /**
     * Check to determine if the handler for the pin editor keyboard shortcut
     * should be created.
     */
    void checkCreatePinEditorShortcutKeyHandler() {
        if (pinEditorHandlerActivation == null) {
            final Shell shell = window.getShell();
            final IHandler pinEditorHandler = new AbstractHandler() {
                public final Object execute(final ExecutionEvent event) {
                    // check if the "Close editors automatically" preference is
                    // set
                    IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
                    if (store.getBoolean(IPreferenceConstants.REUSE_EDITORS_BOOLEAN)
                            || ((TabBehaviour) Tweaklets.get(TabBehaviour.KEY)).alwaysShowPinAction()) {

                        IWorkbenchPartReference ref = editorPresentation.getVisibleEditor();
                        if (ref instanceof WorkbenchPartReference) {
                            WorkbenchPartReference concreteRef = (WorkbenchPartReference) ref;

                            concreteRef.setPinned(concreteRef.isPinned());
                        }
                    }
                    return null;
                }
            };

            // Assign the handler for the pin editor keyboard shortcut.
            final IHandlerService handlerService = (IHandlerService) window.getWorkbench()
                    .getService(IHandlerService.class);
            pinEditorHandlerActivation = handlerService.activateHandler(
                    IWorkbenchCommandConstants.WINDOW_PIN_EDITOR, pinEditorHandler,
                    new ActiveShellExpression(shell));
        }
    }

    /**
     * Method to create the editor's pin ImageDescriptor
     * 
     * @return the single image descriptor for the editor's pin icon
     */
    ImageDescriptor getEditorPinImageDesc() {
        ImageRegistry registry = JFaceResources.getImageRegistry();
        ImageDescriptor pinDesc = registry.getDescriptor(PIN_EDITOR_KEY);
        // Avoid registering twice
        if (pinDesc == null) {
            pinDesc = WorkbenchImages.getWorkbenchImageDescriptor(PIN_EDITOR);
            registry.put(PIN_EDITOR_KEY, pinDesc);

        }
        return pinDesc;
    }

    /**
     * Answer a list of dirty editors.
     */
    private List collectDirtyEditors() {
        List result = new ArrayList(3);
        IEditorReference[] editors = page.getEditorReferences();
        for (int i = 0; i < editors.length; i++) {
            IEditorPart part = (IEditorPart) editors[i].getPart(false);
            if (part != null && part.isDirty()) {
                result.add(part);
            }

        }
        return result;
    }

    /**
     * Returns whether the manager contains an editor.
     */
    public boolean containsEditor(IEditorReference ref) {
        IEditorReference[] editors = page.getEditorReferences();
        for (int i = 0; i < editors.length; i++) {
            if (ref == editors[i]) {
                return true;
            }
        }
        return false;
    }

    /*
     * Creates the action bars for an editor. Editors of the same type should
     * share a single editor action bar, so this implementation may return an
     * existing action bar vector.
     */
    private EditorActionBars createEditorActionBars(EditorDescriptor desc, final IEditorSite site) {
        // Get the editor type.
        String type = desc.getId();

        // If an action bar already exists for this editor type return it.
        EditorActionBars actionBars = (EditorActionBars) actionCache.get(type);
        if (actionBars != null) {
            actionBars.addRef();
            return actionBars;
        }

        // Create a new action bar set.
        actionBars = new EditorActionBars(page, site.getWorkbenchWindow(), type);
        actionBars.addRef();
        actionCache.put(type, actionBars);

        // Read base contributor.
        IEditorActionBarContributor contr = desc.createActionBarContributor();
        if (contr != null) {
            actionBars.setEditorContributor(contr);
            contr.init(actionBars, page);
        }

        // Read action extensions.
        EditorActionBuilder builder = new EditorActionBuilder();
        contr = builder.readActionExtensions(desc);
        if (contr != null) {
            actionBars.setExtensionContributor(contr);
            contr.init(actionBars, page);
        }

        // Return action bars.
        return actionBars;
    }

    /*
     * Creates the action bars for an editor.
     */
    private EditorActionBars createEmptyEditorActionBars(final IEditorSite site) {
        // Get the editor type.
        String type = String.valueOf(System.currentTimeMillis());

        // Create a new action bar set.
        // Note: It is an empty set.
        EditorActionBars actionBars = new EditorActionBars(page, site.getWorkbenchWindow(), type);
        actionBars.addRef();
        actionCache.put(type, actionBars);

        // Return action bars.
        return actionBars;
    }

    /*
     * Dispose
     */
    void disposeEditorActionBars(EditorActionBars actionBars) {
        actionBars.removeRef();
        if (actionBars.getRef() <= 0) {
            String type = actionBars.getEditorType();
            actionCache.remove(type);
            // refresh the cool bar manager before disposing of a cool item
            ICoolBarManager2 coolBar = (ICoolBarManager2) window.getCoolBarManager2();
            if (coolBar != null) {
                coolBar.refresh();
            }
            actionBars.dispose();
        }
    }

    /**
     * Returns an open editor matching the given editor input. If none match,
     * returns <code>null</code>.
     * 
     * @param input
     *            the editor input
     * @return the matching editor, or <code>null</code> if no match found
     */
    public IEditorPart findEditor(IEditorInput input) {
        return findEditor(null, input, IWorkbenchPage.MATCH_INPUT);
    }

    /**
     * Returns an open editor matching the given editor input and/or editor id,
     * as specified by matchFlags. If none match, returns <code>null</code>.
     * 
     * @param editorId
     *            the editor id
     * @param input
     *            the editor input
     * @param matchFlags
     *            flags specifying which aspects to match
     * @return the matching editor, or <code>null</code> if no match found
     * @since 3.1
     */
    public IEditorPart findEditor(String editorId, IEditorInput input, int matchFlags) {
        IEditorReference[] refs = findEditors(input, editorId, matchFlags);
        if (refs.length == 0) {
            return null;
        }
        return refs[0].getEditor(true);
    }

    /**
     * Returns the open editor references matching the given editor input and/or
     * editor id, as specified by matchFlags. If none match, returns an empty
     * array.
     * 
     * @param editorId
     *            the editor id
     * @param input
     *            the editor input
     * @param matchFlags
     *            flags specifying which aspects to match
     * @return the matching editor, or an empty array if no match found
     * @since 3.1
     */
    public IEditorReference[] findEditors(IEditorInput input, String editorId, int matchFlags) {
        if (matchFlags == IWorkbenchPage.MATCH_NONE) {
            return new IEditorReference[0];
        }
        List result = new ArrayList();
        ArrayList othersList = new ArrayList(Arrays.asList(page.getEditorReferences()));
        if (!othersList.isEmpty()) {
            IEditorReference active = page.getActiveEditorReference();
            if (active != null) {
                othersList.remove(active);
                ArrayList activeList = new ArrayList(1);
                activeList.add(active);
                findEditors(activeList, input, editorId, matchFlags, result);
            }
            findEditors(othersList, input, editorId, matchFlags, result);
        }
        return (IEditorReference[]) result.toArray(new IEditorReference[result.size()]);
    }

    /**
     * Populates a list with open editor(s) matching the given editor id and/or editor input.
     * 
     * @param editorId
     *            the editor id
     * @param input
     *            the editor input
     * @param editorList
     *            a mutable list containing the references for the editors to
     *            check (warning: items may be removed)
     * @param result
     *            the list to which matching editor references should be added
     * @since 3.1
     */
    private void findEditors(List editorList, IEditorInput input, String editorId, int matchFlags, List result) {
        if (matchFlags == IWorkbenchPage.MATCH_NONE) {
            return;
        }

        // Phase 0: Remove editors whose ids don't match (if matching by id)
        if (((matchFlags & IWorkbenchPage.MATCH_ID) != 0) && editorId != null) {
            for (Iterator i = editorList.iterator(); i.hasNext();) {
                EditorReference editor = (EditorReference) i.next();
                if (!editorId.equals(editor.getId())) {
                    i.remove();
                }
            }
        }

        // If not matching on editor input, just return the remaining editors.
        // In practice, this case is never used.
        if ((matchFlags & IWorkbenchPage.MATCH_INPUT) == 0) {
            result.addAll(editorList);
            return;
        }

        // Phase 1: check editors that have their own matching strategy
        for (Iterator i = editorList.iterator(); i.hasNext();) {
            EditorReference editor = (EditorReference) i.next();
            IEditorDescriptor desc = editor.getDescriptor();
            if (desc != null) {
                IEditorMatchingStrategy matchingStrategy = desc.getEditorMatchingStrategy();
                if (matchingStrategy != null) {
                    i.remove(); // We're handling this one here, so remove it
                    // from the list.
                    if (matchingStrategy.matches(editor, input)) {
                        result.add(editor);
                    }
                }
            }
        }

        // Phase 2: check materialized editors (without their own matching
        // strategy)
        for (Iterator i = editorList.iterator(); i.hasNext();) {
            IEditorReference editor = (IEditorReference) i.next();
            IEditorPart part = (IEditorPart) editor.getPart(false);
            if (part != null) {
                i.remove(); // We're handling this one here, so remove it from
                // the list.
                if (part.getEditorInput() != null && part.getEditorInput().equals(input)) {
                    result.add(editor);
                }
            }
        }

        // Phase 3: check unmaterialized editors for input equality,
        // delaying plug-in activation further by only restoring the editor
        // input
        // if the editor reference's factory id and name match.
        String name = input.getName();
        IPersistableElement persistable = input.getPersistable();
        if (name == null || persistable == null) {
            return;
        }
        String id = persistable.getFactoryId();
        if (id == null) {
            return;
        }
        for (Iterator i = editorList.iterator(); i.hasNext();) {
            EditorReference editor = (EditorReference) i.next();
            if (name.equals(editor.getName()) && id.equals(editor.getFactoryId())) {
                IEditorInput restoredInput;
                try {
                    restoredInput = editor.getEditorInput();
                    if (Util.equals(restoredInput, input)) {
                        result.add(editor);
                    }
                } catch (PartInitException e1) {
                    WorkbenchPlugin.log(e1);
                }
            }
        }
    }

    // RAP [rh] unused code,    
    //   /**
    //    * Returns the SWT Display.
    //    */
    //   private Display getDisplay() {
    //      return window.getShell().getDisplay();
    //   }

    /**
     * Answer the number of editors.
     */
    public int getEditorCount() {
        return page.getEditorReferences().length;
    }

    /*
     * Answer the editor registry.
     */
    private IEditorRegistry getEditorRegistry() {
        return WorkbenchPlugin.getDefault().getEditorRegistry();
    }

    /*
     * See IWorkbenchPage.
     */
    public IEditorPart[] getDirtyEditors() {
        List dirtyEditors = collectDirtyEditors();
        return (IEditorPart[]) dirtyEditors.toArray(new IEditorPart[dirtyEditors.size()]);
    }

    /*
     * See IWorkbenchPage.
     */
    public IEditorReference[] getEditors() {
        return page.getEditorReferences();
    }

    /*
     * See IWorkbenchPage#getFocusEditor
     */
    public IEditorPart getVisibleEditor() {
        IEditorReference ref = editorPresentation.getVisibleEditor();
        if (ref == null) {
            return null;
        }
        return (IEditorPart) ref.getPart(true);
    }

    /**
     * Answer true if save is needed in any one of the editors.
     */
    public boolean isSaveAllNeeded() {
        IEditorReference[] editors = page.getEditorReferences();
        for (int i = 0; i < editors.length; i++) {
            IEditorReference ed = editors[i];
            if (ed.isDirty()) {
                return true;
            }
        }
        return false;
    }

    /*
     * Prompt the user to save the reusable editor. Return false if a new editor
     * should be opened.
     */
    private IEditorReference findReusableEditor(EditorDescriptor desc) {
        return ((TabBehaviour) Tweaklets.get(TabBehaviour.KEY)).findReusableEditor(page);
    }

    /**
     * @param editorId
     *            the editor part id
     * @param input
     *            the input
     * @param setVisible
     *            if this is to be created visible ... not used
     * @param editorState
     *            an {@link IMemento} &lt;editorState&gt; for persistable
     *            editors. Can be <code>null</code>.
     * @return a created editor reference
     * @throws PartInitException
     */
    public IEditorReference openEditor(String editorId, IEditorInput input, boolean setVisible,
            IMemento editorState) throws PartInitException {
        if (editorId == null || input == null) {
            throw new IllegalArgumentException();
        }

        IEditorRegistry reg = getEditorRegistry();
        EditorDescriptor desc = (EditorDescriptor) reg.findEditor(editorId);
        if (desc == null) {
            throw new PartInitException(
                    NLS.bind(WorkbenchMessages.get().EditorManager_unknownEditorIDMessage, editorId));
        }

        IEditorReference result = openEditorFromDescriptor(desc, input, editorState);
        return result;
    }

    /*
     * Open a new editor
     */
    public IEditorReference openEditorFromDescriptor(EditorDescriptor desc, IEditorInput input,
            IMemento editorState) throws PartInitException {
        IEditorReference result = null;
        if (desc.isInternal()) {
            result = reuseInternalEditor(desc, input);
            if (result == null) {
                result = new EditorReference(this, input, desc, editorState);
            }
            // RAP [rh] Not supported: external editors, inplace editors         
            //      } else if (desc.getId()
            //            .equals(IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID)) {
            //         if (ComponentSupport.inPlaceEditorSupported()) {
            //            result = new EditorReference(this, input, desc);
            //         }
            //      } else if (desc.getId().equals(
            //            IEditorRegistry.SYSTEM_EXTERNAL_EDITOR_ID)) {
            //         IPathEditorInput pathInput = getPathEditorInput(input);
            //         if (pathInput != null) {
            //            result = openSystemExternalEditor(pathInput.getPath());
            //         } else {
            //            throw new PartInitException(
            //                  WorkbenchMessages.get().EditorManager_systemEditorError);
            //         }
            //      } else if (desc.isOpenExternal()) {
            //         result = openExternalEditor(desc, input);
        } else {
            // this should never happen
            throw new PartInitException(
                    NLS.bind(WorkbenchMessages.get().EditorManager_invalidDescriptor, desc.getId()));
        }

        if (result != null) {
            createEditorTab((EditorReference) result, ""); //$NON-NLS-1$
        }

        Workbench wb = (Workbench) window.getWorkbench();
        wb.getEditorHistory().add(input, desc);
        return result;
    }

    // RAP [rh] external editors not supported   
    //   /**
    //    * Open a specific external editor on an file based on the descriptor.
    //    */
    //   private IEditorReference openExternalEditor(final EditorDescriptor desc,
    //         IEditorInput input) throws PartInitException {
    //      final CoreException ex[] = new CoreException[1];
    //
    //      final IPathEditorInput pathInput = getPathEditorInput(input);
    //      if (pathInput != null && pathInput.getPath() != null) {
    //         BusyIndicator.showWhile(getDisplay(), new Runnable() {
    //            public void run() {
    //               try {
    //                  if (desc.getLauncher() != null) {
    //                     // open using launcher
    //                     Object launcher = WorkbenchPlugin.createExtension(
    //                           desc.getConfigurationElement(), "launcher"); //$NON-NLS-1$
    //                     ((IEditorLauncher) launcher).open(pathInput
    //                           .getPath());
    //                  } else {
    //                     // open using command
    //                     ExternalEditor oEditor = new ExternalEditor(
    //                           pathInput.getPath(), desc);
    //                     oEditor.open();
    //                  }
    //               } catch (CoreException e) {
    //                  ex[0] = e;
    //               }
    //            }
    //         });
    //      } else {
    //         throw new PartInitException(NLS.bind(
    //               WorkbenchMessages.get().EditorManager_errorOpeningExternalEditor,
    //               desc.getFileName(), desc.getId()));
    //      }
    //
    //      if (ex[0] != null) {
    //         throw new PartInitException(NLS.bind(
    //               WorkbenchMessages.get().EditorManager_errorOpeningExternalEditor,
    //               desc.getFileName(), desc.getId()), ex[0]);
    //      }
    //
    //      // we do not have an editor part for external editors
    //      return null;
    //   }

    /**
     * Create the part and reference for each inner editor.
     * 
     * @param ref
     *            the AbstractMultiEditor reference
     * @param part
     *            the part
     * @param input
     *            the AbstractMultiEditor input
     * @return the array of inner references to store in the AbstractMultiEditor reference
     */
    IEditorReference[] openMultiEditor(final IEditorReference ref, final AbstractMultiEditor part,
            final MultiEditorInput input) throws PartInitException {

        String[] editorArray = input.getEditors();
        IEditorInput[] inputArray = input.getInput();

        // find all descriptors
        EditorDescriptor[] descArray = new EditorDescriptor[editorArray.length];
        IEditorReference refArray[] = new IEditorReference[editorArray.length];
        IEditorPart partArray[] = new IEditorPart[editorArray.length];

        IEditorRegistry reg = getEditorRegistry();
        for (int i = 0; i < editorArray.length; i++) {
            EditorDescriptor innerDesc = (EditorDescriptor) reg.findEditor(editorArray[i]);
            if (innerDesc == null) {
                throw new PartInitException(
                        NLS.bind(WorkbenchMessages.get().EditorManager_unknownEditorIDMessage, editorArray[i]));
            }
            descArray[i] = innerDesc;
            InnerEditor innerRef = new InnerEditor(ref, part, inputArray[i], descArray[i]);
            refArray[i] = innerRef;
            partArray[i] = innerRef.getEditor(true);
        }
        part.setChildren(partArray);
        return refArray;
    }

    /*
     * Opens an editor part.
     */
    private void createEditorTab(final EditorReference ref, final String workbookId) throws PartInitException {

        editorPresentation.addEditor(ref, workbookId, true);

    }

    /*
     * Create the site and initialize it with its action bars.
     */
    EditorSite createSite(final IEditorReference ref, final IEditorPart part, final EditorDescriptor desc,
            final IEditorInput input) throws PartInitException {
        EditorSite site = new EditorSite(ref, part, page, desc);
        if (desc != null) {
            site.setActionBars(createEditorActionBars(desc, site));
        } else {
            site.setActionBars(createEmptyEditorActionBars(site));
        }
        final String label = part.getTitle(); // debugging only
        try {
            try {
                UIStats.start(UIStats.INIT_PART, label);
                part.init(site, input);
            } finally {
                UIStats.end(UIStats.INIT_PART, part, label);
            }

            // Sanity-check the site
            if (part.getSite() != site || part.getEditorSite() != site) {
                throw new PartInitException(
                        NLS.bind(WorkbenchMessages.get().EditorManager_siteIncorrect, desc.getId()));
            }

        } catch (Exception e) {
            disposeEditorActionBars((EditorActionBars) site.getActionBars());
            site.dispose();
            if (e instanceof PartInitException) {
                throw (PartInitException) e;
            }

            throw new PartInitException(WorkbenchMessages.get().EditorManager_errorInInit, e);
        }

        return site;
    }

    /*
     * See IWorkbenchPage.
     */
    private IEditorReference reuseInternalEditor(EditorDescriptor desc, IEditorInput input)
            throws PartInitException {

        Assert.isNotNull(desc, "descriptor must not be null"); //$NON-NLS-1$
        Assert.isNotNull(input, "input must not be null"); //$NON-NLS-1$

        IEditorReference reusableEditorRef = findReusableEditor(desc);
        if (reusableEditorRef != null) {
            return ((TabBehaviour) Tweaklets.get(TabBehaviour.KEY)).reuseInternalEditor(page, this,
                    editorPresentation, desc, input, reusableEditorRef);
        }
        return null;
    }

    IEditorPart createPart(final EditorDescriptor desc) throws PartInitException {
        try {
            IEditorPart result = desc.createEditor();
            IConfigurationElement element = desc.getConfigurationElement();
            if (element != null) {
                page.getExtensionTracker().registerObject(element.getDeclaringExtension(), result,
                        IExtensionTracker.REF_WEAK);
            }
            return result;
        } catch (CoreException e) {
            throw new PartInitException(StatusUtil.newStatus(desc.getPluginID(),
                    WorkbenchMessages.get().EditorManager_instantiationError, e));
        }
    }

    // RAP [rh] external editors not supported   
    //   /**
    //    * Open a system external editor on the input path.
    //    */
    //   private IEditorReference openSystemExternalEditor(final IPath location)
    //         throws PartInitException {
    //      if (location == null) {
    //         throw new IllegalArgumentException();
    //      }
    //
    //      final boolean result[] = { false };
    //      BusyIndicator.showWhile(getDisplay(), new Runnable() {
    //         public void run() {
    //            if (location != null) {
    //               result[0] = Program.launch(location.toOSString());
    //            }
    //         }
    //      });
    //
    //      if (!result[0]) {
    //         throw new PartInitException(NLS.bind(
    //               WorkbenchMessages.get().EditorManager_unableToOpenExternalEditor,
    //               location));
    //      }
    //
    //      // We do not have an editor part for external editors
    //      return null;
    //   }

    ImageDescriptor findImage(EditorDescriptor desc, IPath path) {
        if (desc == null) {
            // @issue what should be the default image?
            return ImageDescriptor.getMissingImageDescriptor();
        }

        if (desc.isOpenExternal() && path != null) {
            return PlatformUI.getWorkbench().getEditorRegistry().getImageDescriptor(path.toOSString());
        }

        return desc.getImageDescriptor();
    }

    /**
     * @see org.eclipse.ui.IPersistable
     */
    public IStatus restoreState(IMemento memento) {
        // Restore the editor area workbooks layout/relationship
        final MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,
                WorkbenchMessages.get().EditorManager_problemsRestoringEditors, null);
        final String activeWorkbookID[] = new String[1];
        final ArrayList visibleEditors = new ArrayList(5);
        final IEditorReference activeEditor[] = new IEditorReference[1];

        IMemento areaMem = memento.getChild(IWorkbenchConstants.TAG_AREA);
        if (areaMem != null) {
            result.add(editorPresentation.restoreState(areaMem));
            activeWorkbookID[0] = areaMem.getString(IWorkbenchConstants.TAG_ACTIVE_WORKBOOK);
        }

        // Loop through the editors.

        IMemento[] editorMems = memento.getChildren(IWorkbenchConstants.TAG_EDITOR);
        for (int x = 0; x < editorMems.length; x++) {
            // for dynamic UI - call restoreEditorState to replace code which is
            // commented out
            restoreEditorState(editorMems[x], visibleEditors, activeEditor, result);
        }

        // restore the presentation
        if (areaMem != null) {
            result.add(editorPresentation.restorePresentationState(areaMem));
        }
        try {
            StartupThreading.runWithThrowable(new StartupRunnable() {

                public void runWithException() throws Throwable {
                    // Update each workbook with its visible editor.
                    for (int i = 0; i < visibleEditors.size(); i++) {
                        setVisibleEditor((IEditorReference) visibleEditors.get(i), false);
                    }

                    // Update the active workbook
                    if (activeWorkbookID[0] != null) {
                        editorPresentation.setActiveEditorWorkbookFromID(activeWorkbookID[0]);
                    }

                    if (activeEditor[0] != null) {
                        IWorkbenchPart editor = activeEditor[0].getPart(true);

                        if (editor != null) {
                            page.activate(editor);
                        }
                    }
                }
            });
        } catch (Throwable t) {
            // The exception is already logged.
            result.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0,
                    WorkbenchMessages.get().EditorManager_exceptionRestoringEditor, t));
        }

        return result;
    }

    /**
     * Save all of the editors in the workbench. Return true if successful.
     * Return false if the user has canceled the command.
     * @param confirm true if the user should be prompted before the save
     * @param closing true if the page is being closed
     * @param addNonPartSources true if saveables from non-part sources should be saved too.
     * @return false if the user canceled or an error occurred while saving
     */
    public boolean saveAll(boolean confirm, boolean closing, boolean addNonPartSources) {
        // Get the list of dirty editors and views. If it is
        // empty just return.
        ISaveablePart[] parts = page.getDirtyParts();
        if (parts.length == 0) {
            return true;
        }
        // saveAll below expects a mutable list
        List dirtyParts = new ArrayList(parts.length);
        for (int i = 0; i < parts.length; i++) {
            dirtyParts.add(parts[i]);
        }

        // If confirmation is required ..
        return saveAll(dirtyParts, confirm, closing, addNonPartSources, window);
    }

    /**
     * Saves the given dirty editors and views, optionally prompting the user.
     * 
     * @param dirtyParts
     *            the dirty views and editors
     * @param confirm
     *            <code>true</code> to prompt whether to save, <code>false</code>
     *            to save without prompting
     * @param closing
     *            <code>true</code> if the parts are being closed,
     *            <code>false</code> if just being saved without closing
     * @param addNonPartSources true if non-part sources should be saved too
     * @param window
     *            the window to use as the parent for the dialog that prompts to
     *            save multiple dirty editors and views
     * @return <code>true</code> on success, <code>false</code> if the user
     *         canceled the save or an error occurred while saving
     */
    public static boolean saveAll(List dirtyParts, boolean confirm, boolean closing, boolean addNonPartSources,
            final IWorkbenchWindow window) {
        return saveAll(dirtyParts, confirm, closing, addNonPartSources, window, window);
    }

    /**
     * Saves the given dirty editors and views, optionally prompting the user.
     * 
     * @param dirtyParts
     *            the dirty views and editors
     * @param confirm
     *            <code>true</code> to prompt whether to save,
     *            <code>false</code> to save without prompting
     * @param closing
     *            <code>true</code> if the parts are being closed,
     *            <code>false</code> if just being saved without closing
     * @param addNonPartSources
     *            true if non-part sources should be saved too
     * @param runnableContext
     *            the context in which to run long-running operations
     * @param shellProvider
     *            providing the shell to use as the parent for the dialog that
     *            prompts to save multiple dirty editors and views
     * @return <code>true</code> on success, <code>false</code> if the user
     *         canceled the save
     */
    public static boolean saveAll(List dirtyParts, final boolean confirm, final boolean closing,
            boolean addNonPartSources, final IRunnableContext runnableContext, final IShellProvider shellProvider) {
        // clone the input list
        dirtyParts = new ArrayList(dirtyParts);

        if (closing) {
            // if the parts are going to be closed, then we only save those that
            // need to be saved when closed, see bug 272070
            for (Iterator it = dirtyParts.iterator(); it.hasNext();) {
                ISaveablePart saveablePart = (ISaveablePart) it.next();
                if (!saveablePart.isSaveOnCloseNeeded()) {
                    it.remove();
                }
            }
        }

        List modelsToSave;
        if (confirm) {
            boolean saveable2Processed = false;
            // Process all parts that implement ISaveablePart2.
            // These parts are removed from the list after saving
            // them. We then need to restore the workbench to
            // its previous state, for now this is just last
            // active perspective.
            // Note that the given parts may come from multiple
            // windows, pages and perspectives.
            ListIterator listIterator = dirtyParts.listIterator();

            WorkbenchPage currentPage = null;
            Perspective currentPageOriginalPerspective = null;
            while (listIterator.hasNext()) {
                IWorkbenchPart part = (IWorkbenchPart) listIterator.next();
                if (part instanceof ISaveablePart2) {
                    WorkbenchPage page = (WorkbenchPage) part.getSite().getPage();
                    if (!Util.equals(currentPage, page)) {
                        if (currentPage != null && currentPageOriginalPerspective != null) {
                            if (!currentPageOriginalPerspective.equals(currentPage.getActivePerspective())) {
                                currentPage.setPerspective(currentPageOriginalPerspective.getDesc());
                            }
                        }
                        currentPage = page;
                        currentPageOriginalPerspective = page.getActivePerspective();
                    }
                    if (confirm) {
                        if (part instanceof IViewPart) {
                            Perspective perspective = page.getFirstPerspectiveWithView((IViewPart) part);
                            if (perspective != null) {
                                page.setPerspective(perspective.getDesc());
                            }
                        }
                        // show the window containing the page?
                        IWorkbenchWindow partsWindow = page.getWorkbenchWindow();
                        if (partsWindow != partsWindow.getWorkbench().getActiveWorkbenchWindow()) {
                            Shell shell = partsWindow.getShell();
                            if (shell.getMinimized()) {
                                shell.setMinimized(false);
                            }
                            shell.setActive();
                        }
                        page.bringToTop(part);
                    }
                    // try to save the part
                    int choice = SaveableHelper.savePart((ISaveablePart2) part, page.getWorkbenchWindow(), confirm);
                    if (choice == ISaveablePart2.CANCEL) {
                        // If the user cancels, don't restore the previous
                        // workbench state, as that will
                        // be an unexpected switch from the current state.
                        return false;
                    } else if (choice != ISaveablePart2.DEFAULT) {
                        saveable2Processed = true;
                        listIterator.remove();
                    }
                }
            }

            // try to restore the workbench to its previous state
            if (currentPage != null && currentPageOriginalPerspective != null) {
                if (!currentPageOriginalPerspective.equals(currentPage.getActivePerspective())) {
                    currentPage.setPerspective(currentPageOriginalPerspective.getDesc());
                }
            }

            // if processing a ISaveablePart2 caused other parts to be
            // saved, remove them from the list presented to the user.
            if (saveable2Processed) {
                listIterator = dirtyParts.listIterator();
                while (listIterator.hasNext()) {
                    ISaveablePart part = (ISaveablePart) listIterator.next();
                    if (!part.isDirty()) {
                        listIterator.remove();
                    }
                }
            }

            modelsToSave = convertToSaveables(dirtyParts, closing, addNonPartSources);

            // If nothing to save, return.
            if (modelsToSave.isEmpty()) {
                return true;
            }
            boolean canceled = SaveableHelper.waitForBackgroundSaveJobs(modelsToSave);
            if (canceled) {
                return false;
            }
            // Use a simpler dialog if there's only one
            if (modelsToSave.size() == 1) {
                Saveable model = (Saveable) modelsToSave.get(0);
                String message = NLS.bind(WorkbenchMessages.get().EditorManager_saveChangesQuestion,
                        model.getName());
                // Show a dialog.
                String[] buttons = new String[] { IDialogConstants.get().YES_LABEL, IDialogConstants.get().NO_LABEL,
                        IDialogConstants.get().CANCEL_LABEL };
                MessageDialog d = new MessageDialog(shellProvider.getShell(), WorkbenchMessages.get().Save_Resource,
                        null, message, MessageDialog.QUESTION, buttons, 0) {
                    protected int getShellStyle() {
                        return super.getShellStyle() | SWT.SHEET;
                    }
                };

                int choice = SaveableHelper.testGetAutomatedResponse();
                if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) {
                    choice = d.open();
                }

                // Branch on the user choice.
                // The choice id is based on the order of button labels
                // above.
                switch (choice) {
                case ISaveablePart2.YES: // yes
                    break;
                case ISaveablePart2.NO: // no
                    return true;
                default:
                case ISaveablePart2.CANCEL: // cancel
                    return false;
                }
            } else {
                ListSelectionDialog dlg = new ListSelectionDialog(shellProvider.getShell(), modelsToSave,
                        new ArrayContentProvider(), new WorkbenchPartLabelProvider(), RESOURCES_TO_SAVE_MESSAGE) {
                    protected int getShellStyle() {
                        return super.getShellStyle() | SWT.SHEET;
                    }
                };
                dlg.setInitialSelections(modelsToSave.toArray());
                dlg.setTitle(SAVE_RESOURCES_TITLE);

                // this "if" statement aids in testing.
                if (SaveableHelper.testGetAutomatedResponse() == SaveableHelper.USER_RESPONSE) {
                    int result = dlg.open();
                    //Just return false to prevent the operation continuing
                    if (result == IDialogConstants.CANCEL_ID) {
                        return false;
                    }

                    modelsToSave = Arrays.asList(dlg.getResult());
                }
            }
        } else {
            modelsToSave = convertToSaveables(dirtyParts, closing, addNonPartSources);
        }

        // If the editor list is empty return.
        if (modelsToSave.isEmpty()) {
            return true;
        }

        // Create save block.
        final List finalModels = modelsToSave;
        IRunnableWithProgress progressOp = new IRunnableWithProgress() {
            public void run(IProgressMonitor monitor) {
                IProgressMonitor monitorWrap = new EventLoopProgressMonitor(monitor);
                monitorWrap.beginTask(WorkbenchMessages.get().Saving_Modifications, finalModels.size());
                for (Iterator i = finalModels.iterator(); i.hasNext();) {
                    Saveable model = (Saveable) i.next();
                    // handle case where this model got saved as a result of saving another
                    if (!model.isDirty()) {
                        monitor.worked(1);
                        continue;
                    }
                    SaveableHelper.doSaveModel(model, new SubProgressMonitor(monitorWrap, 1), shellProvider,
                            closing || confirm);
                    if (monitorWrap.isCanceled()) {
                        break;
                    }
                }
                monitorWrap.done();
            }
        };

        // Do the save.
        return SaveableHelper.runProgressMonitorOperation(WorkbenchMessages.get().Save_All, progressOp,
                runnableContext, shellProvider);
    }

    /**
     * For each part (view or editor) in the given list, attempts to convert it
     * to one or more saveable models. Duplicate models are removed. If closing
     * is true, then models that will remain open in parts other than the given
     * parts are removed.
     * 
     * @param parts
     *            the parts (list of IViewPart or IEditorPart)
     * @param closing
     *            whether the parts are being closed
     * @param addNonPartSources
     *            whether non-part sources should be added (true for the Save
     *            All action, see bug 139004)
     * @return the dirty models
     */
    private static List convertToSaveables(List parts, boolean closing, boolean addNonPartSources) {
        ArrayList result = new ArrayList();
        HashSet seen = new HashSet();
        for (Iterator i = parts.iterator(); i.hasNext();) {
            IWorkbenchPart part = (IWorkbenchPart) i.next();
            Saveable[] saveables = getSaveables(part);
            for (int j = 0; j < saveables.length; j++) {
                Saveable saveable = saveables[j];
                if (saveable.isDirty() && !seen.contains(saveable)) {
                    seen.add(saveable);
                    if (!closing || closingLastPartShowingModel(saveable, parts, part.getSite().getPage())) {
                        result.add(saveable);
                    }
                }
            }
        }
        if (addNonPartSources) {
            SaveablesList saveablesList = (SaveablesList) PlatformUI.getWorkbench()
                    .getService(ISaveablesLifecycleListener.class);
            ISaveablesSource[] nonPartSources = saveablesList.getNonPartSources();
            for (int i = 0; i < nonPartSources.length; i++) {
                Saveable[] saveables = nonPartSources[i].getSaveables();
                for (int j = 0; j < saveables.length; j++) {
                    Saveable saveable = saveables[j];
                    if (saveable.isDirty() && !seen.contains(saveable)) {
                        seen.add(saveable);
                        result.add(saveable);
                    }
                }
            }
        }
        return result;
    }

    /**
     * Returns the saveable models provided by the given part.
     * If the part does not provide any models, a default model
     * is returned representing the part.
     * 
     * @param part the workbench part
     * @return the saveable models
     */
    private static Saveable[] getSaveables(IWorkbenchPart part) {
        if (part instanceof ISaveablesSource) {
            ISaveablesSource source = (ISaveablesSource) part;
            return source.getSaveables();
        }
        return new Saveable[] { new DefaultSaveable(part) };
    }

    /**
     * Returns true if, in the given page, no more parts will reference the
     * given model if the given parts are closed.
     * 
     * @param model
     *            the model
     * @param closingParts
     *            the parts being closed (list of IViewPart or IEditorPart)
     * @param page
     *            the page
     * @return <code>true</code> if no more parts in the page will reference
     *         the given model, <code>false</code> otherwise
     */
    private static boolean closingLastPartShowingModel(Saveable model, List closingParts, IWorkbenchPage page) {
        HashSet closingPartsWithSameModel = new HashSet();
        for (Iterator i = closingParts.iterator(); i.hasNext();) {
            IWorkbenchPart part = (IWorkbenchPart) i.next();
            Saveable[] models = getSaveables(part);
            if (Arrays.asList(models).contains(model)) {
                closingPartsWithSameModel.add(part);
            }
        }
        IWorkbenchPartReference[] pagePartRefs = ((WorkbenchPage) page).getAllParts();
        HashSet pagePartsWithSameModels = new HashSet();
        for (int i = 0; i < pagePartRefs.length; i++) {
            IWorkbenchPartReference partRef = pagePartRefs[i];
            IWorkbenchPart part = partRef.getPart(false);
            if (part != null) {
                Saveable[] models = getSaveables(part);
                if (Arrays.asList(models).contains(model)) {
                    pagePartsWithSameModels.add(part);
                }
            }
        }
        for (Iterator i = closingPartsWithSameModel.iterator(); i.hasNext();) {
            IWorkbenchPart part = (IWorkbenchPart) i.next();
            pagePartsWithSameModels.remove(part);
        }
        return pagePartsWithSameModels.isEmpty();
    }

    /*
     * Saves the workbench part.
     */
    public boolean savePart(final ISaveablePart saveable, IWorkbenchPart part, boolean confirm) {
        return SaveableHelper.savePart(saveable, part, window, confirm);
    }

    /**
     * @see IPersistablePart
     */
    public IStatus saveState(final IMemento memento) {

        final MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,
                WorkbenchMessages.get().EditorManager_problemsSavingEditors, null);

        // Save the editor area workbooks layout/relationship
        IMemento editorAreaMem = memento.createChild(IWorkbenchConstants.TAG_AREA);
        result.add(editorPresentation.saveState(editorAreaMem));

        // Save the active workbook id
        editorAreaMem.putString(IWorkbenchConstants.TAG_ACTIVE_WORKBOOK,
                editorPresentation.getActiveEditorWorkbookID());

        // Get each workbook
        ArrayList workbooks = editorPresentation.getWorkbooks();

        for (Iterator iter = workbooks.iterator(); iter.hasNext();) {
            EditorStack workbook = (EditorStack) iter.next();

            // Use the list of editors found in EditorStack; fix for 24091
            EditorPane editorPanes[] = workbook.getEditors();

            for (int i = 0; i < editorPanes.length; i++) {
                // Save each open editor.
                IEditorReference editorReference = editorPanes[i].getEditorReference();
                EditorReference e = (EditorReference) editorReference;
                final IEditorPart editor = editorReference.getEditor(false);
                if (editor == null) {
                    if (e.getMemento() != null) {
                        IMemento editorMem = memento.createChild(IWorkbenchConstants.TAG_EDITOR);
                        editorMem.putMemento(e.getMemento());
                    }
                    continue;
                }

                // for dynamic UI - add the next line to replace the subsequent
                // code which is commented out
                saveEditorState(memento, e, result);
            }
        }
        return result;
    }

    /**
     * Shows an editor. If <code>setFocus == true</code> then give it focus,
     * too.
     * 
     * @return true if the active editor was changed, false if not.
     */
    public boolean setVisibleEditor(IEditorReference newEd, boolean setFocus) {
        return editorPresentation.setVisibleEditor(newEd, setFocus);
    }

    // RAP [rh] unused code   
    //   private IPathEditorInput getPathEditorInput(IEditorInput input) {
    //      if (input instanceof IPathEditorInput) {
    //         return (IPathEditorInput) input;
    //      }
    //
    //      return (IPathEditorInput) Util.getAdapter(input, IPathEditorInput.class);
    //   }

    private class InnerEditor extends EditorReference {

        private IEditorReference outerEditor;

        private AbstractMultiEditor outerEditorPart;

        public InnerEditor(IEditorReference outerEditor, AbstractMultiEditor outerEditorPart, IEditorInput input,
                EditorDescriptor desc) {
            super(EditorManager.this, input, desc);
            this.outerEditor = outerEditor;
            this.outerEditorPart = outerEditorPart;
        }

        protected void doDisposePart() {
            this.outerEditorPart = null;
            super.doDisposePart();
        }

        protected PartPane createPane() {
            // MultiEditor backwards compatibility
            return new MultiEditorInnerPane((EditorPane) ((EditorReference) outerEditor).getPane(), this, page,
                    editorPresentation.getActiveWorkbook(), outerEditorPart instanceof MultiEditor);
        }

        protected Composite getPaneControlContainer() {
            // MultiEditor backwards compatibility
            if (outerEditorPart instanceof MultiEditor) {
                return super.getPaneControlContainer();
            }
            return outerEditorPart.getInnerEditorContainer(this);
        }

    }

    /*
     * Made public for Mylar in 3.3 - see bug 138666. Can be made private once
     * we have real API for this.
     */
    public void restoreEditorState(IMemento editorMem, ArrayList visibleEditors, IEditorReference[] activeEditor,
            MultiStatus result) {
        // String strFocus = editorMem.getString(IWorkbenchConstants.TAG_FOCUS);
        // boolean visibleEditor = "true".equals(strFocus); //$NON-NLS-1$
        final EditorReference e = new EditorReference(this, editorMem);

        // if the editor is not visible, ensure it is put in the correct
        // workbook. PR 24091

        final String workbookID = editorMem.getString(IWorkbenchConstants.TAG_WORKBOOK);

        try {
            StartupThreading.runWithPartInitExceptions(new StartupRunnable() {

                public void runWithException() throws Throwable {
                    createEditorTab(e, workbookID);
                }
            });

        } catch (PartInitException ex) {
            result.add(ex.getStatus());
        }

        String strActivePart = editorMem.getString(IWorkbenchConstants.TAG_ACTIVE_PART);
        if ("true".equals(strActivePart)) { //$NON-NLS-1$
            activeEditor[0] = e;
        }

        String strFocus = editorMem.getString(IWorkbenchConstants.TAG_FOCUS);
        boolean visibleEditor = "true".equals(strFocus); //$NON-NLS-1$
        if (visibleEditor) {
            visibleEditors.add(e);
        }
    }

    // for dynamic UI
    protected void saveEditorState(IMemento mem, IEditorReference ed, MultiStatus res) {
        final EditorReference editorRef = (EditorReference) ed;
        final IEditorPart editor = ed.getEditor(false);
        final IMemento memento = mem;
        final MultiStatus result = res;
        if (!(editor.getEditorSite() instanceof EditorSite)) {
            return;
        }
        final EditorSite site = (EditorSite) editor.getEditorSite();
        if (site.getPane() instanceof MultiEditorInnerPane) {
            return;
        }

        SafeRunner.run(new SafeRunnable() {
            public void run() {
                // Get the input.
                IEditorInput input = editor.getEditorInput();
                if (!input.exists()) {
                    return;
                }
                IPersistableElement persistable = input.getPersistable();
                if (persistable == null) {
                    return;
                }

                // Save editor.
                IMemento editorMem = memento.createChild(IWorkbenchConstants.TAG_EDITOR);
                editorMem.putString(IWorkbenchConstants.TAG_TITLE, editorRef.getTitle());
                editorMem.putString(IWorkbenchConstants.TAG_NAME, editorRef.getName());
                editorMem.putString(IWorkbenchConstants.TAG_ID, editorRef.getId());
                editorMem.putString(IWorkbenchConstants.TAG_TOOLTIP, editorRef.getTitleToolTip());

                editorMem.putString(IWorkbenchConstants.TAG_PART_NAME, editorRef.getPartName());

                if (editor instanceof IWorkbenchPart3) {
                    Map properties = ((IWorkbenchPart3) editor).getPartProperties();
                    if (!properties.isEmpty()) {
                        IMemento propBag = editorMem.createChild(IWorkbenchConstants.TAG_PROPERTIES);
                        Iterator i = properties.entrySet().iterator();
                        while (i.hasNext()) {
                            Map.Entry entry = (Map.Entry) i.next();
                            IMemento p = propBag.createChild(IWorkbenchConstants.TAG_PROPERTY,
                                    (String) entry.getKey());
                            p.putTextData((String) entry.getValue());
                        }
                    }
                }

                if (editorRef.isPinned()) {
                    editorMem.putString(IWorkbenchConstants.TAG_PINNED, "true"); //$NON-NLS-1$
                }

                EditorPane editorPane = (EditorPane) ((EditorSite) editor.getEditorSite()).getPane();
                editorMem.putString(IWorkbenchConstants.TAG_WORKBOOK, editorPane.getWorkbook().getID());

                if (editor == page.getActiveEditor()) {
                    editorMem.putString(IWorkbenchConstants.TAG_ACTIVE_PART, "true"); //$NON-NLS-1$
                }

                if (editorPane == editorPane.getWorkbook().getSelection()) {
                    editorMem.putString(IWorkbenchConstants.TAG_FOCUS, "true"); //$NON-NLS-1$
                }

                if (input instanceof IPathEditorInput) {
                    IPath path = ((IPathEditorInput) input).getPath();
                    if (path != null) {
                        editorMem.putString(IWorkbenchConstants.TAG_PATH, path.toString());
                    }
                }

                // Save input.
                IMemento inputMem = editorMem.createChild(IWorkbenchConstants.TAG_INPUT);
                inputMem.putString(IWorkbenchConstants.TAG_FACTORY_ID, persistable.getFactoryId());
                persistable.saveState(inputMem);

                // any editors that want to persist state
                if (editor instanceof IPersistableEditor) {
                    IMemento editorState = editorMem.createChild(IWorkbenchConstants.TAG_EDITOR_STATE);
                    ((IPersistableEditor) editor).saveState(editorState);
                }
            }

            public void handleException(Throwable e) {
                result.add(new Status(IStatus.ERROR, PlatformUI.PLUGIN_ID, 0,
                        NLS.bind(WorkbenchMessages.get().EditorManager_unableToSaveEditor, editorRef.getTitle()),
                        e));
            }
        });
    }

    // for dynamic UI
    public IMemento getMemento(IEditorReference e) {
        if (e instanceof EditorReference) {
            return ((EditorReference) e).getMemento();
        }
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#removeExtension(org.eclipse.core.runtime.IExtension,
     *      java.lang.Object[])
     */
    public void removeExtension(IExtension source, Object[] objects) {
        for (int i = 0; i < objects.length; i++) {
            if (objects[i] instanceof IEditorPart) {
                // close the editor and clean up the editor history

                IEditorPart editor = (IEditorPart) objects[i];
                IEditorInput input = editor.getEditorInput();
                page.closeEditor(editor, true);
                ((Workbench) window.getWorkbench()).getEditorHistory().remove(input);
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.core.runtime.dynamicHelpers.IExtensionChangeHandler#addExtension(org.eclipse.core.runtime.dynamicHelpers.IExtensionTracker,
     *      org.eclipse.core.runtime.IExtension)
     */
    public void addExtension(IExtensionTracker tracker, IExtension extension) {
        // Nothing to do
    }

    /**
     * @return
     */
    /*package*/ IEditorReference openEmptyTab() {
        IEditorInput input = new NullEditorInput();
        EditorDescriptor desc = (EditorDescriptor) ((EditorRegistry) getEditorRegistry())
                .findEditor(EditorRegistry.EMPTY_EDITOR_ID);
        EditorReference result = new EditorReference(this, input, desc);
        try {
            createEditorTab(result, ""); //$NON-NLS-1$
            return result;
        } catch (PartInitException e) {
            StatusManager.getManager().handle(StatusUtil.newStatus(WorkbenchPlugin.PI_WORKBENCH, e));
        }
        return null;
    }

    public static boolean useIPersistableEditor() {
        IPreferenceStore store = WorkbenchPlugin.getDefault().getPreferenceStore();
        return store.getBoolean(IPreferenceConstants.USE_IPERSISTABLE_EDITORS);
    }
}