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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2009, 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
 *     Tristan Hume - <trishume@gmail.com> -
 *           Fix for Bug 2369 [Workbench] Would like to be able to save workspace without exiting
 *           Implemented workbench auto-save to correctly restore state in case of crash.
 ******************************************************************************/

package org.eclipse.e4.ui.internal.workbench.swt;

import org.eclipse.e4.ui.workbench.IWorkbench;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IProduct;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.RegistryFactory;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.e4.core.contexts.IContextFunction;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.contexts.RunAndTrack;
import org.eclipse.e4.core.internal.services.EclipseAdapter;
import org.eclipse.e4.core.services.adapter.Adapter;
import org.eclipse.e4.core.services.contributions.IContributionFactory;
import org.eclipse.e4.core.services.log.ILoggerProvider;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.core.services.translation.TranslationProviderFactory;
import org.eclipse.e4.core.services.translation.TranslationService;
import org.eclipse.e4.ui.di.UISynchronize;
import org.eclipse.e4.ui.internal.workbench.ActiveChildLookupFunction;
import org.eclipse.e4.ui.internal.workbench.ActivePartLookupFunction;
import org.eclipse.e4.ui.internal.workbench.DefaultLoggerProvider;
import org.eclipse.e4.ui.internal.workbench.E4Workbench;
import org.eclipse.e4.ui.internal.workbench.ExceptionHandler;
import org.eclipse.e4.ui.internal.workbench.ModelServiceImpl;
import org.eclipse.e4.ui.internal.workbench.PlaceholderResolver;
import org.eclipse.e4.ui.internal.workbench.ReflectionContributionFactory;
import org.eclipse.e4.ui.internal.workbench.ResourceHandler;
import org.eclipse.e4.ui.internal.workbench.SelectionAggregator;
import org.eclipse.e4.ui.internal.workbench.SelectionServiceImpl;
import org.eclipse.e4.ui.internal.workbench.WorkbenchLogger;
import org.eclipse.e4.ui.model.application.MAddon;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.basic.MWindow;
import org.eclipse.e4.ui.model.application.ui.basic.impl.BasicPackageImpl;
import org.eclipse.e4.ui.model.application.ui.impl.UiPackageImpl;
import org.eclipse.e4.ui.services.IServiceConstants;
import org.eclipse.e4.ui.services.IStylingEngine;
import org.eclipse.e4.ui.workbench.IExceptionHandler;
import org.eclipse.e4.ui.workbench.IModelResourceHandler;
import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate;
import org.eclipse.e4.ui.workbench.lifecycle.PreSave;
import org.eclipse.e4.ui.workbench.lifecycle.ProcessAdditions;
import org.eclipse.e4.ui.workbench.lifecycle.ProcessRemovals;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.modeling.EPlaceholderResolver;
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
import org.eclipse.e4.ui.workbench.swt.internal.copy.WorkbenchSWTMessages;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.osgi.framework.Bundle;
import org.w3c.dom.css.CSSStyleDeclaration;

/**
 *
 */
public class E4Application implements IApplication {

    private static final String PLUGIN_ID = "org.eclipse.e4.ui.workbench.swt"; //$NON-NLS-1$

    // Copied from IDEApplication
    public static final String METADATA_FOLDER = ".metadata"; //$NON-NLS-1$
    private static final String VERSION_FILENAME = "version.ini"; //$NON-NLS-1$
    private static final String WORKSPACE_VERSION_KEY = "org.eclipse.core.runtime"; //$NON-NLS-1$
    private static final String WORKSPACE_VERSION_VALUE = "2"; //$NON-NLS-1$
    private static final String APPLICATION_MODEL_PATH_DEFAULT = "Application.e4xmi";

    private String[] args;

    private IModelResourceHandler handler;
    private Display display = null;
    private E4Workbench workbench = null;

    public static final String THEME_ID = "cssTheme";

    private Object lcManager;

    public Display getApplicationDisplay() {
        if (display == null) {
            display = Display.getDefault();
        }
        return display;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.
     * IApplicationContext)
     */
    public Object start(IApplicationContext applicationContext) throws Exception {
        // set the display name before the Display is
        // created to ensure the app name is used in any
        // platform menus, etc. See
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=329456#c14
        IProduct product = Platform.getProduct();
        if (product != null && product.getName() != null) {
            Display.setAppName(product.getName());
        }
        Display display = getApplicationDisplay();
        Location instanceLocation = null;
        try {
            E4Workbench workbench = createE4Workbench(applicationContext, display);

            instanceLocation = (Location) workbench.getContext().get(E4Workbench.INSTANCE_LOCATION);
            Shell shell = display.getActiveShell();
            if (shell == null) {
                shell = new Shell();
                // place it off so it's not visible
                shell.setLocation(0, 10000);
            }

            if (!checkInstanceLocation(instanceLocation, shell))
                return EXIT_OK;

            IEclipseContext workbenchContext = workbench.getContext();
            workbenchContext.set(Display.class, display);

            // Create and run the UI (if any)
            workbench.createAndRunUI(workbench.getApplication());

            // Save the model into the targetURI
            if (lcManager != null) {
                ContextInjectionFactory.invoke(lcManager, PreSave.class, workbenchContext, null);
            }
            saveModel();
            workbench.close();
            return EXIT_OK;
        } finally {
            if (display != null)
                display.dispose();
            if (instanceLocation != null)
                instanceLocation.release();
        }
    }

    public void saveModel() {
        try {
            handler.save();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public E4Workbench createE4Workbench(IApplicationContext applicationContext, final Display display) {
        args = (String[]) applicationContext.getArguments().get(IApplicationContext.APPLICATION_ARGS);

        IEclipseContext appContext = createDefaultContext();
        appContext.set(Realm.class, SWTObservables.getRealm(display));
        appContext.set(UISynchronize.class, new UISynchronize() {

            public void syncExec(Runnable runnable) {
                display.syncExec(runnable);
            }

            public void asyncExec(Runnable runnable) {
                display.asyncExec(runnable);
            }
        });
        appContext.set(IApplicationContext.class, applicationContext);

        // Check if DS is running
        if (!appContext.containsKey("org.eclipse.e4.ui.workbench.modeling.EPartService")) {
            throw new IllegalStateException(
                    "Core services not available. Please make sure that a declarative service implementation (such as the bundle 'org.eclipse.equinox.ds') is available!");
        }

        // Get the factory to create DI instances with
        IContributionFactory factory = (IContributionFactory) appContext.get(IContributionFactory.class.getName());

        // Install the life-cycle manager for this session if there's one
        // defined
        String lifeCycleURI = getArgValue(IWorkbench.LIFE_CYCLE_URI_ARG, applicationContext, false);
        if (lifeCycleURI != null) {
            lcManager = factory.create(lifeCycleURI, appContext);
            if (lcManager != null) {
                // Let the manager manipulate the appContext if desired
                ContextInjectionFactory.invoke(lcManager, PostContextCreate.class, appContext, null);
            }
        }
        // Create the app model and its context
        MApplication appModel = loadApplicationModel(applicationContext, appContext);
        appModel.setContext(appContext);

        boolean isRtl = ((Window.getDefaultOrientation() & SWT.RIGHT_TO_LEFT) != 0);
        appModel.getTransientData().put(E4Workbench.RTL_MODE, isRtl);

        // for compatibility layer: set the application in the OSGi service
        // context (see Workbench#getInstance())
        if (!E4Workbench.getServiceContext().containsKey(MApplication.class.getName())) {
            // first one wins.
            E4Workbench.getServiceContext().set(MApplication.class.getName(), appModel);
        }

        // Set the app's context after adding itself
        appContext.set(MApplication.class.getName(), appModel);

        // This context will be used by the injector for its
        // extended data suppliers
        ContextInjectionFactory.setDefault(appContext);

        // adds basic services to the contexts
        initializeServices(appModel);

        // let the life cycle manager add to the model
        if (lcManager != null) {
            ContextInjectionFactory.invoke(lcManager, ProcessAdditions.class, appContext, null);
            ContextInjectionFactory.invoke(lcManager, ProcessRemovals.class, appContext, null);
        }

        // Create the addons
        IEclipseContext addonStaticContext = EclipseContextFactory.create();
        for (MAddon addon : appModel.getAddons()) {
            addonStaticContext.set(MAddon.class, addon);
            Object obj = factory.create(addon.getContributionURI(), appContext, addonStaticContext);
            addon.setObject(obj);
        }

        // Parse out parameters from both the command line and/or the product
        // definition (if any) and put them in the context
        String xmiURI = getArgValue(IWorkbench.XMI_URI_ARG, applicationContext, false);
        appContext.set(IWorkbench.XMI_URI_ARG, xmiURI);

        String themeId = getArgValue(E4Application.THEME_ID, applicationContext, false);
        appContext.set(E4Application.THEME_ID, themeId);

        String cssURI = getArgValue(IWorkbench.CSS_URI_ARG, applicationContext, false);
        if (cssURI != null) {
            appContext.set(IWorkbench.CSS_URI_ARG, cssURI);
        }

        // Temporary to support old property as well
        if (cssURI != null && !cssURI.startsWith("platform:")) {
            System.err.println(
                    "Warning " + cssURI + " changed its meaning it is used now to run without theme support");
            appContext.set(E4Application.THEME_ID, cssURI);
        }

        String cssResourcesURI = getArgValue(IWorkbench.CSS_RESOURCE_URI_ARG, applicationContext, false);
        appContext.set(IWorkbench.CSS_RESOURCE_URI_ARG, cssResourcesURI);
        appContext.set(E4Workbench.RENDERER_FACTORY_URI,
                getArgValue(E4Workbench.RENDERER_FACTORY_URI, applicationContext, false));

        // This is a default arg, if missing we use the default rendering engine
        String presentationURI = getArgValue(IWorkbench.PRESENTATION_URI_ARG, applicationContext, false);
        if (presentationURI == null) {
            presentationURI = PartRenderingEngine.engineURI;
        }
        appContext.set(IWorkbench.PRESENTATION_URI_ARG, presentationURI);

        // Instantiate the Workbench (which is responsible for
        // 'running' the UI (if any)...
        return workbench = new E4Workbench(appModel, appContext);
    }

    private MApplication loadApplicationModel(IApplicationContext appContext, IEclipseContext eclipseContext) {
        MApplication theApp = null;

        Location instanceLocation = WorkbenchSWTActivator.getDefault().getInstanceLocation();

        String appModelPath = getArgValue(IWorkbench.XMI_URI_ARG, appContext, false);
        if (appModelPath == null || appModelPath.length() == 0) {
            Bundle brandingBundle = appContext.getBrandingBundle();
            if (brandingBundle != null)
                appModelPath = brandingBundle.getSymbolicName() + "/"
                        + E4Application.APPLICATION_MODEL_PATH_DEFAULT;
        }
        Assert.isNotNull(appModelPath, IWorkbench.XMI_URI_ARG + " argument missing"); //$NON-NLS-1$
        final URI initialWorkbenchDefinitionInstance = URI.createPlatformPluginURI(appModelPath, true);

        eclipseContext.set(E4Workbench.INITIAL_WORKBENCH_MODEL_URI, initialWorkbenchDefinitionInstance);
        eclipseContext.set(E4Workbench.INSTANCE_LOCATION, instanceLocation);

        // Save and restore
        boolean saveAndRestore;
        String value = getArgValue(IWorkbench.PERSIST_STATE, appContext, false);

        saveAndRestore = value == null || Boolean.parseBoolean(value);

        eclipseContext.set(IWorkbench.PERSIST_STATE, Boolean.valueOf(saveAndRestore));

        // Persisted state
        boolean clearPersistedState;
        value = getArgValue(IWorkbench.CLEAR_PERSISTED_STATE, appContext, true);
        clearPersistedState = value != null && Boolean.parseBoolean(value);
        eclipseContext.set(IWorkbench.CLEAR_PERSISTED_STATE, Boolean.valueOf(clearPersistedState));

        // Delta save and restore
        boolean deltaRestore;
        value = getArgValue(E4Workbench.DELTA_RESTORE, appContext, false);
        deltaRestore = value == null || Boolean.parseBoolean(value);
        eclipseContext.set(E4Workbench.DELTA_RESTORE, Boolean.valueOf(deltaRestore));

        String resourceHandler = getArgValue(IWorkbench.MODEL_RESOURCE_HANDLER, appContext, false);

        if (resourceHandler == null) {
            resourceHandler = "bundleclass://org.eclipse.e4.ui.workbench/" + ResourceHandler.class.getName();
        }

        IContributionFactory factory = eclipseContext.get(IContributionFactory.class);

        handler = (IModelResourceHandler) factory.create(resourceHandler, eclipseContext);
        eclipseContext.set(IModelResourceHandler.class, handler);

        Resource resource = handler.loadMostRecentModel();
        theApp = (MApplication) resource.getContents().get(0);

        return theApp;
    }

    private String getArgValue(String argName, IApplicationContext appContext, boolean singledCmdArgValue) {
        // Is it in the arg list ?
        if (argName == null || argName.length() == 0)
            return null;

        if (singledCmdArgValue) {
            for (int i = 0; i < args.length; i++) {
                if (("-" + argName).equals(args[i]))
                    return "true";
            }
        } else {
            for (int i = 0; i < args.length; i++) {
                if (("-" + argName).equals(args[i]) && i + 1 < args.length)
                    return args[i + 1];
            }
        }

        final String brandingProperty = appContext.getBrandingProperty(argName);
        return brandingProperty == null ? System.getProperty(argName) : brandingProperty;
    }

    public void stop() {
        if (workbench != null) {
            workbench.close();
        }
    }

    // TODO This should go into a different bundle
    public static IEclipseContext createDefaultHeadlessContext() {
        IEclipseContext serviceContext = E4Workbench.getServiceContext();

        IExtensionRegistry registry = RegistryFactory.getRegistry();
        ExceptionHandler exceptionHandler = new ExceptionHandler();
        ReflectionContributionFactory contributionFactory = new ReflectionContributionFactory(registry);
        serviceContext.set(IContributionFactory.class, contributionFactory);
        serviceContext.set(IExceptionHandler.class, exceptionHandler);
        serviceContext.set(IExtensionRegistry.class, registry);

        // translation
        String locale = Locale.getDefault().toString();
        serviceContext.set(TranslationService.LOCALE, locale);
        TranslationService bundleTranslationProvider = TranslationProviderFactory
                .bundleTranslationService(serviceContext);
        serviceContext.set(TranslationService.class, bundleTranslationProvider);

        serviceContext.set(Adapter.class, ContextInjectionFactory.make(EclipseAdapter.class, serviceContext));

        // No default log provider available
        if (serviceContext.get(ILoggerProvider.class) == null) {
            serviceContext.set(ILoggerProvider.class,
                    ContextInjectionFactory.make(DefaultLoggerProvider.class, serviceContext));
        }

        return serviceContext;
    }

    // TODO This should go into a different bundle
    public static IEclipseContext createDefaultContext() {
        IEclipseContext serviceContext = createDefaultHeadlessContext();
        final IEclipseContext appContext = serviceContext.createChild("WorkbenchContext"); //$NON-NLS-1$

        appContext.set(Logger.class, ContextInjectionFactory.make(WorkbenchLogger.class, appContext));

        appContext.set(EModelService.class, new ModelServiceImpl(appContext));

        appContext.set(EPlaceholderResolver.class, new PlaceholderResolver());

        // setup for commands and handlers
        appContext.set(IServiceConstants.ACTIVE_PART, new ActivePartLookupFunction());

        appContext.set(IServiceConstants.ACTIVE_SHELL,
                new ActiveChildLookupFunction(IServiceConstants.ACTIVE_SHELL, E4Workbench.LOCAL_ACTIVE_SHELL));

        appContext.set(IStylingEngine.class, new IStylingEngine() {
            public void setClassname(Object widget, String classname) {
            }

            public void setId(Object widget, String id) {
            }

            public void style(Object widget) {
            }

            public CSSStyleDeclaration getStyle(Object widget) {
                return null;
            }

            public void setClassnameAndId(Object widget, String classname, String id) {
            }
        });

        return appContext;
    }

    /**
     * Simplified copy of IDEAplication processing that does not offer to choose
     * a workspace location.
     */
    private boolean checkInstanceLocation(Location instanceLocation, Shell shell) {
        if (instanceLocation == null) {
            MessageDialog.openError(shell, WorkbenchSWTMessages.IDEApplication_workspaceMandatoryTitle,
                    WorkbenchSWTMessages.IDEApplication_workspaceMandatoryMessage);
            return false;
        }

        // -data "/valid/path", workspace already set
        if (instanceLocation.isSet()) {
            // make sure the meta data version is compatible (or the user has
            // chosen to overwrite it).
            if (!checkValidWorkspace(shell, instanceLocation.getURL())) {
                return false;
            }

            // at this point its valid, so try to lock it and update the
            // metadata version information if successful
            try {
                if (instanceLocation.lock()) {
                    writeWorkspaceVersion();
                    return true;
                }

                // we failed to create the directory.
                // Two possibilities:
                // 1. directory is already in use
                // 2. directory could not be created
                File workspaceDirectory = new File(instanceLocation.getURL().getFile());
                if (workspaceDirectory.exists()) {
                    MessageDialog.openError(shell, WorkbenchSWTMessages.IDEApplication_workspaceCannotLockTitle,
                            WorkbenchSWTMessages.IDEApplication_workspaceCannotLockMessage);
                } else {
                    MessageDialog.openError(shell, WorkbenchSWTMessages.IDEApplication_workspaceCannotBeSetTitle,
                            WorkbenchSWTMessages.IDEApplication_workspaceCannotBeSetMessage);
                }
            } catch (IOException e) {
                Logger logger = new WorkbenchLogger(PLUGIN_ID);
                logger.error(e);
                MessageDialog.openError(shell, WorkbenchSWTMessages.InternalError, e.getMessage());
            }
            return false;
        }
        /*
         * // -data @noDefault or -data not specified, prompt and set
         * ChooseWorkspaceData launchData = new ChooseWorkspaceData(instanceLoc
         * .getDefault());
         * 
         * boolean force = false; while (true) { URL workspaceUrl =
         * promptForWorkspace(shell, launchData, force); if (workspaceUrl ==
         * null) { return false; }
         * 
         * // if there is an error with the first selection, then force the //
         * dialog to open to give the user a chance to correct force = true;
         * 
         * try { // the operation will fail if the url is not a valid //
         * instance data area, so other checking is unneeded if
         * (instanceLocation.setURL(workspaceUrl, true)) {
         * launchData.writePersistedData(); writeWorkspaceVersion(); return
         * true; } } catch (IllegalStateException e) { MessageDialog .openError(
         * shell, IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetTitle,
         * IDEWorkbenchMessages.IDEApplication_workspaceCannotBeSetMessage);
         * return false; }
         * 
         * // by this point it has been determined that the workspace is //
         * already in use -- force the user to choose again
         * MessageDialog.openError(shell,
         * IDEWorkbenchMessages.IDEApplication_workspaceInUseTitle,
         * IDEWorkbenchMessages.IDEApplication_workspaceInUseMessage); }
         */
        return false;
    }

    /**
     * Return true if the argument directory is ok to use as a workspace and
     * false otherwise. A version check will be performed, and a confirmation
     * box may be displayed on the argument shell if an older version is
     * detected.
     * 
     * @return true if the argument URL is ok to use as a workspace and false
     *         otherwise.
     */
    private boolean checkValidWorkspace(Shell shell, URL url) {
        // a null url is not a valid workspace
        if (url == null) {
            return false;
        }

        String version = readWorkspaceVersion(url);

        // if the version could not be read, then there is not any existing
        // workspace data to trample, e.g., perhaps its a new directory that
        // is just starting to be used as a workspace
        if (version == null) {
            return true;
        }

        final int ide_version = Integer.parseInt(WORKSPACE_VERSION_VALUE);
        int workspace_version = Integer.parseInt(version);

        // equality test is required since any version difference (newer
        // or older) may result in data being trampled
        if (workspace_version == ide_version) {
            return true;
        }

        // At this point workspace has been detected to be from a version
        // other than the current ide version -- find out if the user wants
        // to use it anyhow.
        String title = WorkbenchSWTMessages.IDEApplication_versionTitle;
        String message = NLS.bind(WorkbenchSWTMessages.IDEApplication_versionMessage, url.getFile());

        MessageBox mbox = new MessageBox(shell, SWT.OK | SWT.CANCEL | SWT.ICON_WARNING | SWT.APPLICATION_MODAL);
        mbox.setText(title);
        mbox.setMessage(message);
        return mbox.open() == SWT.OK;
    }

    /**
     * Look at the argument URL for the workspace's version information. Return
     * that version if found and null otherwise.
     */
    private static String readWorkspaceVersion(URL workspace) {
        File versionFile = getVersionFile(workspace, false);
        if (versionFile == null || !versionFile.exists()) {
            return null;
        }

        try {
            // Although the version file is not spec'ed to be a Java properties
            // file, it happens to follow the same format currently, so using
            // Properties to read it is convenient.
            Properties props = new Properties();
            FileInputStream is = new FileInputStream(versionFile);
            try {
                props.load(is);
            } finally {
                is.close();
            }

            return props.getProperty(WORKSPACE_VERSION_KEY);
        } catch (IOException e) {
            Logger logger = new WorkbenchLogger(PLUGIN_ID);
            logger.error(e);
            return null;
        }
    }

    /**
     * Write the version of the metadata into a known file overwriting any
     * existing file contents. Writing the version file isn't really crucial, so
     * the function is silent about failure
     */
    private static void writeWorkspaceVersion() {
        Location instanceLoc = Platform.getInstanceLocation();
        if (instanceLoc == null || instanceLoc.isReadOnly()) {
            return;
        }

        File versionFile = getVersionFile(instanceLoc.getURL(), true);
        if (versionFile == null) {
            return;
        }

        OutputStream output = null;
        try {
            String versionLine = WORKSPACE_VERSION_KEY + '=' + WORKSPACE_VERSION_VALUE;

            output = new FileOutputStream(versionFile);
            output.write(versionLine.getBytes("UTF-8")); //$NON-NLS-1$
        } catch (IOException e) {
            Logger logger = new WorkbenchLogger(PLUGIN_ID);
            logger.error(e);
        } finally {
            try {
                if (output != null) {
                    output.close();
                }
            } catch (IOException e) {
                // do nothing
            }
        }
    }

    /**
     * The version file is stored in the metadata area of the workspace. This
     * method returns an URL to the file or null if the directory or file does
     * not exist (and the create parameter is false).
     * 
     * @param create
     *            If the directory and file does not exist this parameter
     *            controls whether it will be created.
     * @return An url to the file or null if the version file does not exist or
     *         could not be created.
     */
    private static File getVersionFile(URL workspaceUrl, boolean create) {
        if (workspaceUrl == null) {
            return null;
        }

        try {
            // make sure the directory exists
            File metaDir = new File(workspaceUrl.getPath(), METADATA_FOLDER);
            if (!metaDir.exists() && (!create || !metaDir.mkdir())) {
                return null;
            }

            // make sure the file exists
            File versionFile = new File(metaDir, VERSION_FILENAME);
            if (!versionFile.exists() && (!create || !versionFile.createNewFile())) {
                return null;
            }

            return versionFile;
        } catch (IOException e) {
            // cannot log because instance area has not been set
            return null;
        }
    }

    static final private String CONTEXT_INITIALIZED = "org.eclipse.ui.contextInitialized";

    static public void initializeServices(MApplication appModel) {
        IEclipseContext appContext = appModel.getContext();
        // make sure we only add trackers once
        if (appContext.containsKey(CONTEXT_INITIALIZED))
            return;
        appContext.set(CONTEXT_INITIALIZED, "true");
        initializeApplicationServices(appContext);
        List<MWindow> windows = appModel.getChildren();
        for (MWindow childWindow : windows) {
            initializeWindowServices(childWindow);
        }
        ((EObject) appModel).eAdapters().add(new AdapterImpl() {
            public void notifyChanged(Notification notification) {
                if (notification.getFeatureID(MApplication.class) != UiPackageImpl.ELEMENT_CONTAINER__CHILDREN)
                    return;
                if (notification.getEventType() != Notification.ADD)
                    return;
                MWindow childWindow = (MWindow) notification.getNewValue();
                initializeWindowServices(childWindow);
            }
        });
    }

    static public void initializeApplicationServices(IEclipseContext appContext) {
        final IEclipseContext theContext = appContext;
        // we add a special tracker to bring up current selection from
        // the active window to the application level
        appContext.runAndTrack(new RunAndTrack() {
            public boolean changed(IEclipseContext context) {
                IEclipseContext activeChildContext = context.getActiveChild();
                if (activeChildContext != null) {
                    Object selection = activeChildContext.get(IServiceConstants.ACTIVE_SELECTION);
                    theContext.set(IServiceConstants.ACTIVE_SELECTION, selection);
                }
                return true;
            }
        });

        // we create a selection service handle on every node that we are asked
        // about as handle needs to know its context
        appContext.set(ESelectionService.class.getName(), new IContextFunction() {
            public Object compute(IEclipseContext context) {
                return ContextInjectionFactory.make(SelectionServiceImpl.class, context);
            }
        });
    }

    static public void initializeWindowServices(MWindow childWindow) {
        IEclipseContext windowContext = childWindow.getContext();
        initWindowContext(windowContext);
        // Mostly MWindow contexts are lazily created by renderers and is not
        // set at this point.
        ((EObject) childWindow).eAdapters().add(new AdapterImpl() {
            public void notifyChanged(Notification notification) {
                if (notification.getFeatureID(MWindow.class) != BasicPackageImpl.WINDOW__CONTEXT)
                    return;
                IEclipseContext windowContext = (IEclipseContext) notification.getNewValue();
                initWindowContext(windowContext);
            }
        });
    }

    static private void initWindowContext(IEclipseContext windowContext) {
        if (windowContext == null)
            return;
        SelectionAggregator selectionAggregator = ContextInjectionFactory.make(SelectionAggregator.class,
                windowContext);
        windowContext.set(SelectionAggregator.class, selectionAggregator);
    }
}