/*******************************************************************************
* Copyright (c) 2010, 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
*******************************************************************************/
package org.eclipse.e4.ui.internal.workbench.swt;
import static org.eclipse.e4.ui.internal.workbench.swt.Policy.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Hashtable;
import org.eclipse.core.internal.runtime.InternalPlatform;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.DialogSettings;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.osgi.service.datalocation.Location;
import org.eclipse.osgi.service.debug.DebugOptions;
import org.eclipse.osgi.service.debug.DebugOptionsListener;
import org.eclipse.osgi.service.debug.DebugTrace;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;
/**
* The activator class controls the plug-in life cycle
*/
public class WorkbenchSWTActivator implements BundleActivator, DebugOptionsListener {
public static final String PI_RENDERERS = "org.eclipse.e4.ui.workbench.swt"; //$NON-NLS-1$
private BundleContext context;
private ServiceTracker<?, PackageAdmin> pkgAdminTracker;
private ServiceTracker<?, Location> locationTracker;
private static WorkbenchSWTActivator activator;
private DebugTrace trace;
/**
* Get the default activator.
*
* @return a BundleActivator
*/
public static WorkbenchSWTActivator getDefault() {
return activator;
}
/**
* @return this bundles context
*/
public BundleContext getContext() {
return context;
}
@Override
public void start(BundleContext context) throws Exception {
activator = this;
this.context = context;
Hashtable<String, String> props = new Hashtable<>(2);
props.put(DebugOptions.LISTENER_SYMBOLICNAME, PI_RENDERERS);
context.registerService(DebugOptionsListener.class, this, props);
}
@Override
public void stop(BundleContext context) throws Exception {
saveDialogSettings();
if (pkgAdminTracker != null) {
pkgAdminTracker.close();
pkgAdminTracker = null;
}
}
public Bundle getBundle() {
if (context == null) {
return null;
}
return context.getBundle();
}
/**
* @return the PackageAdmin service from this bundle
*/
public PackageAdmin getBundleAdmin() {
if (pkgAdminTracker == null) {
if (context == null) {
return null;
}
pkgAdminTracker = new ServiceTracker<>(context, PackageAdmin.class, null);
pkgAdminTracker.open();
}
return pkgAdminTracker.getService();
}
/**
* @return the instance Location service
*/
public Location getInstanceLocation() {
if (locationTracker == null) {
Filter filter = null;
try {
filter = context.createFilter(Location.INSTANCE_FILTER);
} catch (InvalidSyntaxException e) {
// ignore this. It should never happen as we have tested the
// above format.
}
locationTracker = new ServiceTracker<>(context, filter, null);
locationTracker.open();
}
return locationTracker.getService();
}
public static void trace(String option, String msg, Throwable error) {
activator.getTrace().trace(option, msg, error);
}
@Override
public void optionsChanged(DebugOptions options) {
trace = options.newDebugTrace(PI_RENDERERS);
DEBUG = options.getBooleanOption(PI_RENDERERS + DEBUG_FLAG, false);
DEBUG_MENUS = options.getBooleanOption(PI_RENDERERS + DEBUG_MENUS_FLAG, false);
DEBUG_RENDERER = options.getBooleanOption(PI_RENDERERS + DEBUG_RENDERER_FLAG, false);
}
public DebugTrace getTrace() {
return trace;
}
// //////////////////////////////////////////////////////////////////////
// The following code was copied from AbstractUIPlugin class.
/**
* The name of the dialog settings file (value
* <code>"dialog_settings.xml"</code>).
*/
private static final String FN_DIALOG_SETTINGS = "dialog_settings.xml"; //$NON-NLS-1$
/**
* Storage for dialog and wizard data; <code>null</code> if not yet
* initialized.
*/
private IDialogSettings dialogSettings = null;
/**
* Returns the dialog settings for this UI plug-in. The dialog settings is
* used to hold persistent state data for the various wizards and dialogs of
* this plug-in in the context of a workbench.
* <p>
* If an error occurs reading the dialog store, an empty one is quietly
* created and returned.
* </p>
* <p>
* Subclasses may override this method but are not expected to.
* </p>
*
* @return the dialog settings
*/
public IDialogSettings getDialogSettings() {
if (dialogSettings == null) {
loadDialogSettings();
}
return dialogSettings;
}
/**
* Loads the dialog settings for this plug-in. The default implementation
* first looks for a standard named file in the plug-in's read/write state
* area; if no such file exists, the plug-in's install directory is checked
* to see if one was installed with some default settings; if no file is
* found in either place, a new empty dialog settings is created. If a
* problem occurs, an empty settings is silently used.
* <p>
* This framework method may be overridden, although this is typically
* unnecessary.
* </p>
*/
protected void loadDialogSettings() {
dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
// bug 69387: The instance area should not be created (in the call to
// #getStateLocation) if -data @none or -data @noDefault was used
IPath dataLocation = getStateLocationOrNull();
if (dataLocation != null) {
// try r/w state area in the local file system
String readWritePath = dataLocation.append(FN_DIALOG_SETTINGS).toOSString();
File settingsFile = new File(readWritePath);
if (settingsFile.exists()) {
try {
dialogSettings.load(readWritePath);
} catch (IOException e) {
// load failed so ensure we have an empty settings
dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
}
return;
}
}
// otherwise look for bundle specific dialog settings
Bundle bundle = context.getBundle();
URL dsURL = FileLocator.find(bundle, new Path(FN_DIALOG_SETTINGS), null);
if (dsURL == null) {
return;
}
InputStream is = null;
try {
is = dsURL.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
dialogSettings.load(reader);
} catch (IOException e) {
// load failed so ensure we have an empty settings
dialogSettings = new DialogSettings("Workbench"); //$NON-NLS-1$
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
// do nothing
}
}
}
/**
* Saves this plug-in's dialog settings. Any problems which arise are
* silently ignored.
*/
protected void saveDialogSettings() {
if (dialogSettings == null) {
return;
}
try {
IPath path = getStateLocationOrNull();
if (path == null) {
return;
}
String readWritePath = path.append(FN_DIALOG_SETTINGS).toOSString();
dialogSettings.save(readWritePath);
} catch (IOException e) {
// spec'ed to ignore problems
} catch (IllegalStateException e) {
// spec'ed to ignore problems
}
}
/**
* FOR INTERNAL WORKBENCH USE ONLY.
*
* Returns the path to a location in the file system that can be used to
* persist/restore state between workbench invocations. If the location did
* not exist prior to this call it will be created. Returns
* <code>null</code> if no such location is available.
*
* @return path to a location in the file system where this plug-in can
* persist data between sessions, or <code>null</code> if no such
* location is available.
* @since 3.1
*/
private IPath getStateLocationOrNull() {
// TBD the state location is only accessible from Plugin class
// However, using it causes problems in the activation order
// So, for now, we get it directly.
try {
return InternalPlatform.getDefault().getStateLocation(context.getBundle(), true);
} catch (IllegalStateException e) {
// This occurs if -data=@none is explicitly specified, so ignore
// this silently.
// Is this OK? See bug 85071.
return null;
}
}
}