/******************************************************************************* * Copyright (c) 2009, 2016 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. * Lars Vogel <Lars.Vogel@vogella.com> - Bug 366364, 445724, 446088, 458033, 393171 * Terry Parker <tparker@google.com> - Bug 416673 * Christian Georgi (SAP) - Bug 432480 * Simon Scholz <simon.scholz@vogella.com> - Bug 478896 ******************************************************************************/ package org.eclipse.e4.ui.internal.workbench.swt; 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.nio.charset.StandardCharsets; import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.Properties; import org.eclipse.core.databinding.observable.Realm; 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.ContextFunction; 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.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.URIHelper; 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.IWorkbench; 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.DisplayRealm; 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 static final String PERSPECTIVE_ARG_NAME = "perspective"; private static final String SHOWLOCATION_ARG_NAME = "showLocation"; private static final String DEFAULT_THEME_ID = "org.eclipse.e4.ui.css.theme.e4_default"; public static final String HIGH_CONTRAST_THEME_ID = "org.eclipse.e4.ui.css.theme.high-contrast"; 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; } @Override 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, workbench.getContext())) return EXIT_OK; // Create and run the UI (if any) workbench.createAndRunUI(workbench.getApplication()); saveModel(); workbench.close(); if (workbench.isRestart()) { return EXIT_RESTART; } return EXIT_OK; } finally { if (display != null) display.dispose(); if (instanceLocation != null) instanceLocation.release(); } } public void saveModel() { // Save the model into the targetURI if (lcManager != null && workbench != null) { ContextInjectionFactory.invoke(lcManager, PreSave.class, workbench.getContext(), null); } try { if (!(handler instanceof ResourceHandler) || ((ResourceHandler) handler).hasTopLevelWindows()) { handler.save(); } else { Logger logger = new WorkbenchLogger(PLUGIN_ID); logger.error(new Exception(), // log a stack trace for debugging "Attempted to save a workbench model that had no top-level windows! " //$NON-NLS-1$ + "Skipped saving the model to avoid corruption."); //$NON-NLS-1$ } } catch (IOException e) { Logger logger = new WorkbenchLogger(PLUGIN_ID); logger.error(e, "Error saving the workbench model"); //$NON-NLS-1$ } } public E4Workbench createE4Workbench(IApplicationContext applicationContext, final Display display) { args = (String[]) applicationContext.getArguments().get(IApplicationContext.APPLICATION_ARGS); IEclipseContext appContext = createDefaultContext(); appContext.set(Display.class, display); appContext.set(Realm.class, DisplayRealm.getRealm(display)); appContext.set(UISynchronize.class, new UISynchronize() { @Override public void syncExec(Runnable runnable) { if (display != null && !display.isDisposed()) { display.syncExec(runnable); } } @Override public void asyncExec(Runnable runnable) { if (display != null && !display.isDisposed()) { display.asyncExec(runnable); } } }); appContext.set(IApplicationContext.class, applicationContext); // This context will be used by the injector for its // extended data suppliers ContextInjectionFactory.setDefault(appContext); // Get the factory to create DI instances with IContributionFactory factory = appContext.get(IContributionFactory.class); // Install the life-cycle manager for this session if there's one // defined Optional<String> lifeCycleURI = getArgValue(IWorkbench.LIFE_CYCLE_URI_ARG, applicationContext, false); lifeCycleURI.ifPresent(lifeCycleURIValue -> { lcManager = factory.create(lifeCycleURIValue, appContext); if (lcManager != null) { // Let the manager manipulate the appContext if desired ContextInjectionFactory.invoke(lcManager, PostContextCreate.class, appContext, null); } }); Optional<String> forcedPerspectiveId = getArgValue(PERSPECTIVE_ARG_NAME, applicationContext, false); forcedPerspectiveId.ifPresent(forcedPerspectiveIdValue -> appContext.set(E4Workbench.FORCED_PERSPECTIVE_ID, forcedPerspectiveIdValue)); String showLocation = getLocationFromCommandLine(); if (showLocation != null) { appContext.set(E4Workbench.FORCED_SHOW_LOCATION, showLocation); } // 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)) { // first one wins. E4Workbench.getServiceContext().set(MApplication.class, appModel); } // Set the app's context after adding itself appContext.set(MApplication.class, appModel); // 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 Optional<String> xmiURI = getArgValue(IWorkbench.XMI_URI_ARG, applicationContext, false); xmiURI.ifPresent(xmiURIValue -> { appContext.set(IWorkbench.XMI_URI_ARG, xmiURIValue); }); setCSSContextVariables(applicationContext, appContext); Optional<String> rendererFactoryURI = getArgValue(E4Workbench.RENDERER_FACTORY_URI, applicationContext, false); rendererFactoryURI.ifPresent(rendererFactoryURIValue -> { appContext.set(E4Workbench.RENDERER_FACTORY_URI, rendererFactoryURIValue); }); // This is a default arg, if missing we use the default rendering engine Optional<String> presentationURI = getArgValue(IWorkbench.PRESENTATION_URI_ARG, applicationContext, false); appContext.set(IWorkbench.PRESENTATION_URI_ARG, presentationURI.orElse(PartRenderingEngine.engineURI)); // Instantiate the Workbench (which is responsible for // 'running' the UI (if any)... return workbench = new E4Workbench(appModel, appContext); } private void setCSSContextVariables(IApplicationContext applicationContext, IEclipseContext context) { boolean highContrastMode = getApplicationDisplay().getHighContrast(); Optional<String> cssURI = highContrastMode ? Optional.empty() : getArgValue(IWorkbench.CSS_URI_ARG, applicationContext, false); cssURI.ifPresent(cssURIValue -> { context.set(IWorkbench.CSS_URI_ARG, cssURIValue); }); Optional<String> themeId = highContrastMode ? Optional.of(HIGH_CONTRAST_THEME_ID) : getArgValue(E4Application.THEME_ID, applicationContext, false); if (!themeId.isPresent() && !cssURI.isPresent()) { context.set(E4Application.THEME_ID, DEFAULT_THEME_ID); } else { context.set(E4Application.THEME_ID, themeId.orElseGet(() -> null)); } // validate static CSS URI cssURI.filter(cssURIValue -> !cssURIValue.startsWith("platform:/plugin/")).ifPresent(cssURIValue -> { System.err.println( "Warning. Use the \"platform:/plugin/Bundle-SymbolicName/path/filename.extension\" URI for the parameter: " + IWorkbench.CSS_URI_ARG); // $NON-NLS-1$ context.set(E4Application.THEME_ID, cssURIValue); }); Optional<String> cssResourcesURI = getArgValue(IWorkbench.CSS_RESOURCE_URI_ARG, applicationContext, false); cssResourcesURI.ifPresent(cssResourcesURIValue -> { context.set(IWorkbench.CSS_RESOURCE_URI_ARG, cssResourcesURIValue); }); } private MApplication loadApplicationModel(IApplicationContext appContext, IEclipseContext eclipseContext) { MApplication theApp = null; Location instanceLocation = WorkbenchSWTActivator.getDefault().getInstanceLocation(); URI applicationModelURI = determineApplicationModelURI(appContext); eclipseContext.set(E4Workbench.INITIAL_WORKBENCH_MODEL_URI, applicationModelURI); // Save and restore Boolean saveAndRestore = getArgValue(IWorkbench.PERSIST_STATE, appContext, false) .map(value -> Boolean.parseBoolean(value)).orElse(Boolean.TRUE); eclipseContext.set(IWorkbench.PERSIST_STATE, saveAndRestore); // when -data @none or -data @noDefault options if (instanceLocation != null && instanceLocation.getURL() != null) { eclipseContext.set(E4Workbench.INSTANCE_LOCATION, instanceLocation); } else { eclipseContext.set(IWorkbench.PERSIST_STATE, false); } // Persisted state Boolean clearPersistedState = getArgValue(IWorkbench.CLEAR_PERSISTED_STATE, appContext, true) .map(value -> Boolean.parseBoolean(value)).orElse(Boolean.FALSE); eclipseContext.set(IWorkbench.CLEAR_PERSISTED_STATE, clearPersistedState); String resourceHandler = getArgValue(IWorkbench.MODEL_RESOURCE_HANDLER, appContext, false) .orElse("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; } /** * @param appContext * @return */ private URI determineApplicationModelURI(IApplicationContext appContext) { Optional<String> appModelPath = getArgValue(IWorkbench.XMI_URI_ARG, appContext, false); String appModelPathValue = appModelPath.filter(path -> !path.isEmpty()).orElseGet(() -> { Bundle brandingBundle = appContext.getBrandingBundle(); if (brandingBundle != null) { return brandingBundle.getSymbolicName() + "/" + E4Application.APPLICATION_MODEL_PATH_DEFAULT; } else { Logger logger = new WorkbenchLogger(PLUGIN_ID); logger.error(new Exception(), "applicationXMI parameter not set and no branding plugin defined. "); //$NON-NLS-1$ } return null; }); URI applicationModelURI = null; // check if the appModelPath is already a platform-URI and if so use it if (URIHelper.isPlatformURI(appModelPathValue)) { applicationModelURI = URI.createURI(appModelPathValue, true); } else { applicationModelURI = URI.createPlatformPluginURI(appModelPathValue, true); } return applicationModelURI; } /** * Finds an argument's value in the app's command line arguments, branding, * and system properties * * @param argName * the argument name * @param appContext * the application context * @param singledCmdArgValue * whether it's a single-valued argument * @return an {@link Optional} containing the value or an empty * {@link Optional}, if no value could be found */ private Optional<String> getArgValue(String argName, IApplicationContext appContext, boolean singledCmdArgValue) { // Is it in the arg list ? if (argName == null || argName.length() == 0) return Optional.empty(); if (singledCmdArgValue) { for (String arg : args) { if (("-" + argName).equals(arg)) return Optional.of("true"); } } else { for (int i = 0; i < args.length; i++) { if (("-" + argName).equals(args[i]) && i + 1 < args.length) return Optional.of(args[i + 1]); } } final String brandingProperty = appContext.getBrandingProperty(argName); return Optional.ofNullable(brandingProperty).map(brandingPropertyValue -> Optional.of(brandingPropertyValue)) .orElse(Optional.ofNullable(System.getProperty(argName))); } /** * @return the value of the {@link E4Application#SHOWLOCATION_ARG_NAME * showlocation} command line argument, or <code>null</code> if it * is not set */ private String getLocationFromCommandLine() { final String fullArgName = "-" + SHOWLOCATION_ARG_NAME; for (int i = 0; i < args.length; i++) { // ignore case for compatibility reasons if (fullArgName.equalsIgnoreCase(args[i])) { // $NON-NLS-1$ String name = null; if (args.length > i + 1) { name = args[i + 1]; } if (name != null && name.indexOf("-") == -1) { //$NON-NLS-1$ return name; } return Platform.getLocation().toOSString(); } } return null; } @Override 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); 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$ // make application context available for dependency injection under the E4Application.APPLICATION_CONTEXT_KEY key appContext.set(IWorkbench.APPLICATION_CONTEXT_KEY, appContext); 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() { @Override public void setClassname(Object widget, String classname) { } @Override public void setId(Object widget, String id) { } @Override public void style(Object widget) { } @Override public CSSStyleDeclaration getStyle(Object widget) { return null; } @Override public void setClassnameAndId(Object widget, String classname, String id) { } }); // translation initializeLocalization(appContext); return appContext; } /** * Initializes the given context with the locale and the TranslationService * to use. * * @param appContext * The application context to which the locale and the * TranslationService should be set. */ private static void initializeLocalization(IEclipseContext appContext) { appContext.set(TranslationService.LOCALE, Locale.getDefault()); appContext.set(TranslationService.class, TranslationProviderFactory.bundleTranslationService(appContext)); } /** * Simplified copy of IDEAplication processing that does not offer to choose * a workspace location. */ private boolean checkInstanceLocation(Location instanceLocation, Shell shell, IEclipseContext context) { // Eclipse has been run with -data @none or -data @noDefault options so // we don't need to validate the location if (instanceLocation == null && Boolean.FALSE.equals(context.get(IWorkbench.PERSIST_STATE))) { return true; } 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; } 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(StandardCharsets.UTF_8)); } 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() { @Override 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() { @Override 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 ContextFunction() { @Override public Object compute(IEclipseContext context, String contextKey) { 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() { @Override 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); } }