package name.abuchen.portfolio.bootstrap; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.e4.core.contexts.IEclipseContext; import org.eclipse.e4.core.di.extensions.Preference; import org.eclipse.e4.core.services.log.Logger; import org.eclipse.e4.core.services.statusreporter.StatusReporter; import org.eclipse.e4.ui.internal.workbench.swt.IEventLoopAdvisor; import org.eclipse.e4.ui.model.application.MAddon; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.MApplicationElement; import org.eclipse.e4.ui.model.application.ui.MElementContainer; import org.eclipse.e4.ui.model.application.ui.MUIElement; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.workbench.IWorkbench; import org.eclipse.e4.ui.workbench.Selector; import org.eclipse.e4.ui.workbench.lifecycle.PostContextCreate; import org.eclipse.e4.ui.workbench.lifecycle.PreSave; import org.eclipse.e4.ui.workbench.lifecycle.ProcessRemovals; import org.eclipse.e4.ui.workbench.modeling.EModelService; import org.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.swt.widgets.Display; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.Version; import org.osgi.service.prefs.BackingStoreException; @SuppressWarnings("restriction") public class LifeCycleManager { private static final String MODEL_VERSION = "model.version"; //$NON-NLS-1$ private static final String FORCE_CLEAR_PERSISTED_STATE = "model.forceClearPersistedState"; //$NON-NLS-1$ @Inject @Preference(nodePath = "name.abuchen.portfolio.bootstrap") IEclipsePreferences preferences; @Inject Logger logger; @PostContextCreate public void doPostContextCreate(IEclipseContext context) { checkForJava8(); removeClearPersistedStateFlag(); checkForModelChanges(); checkForRequestToClearPersistedState(); setupEventLoopAdvisor(context); } private void checkForJava8() { // if the java version is < 8, show a message dialog because otherwise // the application would silently not start double version = Double.parseDouble(System.getProperty("java.specification.version")); //$NON-NLS-1$ if (version < 1.8) { MessageDialog.openInformation(Display.getDefault().getActiveShell(), Messages.TitleJavaVersion, Messages.MsgMinimumRequiredVersion); throw new UnsupportedOperationException("The minimum Java version required is Java 8"); //$NON-NLS-1$ } } private void removeClearPersistedStateFlag() { // the 'old' update mechanism edited the ini file *after* the upgrade // and added the -clearPersistedState flag. The current mechanism does // not need it, hence it must be remove if present // not applicable on Mac OS X because only update is not supported if (Platform.OS_MACOSX.equals(Platform.getOS())) return; try { IniFileManipulator iniFile = new IniFileManipulator(); iniFile.load(); iniFile.unsetClearPersistedState(); if (iniFile.isDirty()) iniFile.save(); } catch (IOException ignore) { // ignore: in production, it will anyway be removed during the next // update; in development, it will annoy to always report this error } } private void checkForModelChanges() { Version modelVersion = Version.parseVersion(preferences.get(MODEL_VERSION, null)); Version programVersion = FrameworkUtil.getBundle(this.getClass()).getVersion(); if (!modelVersion.equals(programVersion)) { logger.info(MessageFormat.format( "Detected model change from version {0} to version {1}; clearing persisted state", //$NON-NLS-1$ modelVersion.toString(), programVersion.toString())); System.setProperty(IWorkbench.CLEAR_PERSISTED_STATE, Boolean.TRUE.toString()); } } private void checkForRequestToClearPersistedState() { boolean forceClearPersistedState = Boolean .parseBoolean(preferences.get(FORCE_CLEAR_PERSISTED_STATE, Boolean.FALSE.toString())); if (forceClearPersistedState) { logger.info(MessageFormat.format("Clearing persisted state due to ''{0}=true''", //$NON-NLS-1$ FORCE_CLEAR_PERSISTED_STATE)); System.setProperty(IWorkbench.CLEAR_PERSISTED_STATE, Boolean.TRUE.toString()); try { preferences.remove(FORCE_CLEAR_PERSISTED_STATE); preferences.flush(); } catch (BackingStoreException e) { logger.error(e); } } } public void setupEventLoopAdvisor(final IEclipseContext context) { // do not show an error popup if is the annoying NPE on El Capitan // https://bugs.eclipse.org/bugs/show_bug.cgi?id=434393 context.set(IEventLoopAdvisor.class, new IEventLoopAdvisor() { @Override public void eventLoopIdle(final Display display) { display.sleep(); } @Override public void eventLoopException(final Throwable exception) { boolean isAnnoyingNullPointerOnElCapitan = isAnnoyingNullPointerOnElCapitan(exception); StatusReporter statusReporter = (StatusReporter) context.get(StatusReporter.class.getName()); if (!isAnnoyingNullPointerOnElCapitan && statusReporter != null) { statusReporter.show(StatusReporter.ERROR, "Internal Error", exception); //$NON-NLS-1$ } else if (logger != null) { logger.error(exception); } else { exception.printStackTrace(); } } private boolean isAnnoyingNullPointerOnElCapitan(Throwable exception) { if (!(exception instanceof NullPointerException)) return false; StackTraceElement[] stackTrace = exception.getStackTrace(); if (stackTrace == null || stackTrace.length == 0) return true; if (!"org.eclipse.swt.widgets.Control".equals(stackTrace[0].getClassName())) //$NON-NLS-1$ return false; if (!"internal_new_GC".equals(stackTrace[0].getMethodName())) //$NON-NLS-1$ return false; return true; } }); } @ProcessRemovals public void removeDnDAddon(MApplication app) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=394231#c3 for (MAddon addon : new ArrayList<MAddon>(app.getAddons())) { String contributionURI = addon.getContributionURI(); if (contributionURI.contains("ui.workbench.addons.minmax.MinMaxAddon") //$NON-NLS-1$ || contributionURI.contains("ui.workbench.addons.splitteraddon.SplitterAddon")) //$NON-NLS-1$ { app.getAddons().remove(addon); } } } @PreSave public void doPreSave(MApplication app, EPartService partService, EModelService modelService) { saveModelVersion(); removePortfolioPartsWithoutPersistedFile(app, partService, modelService); } private void saveModelVersion() { String modelVersion = preferences.get(MODEL_VERSION, Version.emptyVersion.toString()); String programVersion = FrameworkUtil.getBundle(this.getClass()).getVersion().toString(); if (!modelVersion.equals(programVersion)) { try { preferences.put(MODEL_VERSION, programVersion); preferences.flush(); } catch (BackingStoreException e) { logger.error(e); } } } private void removePortfolioPartsWithoutPersistedFile(MApplication app, EPartService partService, EModelService modelService) { List<MPart> parts = modelService.findElements(app, MPart.class, EModelService.IN_ACTIVE_PERSPECTIVE, new Selector() { @Override public boolean select(MApplicationElement element) { if (!"name.abuchen.portfolio.ui.part.portfolio".equals(element.getElementId())) //$NON-NLS-1$ return false; return element.getPersistedState().get("file") == null; //$NON-NLS-1$ } }); for (MPart part : parts) { MElementContainer<MUIElement> parent = part.getParent(); if (parent.getSelectedElement().equals(part)) parent.setSelectedElement(null); parent.getChildren().remove(part); } } }