Bug 277836 [Compatibility] Setup Compatibility so you can run tests
Allow MPEP and editors opened through open declaration to come up
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/e4/extensions/LegacyViewFactory.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/e4/extensions/LegacyViewFactory.java
index f8acb64..669561c 100644
--- a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/e4/extensions/LegacyViewFactory.java
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/e4/extensions/LegacyViewFactory.java
@@ -1,5 +1,8 @@
 package org.eclipse.e4.extensions;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.e4.core.services.context.EclipseContextFactory;
@@ -17,12 +20,17 @@
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Widget;
+import org.eclipse.ui.IEditorActionBarContributor;
 import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IViewPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.internal.EditorActionBars;
+import org.eclipse.ui.internal.EditorActionBuilder;
 import org.eclipse.ui.internal.EditorSite;
 import org.eclipse.ui.internal.ViewSite;
 import org.eclipse.ui.internal.WorkbenchPage;
+import org.eclipse.ui.internal.registry.EditorDescriptor;
 import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants;
 
 public class LegacyViewFactory extends SWTPartFactory {
@@ -59,14 +67,15 @@
 	private Control createEditor(MContributedPart<MPart<?>> part,
 			IConfigurationElement editorElement) {
 		Composite parent = (Composite) getParentWidget(part);
+		EditorDescriptor desc = new EditorDescriptor(part.getId(),
+				editorElement);
 
 		// part.setPlugin(viewContribution.getContributor().getName());
 		part.setIconURI(editorElement.getAttribute("icon")); //$NON-NLS-1$
 		//part.setName(editorElement.getAttribute("name")); //$NON-NLS-1$
 		IEditorPart impl = null;
 		try {
-			impl = (IEditorPart) editorElement
-					.createExecutableExtension("class"); //$NON-NLS-1$
+			impl = desc.createEditor();
 		} catch (CoreException e) {
 			e.printStackTrace();
 		}
@@ -91,6 +100,9 @@
 					.get(WorkbenchPage.class.getName());
 			ModelEditorReference ref = new ModelEditorReference(part, page);
 			EditorSite site = new EditorSite(ref, impl, page);
+			EditorActionBars bars = getEditorActionBars(desc, page, page
+					.getWorkbenchWindow(), part.getId());
+			site.setActionBars(bars);
 			site.setConfigurationElement(editorElement);
 			impl.init(site, (IEditorInput) localContext.get(IEditorInput.class
 					.getName()));
@@ -104,6 +116,43 @@
 		return null;
 	}
 
+	Map<String, EditorActionBars> actionCache = new HashMap<String, EditorActionBars>();
+
+	private EditorActionBars getEditorActionBars(EditorDescriptor desc,
+			WorkbenchPage page, IWorkbenchWindow workbenchWindow, String type) {
+		// Get the editor type.
+
+		// If an action bar already exists for this editor type return it.
+		EditorActionBars actionBars = actionCache.get(type);
+		if (actionBars != null) {
+			actionBars.addRef();
+			return actionBars;
+		}
+
+		// Create a new action bar set.
+		actionBars = new EditorActionBars(page, workbenchWindow, 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;
+	}
+
 	private Control createView(MContributedPart<MPart<?>> part,
 			IConfigurationElement viewContribution) {
 		Composite parent = (Composite) getParentWidget(part);
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/EditorSite.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/EditorSite.java
index d3728b4..79a10dd 100644
--- a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/EditorSite.java
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/EditorSite.java
@@ -12,12 +12,8 @@
 
 import java.util.ArrayList;
 
-import org.eclipse.jface.action.IMenuManager;
-import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.MenuManager;
-import org.eclipse.jface.internal.provisional.action.ToolBarManager2;
 import org.eclipse.jface.viewers.ISelectionProvider;
-import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.IActionBars2;
 import org.eclipse.ui.IEditorActionBarContributor;
 import org.eclipse.ui.IEditorPart;
@@ -44,29 +40,6 @@
 			WorkbenchPage page) {
 		super(ref, editor, page);
 
-		SubActionBars bars = new SubActionBars(((WorkbenchPage) page)
-				.getActionBars(), serviceLocator) {
-			private IToolBarManager tbMgr;
-			private MenuManager menuMgr;
-
-			@Override
-			public IToolBarManager getToolBarManager() {
-				if (tbMgr == null) {
-					tbMgr = new ToolBarManager2();
-				}
-				return tbMgr;
-			}
-
-			@Override
-			public IMenuManager getMenuManager() {
-				if (menuMgr == null) {
-					menuMgr = new MenuManager();
-				}
-				return menuMgr;
-			}
-		};
-		e4Context.set(IActionBars.class.getName(), bars);
-		setActionBars(bars);
 		// Initialize the services specific to this editor site.
 		initializeDefaultServices();
 	}
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/PartSite.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/PartSite.java
index 2602ed1..9a6be66 100644
--- a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/PartSite.java
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/PartSite.java
@@ -388,6 +388,7 @@
 	 * Sets the action bars for the part.
 	 */
 	public void setActionBars(SubActionBars bars) {
+		e4Context.set(IActionBars.class.getName(), bars);
 		actionBars = bars;
 	}
 
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/ViewSite.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/ViewSite.java
index c509573..3509af1 100644
--- a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/ViewSite.java
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/ViewSite.java
@@ -14,7 +14,6 @@
 import org.eclipse.jface.action.IToolBarManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.internal.provisional.action.ToolBarManager2;
-import org.eclipse.ui.IActionBars;
 import org.eclipse.ui.IViewPart;
 import org.eclipse.ui.IViewReference;
 import org.eclipse.ui.IViewSite;
@@ -55,7 +54,6 @@
 				return menuMgr;
 			}
 		};
-		e4Context.set(IActionBars.class.getName(), bars);
 		setActionBars(bars);
 	}
 
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/registry/EditorDescriptor.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/registry/EditorDescriptor.java
new file mode 100644
index 0000000..7c94906
--- /dev/null
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/internal/registry/EditorDescriptor.java
@@ -0,0 +1,682 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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
+ *     James Blackburn - Bug 256316 getImageDescriptor() is not thread safe 
+ *******************************************************************************/
+package org.eclipse.ui.internal.registry;
+
+import java.io.File;
+import java.io.Serializable;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.program.Program;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorDescriptor;
+import org.eclipse.ui.IEditorMatchingStrategy;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IMemento;
+import org.eclipse.ui.IPluginContribution;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.internal.IWorkbenchConstants;
+import org.eclipse.ui.internal.WorkbenchImages;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.misc.ProgramImageDescriptor;
+import org.eclipse.ui.internal.tweaklets.InterceptContributions;
+import org.eclipse.ui.internal.tweaklets.Tweaklets;
+import org.eclipse.ui.internal.util.Util;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+/**
+ * @see IEditorDescriptor
+ */
+public final class EditorDescriptor implements IEditorDescriptor, Serializable,
+		IPluginContribution {
+
+	/**
+	 * Generated serial version UID for this class.
+	 * 
+	 * @since 3.1
+	 */
+	private static final long serialVersionUID = 3905241225668998961L;
+
+	// @issue the following constants need not be public; see bug 47600
+	/**
+	 * Open internal constant. Value <code>0x01</code>.
+	 */
+	public static final int OPEN_INTERNAL = 0x01;
+
+	/**
+	 * Open in place constant. Value <code>0x02</code>.
+	 */
+	public static final int OPEN_INPLACE = 0x02;
+
+	/**
+	 * Open external constant. Value <code>0x04</code>.
+	 */
+	public static final int OPEN_EXTERNAL = 0x04;
+
+	private String editorName;
+
+	private String imageFilename;
+
+	private transient ImageDescriptor imageDesc;
+	private transient Object imageDescLock = new Object();
+
+	private boolean testImage = true;
+
+	private String className;
+
+	private String launcherName;
+
+	private String fileName;
+
+	private String id = Util.ZERO_LENGTH_STRING;
+
+	private boolean matchingStrategyChecked = false;
+	private IEditorMatchingStrategy matchingStrategy;
+
+	private Program program;
+
+	// The id of the plugin which contributed this editor, null for external
+	// editors
+	private String pluginIdentifier;
+
+	private int openMode = 0;
+
+	private transient IConfigurationElement configurationElement;
+
+	/**
+	 * Create a new instance of an editor descriptor. Limited to internal
+	 * framework calls.
+	 * 
+	 * @param element
+	 * @param id2
+	 */
+	public EditorDescriptor(String id2, IConfigurationElement element) {
+		setID(id2);
+		setConfigurationElement(element);
+	}
+
+	/**
+	 * Create a new instance of an editor descriptor. Limited to internal
+	 * framework calls.
+	 */
+	/* package */EditorDescriptor() {
+		super();
+	}
+
+	/**
+	 * Creates a descriptor for an external program.
+	 * 
+	 * @param filename
+	 *            the external editor full path and filename
+	 * @return the editor descriptor
+	 */
+	public static EditorDescriptor createForProgram(String filename) {
+		if (filename == null) {
+			throw new IllegalArgumentException();
+		}
+		EditorDescriptor editor = new EditorDescriptor();
+
+		editor.setFileName(filename);
+		editor.setID(filename);
+		editor.setOpenMode(OPEN_EXTERNAL);
+
+		// Isolate the program name (no directory or extension)
+		int start = filename.lastIndexOf(File.separator);
+		String name;
+		if (start != -1) {
+			name = filename.substring(start + 1);
+		} else {
+			name = filename;
+		}
+		int end = name.lastIndexOf('.');
+		if (end != -1) {
+			name = name.substring(0, end);
+		}
+		editor.setName(name);
+
+		// get the program icon without storing it in the registry
+		ImageDescriptor imageDescriptor = new ProgramImageDescriptor(filename,
+				0);
+		editor.setImageDescriptor(imageDescriptor);
+
+		return editor;
+	}
+
+	/**
+	 * Return the program called programName. Return null if it is not found.
+	 * 
+	 * @return org.eclipse.swt.program.Program
+	 */
+	private static Program findProgram(String programName) {
+
+		Program[] programs = Program.getPrograms();
+		for (int i = 0; i < programs.length; i++) {
+			if (programs[i].getName().equals(programName)) {
+				return programs[i];
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * Create the editor action bar contributor for editors of this type.
+	 * 
+	 * @return the action bar contributor, or <code>null</code>
+	 */
+	public IEditorActionBarContributor createActionBarContributor() {
+		// Handle case for predefined editor descriptors, like the
+		// one for IEditorRegistry.SYSTEM_INPLACE_EDITOR_ID, which
+		// don't have a configuration element.
+		if (configurationElement == null) {
+			return null;
+		}
+
+		// Get the contributor class name.
+		String className = configurationElement
+				.getAttribute(IWorkbenchRegistryConstants.ATT_CONTRIBUTOR_CLASS);
+		if (className == null) {
+			return null;
+		}
+
+		// Create the contributor object.
+		IEditorActionBarContributor contributor = null;
+		try {
+			contributor = (IEditorActionBarContributor) WorkbenchPlugin
+					.createExtension(configurationElement,
+							IWorkbenchRegistryConstants.ATT_CONTRIBUTOR_CLASS);
+		} catch (CoreException e) {
+			WorkbenchPlugin.log("Unable to create editor contributor: " + //$NON-NLS-1$
+					id, e.getStatus());
+		}
+		return contributor;
+	}
+
+	/**
+	 * Return the editor class name.
+	 * 
+	 * @return the class name
+	 */
+	public String getClassName() {
+		if (configurationElement == null) {
+			return className;
+		}
+		return RegistryReader.getClassValue(configurationElement,
+				IWorkbenchRegistryConstants.ATT_CLASS);
+	}
+
+	/**
+	 * Return the configuration element used to define this editor, or
+	 * <code>null</code>.
+	 * 
+	 * @return the element or null
+	 */
+	public IConfigurationElement getConfigurationElement() {
+		return configurationElement;
+	}
+
+	/**
+	 * Create an editor part based on this descriptor.
+	 * 
+	 * @return the editor part
+	 * @throws CoreException
+	 *             thrown if there is an issue creating the editor
+	 */
+	public IEditorPart createEditor() throws CoreException {
+		Object extension = WorkbenchPlugin.createExtension(
+				getConfigurationElement(),
+				IWorkbenchRegistryConstants.ATT_CLASS);
+		return ((InterceptContributions) Tweaklets
+				.get(InterceptContributions.KEY)).tweakEditor(extension);
+	}
+
+	/**
+	 * Return the file name of the command to execute for this editor.
+	 * 
+	 * @return the file name to execute
+	 */
+	public String getFileName() {
+		if (program == null) {
+			if (configurationElement == null) {
+				return fileName;
+			}
+			return configurationElement
+					.getAttribute(IWorkbenchRegistryConstants.ATT_COMMAND);
+		}
+		return program.getName();
+	}
+
+	/**
+	 * Return the id for this editor.
+	 * 
+	 * @return the id
+	 */
+	public String getId() {
+		if (program == null) {
+			if (configurationElement == null) {
+				return Util.safeString(id);
+			}
+			return Util.safeString(configurationElement
+					.getAttribute(IWorkbenchRegistryConstants.ATT_ID));
+
+		}
+		return Util.safeString(program.getName());
+	}
+
+	/**
+	 * Return the image descriptor describing this editor.
+	 * 
+	 * @return the image descriptor
+	 */
+	public ImageDescriptor getImageDescriptor() {
+		ImageDescriptor tempDescriptor = null;
+
+		synchronized (imageDescLock) {
+			if (!testImage)
+				return imageDesc;
+
+			if (imageDesc == null) {
+				String imageFileName = getImageFilename();
+				String command = getFileName();
+				if (imageFileName != null && configurationElement != null)
+					tempDescriptor = AbstractUIPlugin
+							.imageDescriptorFromPlugin(configurationElement
+									.getNamespaceIdentifier(), imageFileName);
+				else if (command != null)
+					tempDescriptor = WorkbenchImages
+							.getImageDescriptorFromProgram(command, 0);
+			} else
+				tempDescriptor = imageDesc;
+
+			if (tempDescriptor == null) { // still null? return default image
+				imageDesc = WorkbenchImages
+						.getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
+				testImage = false;
+				return imageDesc;
+			}
+		}
+
+		// Verifies that the image descriptor generates an image. If not, the
+		// descriptor is
+		// replaced with the default image.
+		// We must create the image without holding any locks, since there is a
+		// potential for deadlock
+		// on Linux due to SWT's implementation of Image. See bugs 265028 and
+		// 256316 for details.
+		Image img = tempDescriptor.createImage(false);
+		if (img == null) // @issue what should be the default image?
+			tempDescriptor = WorkbenchImages
+					.getImageDescriptor(ISharedImages.IMG_OBJ_FILE);
+		else
+			img.dispose();
+		// <----- End of must-not-lock part
+
+		// reenter synchronized block
+		synchronized (imageDescLock) {
+			// if another thread has set the image description, use it
+			if (!testImage)
+				return imageDesc;
+			// otherwise set the image description we calculated above
+			imageDesc = tempDescriptor;
+			testImage = false;
+			return imageDesc;
+		}
+	}
+
+	/**
+	 * The Image to use to repesent this editor
+	 */
+	/* package */void setImageDescriptor(ImageDescriptor desc) {
+		synchronized (imageDescLock) {
+			imageDesc = desc;
+			testImage = true;
+		}
+	}
+
+	/**
+	 * The name of the image describing this editor.
+	 * 
+	 * @return the image file name
+	 */
+	public String getImageFilename() {
+		if (configurationElement == null) {
+			return imageFilename;
+		}
+		return configurationElement
+				.getAttribute(IWorkbenchRegistryConstants.ATT_ICON);
+	}
+
+	/**
+	 * Return the user printable label for this editor.
+	 * 
+	 * @return the label
+	 */
+	public String getLabel() {
+		if (program == null) {
+			if (configurationElement == null) {
+				return editorName;
+			}
+			return configurationElement
+					.getAttribute(IWorkbenchRegistryConstants.ATT_NAME);
+		}
+		return program.getName();
+	}
+
+	/**
+	 * Returns the class name of the launcher.
+	 * 
+	 * @return the launcher class name
+	 */
+	public String getLauncher() {
+		if (configurationElement == null) {
+			return launcherName;
+		}
+		return configurationElement
+				.getAttribute(IWorkbenchRegistryConstants.ATT_LAUNCHER);
+	}
+
+	/**
+	 * Return the contributing plugin id.
+	 * 
+	 * @return the contributing plugin id
+	 */
+	public String getPluginID() {
+		if (configurationElement != null) {
+			return configurationElement.getNamespace();
+		}
+		return pluginIdentifier;
+	}
+
+	/**
+	 * Get the program for the receiver if there is one.
+	 * 
+	 * @return Program
+	 */
+	public Program getProgram() {
+		return this.program;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IEditorDescriptor#isInternal
+	 */
+	public boolean isInternal() {
+		return getOpenMode() == OPEN_INTERNAL;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IEditorDescriptor#isOpenInPlace
+	 */
+	public boolean isOpenInPlace() {
+		return getOpenMode() == OPEN_INPLACE;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IEditorDescriptor#isOpenExternal
+	 */
+	public boolean isOpenExternal() {
+		return getOpenMode() == OPEN_EXTERNAL;
+	}
+
+	/**
+	 * Load the object properties from a memento.
+	 * 
+	 * @return <code>true</code> if the values are valid, <code>false</code>
+	 *         otherwise
+	 */
+	protected boolean loadValues(IMemento memento) {
+		editorName = memento.getString(IWorkbenchConstants.TAG_LABEL);
+		imageFilename = memento.getString(IWorkbenchConstants.TAG_IMAGE);
+		className = memento.getString(IWorkbenchConstants.TAG_CLASS);
+		launcherName = memento.getString(IWorkbenchConstants.TAG_LAUNCHER);
+		fileName = memento.getString(IWorkbenchConstants.TAG_FILE);
+		id = Util.safeString(memento.getString(IWorkbenchConstants.TAG_ID));
+		pluginIdentifier = memento.getString(IWorkbenchConstants.TAG_PLUGIN);
+
+		Integer openModeInt = memento
+				.getInteger(IWorkbenchConstants.TAG_OPEN_MODE);
+		if (openModeInt != null) {
+			openMode = openModeInt.intValue();
+		} else {
+			// legacy: handle the older attribute names, needed to allow reading
+			// of pre-3.0-RCP workspaces
+			boolean internal = new Boolean(memento
+					.getString(IWorkbenchConstants.TAG_INTERNAL))
+					.booleanValue();
+			boolean openInPlace = new Boolean(memento
+					.getString(IWorkbenchConstants.TAG_OPEN_IN_PLACE))
+					.booleanValue();
+			if (internal) {
+				openMode = OPEN_INTERNAL;
+			} else {
+				if (openInPlace) {
+					openMode = OPEN_INPLACE;
+				} else {
+					openMode = OPEN_EXTERNAL;
+				}
+			}
+		}
+		if (openMode != OPEN_EXTERNAL && openMode != OPEN_INTERNAL
+				&& openMode != OPEN_INPLACE) {
+			WorkbenchPlugin
+					.log("Ignoring editor descriptor with invalid openMode: " + this); //$NON-NLS-1$
+			return false;
+		}
+
+		String programName = memento
+				.getString(IWorkbenchConstants.TAG_PROGRAM_NAME);
+		if (programName != null) {
+			this.program = findProgram(programName);
+		}
+		return true;
+	}
+
+	/**
+	 * Save the object values in a IMemento
+	 */
+	protected void saveValues(IMemento memento) {
+		memento.putString(IWorkbenchConstants.TAG_LABEL, getLabel());
+		memento.putString(IWorkbenchConstants.TAG_IMAGE, getImageFilename());
+		memento.putString(IWorkbenchConstants.TAG_CLASS, getClassName());
+		memento.putString(IWorkbenchConstants.TAG_LAUNCHER, getLauncher());
+		memento.putString(IWorkbenchConstants.TAG_FILE, getFileName());
+		memento.putString(IWorkbenchConstants.TAG_ID, getId());
+		memento.putString(IWorkbenchConstants.TAG_PLUGIN, getPluginId());
+
+		memento.putInteger(IWorkbenchConstants.TAG_OPEN_MODE, getOpenMode());
+		// legacy: handle the older attribute names, needed to allow reading of
+		// workspace by pre-3.0-RCP eclipses
+		memento.putString(IWorkbenchConstants.TAG_INTERNAL, String
+				.valueOf(isInternal()));
+		memento.putString(IWorkbenchConstants.TAG_OPEN_IN_PLACE, String
+				.valueOf(isOpenInPlace()));
+
+		if (this.program != null) {
+			memento.putString(IWorkbenchConstants.TAG_PROGRAM_NAME,
+					this.program.getName());
+		}
+	}
+
+	/**
+	 * Return the open mode of this editor.
+	 * 
+	 * @return the open mode of this editor
+	 * @since 3.1
+	 */
+	private int getOpenMode() {
+		if (configurationElement == null) { // if we've been serialized, return
+			// our serialized value
+			return openMode;
+		} else if (getLauncher() != null) {
+			// open using a launcer
+			return EditorDescriptor.OPEN_EXTERNAL;
+		} else if (getFileName() != null) {
+			// open using an external editor
+			return EditorDescriptor.OPEN_EXTERNAL;
+		} else if (getPluginId() != null) {
+			// open using an internal editor
+			return EditorDescriptor.OPEN_INTERNAL;
+		} else {
+			return 0; // default for system editor
+		}
+	}
+
+	/**
+	 * Set the class name of an internal editor.
+	 */
+	/* package */void setClassName(String newClassName) {
+		className = newClassName;
+	}
+
+	/**
+	 * Set the configuration element which contributed this editor.
+	 */
+	/* package */void setConfigurationElement(
+			IConfigurationElement newConfigurationElement) {
+		configurationElement = newConfigurationElement;
+	}
+
+	/**
+	 * Set the filename of an external editor.
+	 */
+	/* package */void setFileName(String aFileName) {
+		fileName = aFileName;
+	}
+
+	/**
+	 * Set the id of the editor. For internal editors this is the id as provided
+	 * in the extension point For external editors it is path and filename of
+	 * the editor
+	 */
+	/* package */void setID(String anID) {
+		Assert.isNotNull(anID);
+		id = anID;
+	}
+
+	/**
+	 * The name of the image to use for this editor.
+	 */
+	/* package */void setImageFilename(String aFileName) {
+		imageFilename = aFileName;
+	}
+
+	/**
+	 * Sets the new launcher class name
+	 * 
+	 * @param newLauncher
+	 *            the new launcher
+	 */
+	/* package */void setLauncher(String newLauncher) {
+		launcherName = newLauncher;
+	}
+
+	/**
+	 * The label to show for this editor.
+	 */
+	/* package */void setName(String newName) {
+		editorName = newName;
+	}
+
+	/**
+	 * Sets the open mode of this editor descriptor.
+	 * 
+	 * @param mode
+	 *            the open mode
+	 * 
+	 * @issue this method is public as a temporary fix for bug 47600
+	 */
+	public void setOpenMode(int mode) {
+		openMode = mode;
+	}
+
+	/**
+	 * The id of the plugin which contributed this editor, null for external
+	 * editors.
+	 */
+	/* package */void setPluginIdentifier(String anID) {
+		pluginIdentifier = anID;
+	}
+
+	/**
+	 * Set the receivers program.
+	 * 
+	 * @param newProgram
+	 */
+	/* package */void setProgram(Program newProgram) {
+
+		this.program = newProgram;
+		if (editorName == null) {
+			setName(newProgram.getName());
+		}
+	}
+
+	/**
+	 * For debugging purposes only.
+	 */
+	public String toString() {
+		return "EditorDescriptor(id=" + getId() + ", label=" + getLabel() + ")"; //$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-1$
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.activities.support.IPluginContribution#getLocalId()
+	 */
+	public String getLocalId() {
+		return getId();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.activities.support.IPluginContribution#getPluginId()
+	 */
+	public String getPluginId() {
+		return getPluginID();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IEditorDescriptor#getEditorManagementPolicy()
+	 */
+	public IEditorMatchingStrategy getEditorMatchingStrategy() {
+		if (matchingStrategy == null && !matchingStrategyChecked) {
+			matchingStrategyChecked = true;
+			if (program == null && configurationElement != null) {
+				if (configurationElement
+						.getAttribute(IWorkbenchRegistryConstants.ATT_MATCHING_STRATEGY) != null) {
+					try {
+						matchingStrategy = (IEditorMatchingStrategy) WorkbenchPlugin
+								.createExtension(
+										configurationElement,
+										IWorkbenchRegistryConstants.ATT_MATCHING_STRATEGY);
+					} catch (CoreException e) {
+						WorkbenchPlugin
+								.log(
+										"Error creating editor management policy for editor id " + getId(), e); //$NON-NLS-1$
+					}
+				}
+			}
+		}
+		return matchingStrategy;
+	}
+
+}
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorPart.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorPart.java
new file mode 100644
index 0000000..cb95109
--- /dev/null
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorPart.java
@@ -0,0 +1,1311 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2009 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
+ *******************************************************************************/
+package org.eclipse.ui.part;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.util.Tracing;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.e4.core.services.context.EclipseContextFactory;
+import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.workbench.ui.internal.UISchedulerStrategy;
+import org.eclipse.jface.dialogs.IPageChangeProvider;
+import org.eclipse.jface.dialogs.IPageChangedListener;
+import org.eclipse.jface.dialogs.PageChangedEvent;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.SafeRunnable;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.custom.CTabItem;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IKeyBindingService;
+import org.eclipse.ui.INestableKeyBindingService;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.IPropertyListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.handlers.IHandlerService;
+import org.eclipse.ui.internal.EditorSite;
+import org.eclipse.ui.internal.PartSite;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.misc.Policy;
+import org.eclipse.ui.internal.services.INestable;
+import org.eclipse.ui.internal.services.IServiceLocatorCreator;
+import org.eclipse.ui.internal.services.ServiceLocator;
+import org.eclipse.ui.internal.util.Util;
+import org.eclipse.ui.services.IDisposable;
+import org.eclipse.ui.services.IServiceLocator;
+
+/**
+ * A multi-page editor is an editor with multiple pages, each of which may
+ * contain an editor or an arbitrary SWT control.
+ * <p>
+ * Subclasses must implement the following methods:
+ * <ul>
+ * <li><code>createPages</code> - to create the required pages by calling one of
+ * the <code>addPage</code> methods</li>
+ * <li><code>IEditorPart.doSave</code> - to save contents of editor</li>
+ * <li><code>IEditorPart.doSaveAs</code> - to save contents of editor</li>
+ * <li><code>IEditorPart.isSaveAsAllowed</code> - to enable Save As</li>
+ * <li><code>IEditorPart.gotoMarker</code> - to scroll to a marker</li>
+ * </ul>
+ * </p>
+ * <p>
+ * Multi-page editors have a single action bar contributor, which manages
+ * contributions for all the pages. The contributor must be a subclass of
+ * <code>MultiPageEditorActionBarContributor</code>. Note that since any nested
+ * editors are created directly in code by callers of
+ * <code>addPage(IEditorPart,IEditorInput)</code>, nested editors do not have
+ * their own contributors.
+ * </p>
+ * <p>
+ * As of 3.5 multi-page editors will post PageChangedEvents at the end of
+ * {@link #pageChange(int)}. Subclasses may override {@link #getSelectedPage()}
+ * to return a page appropriate to their multi-page editor. IPartListener2
+ * listeners registered with the IPartService can implement IPageChangedListener
+ * to be notified about all page change events within the workbench page or
+ * workbench window.
+ * </p>
+ * 
+ * @see org.eclipse.ui.part.MultiPageEditorActionBarContributor
+ * @see org.eclipse.jface.dialogs.IPageChangeProvider
+ * @see org.eclipse.jface.dialogs.IPageChangedListener
+ * @see org.eclipse.ui.IPartService
+ */
+public abstract class MultiPageEditorPart extends EditorPart implements
+		IPageChangeProvider {
+
+	private static final String COMMAND_NEXT_SUB_TAB = "org.eclipse.ui.navigate.nextSubTab"; //$NON-NLS-1$
+	private static final String COMMAND_PREVIOUS_SUB_TAB = "org.eclipse.ui.navigate.previousSubTab"; //$NON-NLS-1$
+
+	/**
+	 * Subclasses that override {@link #createPageContainer(Composite)} can use
+	 * this constant to get a site for the container that can be active while
+	 * the current page is deactivated.
+	 * 
+	 * @since 3.4
+	 * @see #activateSite()
+	 * @see #deactivateSite(boolean, boolean)
+	 * @see #getPageSite(int)
+	 */
+	protected static final int PAGE_CONTAINER_SITE = 65535;
+
+	/**
+	 * Private tracing output.
+	 */
+	private static final String TRACING_COMPONENT = "MPE"; //$NON-NLS-1$
+
+	/**
+	 * The active service locator. This value may be <code>null</code> if there
+	 * is no selected page, or if the selected page is a control with no site.
+	 */
+	private INestable activeServiceLocator;
+
+	/**
+	 * The container widget.
+	 */
+	private CTabFolder container;
+
+	/**
+	 * List of nested editors. Element type: IEditorPart. Need to hang onto them
+	 * here, in addition to using get/setData on the items, because dispose()
+	 * needs to access them, but widgetry has already been disposed at that
+	 * point.
+	 */
+	private ArrayList nestedEditors = new ArrayList(3);
+
+	private List pageSites = new ArrayList(3);
+
+	private IServiceLocator pageContainerSite;
+
+	private ListenerList pageChangeListeners = new ListenerList(
+			ListenerList.IDENTITY);
+
+	/**
+	 * Creates an empty multi-page editor with no pages.
+	 */
+	protected MultiPageEditorPart() {
+		super();
+	}
+
+	/**
+	 * Creates and adds a new page containing the given control to this
+	 * multi-page editor. The control may be <code>null</code>, allowing it to
+	 * be created and set later using <code>setControl</code>.
+	 * 
+	 * @param control
+	 *            the control, or <code>null</code>
+	 * @return the index of the new page
+	 * 
+	 * @see MultiPageEditorPart#setControl(int, Control)
+	 */
+	public int addPage(Control control) {
+		int index = getPageCount();
+		addPage(index, control);
+		return index;
+	}
+
+	/**
+	 * Creates and adds a new page containing the given control to this
+	 * multi-page editor. The page is added at the given index. The control may
+	 * be <code>null</code>, allowing it to be created and set later using
+	 * <code>setControl</code>.
+	 * 
+	 * @param index
+	 *            the index at which to add the page (0-based)
+	 * @param control
+	 *            the control, or <code>null</code>
+	 * 
+	 * @see MultiPageEditorPart#setControl(int, Control)
+	 */
+	public void addPage(int index, Control control) {
+		createItem(index, control);
+	}
+
+	/**
+	 * Creates and adds a new page containing the given editor to this
+	 * multi-page editor. This also hooks a property change listener on the
+	 * nested editor.
+	 * 
+	 * @param editor
+	 *            the nested editor
+	 * @param input
+	 *            the input for the nested editor
+	 * @return the index of the new page
+	 * @exception PartInitException
+	 *                if a new page could not be created
+	 * 
+	 * @see MultiPageEditorPart#handlePropertyChange(int) the handler for
+	 *      property change events from the nested editor
+	 */
+	public int addPage(IEditorPart editor, IEditorInput input)
+			throws PartInitException {
+		int index = getPageCount();
+		addPage(index, editor, input);
+		return index;
+	}
+
+	/**
+	 * Creates and adds a new page containing the given editor to this
+	 * multi-page editor. The page is added at the given index. This also hooks
+	 * a property change listener on the nested editor.
+	 * 
+	 * @param index
+	 *            the index at which to add the page (0-based)
+	 * @param editor
+	 *            the nested editor
+	 * @param input
+	 *            the input for the nested editor
+	 * @exception PartInitException
+	 *                if a new page could not be created
+	 * 
+	 * @see MultiPageEditorPart#handlePropertyChange(int) the handler for
+	 *      property change events from the nested editor
+	 */
+	public void addPage(int index, IEditorPart editor, IEditorInput input)
+			throws PartInitException {
+		IEditorSite site = createSite(editor);
+		// call init first so that if an exception is thrown, we have created no
+		// new widgets
+		editor.init(site, input);
+		Composite parent2 = new Composite(getContainer(),
+				getOrientation(editor));
+		parent2.setLayout(new FillLayout());
+		editor.createPartControl(parent2);
+		editor.addPropertyListener(new IPropertyListener() {
+			public void propertyChanged(Object source, int propertyId) {
+				MultiPageEditorPart.this.handlePropertyChange(propertyId);
+			}
+		});
+		// create item for page only after createPartControl has succeeded
+		Item item = createItem(index, parent2);
+		// remember the editor, as both data on the item, and in the list of
+		// editors (see field comment)
+		item.setData(editor);
+		nestedEditors.add(editor);
+	}
+
+	/**
+	 * Get the orientation of the editor.
+	 * 
+	 * @param editor
+	 * @return int the orientation flag
+	 * @see SWT#RIGHT_TO_LEFT
+	 * @see SWT#LEFT_TO_RIGHT
+	 * @see SWT#NONE
+	 */
+	private int getOrientation(IEditorPart editor) {
+		if (editor instanceof IWorkbenchPartOrientation) {
+			return ((IWorkbenchPartOrientation) editor).getOrientation();
+		}
+		return getOrientation();
+	}
+
+	/**
+	 * Creates an empty container. Creates a CTabFolder with no style bits set,
+	 * and hooks a selection listener which calls <code>pageChange()</code>
+	 * whenever the selected tab changes.
+	 * 
+	 * @param parent
+	 *            The composite in which the container tab folder should be
+	 *            created; must not be <code>null</code>.
+	 * @return a new container
+	 */
+	private CTabFolder createContainer(Composite parent) {
+		// use SWT.FLAT style so that an extra 1 pixel border is not reserved
+		// inside the folder
+		parent.setLayout(new FillLayout());
+		final CTabFolder newContainer = new CTabFolder(parent, SWT.BOTTOM
+				| SWT.FLAT);
+		newContainer.addSelectionListener(new SelectionAdapter() {
+			public void widgetSelected(SelectionEvent e) {
+				int newPageIndex = newContainer.indexOf((CTabItem) e.item);
+				pageChange(newPageIndex);
+			}
+		});
+		newContainer.addTraverseListener(new TraverseListener() {
+			// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=199499 :
+			// Switching tabs by Ctrl+PageUp/PageDown must not be caught on the
+			// inner tab set
+			public void keyTraversed(TraverseEvent e) {
+				switch (e.detail) {
+				case SWT.TRAVERSE_PAGE_NEXT:
+				case SWT.TRAVERSE_PAGE_PREVIOUS:
+					int detail = e.detail;
+					e.doit = true;
+					e.detail = SWT.TRAVERSE_NONE;
+					Control control = newContainer.getParent();
+					do {
+						if (control.traverse(detail))
+							return;
+						if (control.getListeners(SWT.Traverse).length != 0)
+							return;
+						if (control instanceof Shell)
+							return;
+						control = control.getParent();
+					} while (control != null);
+				}
+			}
+		});
+		return newContainer;
+	}
+
+	/**
+	 * Creates a tab item at the given index and places the given control in the
+	 * new item. The item is a CTabItem with no style bits set.
+	 * 
+	 * @param index
+	 *            the index at which to add the control
+	 * @param control
+	 *            is the control to be placed in an item
+	 * @return a new item
+	 */
+	private CTabItem createItem(int index, Control control) {
+		CTabItem item = new CTabItem(getTabFolder(), SWT.NONE, index);
+		item.setControl(control);
+		return item;
+	}
+
+	/**
+	 * Creates the pages of this multi-page editor.
+	 * <p>
+	 * Subclasses must implement this method.
+	 * </p>
+	 */
+	protected abstract void createPages();
+
+	/**
+	 * The <code>MultiPageEditor</code> implementation of this
+	 * <code>IWorkbenchPart</code> method creates the control for the multi-page
+	 * editor by calling <code>createContainer</code>, then
+	 * <code>createPages</code>. Subclasses should implement
+	 * <code>createPages</code> rather than overriding this method.
+	 * 
+	 * @param parent
+	 *            The parent in which the editor should be created; must not be
+	 *            <code>null</code>.
+	 */
+	public final void createPartControl(Composite parent) {
+		Composite pageContainer = createPageContainer(parent);
+		this.container = createContainer(pageContainer);
+		createPages();
+		// set the active page (page 0 by default), unless it has already been
+		// done
+		if (getActivePage() == -1) {
+			setActivePage(0);
+			IEditorPart part = getEditor(0);
+			if (part != null) {
+				final IServiceLocator serviceLocator = part.getEditorSite();
+				if (serviceLocator instanceof INestable) {
+					activeServiceLocator = (INestable) serviceLocator;
+					activeServiceLocator.activate();
+				}
+			}
+		}
+		initializePageSwitching();
+		initializeSubTabSwitching();
+	}
+
+	/**
+	 * Initialize the MultiPageEditorPart to use the page switching command.
+	 * Clients can override this method with an empty body if they wish to
+	 * opt-out.
+	 * 
+	 * @since 3.4
+	 */
+	protected void initializePageSwitching() {
+		new PageSwitcher(this) {
+			public Object[] getPages() {
+				int pageCount = getPageCount();
+				Object[] result = new Object[pageCount];
+				for (int i = 0; i < pageCount; i++) {
+					result[i] = new Integer(i);
+				}
+				return result;
+			}
+
+			public String getName(Object page) {
+				return getPageText(((Integer) page).intValue());
+			}
+
+			public ImageDescriptor getImageDescriptor(Object page) {
+				Image image = getPageImage(((Integer) page).intValue());
+				if (image == null)
+					return null;
+
+				return ImageDescriptor.createFromImage(image);
+			}
+
+			public void activatePage(Object page) {
+				setActivePage(((Integer) page).intValue());
+			}
+
+			public int getCurrentPageIndex() {
+				return getActivePage();
+			}
+		};
+	}
+
+	/**
+	 * Initialize the MultiPageEditorPart to use the sub-tab switching commands.
+	 * 
+	 * @since 3.5
+	 */
+	private void initializeSubTabSwitching() {
+		IHandlerService service = (IHandlerService) getSite().getService(
+				IHandlerService.class);
+		service.activateHandler(COMMAND_NEXT_SUB_TAB, new AbstractHandler() {
+			/**
+			 * {@inheritDoc}
+			 * 
+			 * @throws ExecutionException
+			 *             if an exception occurred during execution
+			 */
+			public Object execute(ExecutionEvent event)
+					throws ExecutionException {
+				int n = getPageCount();
+				if (n == 0)
+					return null;
+
+				int i = getActivePage() + 1;
+				if (i >= n)
+					i = 0;
+				setActivePage(i);
+				return null;
+			}
+		});
+
+		service.activateHandler(COMMAND_PREVIOUS_SUB_TAB,
+				new AbstractHandler() {
+					/**
+					 * {@inheritDoc}
+					 * 
+					 * @throws ExecutionException
+					 *             if an exception occurred during execution
+					 */
+					public Object execute(ExecutionEvent event)
+							throws ExecutionException {
+						int n = getPageCount();
+						if (n == 0)
+							return null;
+
+						int i = getActivePage() - 1;
+						if (i < 0)
+							i = n - 1;
+						setActivePage(i);
+						return null;
+					}
+				});
+	}
+
+	/**
+	 * Creates the parent control for the container returned by
+	 * {@link #getContainer() }.
+	 * 
+	 * <p>
+	 * Subclasses may extend and must call super implementation first.
+	 * </p>
+	 * 
+	 * @param parent
+	 *            the parent for all of the editors contents.
+	 * @return the parent for this editor's container. Must not be
+	 *         <code>null</code>.
+	 * 
+	 * @since 3.2
+	 */
+	protected Composite createPageContainer(Composite parent) {
+		return parent;
+	}
+
+	/**
+	 * Creates the site for the given nested editor. The
+	 * <code>MultiPageEditorPart</code> implementation of this method creates an
+	 * instance of <code>MultiPageEditorSite</code>. Subclasses may reimplement
+	 * to create more specialized sites.
+	 * 
+	 * @param editor
+	 *            the nested editor
+	 * @return the editor site
+	 */
+	protected IEditorSite createSite(IEditorPart editor) {
+		return new MultiPageEditorSite(this, editor);
+	}
+
+	/**
+	 * The <code>MultiPageEditorPart</code> implementation of this
+	 * <code>IWorkbenchPart</code> method disposes all nested editors.
+	 * Subclasses may extend.
+	 */
+	public void dispose() {
+		pageChangeListeners.clear();
+		for (int i = 0; i < nestedEditors.size(); ++i) {
+			IEditorPart editor = (IEditorPart) nestedEditors.get(i);
+			disposePart(editor);
+		}
+		nestedEditors.clear();
+		if (pageContainerSite instanceof IDisposable) {
+			((IDisposable) pageContainerSite).dispose();
+			pageContainerSite = null;
+		}
+		for (int i = 0; i < pageSites.size(); i++) {
+			IServiceLocator sl = (IServiceLocator) pageSites.get(i);
+			if (sl instanceof IDisposable) {
+				((IDisposable) sl).dispose();
+			}
+		}
+		pageSites.clear();
+		super.dispose();
+	}
+
+	/**
+	 * Returns the active nested editor if there is one.
+	 * <p>
+	 * Subclasses should not override this method
+	 * </p>
+	 * 
+	 * @nooverride
+	 * @return the active nested editor, or <code>null</code> if none
+	 */
+	protected IEditorPart getActiveEditor() {
+		int index = getActivePage();
+		if (index != -1) {
+			return getEditor(index);
+		}
+		return null;
+	}
+
+	/**
+	 * Returns the index of the currently active page, or -1 if there is no
+	 * active page.
+	 * <p>
+	 * Subclasses should not override this method
+	 * </p>
+	 * 
+	 * @nooverride
+	 * 
+	 * @return the index of the active page, or -1 if there is no active page
+	 * @since 3.5
+	 */
+	public int getActivePage() {
+		CTabFolder tabFolder = getTabFolder();
+		if (tabFolder != null && !tabFolder.isDisposed()) {
+			return tabFolder.getSelectionIndex();
+		}
+		return -1;
+	}
+
+	/**
+	 * Returns the composite control containing this multi-page editor's pages.
+	 * This should be used as the parent when creating controls for the
+	 * individual pages. That is, when calling <code>addPage(Control)</code>,
+	 * the passed control should be a child of this container.
+	 * <p>
+	 * Warning: Clients should not assume that the container is any particular
+	 * subclass of Composite. The actual class used may change in order to
+	 * improve the look and feel of multi-page editors. Any code making
+	 * assumptions on the particular subclass would thus be broken.
+	 * </p>
+	 * <p>
+	 * Subclasses should not override this method
+	 * </p>
+	 * 
+	 * @return the composite, or <code>null</code> if
+	 *         <code>createPartControl</code> has not been called yet
+	 */
+	protected Composite getContainer() {
+		return container;
+	}
+
+	/**
+	 * Returns the control for the given page index, or <code>null</code> if no
+	 * control has been set for the page. The page index must be valid.
+	 * <p>
+	 * Subclasses should not override this method
+	 * </p>
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @return the control for the specified page, or <code>null</code> if none
+	 *         has been set
+	 */
+	protected Control getControl(int pageIndex) {
+		return getItem(pageIndex).getControl();
+	}
+
+	/**
+	 * Returns the editor for the given page index. The page index must be
+	 * valid.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @return the editor for the specified page, or <code>null</code> if the
+	 *         specified page was not created with
+	 *         <code>addPage(IEditorPart,IEditorInput)</code>
+	 */
+	protected IEditorPart getEditor(int pageIndex) {
+		Item item = getItem(pageIndex);
+		if (item != null) {
+			Object data = item.getData();
+			if (data instanceof IEditorPart) {
+				return (IEditorPart) data;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Returns the service locator for the given page index. This method can be
+	 * used to create service locators for pages that are just controls. The
+	 * page index must be valid.
+	 * <p>
+	 * This will return the editor site service locator for an editor, and
+	 * create one for a page that is just a control.
+	 * </p>
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @return the editor for the specified page, or <code>null</code> if the
+	 *         specified page was not created with
+	 *         <code>addPage(IEditorPart,IEditorInput)</code>
+	 * @since 3.4
+	 */
+	protected final IServiceLocator getPageSite(int pageIndex) {
+		if (pageIndex == PAGE_CONTAINER_SITE) {
+			return getPageContainerSite();
+		}
+
+		Item item = getItem(pageIndex);
+		if (item != null) {
+			Object data = item.getData();
+			if (data instanceof IEditorPart) {
+				return ((IEditorPart) data).getSite();
+			} else if (data instanceof IServiceLocator) {
+				return (IServiceLocator) data;
+			} else if (data == null) {
+				IServiceLocatorCreator slc = (IServiceLocatorCreator) getSite()
+						.getService(IServiceLocatorCreator.class);
+				IServiceLocator sl = slc.createServiceLocator(getSite(), null,
+						new IDisposable() {
+							public void dispose() {
+								final Control control = ((PartSite) getSite())
+										.getPane().getControl();
+								if (control != null && !control.isDisposed()) {
+									((PartSite) getSite()).getPane().doHide();
+								}
+							}
+						});
+				IEclipseContext e4Context = EclipseContextFactory.create(
+						((EditorSite) getSite()).getContext(),
+						UISchedulerStrategy.getInstance());
+				((ServiceLocator) sl).setContext(e4Context);
+				item.setData(sl);
+				pageSites.add(sl);
+				return sl;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * @return A site that can be used with a header.
+	 * @since 3.4
+	 * @see #createPageContainer(Composite)
+	 * @see #PAGE_CONTAINER_SITE
+	 * @see #getPageSite(int)
+	 */
+	private IServiceLocator getPageContainerSite() {
+		if (pageContainerSite == null) {
+			IServiceLocatorCreator slc = (IServiceLocatorCreator) getSite()
+					.getService(IServiceLocatorCreator.class);
+			pageContainerSite = slc.createServiceLocator(getSite(), null,
+					new IDisposable() {
+						public void dispose() {
+							final Control control = ((PartSite) getSite())
+									.getPane().getControl();
+							if (control != null && !control.isDisposed()) {
+								((PartSite) getSite()).getPane().doHide();
+							}
+						}
+					});
+			IEclipseContext e4Context = EclipseContextFactory.create(
+					((EditorSite) getSite()).getContext(), UISchedulerStrategy
+							.getInstance());
+			((ServiceLocator) pageContainerSite).setContext(e4Context);
+		}
+		return pageContainerSite;
+	}
+
+	/**
+	 * Returns the tab item for the given page index (page index is 0-based).
+	 * The page index must be valid.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @return the tab item for the given page index
+	 */
+	private CTabItem getItem(int pageIndex) {
+		return getTabFolder().getItem(pageIndex);
+	}
+
+	/**
+	 * Returns the number of pages in this multi-page editor.
+	 * 
+	 * @return the number of pages
+	 */
+	protected int getPageCount() {
+		CTabFolder folder = getTabFolder();
+		// May not have been created yet, or may have been disposed.
+		if (folder != null && !folder.isDisposed()) {
+			return folder.getItemCount();
+		}
+		return 0;
+	}
+
+	/**
+	 * Returns the image for the page with the given index, or <code>null</code>
+	 * if no image has been set for the page. The page index must be valid.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @return the image, or <code>null</code> if none
+	 */
+	protected Image getPageImage(int pageIndex) {
+		return getItem(pageIndex).getImage();
+	}
+
+	/**
+	 * Returns the text label for the page with the given index. Returns the
+	 * empty string if no text label has been set for the page. The page index
+	 * must be valid.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @return the text label for the page
+	 */
+	protected String getPageText(int pageIndex) {
+		return getItem(pageIndex).getText();
+	}
+
+	/**
+	 * Returns the tab folder containing this multi-page editor's pages.
+	 * 
+	 * @return the tab folder, or <code>null</code> if
+	 *         <code>createPartControl</code> has not been called yet
+	 */
+	private CTabFolder getTabFolder() {
+		return container;
+	}
+
+	/**
+	 * Handles a property change notification from a nested editor. The default
+	 * implementation simply forwards the change to listeners on this multi-page
+	 * editor by calling <code>firePropertyChange</code> with the same property
+	 * id. For example, if the dirty state of a nested editor changes (property
+	 * id <code>IEditorPart.PROP_DIRTY</code>), this method handles it by firing
+	 * a property change event for <code>IEditorPart.PROP_DIRTY</code> to
+	 * property listeners on this multi-page editor.
+	 * <p>
+	 * Subclasses may extend or reimplement this method.
+	 * </p>
+	 * 
+	 * @param propertyId
+	 *            the id of the property that changed
+	 */
+	protected void handlePropertyChange(int propertyId) {
+		firePropertyChange(propertyId);
+	}
+
+	/**
+	 * The <code>MultiPageEditorPart</code> implementation of this
+	 * <code>IEditorPart</code> method sets its site to the given site, its
+	 * input to the given input, and the site's selection provider to a
+	 * <code>MultiPageSelectionProvider</code>. Subclasses may extend this
+	 * method.
+	 * 
+	 * @param site
+	 *            The site for which this part is being created; must not be
+	 *            <code>null</code>.
+	 * @param input
+	 *            The input on which this editor should be created; must not be
+	 *            <code>null</code>.
+	 * @throws PartInitException
+	 *             If the initialization of the part fails -- currently never.
+	 */
+	public void init(IEditorSite site, IEditorInput input)
+			throws PartInitException {
+		setSite(site);
+		setInput(input);
+		site.setSelectionProvider(new MultiPageSelectionProvider(this));
+	}
+
+	/**
+	 * The <code>MultiPageEditorPart</code> implementation of this
+	 * <code>IEditorPart</code> method returns whether the contents of any of
+	 * this multi-page editor's nested editors have changed since the last save.
+	 * Pages created with <code>addPage(Control)</code> are ignored.
+	 * <p>
+	 * Subclasses may extend or reimplement this method.
+	 * </p>
+	 * 
+	 * @return <code>true</code> if any of the nested editors are dirty;
+	 *         <code>false</code> otherwise.
+	 */
+	public boolean isDirty() {
+		// use nestedEditors to avoid SWT requests; see bug 12996
+		for (Iterator i = nestedEditors.iterator(); i.hasNext();) {
+			IEditorPart editor = (IEditorPart) i.next();
+			if (editor.isDirty()) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Notifies this multi-page editor that the page with the given id has been
+	 * activated. This method is called when the user selects a different tab.
+	 * <p>
+	 * The <code>MultiPageEditorPart</code> implementation of this method sets
+	 * focus to the new page, and notifies the action bar contributor (if there
+	 * is one). This checks whether the action bar contributor is an instance of
+	 * <code>MultiPageEditorActionBarContributor</code>, and, if so, calls
+	 * <code>setActivePage</code> with the active nested editor. This also fires
+	 * a selection change event if required.
+	 * </p>
+	 * <p>
+	 * Subclasses may extend this method.
+	 * </p>
+	 * 
+	 * @param newPageIndex
+	 *            the index of the activated page
+	 */
+	protected void pageChange(int newPageIndex) {
+		deactivateSite(false, false);
+
+		IPartService partService = (IPartService) getSite().getService(
+				IPartService.class);
+		if (partService.getActivePart() == this) {
+			setFocus();
+		}
+
+		IEditorPart activeEditor = getEditor(newPageIndex);
+
+		IEditorActionBarContributor contributor = getEditorSite()
+				.getActionBarContributor();
+		if (contributor != null
+				&& contributor instanceof MultiPageEditorActionBarContributor) {
+			((MultiPageEditorActionBarContributor) contributor)
+					.setActivePage(activeEditor);
+		}
+
+		if (activeEditor != null) {
+			ISelectionProvider selectionProvider = activeEditor.getSite()
+					.getSelectionProvider();
+			if (selectionProvider != null) {
+				ISelectionProvider outerProvider = getSite()
+						.getSelectionProvider();
+				if (outerProvider instanceof MultiPageSelectionProvider) {
+					SelectionChangedEvent event = new SelectionChangedEvent(
+							selectionProvider, selectionProvider.getSelection());
+
+					MultiPageSelectionProvider provider = (MultiPageSelectionProvider) outerProvider;
+					provider.fireSelectionChanged(event);
+					provider.firePostSelectionChanged(event);
+				} else {
+					if (Policy.DEBUG_MPE) {
+						Tracing.printTrace(TRACING_COMPONENT,
+								"MultiPageEditorPart " + getTitle() //$NON-NLS-1$
+										+ " did not propogate selection for " //$NON-NLS-1$
+										+ activeEditor.getTitle());
+					}
+				}
+			}
+		}
+
+		activateSite();
+		Object selectedPage = getSelectedPage();
+		if (selectedPage != null) {
+			firePageChanged(new PageChangedEvent(this, selectedPage));
+		}
+	}
+
+	/**
+	 * This method can be used by implementors of
+	 * {@link MultiPageEditorPart#createPageContainer(Composite)} to deactivate
+	 * the active inner editor services while their header has focus. A
+	 * deactivateSite() must have a matching call to activateSite() when
+	 * appropriate.
+	 * <p>
+	 * An new inner editor will have its site activated on a
+	 * {@link MultiPageEditorPart#pageChange(int)}.
+	 * </p>
+	 * <p>
+	 * <b>Note:</b> This API is evolving in 3.4 and this might not be its final
+	 * form.
+	 * </p>
+	 * 
+	 * @param immediate
+	 *            immediately deactivate the legacy keybinding service
+	 * @param containerSiteActive
+	 *            Leave the page container site active.
+	 * @since 3.4
+	 * @see #activateSite()
+	 * @see #createPageContainer(Composite)
+	 * @see #getPageSite(int)
+	 * @see #PAGE_CONTAINER_SITE
+	 */
+	protected final void deactivateSite(boolean immediate,
+			boolean containerSiteActive) {
+		// Deactivate the nested services from the last active service locator.
+		if (activeServiceLocator != null) {
+			activeServiceLocator.deactivate();
+			activeServiceLocator = null;
+		}
+
+		final int pageIndex = getActivePage();
+		final IKeyBindingService service = getSite().getKeyBindingService();
+		if (pageIndex < 0 || pageIndex >= getPageCount() || immediate) {
+			// There is no selected page, so deactivate the active service.
+			if (service instanceof INestableKeyBindingService) {
+				final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
+				nestableService.activateKeyBindingService(null);
+			} else {
+				WorkbenchPlugin
+						.log("MultiPageEditorPart.deactivateSite()   Parent key binding service was not an instance of INestableKeyBindingService.  It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+
+		if (containerSiteActive) {
+			IServiceLocator containerSite = getPageContainerSite();
+			if (containerSite instanceof INestable) {
+				activeServiceLocator = (INestable) containerSite;
+				activeServiceLocator.activate();
+			}
+		}
+	}
+
+	/**
+	 * This method can be used by implementors of
+	 * {@link #createPageContainer(Composite)} to activate the active inner
+	 * editor services when their header loses focus.
+	 * <p>
+	 * An new inner editor will have its site activated on a
+	 * {@link #pageChange(int)}.
+	 * </p>
+	 * <p>
+	 * <b>Note:</b> This API is evolving in 3.4 and this might not be its final
+	 * form.
+	 * </p>
+	 * 
+	 * @since 3.4
+	 * @see #deactivateSite(boolean,boolean)
+	 * @see #createPageContainer(Composite)
+	 * @see #getPageSite(int)
+	 */
+	protected final void activateSite() {
+		if (activeServiceLocator != null) {
+			activeServiceLocator.deactivate();
+			activeServiceLocator = null;
+		}
+
+		final IKeyBindingService service = getSite().getKeyBindingService();
+		final int pageIndex = getActivePage();
+		final IEditorPart editor = getEditor(pageIndex);
+
+		if (editor != null) {
+			// active the service for this inner editor
+			if (service instanceof INestableKeyBindingService) {
+				final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
+				nestableService.activateKeyBindingService(editor
+						.getEditorSite());
+
+			} else {
+				WorkbenchPlugin
+						.log("MultiPageEditorPart.activateSite()   Parent key binding service was not an instance of INestableKeyBindingService.  It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+			// Activate the services for the new service locator.
+			final IServiceLocator serviceLocator = editor.getEditorSite();
+			if (serviceLocator instanceof INestable) {
+				activeServiceLocator = (INestable) serviceLocator;
+				activeServiceLocator.activate();
+			}
+
+		} else {
+			Item item = getItem(pageIndex);
+
+			// There is no selected editor, so deactivate the active service.
+			if (service instanceof INestableKeyBindingService) {
+				final INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
+				nestableService.activateKeyBindingService(null);
+			} else {
+				WorkbenchPlugin
+						.log("MultiPageEditorPart.activateSite()   Parent key binding service was not an instance of INestableKeyBindingService.  It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+
+			if (item.getData() instanceof INestable) {
+				activeServiceLocator = (INestable) item.getData();
+				activeServiceLocator.activate();
+			}
+		}
+	}
+
+	/**
+	 * Disposes the given part and its site.
+	 * 
+	 * @param part
+	 *            The part to dispose; must not be <code>null</code>.
+	 */
+	private void disposePart(final IWorkbenchPart part) {
+		SafeRunner.run(new ISafeRunnable() {
+			public void run() {
+				IWorkbenchPartSite partSite = part.getSite();
+				part.dispose();
+				if (partSite instanceof MultiPageEditorSite) {
+					((MultiPageEditorSite) partSite).dispose();
+				}
+			}
+
+			public void handleException(Throwable e) {
+				// Exception has already being logged by Core. Do nothing.
+			}
+		});
+	}
+
+	/**
+	 * Removes the page with the given index from this multi-page editor. The
+	 * controls for the page are disposed of; if the page has an editor, it is
+	 * disposed of too. The page index must be valid.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @see MultiPageEditorPart#addPage(Control)
+	 * @see MultiPageEditorPart#addPage(IEditorPart, IEditorInput)
+	 */
+	public void removePage(int pageIndex) {
+		Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());
+		// get editor (if any) before disposing item
+		IEditorPart editor = getEditor(pageIndex);
+
+		// get control for the item if it's not an editor
+		CTabItem item = getItem(pageIndex);
+		IServiceLocator pageLocator = null;
+		if (item.getData() instanceof IServiceLocator) {
+			pageLocator = (IServiceLocator) item.getData();
+		}
+		Control pageControl = item.getControl();
+
+		// dispose item before disposing editor, in case there's an exception
+		// in editor's dispose
+		item.dispose();
+
+		if (pageControl != null) {
+			pageControl.dispose();
+		}
+
+		// dispose editor (if any)
+		if (editor != null) {
+			nestedEditors.remove(editor);
+			disposePart(editor);
+		}
+		if (pageLocator != null) {
+			pageSites.remove(pageLocator);
+			if (pageLocator instanceof IDisposable) {
+				((IDisposable) pageLocator).dispose();
+			}
+		}
+	}
+
+	/**
+	 * Sets the currently active page.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page to be activated; the index must be valid
+	 */
+	protected void setActivePage(int pageIndex) {
+		Assert.isTrue(pageIndex >= 0 && pageIndex < getPageCount());
+		getTabFolder().setSelection(pageIndex);
+		pageChange(pageIndex);
+	}
+
+	/**
+	 * Sets the control for the given page index. The page index must be valid.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @param control
+	 *            the control for the specified page, or <code>null</code> to
+	 *            clear the control
+	 */
+	protected void setControl(int pageIndex, Control control) {
+		getItem(pageIndex).setControl(control);
+	}
+
+	/**
+	 * The <code>MultiPageEditor</code> implementation of this
+	 * <code>IWorkbenchPart</code> method sets focus on the active nested
+	 * editor, if there is one.
+	 * <p>
+	 * Subclasses may extend or reimplement.
+	 * </p>
+	 */
+	public void setFocus() {
+		setFocus(getActivePage());
+	}
+
+	/**
+	 * Sets focus to the control for the given page. If the page has an editor,
+	 * this calls its <code>setFocus()</code> method. Otherwise, this calls
+	 * <code>setFocus</code> on the control for the page.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 */
+	private void setFocus(int pageIndex) {
+		if (pageIndex < 0 || pageIndex >= getPageCount()) {
+			// page index out of bounds, don't set focus.
+			return;
+		}
+		final IEditorPart editor = getEditor(pageIndex);
+		if (editor != null) {
+			editor.setFocus();
+
+		} else {
+			// Give the page's control focus.
+			final Control control = getControl(pageIndex);
+			if (control != null) {
+				control.setFocus();
+			}
+		}
+	}
+
+	/**
+	 * Sets the image for the page with the given index, or <code>null</code> to
+	 * clear the image for the page. The page index must be valid.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @param image
+	 *            the image, or <code>null</code>
+	 */
+	protected void setPageImage(int pageIndex, Image image) {
+		getItem(pageIndex).setImage(image);
+	}
+
+	/**
+	 * Sets the text label for the page with the given index. The page index
+	 * must be valid. The text label must not be null.
+	 * 
+	 * @param pageIndex
+	 *            the index of the page
+	 * @param text
+	 *            the text label
+	 */
+	protected void setPageText(int pageIndex, String text) {
+		getItem(pageIndex).setText(text);
+	}
+
+	/**
+	 * If there is an adapter registered against the subclass of
+	 * MultiPageEditorPart return that. Otherwise, delegate to the internal
+	 * editor.
+	 * 
+	 * @see org.eclipse.ui.part.WorkbenchPart#getAdapter(java.lang.Class)
+	 */
+	public Object getAdapter(Class adapter) {
+		Object result = super.getAdapter(adapter);
+		// restrict delegating to the UI thread for bug 144851
+		if (result == null && Display.getCurrent() != null) {
+			IEditorPart innerEditor = getActiveEditor();
+			// see bug 138823 - prevent some subclasses from causing
+			// an infinite loop
+			if (innerEditor != null && innerEditor != this) {
+				result = Util.getAdapter(innerEditor, adapter);
+			}
+		}
+		return result;
+	}
+
+	/**
+	 * Find the editors contained in this multi-page editor whose editor input
+	 * match the provided input.
+	 * 
+	 * @param input
+	 *            the editor input
+	 * @return the editors contained in this multi-page editor whose editor
+	 *         input match the provided input
+	 * @since 3.3
+	 */
+	public final IEditorPart[] findEditors(IEditorInput input) {
+		List result = new ArrayList();
+		int count = getPageCount();
+		for (int i = 0; i < count; i++) {
+			IEditorPart editor = getEditor(i);
+			if (editor != null && editor.getEditorInput() != null
+					&& editor.getEditorInput().equals(input)) {
+				result.add(editor);
+			}
+		}
+		return (IEditorPart[]) result.toArray(new IEditorPart[result.size()]);
+	}
+
+	/**
+	 * Set the active page of this multi-page editor to the page that contains
+	 * the given editor part. This method has no effect of the given editor part
+	 * is not contained in this multi-page editor.
+	 * 
+	 * @param editorPart
+	 *            the editor part
+	 * @since 3.3
+	 */
+	public final void setActiveEditor(IEditorPart editorPart) {
+		int count = getPageCount();
+		for (int i = 0; i < count; i++) {
+			IEditorPart editor = getEditor(i);
+			if (editor == editorPart) {
+				setActivePage(i);
+				break;
+			}
+		}
+	}
+
+	/**
+	 * Returns the selected page for the current active page index, either the
+	 * IEditorPart for editors or the Control for other pages.
+	 * <p>
+	 * <b>Note:</b> clients may override this method to return a page
+	 * appropriate for their editors. Maybe be <code>null</code>.
+	 * </p>
+	 * 
+	 * @return The IEditorPart or Control representing the current active page,
+	 *         or <code>null</code> if there are no active pages.
+	 * @since 3.5
+	 * @see #getActivePage()
+	 */
+	public Object getSelectedPage() {
+		int index = getActivePage();
+		if (index == -1) {
+			return null;
+		}
+		IEditorPart editor = getEditor(index);
+		if (editor != null) {
+			return editor;
+		}
+		return getControl(index);
+	}
+
+	/**
+	 * Add the page change listener to be notified when the page changes. The
+	 * newly selected page will be the Object returned from
+	 * {@link #getSelectedPage()}. In the default case, this will be the active
+	 * page Control, IEditorPart, or <code>null</code>.
+	 * <p>
+	 * This method has no effect if the listener has already been added.
+	 * </p>
+	 * 
+	 * @nooverride
+	 * 
+	 * @since 3.5
+	 */
+	public void addPageChangedListener(IPageChangedListener listener) {
+		pageChangeListeners.add(listener);
+	}
+
+	/**
+	 * Remove the page change listener.
+	 * <p>
+	 * This method has no effect if the listener is not in the list.
+	 * </p>
+	 * 
+	 * @nooverride
+	 * 
+	 * @since 3.5
+	 */
+	public void removePageChangedListener(IPageChangedListener listener) {
+		pageChangeListeners.remove(listener);
+	}
+
+	private void firePageChanged(final PageChangedEvent event) {
+		Object[] listeners = pageChangeListeners.getListeners();
+		for (int i = 0; i < listeners.length; ++i) {
+			final IPageChangedListener l = (IPageChangedListener) listeners[i];
+			SafeRunnable.run(new SafeRunnable() {
+				public void run() {
+					l.pageChanged(event);
+				}
+			});
+		}
+	}
+}
diff --git a/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorSite.java b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorSite.java
new file mode 100644
index 0000000..7f1776e
--- /dev/null
+++ b/bundles/org.eclipse.e4.ui.workbench.fragment/src/org/eclipse/ui/part/MultiPageEditorSite.java
@@ -0,0 +1,555 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ *******************************************************************************/
+package org.eclipse.ui.part;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.e4.core.services.context.EclipseContextFactory;
+import org.eclipse.e4.core.services.context.IEclipseContext;
+import org.eclipse.e4.workbench.ui.internal.UISchedulerStrategy;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.viewers.ILabelDecorator;
+import org.eclipse.jface.viewers.IPostSelectionProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IEditorActionBarContributor;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IKeyBindingService;
+import org.eclipse.ui.INestableKeyBindingService;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.internal.EditorSite;
+import org.eclipse.ui.internal.PartSite;
+import org.eclipse.ui.internal.PopupMenuExtender;
+import org.eclipse.ui.internal.WorkbenchPlugin;
+import org.eclipse.ui.internal.part.IMultiPageEditorSiteHolder;
+import org.eclipse.ui.internal.services.INestable;
+import org.eclipse.ui.internal.services.IServiceLocatorCreator;
+import org.eclipse.ui.internal.services.IWorkbenchLocationService;
+import org.eclipse.ui.internal.services.ServiceLocator;
+import org.eclipse.ui.internal.services.WorkbenchLocationService;
+import org.eclipse.ui.services.IDisposable;
+import org.eclipse.ui.services.IServiceLocator;
+import org.eclipse.ui.services.IServiceScopes;
+
+/**
+ * Site for a nested editor within a multi-page editor. Selection is handled by
+ * forwarding the event to the multi-page editor's selection listeners; most
+ * other methods are forwarded to the multi-page editor's site.
+ * <p>
+ * The base implementation of <code>MultiPageEditor.createSite</code> creates an
+ * instance of this class. This class may be instantiated or subclassed.
+ * </p>
+ */
+public class MultiPageEditorSite implements IEditorSite, INestable {
+
+	/**
+	 * The nested editor.
+	 */
+	private IEditorPart editor;
+
+	/**
+	 * The list of popup menu extenders; <code>null</code> if none registered.
+	 */
+	private ArrayList menuExtenders;
+
+	/**
+	 * The multi-page editor.
+	 */
+	private MultiPageEditorPart multiPageEditor;
+
+	/**
+	 * The post selection changed listener.
+	 */
+	private ISelectionChangedListener postSelectionChangedListener = null;
+
+	/**
+	 * The selection change listener, initialized lazily; <code>null</code> if
+	 * not yet created.
+	 */
+	private ISelectionChangedListener selectionChangedListener = null;
+
+	/**
+	 * The selection provider; <code>null</code> if none.
+	 * 
+	 * @see MultiPageEditorSite#setSelectionProvider(ISelectionProvider)
+	 */
+	private ISelectionProvider selectionProvider = null;
+
+	/**
+	 * The cached copy of the key binding service specific to this multi-page
+	 * editor site. This value is <code>null</code> if it is not yet
+	 * initialized.
+	 */
+	private IKeyBindingService service = null;
+
+	/**
+	 * The local service locator for this multi-page editor site. This value is
+	 * never <code>null</code>.
+	 */
+	private final ServiceLocator serviceLocator;
+
+	/**
+	 * Creates a site for the given editor nested within the given multi-page
+	 * editor.
+	 * 
+	 * @param multiPageEditor
+	 *            the multi-page editor
+	 * @param editor
+	 *            the nested editor
+	 */
+	public MultiPageEditorSite(MultiPageEditorPart multiPageEditor,
+			IEditorPart editor) {
+		Assert.isNotNull(multiPageEditor);
+		Assert.isNotNull(editor);
+		this.multiPageEditor = multiPageEditor;
+		this.editor = editor;
+
+		final IServiceLocator parentServiceLocator = multiPageEditor.getSite();
+		IServiceLocatorCreator slc = (IServiceLocatorCreator) parentServiceLocator
+				.getService(IServiceLocatorCreator.class);
+		this.serviceLocator = (ServiceLocator) slc.createServiceLocator(
+				multiPageEditor.getSite(), null, new IDisposable() {
+					public void dispose() {
+						final Control control = ((PartSite) getMultiPageEditor()
+								.getSite()).getPane().getControl();
+						if (control != null && !control.isDisposed()) {
+							((PartSite) getMultiPageEditor().getSite())
+									.getPane().doHide();
+						}
+					}
+				});
+		IEclipseContext e4Context = EclipseContextFactory.create(
+				((EditorSite) multiPageEditor.getSite()).getContext(),
+				UISchedulerStrategy.getInstance());
+		this.serviceLocator.setContext(e4Context);
+
+		initializeDefaultServices();
+	}
+
+	/**
+	 * Initialize the slave services for this site.
+	 */
+	private void initializeDefaultServices() {
+		serviceLocator.registerService(IWorkbenchLocationService.class,
+				new WorkbenchLocationService(IServiceScopes.MPESITE_SCOPE,
+						getWorkbenchWindow().getWorkbench(),
+						getWorkbenchWindow(), getMultiPageEditor().getSite(),
+						this, null, 3));
+		serviceLocator.registerService(IMultiPageEditorSiteHolder.class,
+				new IMultiPageEditorSiteHolder() {
+					public MultiPageEditorSite getSite() {
+						return MultiPageEditorSite.this;
+					}
+				});
+	}
+
+	/**
+	 * Notifies the multi page editor service that the component within which it
+	 * exists has become active.
+	 * 
+	 * @since 3.2
+	 */
+	public final void activate() {
+		serviceLocator.activate();
+	}
+
+	/**
+	 * Notifies the multi page editor service that the component within which it
+	 * exists has been deactived.
+	 * 
+	 * @since 3.2
+	 */
+	public final void deactivate() {
+		serviceLocator.deactivate();
+	}
+
+	/**
+	 * Dispose the contributions.
+	 */
+	public void dispose() {
+		if (menuExtenders != null) {
+			for (int i = 0; i < menuExtenders.size(); i++) {
+				((PopupMenuExtender) menuExtenders.get(i)).dispose();
+			}
+			menuExtenders = null;
+		}
+
+		// Remove myself from the list of nested key binding services.
+		if (service != null) {
+			IKeyBindingService parentService = getEditor().getSite()
+					.getKeyBindingService();
+			if (parentService instanceof INestableKeyBindingService) {
+				INestableKeyBindingService nestableParent = (INestableKeyBindingService) parentService;
+				nestableParent.removeKeyBindingService(this);
+			}
+			service = null;
+		}
+
+		if (serviceLocator != null) {
+			serviceLocator.dispose();
+		}
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IEditorSite</code> method returns <code>null</code>, since nested
+	 * editors do not have their own action bar contributor.
+	 * 
+	 * @return <code>null</code>
+	 */
+	public IEditorActionBarContributor getActionBarContributor() {
+		return null;
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IEditorSite</code> method forwards to the multi-page editor to
+	 * return the action bars.
+	 * 
+	 * @return The action bars from the parent multi-page editor.
+	 */
+	public IActionBars getActionBars() {
+		return multiPageEditor.getEditorSite().getActionBars();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+	 */
+	public Object getAdapter(Class adapter) {
+		return null;
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+	 * to return the decorator manager.
+	 * 
+	 * @return The decorator from the workbench window.
+	 * @deprecated use IWorkbench.getDecoratorManager()
+	 */
+	public ILabelDecorator getDecoratorManager() {
+		return getWorkbenchWindow().getWorkbench().getDecoratorManager()
+				.getLabelDecorator();
+	}
+
+	/**
+	 * Returns the nested editor.
+	 * 
+	 * @return the nested editor
+	 */
+	public IEditorPart getEditor() {
+		return editor;
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method returns an empty string since the
+	 * nested editor is not created from the registry.
+	 * 
+	 * @return An empty string.
+	 */
+	public String getId() {
+		return ""; //$NON-NLS-1$
+	}
+
+	/*
+	 * (non-Javadoc) Method declared on IEditorSite.
+	 */
+	public IKeyBindingService getKeyBindingService() {
+		if (service == null) {
+			service = getMultiPageEditor().getEditorSite()
+					.getKeyBindingService();
+			if (service instanceof INestableKeyBindingService) {
+				INestableKeyBindingService nestableService = (INestableKeyBindingService) service;
+				service = nestableService.getKeyBindingService(this);
+
+			} else {
+				/*
+				 * This is an internal reference, and should not be copied by
+				 * client code. If you are thinking of copying this, DON'T DO
+				 * IT.
+				 */
+				WorkbenchPlugin
+						.log("MultiPageEditorSite.getKeyBindingService()   Parent key binding service was not an instance of INestableKeyBindingService.  It was an instance of " + service.getClass().getName() + " instead."); //$NON-NLS-1$ //$NON-NLS-2$
+			}
+		}
+		return service;
+	}
+
+	/**
+	 * Returns the multi-page editor.
+	 * 
+	 * @return the multi-page editor
+	 */
+	public MultiPageEditorPart getMultiPageEditor() {
+		return multiPageEditor;
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+	 * to return the workbench page.
+	 * 
+	 * @return The workbench page in which this editor site resides.
+	 */
+	public IWorkbenchPage getPage() {
+		return getMultiPageEditor().getSite().getPage();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.eclipse.ui.IWorkbenchPartSite#getPart()
+	 */
+	public IWorkbenchPart getPart() {
+		return editor;
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method returns an empty string since the
+	 * nested editor is not created from the registry.
+	 * 
+	 * @return An empty string.
+	 */
+	public String getPluginId() {
+		return ""; //$NON-NLS-1$
+	}
+
+	/**
+	 * Returns the post selection change listener which listens to the nested
+	 * editor's selection changes.
+	 * 
+	 * @return the post selection change listener.
+	 */
+	private ISelectionChangedListener getPostSelectionChangedListener() {
+		if (postSelectionChangedListener == null) {
+			postSelectionChangedListener = new ISelectionChangedListener() {
+				public void selectionChanged(SelectionChangedEvent event) {
+					MultiPageEditorSite.this.handlePostSelectionChanged(event);
+				}
+			};
+		}
+		return postSelectionChangedListener;
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method returns an empty string since the
+	 * nested editor is not created from the registry.
+	 * 
+	 * @return An empty string.
+	 */
+	public String getRegisteredName() {
+		return ""; //$NON-NLS-1$
+	}
+
+	/**
+	 * Returns the selection changed listener which listens to the nested
+	 * editor's selection changes, and calls <code>handleSelectionChanged</code>
+	 * .
+	 * 
+	 * @return the selection changed listener
+	 */
+	private ISelectionChangedListener getSelectionChangedListener() {
+		if (selectionChangedListener == null) {
+			selectionChangedListener = new ISelectionChangedListener() {
+				public void selectionChanged(SelectionChangedEvent event) {
+					MultiPageEditorSite.this.handleSelectionChanged(event);
+				}
+			};
+		}
+		return selectionChangedListener;
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method returns the selection provider set
+	 * by <code>setSelectionProvider</code>.
+	 * 
+	 * @return The current selection provider.
+	 */
+	public ISelectionProvider getSelectionProvider() {
+		return selectionProvider;
+	}
+
+	public final Object getService(final Class key) {
+		return serviceLocator.getService(key);
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+	 * to return the shell.
+	 * 
+	 * @return The shell in which this editor site resides.
+	 */
+	public Shell getShell() {
+		return getMultiPageEditor().getSite().getShell();
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+	 * to return the workbench window.
+	 * 
+	 * @return The workbench window in which this editor site resides.
+	 */
+	public IWorkbenchWindow getWorkbenchWindow() {
+		return getMultiPageEditor().getSite().getWorkbenchWindow();
+	}
+
+	/**
+	 * Handles a post selection changed even from the nexted editor.
+	 * <p>
+	 * Subclasses may extend or reimplement this method
+	 * 
+	 * @param event
+	 *            the event
+	 * 
+	 * @since 3.2
+	 */
+	protected void handlePostSelectionChanged(SelectionChangedEvent event) {
+		ISelectionProvider parentProvider = getMultiPageEditor().getSite()
+				.getSelectionProvider();
+		if (parentProvider instanceof MultiPageSelectionProvider) {
+			SelectionChangedEvent newEvent = new SelectionChangedEvent(
+					parentProvider, event.getSelection());
+			MultiPageSelectionProvider prov = (MultiPageSelectionProvider) parentProvider;
+			prov.firePostSelectionChanged(newEvent);
+		}
+	}
+
+	/**
+	 * Handles a selection changed event from the nested editor. The default
+	 * implementation gets the selection provider from the multi-page editor's
+	 * site, and calls <code>fireSelectionChanged</code> on it (only if it is an
+	 * instance of <code>MultiPageSelectionProvider</code>), passing a new event
+	 * object.
+	 * <p>
+	 * Subclasses may extend or reimplement this method.
+	 * </p>
+	 * 
+	 * @param event
+	 *            the event
+	 */
+	protected void handleSelectionChanged(SelectionChangedEvent event) {
+		ISelectionProvider parentProvider = getMultiPageEditor().getSite()
+				.getSelectionProvider();
+		if (parentProvider instanceof MultiPageSelectionProvider) {
+			SelectionChangedEvent newEvent = new SelectionChangedEvent(
+					parentProvider, event.getSelection());
+			MultiPageSelectionProvider prov = (MultiPageSelectionProvider) parentProvider;
+			prov.fireSelectionChanged(newEvent);
+		}
+	}
+
+	public final boolean hasService(final Class key) {
+		return serviceLocator.hasService(key);
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+	 * for registration.
+	 * 
+	 * @param menuManager
+	 *            The menu manager
+	 * @param selProvider
+	 *            The selection provider.
+	 */
+	public void registerContextMenu(MenuManager menuManager,
+			ISelectionProvider selProvider) {
+		getMultiPageEditor().getSite().registerContextMenu(menuManager,
+				selProvider);
+	}
+
+	public final void registerContextMenu(final MenuManager menuManager,
+			final ISelectionProvider selectionProvider,
+			final boolean includeEditorInput) {
+		registerContextMenu(getId(), menuManager, selectionProvider,
+				includeEditorInput);
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method forwards to the multi-page editor
+	 * for registration.
+	 * 
+	 * @param menuID
+	 *            The identifier for the menu.
+	 * @param menuMgr
+	 *            The menu manager
+	 * @param selProvider
+	 *            The selection provider.
+	 */
+	public void registerContextMenu(String menuID, MenuManager menuMgr,
+			ISelectionProvider selProvider) {
+		if (menuExtenders == null) {
+			menuExtenders = new ArrayList(1);
+		}
+		PartSite.registerContextMenu(menuID, menuMgr, selProvider, true,
+				editor, menuExtenders);
+	}
+
+	public final void registerContextMenu(final String menuId,
+			final MenuManager menuManager,
+			final ISelectionProvider selectionProvider,
+			final boolean includeEditorInput) {
+		if (menuExtenders == null) {
+			menuExtenders = new ArrayList(1);
+		}
+		PartSite.registerContextMenu(menuId, menuManager, selectionProvider,
+				includeEditorInput, editor, menuExtenders);
+	}
+
+	/**
+	 * The <code>MultiPageEditorSite</code> implementation of this
+	 * <code>IWorkbenchPartSite</code> method remembers the selection provider,
+	 * and also hooks a listener on it, which calls
+	 * <code>handleSelectionChanged</code> when a selection changed event
+	 * occurs.
+	 * 
+	 * @param provider
+	 *            The selection provider.
+	 * @see MultiPageEditorSite#handleSelectionChanged(SelectionChangedEvent)
+	 */
+	public void setSelectionProvider(ISelectionProvider provider) {
+		ISelectionProvider oldSelectionProvider = selectionProvider;
+		selectionProvider = provider;
+		if (oldSelectionProvider != null) {
+			oldSelectionProvider
+					.removeSelectionChangedListener(getSelectionChangedListener());
+			if (oldSelectionProvider instanceof IPostSelectionProvider) {
+				((IPostSelectionProvider) oldSelectionProvider)
+						.removePostSelectionChangedListener(getPostSelectionChangedListener());
+			}
+		}
+		if (selectionProvider != null) {
+			selectionProvider
+					.addSelectionChangedListener(getSelectionChangedListener());
+			if (selectionProvider instanceof IPostSelectionProvider) {
+				((IPostSelectionProvider) selectionProvider)
+						.addPostSelectionChangedListener(getPostSelectionChangedListener());
+			}
+		}
+	}
+}