| /* |
| * Copyright (c) 2014-2017 Eike Stepper (Loehne, Germany) and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * Eike Stepper - initial API and implementation |
| */ |
| package org.eclipse.oomph.setup.ui.recorder; |
| |
| import org.eclipse.oomph.preferences.PreferencesFactory; |
| import org.eclipse.oomph.preferences.util.PreferencesRecorder; |
| import org.eclipse.oomph.setup.PreferenceTask; |
| import org.eclipse.oomph.setup.Scope; |
| import org.eclipse.oomph.setup.SetupTask; |
| import org.eclipse.oomph.setup.User; |
| import org.eclipse.oomph.setup.impl.PreferenceTaskImpl; |
| import org.eclipse.oomph.setup.impl.PreferenceTaskImpl.PreferenceHandler; |
| import org.eclipse.oomph.setup.internal.core.SetupContext; |
| import org.eclipse.oomph.setup.internal.core.util.SetupCoreUtil; |
| import org.eclipse.oomph.setup.internal.sync.DataProvider.NotCurrentException; |
| import org.eclipse.oomph.setup.internal.sync.SyncUtil; |
| import org.eclipse.oomph.setup.internal.sync.Synchronization; |
| import org.eclipse.oomph.setup.internal.sync.Synchronizer; |
| import org.eclipse.oomph.setup.internal.sync.SynchronizerJob; |
| import org.eclipse.oomph.setup.internal.sync.SynchronizerJob.FinishHandler; |
| import org.eclipse.oomph.setup.sync.SyncAction; |
| import org.eclipse.oomph.setup.sync.SyncActionType; |
| import org.eclipse.oomph.setup.sync.SyncDelta; |
| import org.eclipse.oomph.setup.sync.SyncPolicy; |
| import org.eclipse.oomph.setup.ui.SetupUIPlugin; |
| import org.eclipse.oomph.setup.ui.recorder.RecorderTransaction.CommitHandler; |
| import org.eclipse.oomph.setup.ui.synchronizer.OptOutDialog; |
| import org.eclipse.oomph.setup.ui.synchronizer.SynchronizerDialog; |
| import org.eclipse.oomph.setup.ui.synchronizer.SynchronizerDialog.PolicyAndValue; |
| import org.eclipse.oomph.setup.ui.synchronizer.SynchronizerManager; |
| import org.eclipse.oomph.ui.ButtonAnimator; |
| import org.eclipse.oomph.ui.ErrorDialog; |
| import org.eclipse.oomph.ui.UIUtil; |
| import org.eclipse.oomph.util.IOUtil; |
| import org.eclipse.oomph.util.ObjectUtil; |
| import org.eclipse.oomph.util.Pair; |
| import org.eclipse.oomph.util.PropertiesUtil; |
| import org.eclipse.oomph.util.StringUtil; |
| import org.eclipse.oomph.util.UserCallback; |
| |
| import org.eclipse.emf.common.util.EMap; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.resource.URIConverter; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.dialogs.ProgressMonitorDialog; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.preference.IPersistentPreferenceStore; |
| import org.eclipse.jface.preference.IPreferenceNode; |
| import org.eclipse.jface.preference.PreferenceDialog; |
| import org.eclipse.jface.preference.PreferenceManager; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.ToolItem; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.dialogs.PreferencesUtil; |
| import org.eclipse.userstorage.IStorage; |
| import org.eclipse.userstorage.IStorage.Connectedness; |
| import org.eclipse.userstorage.IStorageService; |
| import org.eclipse.userstorage.spi.ICredentialsProvider; |
| import org.eclipse.userstorage.util.ProtocolException; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.Semaphore; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public final class RecorderManager |
| { |
| public static final RecorderManager INSTANCE = new RecorderManager(); |
| |
| private static final IPersistentPreferenceStore SETUP_UI_PREFERENCES = (IPersistentPreferenceStore)SetupUIPlugin.INSTANCE.getPreferenceStore(); |
| |
| private static final UserCallback USER_CALLBACK = new UserCallback() |
| { |
| @Override |
| public void execInUI(boolean async, Runnable runnable) |
| { |
| UIUtil.syncExec(runnable); |
| } |
| }; |
| |
| private static final URI USER_URI = SetupContext.USER_SETUP_URI.appendFragment("/"); //$NON-NLS-1$ |
| |
| private static final URI USER_FILE_URI = SetupContext.resolve(SetupCoreUtil.createResourceSet().getURIConverter().normalize(SetupContext.USER_SETUP_URI)); |
| |
| private static final boolean SYNC_FOLDER_FIXED = PropertiesUtil.isProperty("oomph.setup.sync.folder.fixed"); //$NON-NLS-1$ |
| |
| private static final boolean SYNC_FOLDER_KEEP = PropertiesUtil.isProperty("oomph.setup.sync.folder.keep"); //$NON-NLS-1$ |
| |
| private static final boolean SYNC_FOLDER_DEBUG = PropertiesUtil.isProperty("oomph.setup.sync.folder.debug"); //$NON-NLS-1$ |
| |
| private final EarlySynchronization earlySynchronization = new EarlySynchronization(); |
| |
| private static ToolItem recordItem; |
| |
| private static ToolItem initializeItem; |
| |
| private final DisplayListener displayListener = new DisplayListener(); |
| |
| private Display display; |
| |
| private PreferencesRecorder recorder; |
| |
| private IEditorPart editor; |
| |
| private URI temporaryRecorderTarget; |
| |
| private Runnable reset; |
| |
| private RecorderManager() |
| { |
| } |
| |
| public void record(IEditorPart editor) |
| { |
| this.editor = editor; |
| |
| final boolean wasEnabled = isRecorderEnabled(); |
| setRecorderEnabled(true); |
| |
| // Defer this until the transaction has been processed. |
| reset = new Runnable() |
| { |
| public void run() |
| { |
| RecorderManager.this.editor = null; |
| setRecorderEnabled(wasEnabled); |
| reset = null; |
| } |
| }; |
| |
| PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, null, null, null); |
| dialog.open(); |
| } |
| |
| public void done() |
| { |
| setTemporaryRecorderTarget(null); |
| if (reset != null) |
| { |
| reset.run(); |
| } |
| } |
| |
| public boolean isRecorderEnabled() |
| { |
| String value = SETUP_UI_PREFERENCES.getString(SetupUIPlugin.PREF_ENABLE_PREFERENCE_RECORDER); |
| if (StringUtil.isEmpty(value)) |
| { |
| ResourceSet resourceSet = SetupCoreUtil.createResourceSet(); |
| SetupContext setupContext = SetupContext.createUserOnly(resourceSet); |
| User user = setupContext.getUser(); |
| |
| boolean enabled = user.isPreferenceRecorderDefault(); |
| doSetRecorderEnabled(enabled); |
| return enabled; |
| } |
| |
| return Boolean.parseBoolean(value); |
| } |
| |
| public void setRecorderEnabled(boolean enabled) |
| { |
| if (isRecorderEnabled() != enabled) |
| { |
| try |
| { |
| doSetRecorderEnabled(enabled); |
| } |
| finally |
| { |
| if (enabled) |
| { |
| if (recorder == null) |
| { |
| recorder = new PreferencesRecorder(); |
| } |
| |
| startEarlySynchronization(false); |
| } |
| else |
| { |
| cancelRecording(); |
| } |
| } |
| } |
| } |
| |
| public Set<String> getInitializedPreferencePages() |
| { |
| return getIDs(SetupUIPlugin.PREF_INITIALIZED_PREFERENCE_PAGES); |
| } |
| |
| public void setInitializedPreferencePages(Set<String> ids) |
| { |
| setIDs(SetupUIPlugin.PREF_INITIALIZED_PREFERENCE_PAGES, ids); |
| } |
| |
| public Set<String> getIgnoredPreferencePages() |
| { |
| return getIDs(SetupUIPlugin.PREF_IGNORED_PREFERENCE_PAGES); |
| } |
| |
| public void setIgnoredPreferencePages(Set<String> ids) |
| { |
| setIDs(SetupUIPlugin.PREF_IGNORED_PREFERENCE_PAGES, ids); |
| } |
| |
| private Set<String> getIDs(String key) |
| { |
| Set<String> result = new LinkedHashSet<String>(); |
| String value = SETUP_UI_PREFERENCES.getString(key); |
| if (!StringUtil.isEmpty(value)) |
| { |
| for (String id : value.split(" ")) //$NON-NLS-1$ |
| { |
| result.add(id); |
| } |
| } |
| |
| return result; |
| } |
| |
| private void setIDs(String key, Set<String> ids) |
| { |
| StringBuilder result = new StringBuilder(); |
| for (String id : ids) |
| { |
| if (result.length() != 0) |
| { |
| result.append(' '); |
| } |
| |
| result.append(id); |
| } |
| |
| SETUP_UI_PREFERENCES.setValue(key, result.toString()); |
| |
| try |
| { |
| SETUP_UI_PREFERENCES.save(); |
| } |
| catch (IOException ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex); |
| } |
| } |
| |
| public void cancelRecording() |
| { |
| if (recorder != null) |
| { |
| recorder.done(); |
| recorder = null; |
| } |
| |
| earlySynchronization.stop(); |
| } |
| |
| private void doSetRecorderEnabled(boolean enabled) |
| { |
| SETUP_UI_PREFERENCES.setValue(SetupUIPlugin.PREF_ENABLE_PREFERENCE_RECORDER, Boolean.toString(enabled)); |
| |
| try |
| { |
| SETUP_UI_PREFERENCES.save(); |
| } |
| catch (IOException ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex); |
| } |
| } |
| |
| public Scope getRecorderTargetObject(ResourceSet resourceSet) |
| { |
| URI recorderTarget = getRecorderTarget(); |
| return (Scope)resourceSet.getEObject(recorderTarget, true); |
| } |
| |
| public Scope getRecorderTargetObject() |
| { |
| ResourceSet resourceSet = SetupCoreUtil.createResourceSet(); |
| return getRecorderTargetObject(resourceSet); |
| } |
| |
| public URI getRecorderTarget() |
| { |
| if (temporaryRecorderTarget != null) |
| { |
| return temporaryRecorderTarget; |
| } |
| |
| String value = SETUP_UI_PREFERENCES.getString(SetupUIPlugin.PREF_PREFERENCE_RECORDER_TARGET); |
| if (StringUtil.isEmpty(value)) |
| { |
| return USER_URI; |
| } |
| |
| URI uri = URI.createURI(value); |
| return convertURI(uri); |
| } |
| |
| public URI setRecorderTarget(URI uri) |
| { |
| uri = convertURI(uri); |
| |
| URI oldURI = getRecorderTarget(); |
| if (!ObjectUtil.equals(oldURI, uri)) |
| { |
| SETUP_UI_PREFERENCES.setValue(SetupUIPlugin.PREF_PREFERENCE_RECORDER_TARGET, uri.toString()); |
| |
| try |
| { |
| SETUP_UI_PREFERENCES.save(); |
| } |
| catch (IOException ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex); |
| } |
| |
| return oldURI; |
| } |
| |
| return null; |
| } |
| |
| public void setTemporaryRecorderTarget(URI temporaryRecorderTarget) |
| { |
| this.temporaryRecorderTarget = temporaryRecorderTarget; |
| } |
| |
| public boolean hasTemporaryRecorderTarget() |
| { |
| return temporaryRecorderTarget != null; |
| } |
| |
| public boolean startEarlySynchronization(boolean interactive) |
| { |
| return earlySynchronization.start(interactive); |
| } |
| |
| private SyncInfo awaitEarlySynchronization() |
| { |
| SyncInfo syncInfo = earlySynchronization.await(); |
| if (syncInfo != null) |
| { |
| // Check if the recorder target is still the User scope (it could have been changed meanwhile). |
| Scope recorderTarget = getRecorderTargetObject(); |
| if (recorderTarget instanceof User) |
| { |
| return syncInfo; |
| } |
| } |
| |
| return null; |
| } |
| |
| private void handleRecording(IEditorPart editorPart, Map<URI, Pair<String, String>> values) |
| { |
| try |
| { |
| if (SynchronizerManager.Availability.AVAILABLE) |
| { |
| SynchronizerManager.INSTANCE.offerFirstTimeConnect(UIUtil.getShell()); |
| } |
| |
| RecorderTransaction transaction = editorPart == null ? RecorderTransaction.open() : RecorderTransaction.open(editorPart); |
| transaction.setPreferences(values); |
| |
| // In some cases (such as changing the recorder target in the current recorder transaction or missing credentials) |
| // early synchronization has not been started, yet. We want to be safe and try to start it now (has no effect if already started). |
| boolean started = startEarlySynchronization(true); |
| |
| SyncInfo syncInfo = started ? awaitEarlySynchronization() : null; |
| Synchronization synchronization = syncInfo == null ? null : syncInfo.getSynchronization(); |
| |
| boolean dialogNeeded = false; |
| Set<URI> preferenceURIs = transaction.getPreferences().keySet(); |
| |
| for (Iterator<URI> it = preferenceURIs.iterator(); it.hasNext();) |
| { |
| URI uri = it.next(); |
| String key = PreferencesFactory.eINSTANCE.convertURI(uri); |
| |
| if (synchronization != null) |
| { |
| String syncID = synchronization.getPreferenceIDs().get(key); |
| SyncPolicy remotePolicy = synchronization.getRemotePolicies().get(syncID); |
| if (remotePolicy == SyncPolicy.INCLUDE) |
| { |
| transaction.setPolicy(key, true); // Default to "record". |
| } |
| } |
| |
| Boolean localPolicy = transaction.getPolicy(key); |
| if (localPolicy == null) |
| { |
| PreferenceHandler handler = PreferenceTaskImpl.PreferenceHandler.getHandler(uri); |
| if (handler.isIgnored()) |
| { |
| // Handler policy is "ignore". |
| it.remove(); // Remove the preference change from the transaction. |
| } |
| else |
| { |
| // Handler policy is missing. |
| transaction.setPolicy(key, true); // Default to "record". |
| dialogNeeded = true; // And prompt below... |
| } |
| } |
| else if (!localPolicy) |
| { |
| // Local policy is "ignore". |
| it.remove(); // Remove the preference change from the transaction. |
| } |
| } |
| |
| if (synchronization != null) |
| { |
| try |
| { |
| Map<String, PolicyAndValue> preferenceChanges = new HashMap<String, PolicyAndValue>(); |
| for (URI uri : preferenceURIs) |
| { |
| String key = PreferencesFactory.eINSTANCE.convertURI(uri); |
| if (transaction.getPolicy(key)) |
| { |
| String value = transaction.getPreferences().get(uri).getElement2(); |
| preferenceChanges.put(key, new PolicyAndValue(value)); |
| } |
| else |
| { |
| preferenceChanges.put(key, new PolicyAndValue()); |
| } |
| } |
| |
| Set<String> preferenceKeys = SynchronizerDialog.adjustLocalSnapshot(synchronization, preferenceChanges); |
| |
| Map<String, SyncAction> syncActions = synchronization.synchronizeLocal(); |
| for (SyncAction syncAction : syncActions.values()) |
| { |
| if (syncAction.getComputedType() == SyncActionType.CONFLICT) |
| { |
| Map.Entry<String, String> preference = syncAction.getPreference(); |
| if (preference != null && preferenceKeys.contains(preference.getKey())) |
| { |
| dialogNeeded = true; |
| break; |
| } |
| } |
| } |
| } |
| catch (IOException ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex, IStatus.WARNING); |
| } |
| } |
| |
| if (dialogNeeded) |
| { |
| if (!openSynchronizerDialog(transaction, synchronization)) |
| { |
| transaction.close(); |
| return; |
| } |
| } |
| |
| if (synchronization != null) |
| { |
| // Ensure that the syncIDs are committed to the recorder target. |
| final Map<String, String> preferenceIDs = synchronization.getPreferenceIDs(); |
| |
| transaction.setCommitHandler(new CommitHandler() |
| { |
| public void handlePreferenceTask(PreferenceTask preferenceTask) |
| { |
| String key = preferenceTask.getKey(); |
| |
| String syncID = preferenceIDs.get(key); |
| if (syncID != null) |
| { |
| preferenceTask.setID(syncID); |
| } |
| } |
| }); |
| } |
| |
| try |
| { |
| transaction.commit(); |
| } |
| finally |
| { |
| transaction.close(); |
| } |
| |
| if (synchronization != null) |
| { |
| Scope recorderTarget = syncInfo.getRecorderTarget(); |
| File tmpFolder = syncInfo.getTmpFolder(); |
| |
| if (!dialogNeeded) |
| { |
| EMap<String, SyncPolicy> remotePolicies = synchronization.getRemotePolicies(); |
| boolean remotePoliciesMissing = false; |
| |
| for (Iterator<PreferenceTask> it = transaction.getCommitResult().values().iterator(); it.hasNext();) |
| { |
| PreferenceTask preferenceTask = it.next(); |
| |
| String taskID = preferenceTask.getID(); |
| if (taskID != null) |
| { |
| SyncPolicy remotePolicy = remotePolicies.get(taskID); |
| if (remotePolicy == null) |
| { |
| // Remote policy is missing. |
| remotePoliciesMissing = true; // Prompt below... |
| } |
| else if (remotePolicy == SyncPolicy.EXCLUDE) |
| { |
| it.remove(); |
| } |
| } |
| else |
| { |
| it.remove(); |
| } |
| } |
| |
| // Copy the committed recorder target to the temporary sync folder. |
| copyRecorderTarget(recorderTarget, tmpFolder); |
| |
| if (remotePoliciesMissing) |
| { |
| if (!openSynchronizerDialog(transaction, synchronization)) // Requires the copyRecorderTarget() call above. |
| { |
| return; |
| } |
| } |
| else |
| { |
| try |
| { |
| Map<String, SyncAction> syncActions = synchronization.synchronizeLocal(); // Requires the copyRecorderTarget() call above. |
| Map<String, String> preferenceIDs = synchronization.getPreferenceIDs(); |
| |
| for (Iterator<Map.Entry<String, SyncAction>> it = syncActions.entrySet().iterator(); it.hasNext();) |
| { |
| Map.Entry<String, SyncAction> entry = it.next(); |
| String syncID = entry.getKey(); |
| SyncAction syncAction = entry.getValue(); |
| |
| SyncPolicy remotePolicy = remotePolicies.get(syncID); |
| if (remotePolicy == SyncPolicy.EXCLUDE) |
| { |
| it.remove(); |
| continue; |
| } |
| |
| SyncActionType type = syncAction.getComputedType(); |
| switch (type) |
| { |
| case SET_REMOTE: // Ignore REMOTE -> LOCAL actions. |
| case REMOVE_REMOTE: // Ignore REMOTE -> LOCAL actions. |
| case CONFLICT: // Ignore interactive actions. |
| case EXCLUDE: // Should not occur. |
| case NONE: // Should not occur. |
| it.remove(); |
| continue; |
| } |
| |
| Map.Entry<String, String> preference = syncAction.getPreference(); |
| if (preference == null || !preferenceIDs.containsKey(preference.getKey())) |
| { |
| it.remove(); |
| continue; |
| } |
| } |
| } |
| catch (IOException ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex, IStatus.WARNING); |
| ErrorDialog.open(ex); |
| return; |
| } |
| } |
| } |
| |
| try |
| { |
| applyRemotePreferenceChanges(synchronization); |
| |
| synchronization.commit(); |
| |
| Synchronizer synchronizer = synchronization.getSynchronizer(); |
| synchronizer.copyFilesTo(SynchronizerManager.SYNC_FOLDER); |
| |
| copyRecorderTargetBack(recorderTarget, tmpFolder); |
| } |
| catch (NotCurrentException ex) |
| { |
| ErrorDialog.open(ex); |
| } |
| catch (IOException ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex, IStatus.WARNING); |
| ErrorDialog.open(ex); |
| } |
| } |
| } |
| finally |
| { |
| earlySynchronization.stop(); |
| } |
| } |
| |
| private boolean openSynchronizerDialog(final RecorderTransaction transaction, final Synchronization synchronization) |
| { |
| final boolean[] ok = { true }; |
| UIUtil.syncExec(display, new Runnable() |
| { |
| public void run() |
| { |
| Shell shell = UIUtil.getShell(); |
| |
| SynchronizerDialog dialog = new SynchronizerDialog(shell, transaction, synchronization); |
| int result = dialog.open(); |
| |
| if (!dialog.isEnableRecorder()) |
| { |
| setRecorderEnabled(false); |
| ok[0] = false; |
| } |
| else if (result != SynchronizerDialog.OK) |
| { |
| ok[0] = false; |
| } |
| } |
| }); |
| |
| return ok[0]; |
| } |
| |
| private void applyRemotePreferenceChanges(Synchronization synchronization) |
| { |
| Map<String, SyncAction> syncActions = synchronization.getActions(); |
| if (syncActions != null) |
| { |
| for (SyncAction syncAction : syncActions.values()) |
| { |
| if (syncAction.getEffectiveType() == SyncActionType.SET_REMOTE) |
| { |
| SyncDelta remoteDelta = syncAction.getRemoteDelta(); |
| SetupTask newTask = remoteDelta.getNewTask(); |
| if (newTask instanceof PreferenceTask) |
| { |
| PreferenceTask preferenceTask = (PreferenceTask)newTask; |
| |
| try |
| { |
| executePreferenceTask(preferenceTask); |
| } |
| catch (Exception ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private static URI convertURI(URI uri) |
| { |
| String fragment = uri.fragment(); |
| if (StringUtil.isEmpty(fragment)) |
| { |
| fragment = "/"; //$NON-NLS-1$ |
| } |
| |
| URI resourceURI = uri.trimFragment(); |
| if (resourceURI.equals(USER_FILE_URI)) |
| { |
| resourceURI = SetupContext.USER_SETUP_URI; |
| } |
| |
| return resourceURI.appendFragment(fragment); |
| } |
| |
| @SuppressWarnings("restriction") |
| private static boolean isPreferenceDialog(Shell shell) |
| { |
| Object data = shell.getData(); |
| return data instanceof org.eclipse.ui.internal.dialogs.WorkbenchPreferenceDialog; |
| } |
| |
| @SuppressWarnings("restriction") |
| private void hookRecorderToggleButton(final Shell shell) |
| { |
| try |
| { |
| final org.eclipse.ui.internal.dialogs.WorkbenchPreferenceDialog dialog = (org.eclipse.ui.internal.dialogs.WorkbenchPreferenceDialog)shell.getData(); |
| if (dialog.buttonBar instanceof Composite) |
| { |
| final Composite buttonBar = (Composite)dialog.buttonBar; |
| Control[] children = buttonBar.getChildren(); |
| if (children.length != 0) |
| { |
| Control child = children[0]; |
| if (child instanceof ToolBar) |
| { |
| final ToolBar toolBar = (ToolBar)child; |
| |
| recordItem = new ToolItem(toolBar, SWT.PUSH); |
| updateRecorderToggleButton(); |
| |
| final PreferenceManager preferenceManager = dialog.getPreferenceManager(); |
| recordItem.addSelectionListener(new SelectionAdapter() |
| { |
| @Override |
| public void widgetSelected(SelectionEvent e) |
| { |
| boolean enableRecorder = !isRecorderEnabled(); |
| setRecorderEnabled(enableRecorder); |
| |
| updateRecorderToggleButton(); |
| RecorderPreferencePage.updateEnablement(); |
| |
| if (enableRecorder) |
| { |
| if (SynchronizerManager.Availability.AVAILABLE) |
| { |
| boolean firstTime = SynchronizerManager.INSTANCE.offerFirstTimeConnect(shell); |
| startEarlySynchronization(firstTime); |
| } |
| |
| createInitializeItem(shell, toolBar, dialog, preferenceManager); |
| buttonBar.layout(); |
| } |
| else if (initializeItem != null) |
| { |
| initializeItem.dispose(); |
| } |
| } |
| }); |
| |
| recordItem.addDisposeListener(new DisposeListener() |
| { |
| public void widgetDisposed(DisposeEvent e) |
| { |
| recordItem = null; |
| } |
| }); |
| |
| if (isRecorderEnabled()) |
| { |
| createInitializeItem(shell, toolBar, dialog, preferenceManager); |
| } |
| |
| buttonBar.layout(); |
| } |
| } |
| } |
| } |
| catch (Throwable ex) |
| { |
| // Ignore. |
| } |
| } |
| |
| void disposeInitializeItem() |
| { |
| if (initializeItem != null) |
| { |
| initializeItem.dispose(); |
| } |
| } |
| |
| @SuppressWarnings("restriction") |
| private void createInitializeItem(final Shell shell, ToolBar toolBar, final org.eclipse.ui.internal.dialogs.WorkbenchPreferenceDialog dialog, |
| final PreferenceManager preferenceManager) |
| { |
| if (hasPreferencePagesToInitialize(preferenceManager)) |
| { |
| initializeItem = new ToolItem(toolBar, SWT.PUSH); |
| initializeItem.setImage(SetupUIPlugin.INSTANCE.getSWTImage("bulb0.png")); //$NON-NLS-1$ |
| initializeItem.setToolTipText(Messages.RecorderManager_initializeItem_tooltip); |
| |
| final class Animator extends ButtonAnimator |
| { |
| public Animator(ToolItem toolItem) |
| { |
| super(SetupUIPlugin.INSTANCE, toolItem, "bulb.png", 8); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public Shell getShell() |
| { |
| return shell; |
| } |
| |
| @Override |
| protected boolean doAnimate() |
| { |
| return true; |
| } |
| } |
| |
| new Animator(initializeItem).run(); |
| |
| initializeItem.addSelectionListener(new SelectionAdapter() |
| { |
| @Override |
| public void widgetSelected(SelectionEvent e) |
| { |
| new PreferenceInitializationDialog(dialog, preferenceManager).open(); |
| } |
| }); |
| |
| initializeItem.addDisposeListener(new DisposeListener() |
| { |
| public void widgetDisposed(DisposeEvent e) |
| { |
| initializeItem = null; |
| } |
| }); |
| } |
| } |
| |
| private boolean hasPreferencePagesToInitialize(PreferenceManager preferenceManager) |
| { |
| Set<String> preferencePages = getInitializedPreferencePages(); |
| preferencePages.addAll(getIgnoredPreferencePages()); |
| @SuppressWarnings("all") |
| List<IPreferenceNode> preferenceNodes = preferenceManager.getElements(PreferenceManager.PRE_ORDER); |
| for (IPreferenceNode element : preferenceNodes) |
| { |
| String id = element.getId(); |
| if (!preferencePages.contains(id)) |
| { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static void updateRecorderToggleButton() |
| { |
| if (recordItem != null) |
| { |
| boolean recorderEnabled = INSTANCE.isRecorderEnabled(); |
| |
| recordItem.setImage(SetupUIPlugin.INSTANCE.getSWTImage("recorder_" + (recorderEnabled ? "enabled" : "disabled") + ".png")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ |
| recordItem.setToolTipText(recorderEnabled ? Messages.RecorderManager_recordItem_tooltip_recorderEnabledPushToDisable |
| : Messages.RecorderManager_recordItem_tooltip_recorderDisabledPushToEnabled); |
| } |
| } |
| |
| public static boolean executePreferenceTask(PreferenceTask task) throws Exception |
| { |
| return ((PreferenceTaskImpl)task).execute(USER_CALLBACK); |
| } |
| |
| public static File copyRecorderTarget(Scope recorderTarget, File targetFolder) |
| { |
| URI uri = resolveRecorderTargetURI(recorderTarget); |
| |
| File source = new File(uri.toFileString()); |
| File target = new File(targetFolder, uri.lastSegment()); |
| |
| IOUtil.copyFile(source, target); |
| |
| if (SYNC_FOLDER_DEBUG) |
| { |
| SetupUIPlugin.INSTANCE.log("Copy recorder target to " + target); //$NON-NLS-1$ |
| } |
| |
| return target; |
| } |
| |
| private static File copyRecorderTargetBack(Scope recorderTarget, File targetFolder) |
| { |
| URI uri = resolveRecorderTargetURI(recorderTarget); |
| |
| File source = new File(uri.toFileString()); |
| File target = new File(targetFolder, uri.lastSegment()); |
| |
| IOUtil.copyFile(target, source); |
| |
| if (SYNC_FOLDER_DEBUG) |
| { |
| SetupUIPlugin.INSTANCE.log("Copy recorder target back to " + source); //$NON-NLS-1$ |
| } |
| |
| return target; |
| } |
| |
| private static URI resolveRecorderTargetURI(Scope recorderTarget) |
| { |
| Resource resource = recorderTarget.eResource(); |
| URIConverter uriConverter = resource.getResourceSet().getURIConverter(); |
| |
| URI uri = resource.getURI(); |
| uri = uriConverter.normalize(uri); |
| uri = SetupContext.resolve(uri); |
| return uri; |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| public static class Lifecycle |
| { |
| public static void start(Display display) |
| { |
| INSTANCE.display = display; |
| display.addListener(SWT.Skin, INSTANCE.displayListener); |
| } |
| |
| public static void stop() |
| { |
| INSTANCE.displayListener.stop(); |
| |
| if (INSTANCE.display != null) |
| { |
| UIUtil.asyncExec(INSTANCE.display, new Runnable() |
| { |
| public void run() |
| { |
| if (!INSTANCE.display.isDisposed()) |
| { |
| INSTANCE.display.removeListener(SWT.Skin, INSTANCE.displayListener); |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private final class DisplayListener implements Listener |
| { |
| private boolean stopped; |
| |
| public void stop() |
| { |
| stopped = true; |
| } |
| |
| public void handleEvent(Event event) |
| { |
| if (stopped) |
| { |
| return; |
| } |
| |
| if (event.widget instanceof Shell) |
| { |
| final Shell shell = (Shell)event.widget; |
| if (isPreferenceDialog(shell) && recordItem == null) |
| { |
| UIUtil.asyncExec(display, new Runnable() |
| { |
| public void run() |
| { |
| hookRecorderToggleButton(shell); |
| } |
| }); |
| |
| if (isRecorderEnabled()) |
| { |
| recorder = new PreferencesRecorder(); |
| startEarlySynchronization(false); |
| } |
| |
| shell.addDisposeListener(new DisposeListener() |
| { |
| public void widgetDisposed(DisposeEvent e) |
| { |
| final PreferencesRecorder finalRecorder = recorder; |
| if (finalRecorder == null) |
| { |
| return; |
| } |
| |
| UIUtil.timerExec(100, new Runnable() |
| { |
| public void run() |
| { |
| final Map<URI, Pair<String, String>> values = finalRecorder.done(); |
| recorder = null; |
| for (Iterator<URI> it = values.keySet().iterator(); it.hasNext();) |
| { |
| URI uri = it.next(); |
| String pluginID = uri.segment(0); |
| |
| if (SetupUIPlugin.PLUGIN_ID.equals(pluginID)) |
| { |
| String lastSegment = uri.lastSegment(); |
| if (SetupUIPlugin.PREF_ENABLE_PREFERENCE_RECORDER.equals(lastSegment) // |
| || SetupUIPlugin.PREF_PREFERENCE_RECORDER_TARGET.equals(lastSegment) // |
| || SetupUIPlugin.PREF_IGNORED_PREFERENCE_PAGES.equals(lastSegment) // |
| || SetupUIPlugin.PREF_INITIALIZED_PREFERENCE_PAGES.equals(lastSegment)) |
| { |
| it.remove(); |
| } |
| } |
| } |
| |
| if (values.isEmpty()) |
| { |
| earlySynchronization.stop(); |
| } |
| else |
| { |
| Job job = new Job(Messages.RecorderManager_storePreferencesJob_name) |
| { |
| @Override |
| protected IStatus run(IProgressMonitor monitor) |
| { |
| handleRecording(editor, values); |
| return Status.OK_STATUS; |
| } |
| }; |
| |
| job.setSystem(true); |
| job.schedule(); |
| } |
| } |
| }); |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class EarlySynchronization implements FinishHandler |
| { |
| private Scope recorderTarget; |
| |
| private File tmpFolder; |
| |
| private SynchronizerJob synchronizerJob; |
| |
| public EarlySynchronization() |
| { |
| } |
| |
| public boolean start(boolean interactive) |
| { |
| if (!SynchronizerManager.Availability.AVAILABLE || !SynchronizerManager.ENABLED) |
| { |
| return false; |
| } |
| |
| if (synchronizerJob == null && SynchronizerManager.INSTANCE.isSyncEnabled()) |
| { |
| IStorage storage = SynchronizerManager.INSTANCE.getStorage(); |
| IStorageService service = storage.getService(); |
| if (service == null) |
| { |
| return false; |
| } |
| |
| if (!interactive && storage.getConnectedness() == Connectedness.UNAUTHORIZED) |
| { |
| return false; |
| } |
| |
| recorderTarget = INSTANCE.getRecorderTargetObject(); |
| if (recorderTarget instanceof User) |
| { |
| tmpFolder = null; |
| |
| if (SYNC_FOLDER_FIXED) |
| { |
| try |
| { |
| tmpFolder = new File(PropertiesUtil.getTmpDir(), "oomph.setup.sync"); //$NON-NLS-1$ |
| tmpFolder.mkdirs(); |
| |
| File[] tmpFiles = tmpFolder.listFiles(); |
| if (tmpFiles != null) |
| { |
| for (File file : tmpFiles) |
| { |
| SyncUtil.deleteFile(file); |
| } |
| } |
| } |
| catch (IOException ex) |
| { |
| tmpFolder = null; |
| SetupUIPlugin.INSTANCE.log(ex); |
| } |
| } |
| |
| if (tmpFolder == null) |
| { |
| tmpFolder = IOUtil.createTempFolder("sync-", true); //$NON-NLS-1$ |
| } |
| |
| if (SYNC_FOLDER_DEBUG) |
| { |
| SetupUIPlugin.INSTANCE.log("Early synchronization in " + tmpFolder); //$NON-NLS-1$ |
| } |
| |
| File target = RecorderManager.copyRecorderTarget(recorderTarget, tmpFolder); |
| |
| Synchronizer synchronizer = SynchronizerManager.INSTANCE.createSynchronizer(target, tmpFolder); |
| synchronizer.copyFilesFrom(SynchronizerManager.SYNC_FOLDER); |
| |
| synchronizerJob = new SynchronizerJob(synchronizer, true); |
| synchronizerJob.setService(service); |
| |
| if (interactive) |
| { |
| synchronizerJob.setFinishHandler(this); |
| } |
| else |
| { |
| synchronizerJob.setCredentialsProvider(ICredentialsProvider.CANCEL); |
| } |
| |
| synchronizerJob.schedule(); |
| } |
| } |
| |
| return synchronizerJob != null; |
| } |
| |
| public void stop() |
| { |
| if (!SynchronizerManager.Availability.AVAILABLE || !SynchronizerManager.ENABLED) |
| { |
| return; |
| } |
| |
| if (synchronizerJob != null) |
| { |
| synchronizerJob.stopSynchronization(); |
| synchronizerJob = null; |
| |
| if (!SYNC_FOLDER_KEEP) |
| { |
| boolean deleted = IOUtil.deleteBestEffort(tmpFolder, !SYNC_FOLDER_FIXED); |
| |
| if (SYNC_FOLDER_DEBUG) |
| { |
| if (deleted) |
| { |
| SetupUIPlugin.INSTANCE.log("Deleted " + tmpFolder); //$NON-NLS-1$ |
| } |
| else |
| { |
| SetupUIPlugin.INSTANCE.log("Failed to delete " + tmpFolder); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| } |
| |
| public SyncInfo await() |
| { |
| if (!SynchronizerManager.Availability.AVAILABLE || !SynchronizerManager.ENABLED) |
| { |
| return null; |
| } |
| |
| if (synchronizerJob != null) |
| { |
| final SyncInfo result = new SyncInfo(); |
| result.recorderTarget = recorderTarget; |
| result.tmpFolder = tmpFolder; |
| result.synchronization = synchronizerJob.getSynchronization(); |
| |
| if (result.synchronization == null) |
| { |
| Throwable earlyException = synchronizerJob.getException(); |
| if (earlyException instanceof OperationCanceledException) |
| { |
| // This means that the user couldn't be authenticated. Try again in UI thread below. |
| stop(); |
| result.synchronization = null; |
| |
| if (!start(true)) |
| { |
| return null; |
| } |
| |
| result.tmpFolder = tmpFolder; |
| } |
| else if (earlyException != null) |
| { |
| SynchronizerManager.log(earlyException); |
| return null; |
| } |
| |
| try |
| { |
| final AtomicBoolean canceled = new AtomicBoolean(); |
| final IStorageService service = synchronizerJob.getService(); |
| |
| final Semaphore authenticationSemaphore = service.getAuthenticationSemaphore(); |
| authenticationSemaphore.acquire(); |
| |
| UIUtil.syncExec(new Runnable() |
| { |
| public void run() |
| { |
| try |
| { |
| Shell shell = UIUtil.getShell(); |
| ProgressMonitorDialog dialog = new ProgressMonitorDialog(shell); |
| |
| dialog.run(true, true, new IRunnableWithProgress() |
| { |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException |
| { |
| authenticationSemaphore.release(); |
| |
| String serviceLabel = service.getServiceLabel(); |
| result.synchronization = await(serviceLabel, monitor); |
| } |
| }); |
| } |
| catch (InvocationTargetException ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex); |
| } |
| catch (InterruptedException ex) |
| { |
| canceled.set(true); |
| } |
| } |
| }); |
| |
| if (result.synchronization == null && !canceled.get()) |
| { |
| Throwable exception = synchronizerJob.getException(); |
| if (exception == null || exception instanceof OperationCanceledException) |
| { |
| return null; |
| } |
| |
| throw exception; |
| } |
| } |
| catch (Throwable ex) |
| { |
| SetupUIPlugin.INSTANCE.log(ex); |
| } |
| } |
| |
| return result; |
| } |
| |
| return null; |
| } |
| |
| private Synchronization await(String serviceLabel, IProgressMonitor monitor) |
| { |
| monitor.beginTask(NLS.bind(Messages.RecorderManager_requestDataTask_name, serviceLabel), IProgressMonitor.UNKNOWN); |
| |
| try |
| { |
| return synchronizerJob.awaitSynchronization(monitor); |
| } |
| finally |
| { |
| monitor.done(); |
| } |
| } |
| |
| public void handleFinish(Throwable ex) throws Exception |
| { |
| if (ex instanceof ProtocolException) |
| { |
| ProtocolException protocolException = (ProtocolException)ex; |
| if (protocolException.getStatusCode() == 401) |
| { |
| UIUtil.syncExec(new Runnable() |
| { |
| public void run() |
| { |
| OptOutDialog dialog = new OptOutDialog(UIUtil.getShell(), synchronizerJob.getService()); |
| dialog.open(); |
| if (!dialog.getAnswer()) |
| { |
| SynchronizerManager.INSTANCE.setSyncEnabled(false); |
| } |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class SyncInfo |
| { |
| private Scope recorderTarget; |
| |
| private File tmpFolder; |
| |
| private Synchronization synchronization; |
| |
| public SyncInfo() |
| { |
| } |
| |
| public Scope getRecorderTarget() |
| { |
| return recorderTarget; |
| } |
| |
| public File getTmpFolder() |
| { |
| return tmpFolder; |
| } |
| |
| public Synchronization getSynchronization() |
| { |
| return synchronization; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return SyncInfo.class.getSimpleName() + "[" + EcoreUtil.getURI(recorderTarget) + " --> " + SynchronizerManager.SYNC_FOLDER + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| } |
| } |