org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2013 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.e4.ui.internal.workbench.swt;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.InjectionException;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.services.contributions.IContributionFactory;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.core.services.statusreporter.StatusReporter;
import org.eclipse.e4.ui.bindings.keys.KeyBindingDispatcher;
import org.eclipse.e4.ui.css.core.util.impl.resources.OSGiResourceLocator;
import org.eclipse.e4.ui.css.swt.dom.WidgetElement;
import org.eclipse.e4.ui.css.swt.engine.CSSSWTEngineImpl;
import org.eclipse.e4.ui.css.swt.theme.IThemeEngine;
import org.eclipse.e4.ui.css.swt.theme.IThemeManager;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.e4.ui.di.PersistState;
import org.eclipse.e4.ui.internal.workbench.Activator;
import org.eclipse.e4.ui.internal.workbench.E4Workbench;
import org.eclipse.e4.ui.internal.workbench.Policy;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.MApplicationElement;
import org.eclipse.e4.ui.model.application.MContribution;
import org.eclipse.e4.ui.model.application.ui.MContext;
import org.eclipse.e4.ui.model.application.ui.MElementContainer;
import org.eclipse.e4.ui.model.application.ui.MGenericStack;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
import org.eclipse.e4.ui.model.application.ui.basic.MTrimmedWindow;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.model.application.ui.menu.MMenu;
import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
import org.eclipse.e4.ui.services.IStylingEngine;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.IResourceUtilities;
import org.eclipse.e4.ui.workbench.IWorkbench;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.swt.factories.IRendererFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jface.bindings.keys.SWTKeySupport;
import org.eclipse.jface.bindings.keys.formatting.KeyFormatterFactory;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.testing.TestableObject;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.w3c.dom.Element;
import org.w3c.dom.css.CSSStyleDeclaration;

public class PartRenderingEngine implements IPresentationEngine {
    public static final String EARLY_STARTUP_HOOK = "runEarlyStartup";

    public static final String engineURI = "bundleclass://org.eclipse.e4.ui.workbench.swt/"
            + "org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine";

    private static final String defaultFactoryUrl = "bundleclass://org.eclipse.e4.ui.workbench.renderers.swt/"
            + "org.eclipse.e4.ui.workbench.renderers.swt.WorkbenchRendererFactory";
    private String factoryUrl;

    IRendererFactory curFactory = null;

    private Map<String, AbstractPartRenderer> customRendererMap = new HashMap<String, AbstractPartRenderer>();

    org.eclipse.swt.widgets.Listener keyListener;

    // Life Cycle handlers
    private EventHandler toBeRenderedHandler = new EventHandler() {
        public void handleEvent(Event event) {

            MUIElement changedElement = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT);
            MElementContainer<?> parent = changedElement.getParent();

            // Handle Detached Windows
            if (parent == null) {
                parent = (MElementContainer<?>) ((EObject) changedElement).eContainer();
            }

            boolean menuChild = parent instanceof MMenu;

            // If the parent isn't displayed who cares?
            if (!(parent instanceof MApplication) && (parent == null || parent.getWidget() == null || menuChild))
                return;

            if (changedElement.isToBeRendered()) {
                Activator.trace(Policy.DEBUG_RENDERER, "visible -> true", null); //$NON-NLS-1$

                // Note that the 'createGui' protocol calls 'childAdded'
                Object w = createGui(changedElement);
                if (w instanceof Control && !(w instanceof Shell)) {
                    fixZOrder(changedElement);
                }
            } else {
                Activator.trace(Policy.DEBUG_RENDERER, "visible -> false", null); //$NON-NLS-1$

                // Ensure that the element about to be removed is not the
                // selected element
                if (parent.getSelectedElement() == changedElement)
                    parent.setSelectedElement(null);

                // Un-maximize the element before tearing it down
                if (changedElement.getTags().contains(MAXIMIZED))
                    changedElement.getTags().remove(MAXIMIZED);

                // Note that the 'removeGui' protocol calls 'childRemoved'
                removeGui(changedElement);
            }

        }
    };

    private EventHandler visibilityHandler = new EventHandler() {
        public void handleEvent(Event event) {
            MUIElement changedElement = (MUIElement) event.getProperty(UIEvents.EventTags.ELEMENT);
            MUIElement parent = changedElement.getParent();
            if (parent == null) {
                parent = (MUIElement) ((EObject) changedElement).eContainer();
                if (parent == null) {
                    return;
                }
            }

            AbstractPartRenderer renderer = (AbstractPartRenderer) parent.getRenderer();
            if (renderer == null || parent instanceof MToolBar)
                return;

            // Re-parent the control based on the visible state
            if (changedElement.isVisible()) {
                if (changedElement.isToBeRendered()) {
                    if (changedElement.getWidget() instanceof Control) {
                        // Ensure that the control is under its 'real' parent if
                        // it's visible
                        Composite realComp = (Composite) renderer.getUIContainer(changedElement);
                        Control ctrl = (Control) changedElement.getWidget();
                        ctrl.setParent(realComp);
                        fixZOrder(changedElement);
                    }

                    if (parent instanceof MElementContainer<?>) {
                        renderer.childRendered((MElementContainer<MUIElement>) parent, changedElement);
                    }
                }
            } else {
                // Put the control under the 'limbo' shell
                if (changedElement.getWidget() instanceof Control) {
                    Control ctrl = (Control) changedElement.getWidget();

                    if (!(ctrl instanceof Shell)) {
                        ctrl.getShell().layout(new Control[] { ctrl }, SWT.DEFER);
                    }

                    ctrl.setParent(getLimboShell());
                }

                if (parent instanceof MElementContainer<?>) {
                    renderer.hideChild((MElementContainer<MUIElement>) parent, changedElement);
                }
            }
        }
    };

    private EventHandler trimHandler = new EventHandler() {
        public void handleEvent(Event event) {
            Object changedObj = event.getProperty(UIEvents.EventTags.ELEMENT);
            if (!(changedObj instanceof MTrimmedWindow))
                return;

            MTrimmedWindow window = (MTrimmedWindow) changedObj;
            if (window.getWidget() == null)
                return;

            if (UIEvents.isADD(event)) {
                for (Object o : UIEvents.asIterable(event, UIEvents.EventTags.NEW_VALUE)) {
                    MUIElement added = (MUIElement) o;
                    if (added.isToBeRendered())
                        createGui(added, window.getWidget(), window.getContext());
                }
            } else if (UIEvents.isREMOVE(event)) {
                for (Object o : UIEvents.asIterable(event, UIEvents.EventTags.NEW_VALUE)) {
                    MUIElement removed = (MUIElement) o;
                    if (removed.getRenderer() != null)
                        removeGui(removed);
                }
            }
        }
    };

    private EventHandler childrenHandler = new EventHandler() {
        public void handleEvent(Event event) {

            Object changedObj = event.getProperty(UIEvents.EventTags.ELEMENT);
            if (!(changedObj instanceof MElementContainer<?>))
                return;

            MElementContainer<MUIElement> changedElement = (MElementContainer<MUIElement>) changedObj;
            boolean isApplication = changedObj instanceof MApplication;

            boolean menuChild = changedObj instanceof MMenu;
            // If the parent isn't in the UI then who cares?
            AbstractPartRenderer renderer = getRendererFor(changedElement);
            if ((!isApplication && renderer == null) || menuChild)
                return;

            if (UIEvents.isADD(event)) {
                Activator.trace(Policy.DEBUG_RENDERER, "Child Added", null); //$NON-NLS-1$
                for (Object o : UIEvents.asIterable(event, UIEvents.EventTags.NEW_VALUE)) {
                    MUIElement added = (MUIElement) o;

                    // OK, we have a new -visible- part we either have to create
                    // it or host it under the correct parent. Note that we
                    // explicitly do *not* render non-selected elements in
                    // stacks (to support lazy loading).
                    boolean isStack = changedObj instanceof MGenericStack<?>;
                    boolean hasWidget = added.getWidget() != null;
                    boolean isSelected = added == changedElement.getSelectedElement();
                    boolean renderIt = !isStack || hasWidget || isSelected;
                    if (renderIt) {
                        // NOTE: createGui will call 'childAdded' if successful
                        Object w = createGui(added);
                        if (w instanceof Control && !(w instanceof Shell)) {
                            final Control ctrl = (Control) w;
                            fixZOrder(added);
                            if (!ctrl.isDisposed()) {
                                ctrl.getShell().layout(new Control[] { ctrl }, SWT.DEFER);
                            }
                        }
                    } else {
                        if (renderer != null && added.isToBeRendered())
                            renderer.childRendered(changedElement, added);
                    }

                    // If the element being added is a placeholder, check to see
                    // if
                    // it's 'globally visible' and, if so, remove all other
                    // 'local' placeholders referencing the same element.
                    int newLocation = modelService.getElementLocation(added);
                    if (newLocation == EModelService.IN_SHARED_AREA
                            || newLocation == EModelService.OUTSIDE_PERSPECTIVE) {
                        MWindow topWin = modelService.getTopLevelWindowFor(added);
                        modelService.hideLocalPlaceholders(topWin, null);
                    }
                }
            } else if (UIEvents.isREMOVE(event)) {
                Activator.trace(Policy.DEBUG_RENDERER, "Child Removed", null); //$NON-NLS-1$
                for (Object o : UIEvents.asIterable(event, UIEvents.EventTags.OLD_VALUE)) {
                    MUIElement removed = (MUIElement) o;
                    // Removing invisible elements is a NO-OP as far as the
                    // renderer is concerned
                    if (!removed.isToBeRendered())
                        continue;

                    if (removed.getWidget() instanceof Control) {
                        Control ctrl = (Control) removed.getWidget();
                        ctrl.setLayoutData(null);
                        ctrl.getParent().layout(new Control[] { ctrl }, SWT.CHANGED | SWT.DEFER);
                    }

                    // Ensure that the element about to be removed is not the
                    // selected element
                    if (changedElement.getSelectedElement() == removed)
                        changedElement.setSelectedElement(null);

                    if (renderer != null)
                        renderer.hideChild(changedElement, removed);
                }
            }
        }
    };

    private EventHandler windowsHandler = new EventHandler() {
        public void handleEvent(Event event) {
            childrenHandler.handleEvent(event);
        }
    };

    private IEclipseContext appContext;

    protected Shell testShell;

    protected MApplication theApp;

    @Inject
    @Optional
    protected IEventBroker eventBroker;

    @Inject
    EModelService modelService;

    @Inject
    protected Logger logger;

    private Shell limbo;

    private MUIElement removeRoot = null;

    @Inject
    public PartRenderingEngine(@Named(E4Workbench.RENDERER_FACTORY_URI) @Optional String factoryUrl) {
        if (factoryUrl == null) {
            factoryUrl = defaultFactoryUrl;
        }
        this.factoryUrl = factoryUrl;
    }

    protected void fixZOrder(MUIElement element) {
        MElementContainer<MUIElement> parent = element.getParent();
        if (parent == null) {
            Object container = ((EObject) element).eContainer();
            if (container instanceof MElementContainer<?>) {
                parent = (MElementContainer<MUIElement>) container;
            }
        }
        if (parent == null || !(element.getWidget() instanceof Control))
            return;

        Control elementCtrl = (Control) element.getWidget();
        Control prevCtrl = null;
        for (MUIElement kid : parent.getChildren()) {
            if (kid == element) {
                if (prevCtrl != null)
                    elementCtrl.moveBelow(prevCtrl);
                else
                    elementCtrl.moveAbove(null);
                break;
            } else if (kid.getWidget() instanceof Control) {
                prevCtrl = (Control) kid.getWidget();
            }
        }

        Object widget = parent.getWidget();
        if (widget instanceof Composite) {
            Composite composite = (Composite) widget;
            if (composite.getShell() == elementCtrl.getShell()) {
                Composite temp = elementCtrl.getParent();
                while (temp != composite) {
                    if (temp == null) {
                        return;
                    }
                    temp = temp.getParent();
                }

                composite.layout(true, true);
            }
        }
    }

    /**
     * Initialize a part renderer from the extension point.
     * 
     * @param context
     *            the context for the part factories
     */
    @PostConstruct
    void initialize(IEclipseContext context) {
        this.appContext = context;

        // initialize the correct key-binding display formatter
        KeyFormatterFactory.setDefault(SWTKeySupport.getKeyFormatterForPlatform());

        // Add the renderer to the context
        context.set(IPresentationEngine.class.getName(), this);

        IRendererFactory factory = null;
        IContributionFactory contribFactory = context.get(IContributionFactory.class);
        try {
            factory = (IRendererFactory) contribFactory.create(factoryUrl, context);
        } catch (Exception e) {
            logger.warn(e, "Could not create rendering factory");
        }

        // Try to load the default one
        if (factory == null) {
            try {
                factory = (IRendererFactory) contribFactory.create(defaultFactoryUrl, context);
            } catch (Exception e) {
                logger.error(e, "Could not create default rendering factory");
            }
        }

        if (factory == null) {
            throw new IllegalStateException("Could not create any rendering factory. Aborting ...");
        }

        curFactory = factory;
        context.set(IRendererFactory.class, curFactory);

        // Hook up the widget life-cycle subscriber
        if (eventBroker != null) {
            eventBroker.subscribe(UIEvents.UIElement.TOPIC_TOBERENDERED, toBeRenderedHandler);
            eventBroker.subscribe(UIEvents.UIElement.TOPIC_VISIBLE, visibilityHandler);
            eventBroker.subscribe(UIEvents.ElementContainer.TOPIC_CHILDREN, childrenHandler);
            eventBroker.subscribe(UIEvents.Window.TOPIC_WINDOWS, windowsHandler);
            eventBroker.subscribe(UIEvents.Perspective.TOPIC_WINDOWS, windowsHandler);
            eventBroker.subscribe(UIEvents.TrimmedWindow.TOPIC_TRIMBARS, trimHandler);
        }
    }

    @PreDestroy
    void contextDisposed() {
        if (eventBroker == null)
            return;
        eventBroker.unsubscribe(toBeRenderedHandler);
        eventBroker.unsubscribe(visibilityHandler);
        eventBroker.unsubscribe(childrenHandler);
        eventBroker.unsubscribe(trimHandler);
    }

    private static void populateModelInterfaces(MContext contextModel, IEclipseContext context,
            Class<?>[] interfaces) {
        for (Class<?> intf : interfaces) {
            Activator.trace(Policy.DEBUG_CONTEXTS, "Adding " + intf.getName() + " for " //$NON-NLS-1$ //$NON-NLS-2$
                    + contextModel.getClass().getName(), null);
            context.set(intf.getName(), contextModel);

            populateModelInterfaces(contextModel, context, intf.getInterfaces());
        }
    }

    private String getContextName(MUIElement element) {
        StringBuilder builder = new StringBuilder(element.getClass().getSimpleName());
        String elementId = element.getElementId();
        if (elementId != null && elementId.length() != 0) {
            builder.append(" (").append(elementId).append(") ");
        }
        builder.append("Context");
        return builder.toString();
    }

    public Object createGui(final MUIElement element, final Object parentWidget,
            final IEclipseContext parentContext) {
        final Object[] gui = { null };
        // wrap the handling in a SafeRunner so that exceptions do not prevent
        // the renderer from processing other elements
        SafeRunner.run(new ISafeRunnable() {
            public void handleException(Throwable e) {
                if (e instanceof Error) {
                    // errors are deadly, we shouldn't ignore these
                    throw (Error) e;
                } else {
                    // log exceptions otherwise
                    if (logger != null) {
                        String message = "Exception occurred while rendering: {0}"; //$NON-NLS-1$
                        logger.error(e, NLS.bind(message, element));
                    }
                }
            }

            public void run() throws Exception {
                gui[0] = safeCreateGui(element, parentWidget, parentContext);
            }
        });
        return gui[0];
    }

    public Object safeCreateGui(MUIElement element, Object parentWidget, IEclipseContext parentContext) {
        if (!element.isToBeRendered())
            return null;

        // no creates while processing a remove
        if (removeRoot != null) {
            return null;
        }

        Object currentWidget = element.getWidget();
        if (currentWidget != null) {
            if (currentWidget instanceof Control) {
                Control control = (Control) currentWidget;
                // make sure the control is visible
                if (!(element instanceof MPlaceholder))
                    control.setVisible(true);

                if (parentWidget instanceof Composite) {
                    Composite currentParent = control.getParent();
                    if (currentParent != parentWidget) {
                        // check if the original parent was a tab folder
                        if (currentParent instanceof CTabFolder) {
                            CTabFolder folder = (CTabFolder) currentParent;
                            // if we used to be the tab folder's top right
                            // control, unset it
                            if (folder.getTopRight() == control) {
                                folder.setTopRight(null);
                            }
                        }

                        // the parents are different so we should reparent it
                        control.setParent((Composite) parentWidget);
                    }
                }
            }

            // Reparent the context (or the kid's context)
            if (element instanceof MContext) {
                IEclipseContext ctxt = ((MContext) element).getContext();
                if (ctxt != null)
                    ctxt.setParent(parentContext);
            } else {
                List<MContext> childContexts = modelService.findElements(element, null, MContext.class, null);
                for (MContext c : childContexts) {
                    // Ensure that we only reset the context of our direct
                    // children
                    MUIElement kid = (MUIElement) c;
                    MUIElement parent = kid.getParent();
                    if (parent == null && kid.getCurSharedRef() != null)
                        parent = kid.getCurSharedRef().getParent();
                    if (!(element instanceof MPlaceholder) && parent != element)
                        continue;

                    if (c.getContext() != null && c.getContext().getParent() != parentContext) {
                        c.getContext().setParent(parentContext);
                    }
                }
            }

            // Now that we have a widget let the parent (if any) know
            if (element.getParent() instanceof MUIElement) {
                MElementContainer<MUIElement> parentElement = element.getParent();
                AbstractPartRenderer parentRenderer = getRendererFor(parentElement);
                if (parentRenderer != null)
                    parentRenderer.childRendered(parentElement, element);
            }
            return element.getWidget();
        }

        if (element instanceof MContext) {
            MContext ctxt = (MContext) element;
            // Assert.isTrue(ctxt.getContext() == null,
            // "Before rendering Context should be null");
            if (ctxt.getContext() == null) {
                IEclipseContext lclContext = parentContext.createChild(getContextName(element));
                populateModelInterfaces(ctxt, lclContext, element.getClass().getInterfaces());
                ctxt.setContext(lclContext);

                // System.out.println("New Context: " + lclContext.toString()
                // + " parent: " + parentContext.toString());

                // make sure the context knows about these variables that have
                // been defined in the model
                for (String variable : ctxt.getVariables()) {
                    lclContext.declareModifiable(variable);
                }

                Map<String, String> props = ctxt.getProperties();
                for (String key : props.keySet()) {
                    lclContext.set(key, props.get(key));
                }
            }
        }

        // Create a control appropriate to the part
        Object newWidget = createWidget(element, parentWidget);

        // Remember that we've created the control
        if (newWidget != null) {
            AbstractPartRenderer renderer = getRendererFor(element);

            // Have the renderer hook up any widget specific listeners
            renderer.hookControllerLogic(element);

            // Process its internal structure through the renderer that created
            // it
            if (element instanceof MElementContainer) {
                renderer.processContents((MElementContainer<MUIElement>) element);
            }

            // Allow a final chance to set up
            renderer.postProcess(element);

            // Now that we have a widget let the parent (if any) know
            if (element.getParent() instanceof MUIElement) {
                MElementContainer<MUIElement> parentElement = element.getParent();
                AbstractPartRenderer parentRenderer = getRendererFor(parentElement);
                if (parentRenderer != null)
                    parentRenderer.childRendered(parentElement, element);
            }
        } else {
            // failed to create the widget, dispose its context if necessary
            if (element instanceof MContext) {
                MContext ctxt = (MContext) element;
                IEclipseContext lclContext = ctxt.getContext();
                if (lclContext != null) {
                    lclContext.dispose();
                    ctxt.setContext(null);
                }
            }
        }

        return newWidget;
    }

    private IEclipseContext getContext(MUIElement parent) {
        if (parent instanceof MContext) {
            return ((MContext) parent).getContext();
        }
        return modelService.getContainingContext(parent);
    }

    public Object createGui(final MUIElement element) {
        final Object[] gui = { null };
        // wrap the handling in a SafeRunner so that exceptions do not prevent
        // the renderer from processing other elements
        SafeRunner.run(new ISafeRunnable() {
            public void handleException(Throwable e) {
                if (e instanceof Error) {
                    // errors are deadly, we shouldn't ignore these
                    throw (Error) e;
                } else {
                    // log exceptions otherwise
                    if (logger != null) {
                        String message = "Exception occurred while rendering: {0}"; //$NON-NLS-1$
                        logger.error(e, NLS.bind(message, element));
                    }
                }
            }

            public void run() throws Exception {
                gui[0] = safeCreateGui(element);
            }
        });
        return gui[0];
    }

    private Object safeCreateGui(MUIElement element) {
        // Obtain the necessary parent widget
        Object parent = null;
        MUIElement parentME = element.getParent();
        if (parentME == null)
            parentME = (MUIElement) ((EObject) element).eContainer();
        if (parentME != null) {
            AbstractPartRenderer renderer = getRendererFor(parentME);
            if (renderer != null) {
                if (!element.isVisible()) {
                    parent = getLimboShell();
                } else {
                    parent = renderer.getUIContainer(element);
                }
            }
        }

        // Obtain the necessary parent context
        IEclipseContext parentContext = null;
        if (element.getCurSharedRef() != null) {
            MPlaceholder ph = element.getCurSharedRef();
            parentContext = getContext(ph.getParent());
        } else if (parentContext == null && element.getParent() != null) {
            parentContext = getContext(element.getParent());
        } else if (parentContext == null && element.getParent() == null) {
            parentContext = getContext((MUIElement) ((EObject) element).eContainer());
        }

        return safeCreateGui(element, parent, parentContext);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.eclipse.e4.ui.workbench.IPresentationEngine#focusGui(org.eclipse.
     * e4.ui.model.application.ui.MUIElement)
     */
    public void focusGui(MUIElement element) {
        AbstractPartRenderer renderer = (AbstractPartRenderer) element.getRenderer();
        if (renderer == null || element.getWidget() == null)
            return;

        Object implementation = element instanceof MContribution ? ((MContribution) element).getObject() : null;

        // If there is no class to call @Focus on then revert to the default
        if (!(element instanceof MContribution)) {
            renderer.forceFocus(element);
            return;
        }

        try {
            IEclipseContext context = getContext(element);
            Object defaultValue = new Object();
            Object returnValue = ContextInjectionFactory.invoke(implementation, Focus.class, context, defaultValue);
            if (returnValue == defaultValue) {
                // No @Focus method, force the focus
                renderer.forceFocus(element);
            }
        } catch (InjectionException e) {
            log("Failed to grant focus to element", "Failed to grant focus to element ({0})", //$NON-NLS-1$ //$NON-NLS-2$
                    element.getElementId(), e);
        } catch (RuntimeException e) {
            log("Failed to grant focus to element via DI", //$NON-NLS-1$
                    "Failed to grant focus via DI to element ({0})", element.getElementId(), e); //$NON-NLS-1$
        }
    }

    private void log(String unidentifiedMessage, String identifiedMessage, String id, Exception e) {
        if (id == null || id.length() == 0) {
            logger.error(e, unidentifiedMessage);
        } else {
            logger.error(e, NLS.bind(identifiedMessage, id));
        }
    }

    private Shell getLimboShell() {
        if (limbo == null) {
            limbo = new Shell(Display.getCurrent(), SWT.NONE);
            limbo.setText("PartRenderingEngine's limbo"); //$NON-NLS-1$ // just for debugging, not shown anywhere

            // Place the limbo shell 'off screen'
            limbo.setLocation(0, 10000);

            limbo.setBackgroundMode(SWT.INHERIT_DEFAULT);
            limbo.setData(ShellActivationListener.DIALOG_IGNORE_KEY, Boolean.TRUE);
        }
        return limbo;
    }

    /**
     * @param element
     */
    public void removeGui(final MUIElement element) {
        // wrap the handling in a SafeRunner so that exceptions do not prevent
        // the menu from being shown
        SafeRunner.run(new ISafeRunnable() {
            public void handleException(Throwable e) {
                if (e instanceof Error) {
                    // errors are deadly, we shouldn't ignore these
                    throw (Error) e;
                } else {
                    // log exceptions otherwise
                    if (logger != null) {
                        String message = "Exception occurred while unrendering: {0}"; //$NON-NLS-1$
                        logger.error(e, NLS.bind(message, element));
                    }
                }
            }

            public void run() throws Exception {
                safeRemoveGui(element);
            }
        });
    }

    private void safeRemoveGui(MUIElement element) {
        if (removeRoot == null)
            removeRoot = element;

        // We call 'hideChild' *before* checking if the actual element
        // has been rendered in order to pick up cases of 'lazy loading'
        MUIElement parent = element.getParent();
        AbstractPartRenderer parentRenderer = parent != null ? getRendererFor(parent) : null;
        if (parentRenderer != null) {
            parentRenderer.hideChild(element.getParent(), element);
        }

        AbstractPartRenderer renderer = getRendererFor(element);

        // If the element hasn't been rendered then this is a NO-OP
        if (renderer != null) {

            if (element instanceof MElementContainer<?>) {
                MElementContainer<MUIElement> container = (MElementContainer<MUIElement>) element;
                MUIElement selectedElement = container.getSelectedElement();
                List<MUIElement> children = container.getChildren();
                for (MUIElement child : children) {
                    // remove stuff in the "back" first
                    if (child != selectedElement) {
                        removeGui(child);
                    }
                }

                if (selectedElement != null && children.contains(selectedElement)) {
                    // now remove the selected element
                    removeGui(selectedElement);
                }
            }

            if (element instanceof MPerspective) {
                MPerspective perspective = (MPerspective) element;
                for (MWindow subWindow : perspective.getWindows()) {
                    removeGui(subWindow);
                }
            } else if (element instanceof MWindow) {
                MWindow window = (MWindow) element;
                for (MWindow subWindow : window.getWindows()) {
                    removeGui(subWindow);
                }

                if (window instanceof MTrimmedWindow) {
                    MTrimmedWindow trimmedWindow = (MTrimmedWindow) window;
                    for (MUIElement trimBar : trimmedWindow.getTrimBars()) {
                        removeGui(trimBar);
                    }
                }
            }

            if (element instanceof MContribution) {
                MContribution contribution = (MContribution) element;
                Object client = contribution.getObject();
                IEclipseContext parentContext = renderer.getContext(element);
                if (parentContext != null && client != null) {
                    try {
                        ContextInjectionFactory.invoke(client, PersistState.class, parentContext, null);
                    } catch (Exception e) {
                        if (logger != null) {
                            logger.error(e);
                        }
                    }
                }
            }

            renderer.disposeWidget(element);

            // unset the client object
            if (element instanceof MContribution) {
                MContribution contribution = (MContribution) element;
                Object client = contribution.getObject();
                IEclipseContext parentContext = renderer.getContext(element);
                if (parentContext != null && client != null) {
                    try {
                        ContextInjectionFactory.uninject(client, parentContext);
                    } catch (Exception e) {
                        if (logger != null) {
                            logger.error(e);
                        }
                    }
                }
                contribution.setObject(null);
            }

            // dispose the context
            if (element instanceof MContext) {
                clearContext((MContext) element);
            }
        }

        if (removeRoot == element)
            removeRoot = null;
    }

    private void clearContext(MContext contextME) {
        MContext ctxt = (MContext) contextME;
        IEclipseContext lclContext = ctxt.getContext();
        if (lclContext != null) {
            IEclipseContext parentContext = lclContext.getParent();
            IEclipseContext child = parentContext.getActiveChild();
            if (child == lclContext) {
                child.deactivate();
            }

            ctxt.setContext(null);
            lclContext.dispose();
        }
    }

    protected Object createWidget(MUIElement element, Object parent) {
        AbstractPartRenderer renderer = getRenderer(element, parent);
        if (renderer != null) {
            // Remember which renderer is responsible for this widget
            element.setRenderer(renderer);
            Object newWidget = renderer.createWidget(element, parent);
            if (newWidget != null) {
                renderer.bindWidget(element, newWidget);
                return newWidget;
            }
        }

        return null;
    }

    private AbstractPartRenderer getRenderer(MUIElement uiElement, Object parent) {
        // Is there a custom renderer defined ?
        String customURI = uiElement.getPersistedState().get(IPresentationEngine.CUSTOM_RENDERER_KEY);
        if (customURI != null) {
            if (customRendererMap.get(customURI) instanceof AbstractPartRenderer)
                return customRendererMap.get(customURI);

            IEclipseContext owningContext = modelService.getContainingContext(uiElement);
            IContributionFactory contributionFactory = (IContributionFactory) owningContext
                    .get(IContributionFactory.class.getName());
            Object customRenderer = contributionFactory.create(customURI, owningContext);
            if (customRenderer instanceof AbstractPartRenderer) {
                customRendererMap.put(customURI, (AbstractPartRenderer) customRenderer);
                return (AbstractPartRenderer) customRenderer;
            }
        }

        // If not then use the default renderer
        return curFactory.getRenderer(uiElement, parent);
    }

    protected AbstractPartRenderer getRendererFor(MUIElement element) {
        return (AbstractPartRenderer) element.getRenderer();
    }

    public Object run(final MApplicationElement uiRoot, final IEclipseContext runContext) {
        final Display display;
        if (runContext.get(Display.class) != null) {
            display = runContext.get(Display.class);
        } else {
            display = Display.getDefault();
            runContext.set(Display.class, display);
        }
        Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {

            public void run() {
                initializeStyling(display, runContext);

                // Register an SWT resource handler
                runContext.set(IResourceUtilities.class.getName(), new ResourceUtility());

                // set up the keybinding manager
                KeyBindingDispatcher dispatcher = (KeyBindingDispatcher) ContextInjectionFactory
                        .make(KeyBindingDispatcher.class, runContext);
                runContext.set(KeyBindingDispatcher.class.getName(), dispatcher);
                keyListener = dispatcher.getKeyDownFilter();
                display.addFilter(SWT.KeyDown, keyListener);
                display.addFilter(SWT.Traverse, keyListener);

                // Show the initial UI

                // Create a 'limbo' shell (used to host controls that shouldn't
                // be in the current layout)
                Shell limbo = getLimboShell();
                runContext.set("limbo", limbo);

                // HACK!! we should loop until the display gets disposed...
                // ...then we listen for the last 'main' window to get disposed
                // and dispose the Display
                testShell = null;
                theApp = null;
                boolean spinOnce = true;
                if (uiRoot instanceof MApplication) {
                    ShellActivationListener shellDialogListener = new ShellActivationListener(
                            (MApplication) uiRoot);
                    display.addFilter(SWT.Activate, shellDialogListener);
                    display.addFilter(SWT.Deactivate, shellDialogListener);
                    spinOnce = false; // loop until the app closes
                    theApp = (MApplication) uiRoot;
                    // long startTime = System.currentTimeMillis();
                    MWindow selected = theApp.getSelectedElement();
                    if (selected == null) {
                        for (MWindow window : theApp.getChildren()) {
                            createGui(window);
                        }
                    } else {
                        // render the selected one first
                        createGui(selected);
                        for (MWindow window : theApp.getChildren()) {
                            if (selected != window) {
                                createGui(window);
                            }
                        }
                    }
                    // long endTime = System.currentTimeMillis();
                    // System.out.println("Render: " + (endTime - startTime));
                    // tell the app context we are starting so the splash is
                    // torn down
                    IApplicationContext ac = appContext.get(IApplicationContext.class);
                    if (ac != null) {
                        ac.applicationRunning();
                        if (eventBroker != null)
                            eventBroker.post(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE, theApp);
                    }
                } else if (uiRoot instanceof MUIElement) {
                    if (uiRoot instanceof MWindow) {
                        testShell = (Shell) createGui((MUIElement) uiRoot);
                    } else {
                        // Special handling for partial models (for testing...)
                        testShell = new Shell(display, SWT.SHELL_TRIM);
                        createGui((MUIElement) uiRoot, testShell, null);
                    }
                }

                // allow any early startup extensions to run
                Runnable earlyStartup = (Runnable) runContext.get(EARLY_STARTUP_HOOK);
                if (earlyStartup != null) {
                    earlyStartup.run();
                }

                TestableObject testableObject = (TestableObject) runContext.get(TestableObject.class.getName());
                if (testableObject instanceof E4Testable) {
                    ((E4Testable) testableObject).init(display,
                            (IWorkbench) runContext.get(IWorkbench.class.getName()));
                }

                IEventLoopAdvisor advisor = runContext.getActiveLeaf().get(IEventLoopAdvisor.class);
                if (advisor == null) {
                    advisor = new IEventLoopAdvisor() {
                        public void eventLoopIdle(Display display) {
                            display.sleep();
                        }

                        public void eventLoopException(Throwable exception) {
                            StatusReporter statusReporter = (StatusReporter) appContext
                                    .get(StatusReporter.class.getName());
                            if (statusReporter != null) {
                                statusReporter.show(StatusReporter.ERROR, "Internal Error", exception);
                            } else {
                                if (logger != null) {
                                    logger.error(exception);
                                }
                            }
                        }
                    };
                }
                // Spin the event loop until someone disposes the display
                while (((testShell != null && !testShell.isDisposed())
                        || (theApp != null && someAreVisible(theApp.getChildren()))) && !display.isDisposed()) {
                    try {
                        if (!display.readAndDispatch()) {
                            runContext.processWaiting();
                            if (spinOnce)
                                return;
                            advisor.eventLoopIdle(display);
                        }
                    } catch (ThreadDeath th) {
                        throw th;
                    } catch (Exception ex) {
                        handle(ex, advisor);
                    } catch (Error err) {
                        handle(err, advisor);
                    }
                }
                if (!spinOnce) {
                    cleanUp();
                }
            }

            private void handle(Throwable ex, IEventLoopAdvisor advisor) {
                try {
                    advisor.eventLoopException(ex);
                } catch (Throwable t) {
                    if (t instanceof ThreadDeath) {
                        throw (ThreadDeath) t;
                    }

                    // couldn't handle the exception, print to console
                    t.printStackTrace();
                }
            }
        });

        return IApplication.EXIT_OK;
    }

    protected boolean someAreVisible(List<MWindow> windows) {
        // This method is called from the event dispatch loop, so the
        // following optimization is in order...

        // Ideally, we'd just do:
        // for (MWindow win : theApp.getChildren()) {
        // But this creates an iterator (which must be GC'd)
        // at every call. The code below creates no objects.
        final int limit = windows.size();
        for (int i = 0; i < limit; i++) {
            final MWindow win = windows.get(i);
            // Note: Removed isVisible test, as this should have
            // no impact on the whether the event loop
            // terminates - non-visible windows still exists
            // and can receive events.
            // Note: isToBeRendered() == true => win.getWidget() != null
            // but I'm not sure whether there is latency between setting
            // toBeRendered and the creation of the widget. So, keeping
            // both tests seems prudent.
            if (win.isToBeRendered() && win.getWidget() != null) {
                return true;
            }
        }
        return false;
    }

    public void stop() {
        // FIXME Without this call the test-suite fails
        cleanUp();
        if (theApp != null) {
            for (MWindow window : theApp.getChildren()) {
                if (window.getWidget() != null) {
                    removeGui(window);
                }
            }
        } else if (testShell != null && !testShell.isDisposed()) {
            Object model = testShell.getData(AbstractPartRenderer.OWNING_ME);
            if (model instanceof MUIElement) {
                removeGui((MUIElement) model);
            } else {
                testShell.close();
            }
        }
    }

    /*
     * There are situations where this is called more than once until we know
     * why this is needed we should make this safe for multiple calls
     */
    private void cleanUp() {
        if (keyListener != null) {
            Display display = Display.getDefault();
            if (!display.isDisposed()) {
                display.removeFilter(SWT.KeyDown, keyListener);
                display.removeFilter(SWT.Traverse, keyListener);
                keyListener = null;
            }
        }
    }

    public static void initializeStyling(Display display, IEclipseContext appContext) {
        String cssTheme = (String) appContext.get(E4Application.THEME_ID);
        String cssURI = (String) appContext.get(IWorkbench.CSS_URI_ARG);

        if (cssTheme != null) {
            String cssResourcesURI = (String) appContext.get(IWorkbench.CSS_RESOURCE_URI_ARG);

            Bundle bundle = WorkbenchSWTActivator.getDefault().getBundle();
            BundleContext context = bundle.getBundleContext();
            ServiceReference ref = context.getServiceReference(IThemeManager.class.getName());
            IThemeManager mgr = (IThemeManager) context.getService(ref);
            final IThemeEngine engine = mgr.getEngineForDisplay(display);

            // Store the app context
            IContributionFactory contribution = (IContributionFactory) appContext
                    .get(IContributionFactory.class.getName());
            IEclipseContext cssContext = EclipseContextFactory.create();
            cssContext.set(IContributionFactory.class.getName(), contribution);
            display.setData("org.eclipse.e4.ui.css.context", cssContext); //$NON-NLS-1$

            // Create the OSGi resource locator
            if (cssResourcesURI != null) {
                // TODO: Should this be set through an extension as well?
                engine.registerResourceLocator(new OSGiResourceLocator(cssResourcesURI));
            }

            engine.restore(cssTheme);
            // TODO Should we create an empty default theme?

            appContext.set(IThemeEngine.class.getName(), engine);

            appContext.set(IStylingEngine.SERVICE_NAME, new IStylingEngine() {
                public void setClassname(Object widget, String classname) {
                    WidgetElement.setCSSClass((Widget) widget, classname);
                    engine.applyStyles((Widget) widget, true);
                }

                public void setId(Object widget, String id) {
                    WidgetElement.setID((Widget) widget, id);
                    engine.applyStyles((Widget) widget, true);
                }

                public void style(Object widget) {
                    engine.applyStyles((Widget) widget, true);
                }

                public CSSStyleDeclaration getStyle(Object widget) {
                    return engine.getStyle((Widget) widget);
                }

                public void setClassnameAndId(Object widget, String classname, String id) {
                    WidgetElement.setCSSClass((Widget) widget, classname);
                    WidgetElement.setID((Widget) widget, id);
                    engine.applyStyles((Widget) widget, true);
                }

            });
        } else if (cssURI != null) {
            String cssResourcesURI = (String) appContext.get(IWorkbench.CSS_RESOURCE_URI_ARG);
            final CSSSWTEngineImpl engine = new CSSSWTEngineImpl(display, true);
            WidgetElement.setEngine(display, engine);
            if (cssResourcesURI != null) {
                engine.getResourcesLocatorManager()
                        .registerResourceLocator(new OSGiResourceLocator(cssResourcesURI.toString()));
            }
            // FIXME: is this needed?
            display.setData("org.eclipse.e4.ui.css.context", appContext); //$NON-NLS-1$
            appContext.set(IStylingEngine.SERVICE_NAME, new IStylingEngine() {
                public void setClassname(Object widget, String classname) {
                    WidgetElement.setCSSClass((Widget) widget, classname);
                    engine.applyStyles((Widget) widget, true);
                }

                public void setId(Object widget, String id) {
                    WidgetElement.setID((Widget) widget, id);
                    engine.applyStyles((Widget) widget, true);
                }

                public void style(Object widget) {
                    engine.applyStyles((Widget) widget, true);
                }

                public CSSStyleDeclaration getStyle(Object widget) {
                    Element e = engine.getCSSElementContext(widget).getElement();
                    if (e == null) {
                        return null;
                    }
                    return engine.getViewCSS().getComputedStyle(e, null);
                }

                public void setClassnameAndId(Object widget, String classname, String id) {
                    WidgetElement.setCSSClass((Widget) widget, classname);
                    WidgetElement.setID((Widget) widget, id);
                    engine.applyStyles((Widget) widget, true);
                }

            });

            URL url;
            InputStream stream = null;
            try {
                url = FileLocator.resolve(new URL(cssURI));
                stream = url.openStream();
                engine.parseStyleSheet(stream);
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

            Shell[] shells = display.getShells();
            for (Shell s : shells) {
                try {
                    s.setRedraw(false);
                    s.reskin(SWT.ALL);
                    engine.applyStyles(s, true);
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } finally {
                    s.setRedraw(true);
                }
            }
        }

        CSSRenderingUtils cssUtils = ContextInjectionFactory.make(CSSRenderingUtils.class, appContext);
        appContext.set(CSSRenderingUtils.class, cssUtils);

    }
}