/******************************************************************************* | |
* Copyright (c) 2011, 2012 Tasktop Technologies. | |
* 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: | |
* Tasktop Technologies - initial API and implementation | |
*******************************************************************************/ | |
package org.eclipse.mylyn.internal.context.tasks.ui; | |
import java.io.File; | |
import java.lang.reflect.InvocationTargetException; | |
import java.net.URLDecoder; | |
import java.util.HashMap; | |
import java.util.Map; | |
import org.eclipse.core.runtime.CoreException; | |
import org.eclipse.core.runtime.IAdaptable; | |
import org.eclipse.core.runtime.IPath; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.NullProgressMonitor; | |
import org.eclipse.core.runtime.Path; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.mylyn.commons.core.CommonListenerList; | |
import org.eclipse.mylyn.commons.core.CommonListenerList.Notifier; | |
import org.eclipse.mylyn.commons.core.CoreUtil; | |
import org.eclipse.mylyn.commons.core.StatusHandler; | |
import org.eclipse.mylyn.commons.core.storage.CommonStore; | |
import org.eclipse.mylyn.commons.core.storage.ICommonStorable; | |
import org.eclipse.mylyn.context.core.ContextCore; | |
import org.eclipse.mylyn.context.core.IInteractionContext; | |
import org.eclipse.mylyn.internal.context.core.ContextCorePlugin; | |
import org.eclipse.mylyn.internal.context.core.InteractionContext; | |
import org.eclipse.mylyn.internal.context.core.InteractionContextManager; | |
import org.eclipse.mylyn.internal.context.tasks.ui.TaskContextStoreEvent.Kind; | |
import org.eclipse.mylyn.internal.tasks.core.ITasksCoreConstants; | |
import org.eclipse.mylyn.internal.tasks.core.RepositoryTaskHandleUtil; | |
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin; | |
import org.eclipse.mylyn.monitor.core.InteractionEvent; | |
import org.eclipse.mylyn.tasks.core.ITask; | |
import org.eclipse.mylyn.tasks.core.TaskRepository; | |
import org.eclipse.mylyn.tasks.core.context.AbstractTaskContextStore; | |
import org.eclipse.mylyn.tasks.ui.TasksUi; | |
import org.eclipse.osgi.util.NLS; | |
import com.google.common.collect.ImmutableMap; | |
/** | |
* @author Steffen Pingel | |
*/ | |
public class TaskContextStore extends AbstractTaskContextStore { | |
private static final String FOLDER_DATA = "data"; //$NON-NLS-1$ | |
private final CommonListenerList<TaskContextStoreListener> listeners; | |
private File directory; | |
private CommonStore taskStore; | |
private final ContextStatePersistenceHandler stateHandler; | |
public TaskContextStore() { | |
this.listeners = new CommonListenerList<TaskContextStoreListener>(TasksUiPlugin.ID_PLUGIN); | |
this.stateHandler = new ContextStatePersistenceHandler(); | |
} | |
public void addListener(TaskContextStoreListener listener) { | |
listeners.add(listener); | |
} | |
@Override | |
public void clearContext(ITask task) { | |
ContextCorePlugin.getContextManager().deleteContext(task.getHandleIdentifier()); | |
stateHandler.clear(task); | |
final TaskContextStoreEvent event = new TaskContextStoreEvent(Kind.CLEAR, task); | |
listeners.notify(new Notifier<TaskContextStoreListener>() { | |
@Override | |
public void run(TaskContextStoreListener listener) throws Exception { | |
listener.taskContextChanged(event); | |
} | |
}); | |
} | |
@Override | |
public IAdaptable copyContext(ITask sourceTask, ITask targetTask) { | |
IInteractionContext result = copyContextInternal(sourceTask, targetTask); | |
stateHandler.copy(sourceTask, targetTask); | |
final TaskContextStoreEvent event = new TaskContextStoreEvent(Kind.COPY, sourceTask, targetTask); | |
listeners.notify(new Notifier<TaskContextStoreListener>() { | |
@Override | |
public void run(TaskContextStoreListener listener) throws Exception { | |
listener.taskContextChanged(event); | |
} | |
}); | |
return asAdaptable(result); | |
} | |
@Override | |
public void deleteContext(ITask task) { | |
ICommonStorable storable = getStorable(task); | |
try { | |
storable.deleteAll(); | |
} catch (CoreException e) { | |
StatusHandler.log(new Status(IStatus.WARNING, TasksUiPlugin.ID_PLUGIN, | |
"Unexpected error while deleting context state", e)); //$NON-NLS-1$ | |
} finally { | |
storable.release(); | |
} | |
ContextCorePlugin.getContextManager().deleteContext(task.getHandleIdentifier()); | |
stateHandler.clear(task); | |
final TaskContextStoreEvent event = new TaskContextStoreEvent(Kind.DELETE, task); | |
listeners.notify(new Notifier<TaskContextStoreListener>() { | |
@Override | |
public void run(TaskContextStoreListener listener) throws Exception { | |
listener.taskContextChanged(event); | |
} | |
}); | |
} | |
@Override | |
public File getFileForContext(ITask task) { | |
return ContextCorePlugin.getContextStore().getFileForContext(task.getHandleIdentifier()); | |
} | |
public ICommonStorable getStorable(ITask task) { | |
return getTaskStore().get(getPath(task)); | |
} | |
@Override | |
public boolean hasContext(ITask task) { | |
return ContextCore.getContextStore().hasContext(task.getHandleIdentifier()); | |
} | |
@Override | |
public void mergeContext(ITask sourceTask, ITask targetTask) { | |
ContextCorePlugin.getContextStore().merge(sourceTask.getHandleIdentifier(), targetTask.getHandleIdentifier()); | |
stateHandler.merge(sourceTask, targetTask); | |
final TaskContextStoreEvent event = new TaskContextStoreEvent(Kind.MERGE, sourceTask, targetTask); | |
listeners.notify(new Notifier<TaskContextStoreListener>() { | |
@Override | |
public void run(TaskContextStoreListener listener) throws Exception { | |
listener.taskContextChanged(event); | |
} | |
}); | |
} | |
@Override | |
public IAdaptable moveContext(ITask sourceTask, ITask targetTask) { | |
final IInteractionContext result = copyContextInternal(sourceTask, targetTask); | |
// move task activity | |
moveTaskActivity(ImmutableMap.of(sourceTask.getHandleIdentifier(), targetTask.getHandleIdentifier())); | |
moveContextInStore(sourceTask, targetTask); | |
return asAdaptable(result); | |
} | |
@Override | |
public void moveContext(Map<ITask, ITask> tasks) { | |
Map<String, String> handles = new HashMap<>(); | |
for (ITask sourceTask : tasks.keySet()) { | |
handles.put(sourceTask.getHandleIdentifier(), tasks.get(sourceTask).getHandleIdentifier()); | |
copyContextInternal(sourceTask, tasks.get(sourceTask)); | |
} | |
moveTaskActivity(handles); | |
for (ITask sourceTask : tasks.keySet()) { | |
moveContextInStore(sourceTask, tasks.get(sourceTask)); | |
} | |
} | |
private void moveTaskActivity(Map<String, String> handles) { | |
ChangeActivityHandleOperation operation = new ChangeActivityHandleOperation(handles); | |
try { | |
operation.run(new NullProgressMonitor()); | |
} catch (InvocationTargetException e) { | |
StatusHandler.log( | |
new Status(IStatus.WARNING, TasksUiPlugin.ID_PLUGIN, "Failed to migrate activity to new task", e)); //$NON-NLS-1$ | |
} catch (InterruptedException e) { | |
// ignore | |
} | |
} | |
private void moveContextInStore(ITask sourceTask, ITask targetTask) { | |
try { | |
getTaskStore().move(getPath(sourceTask), getPath(targetTask)); | |
} catch (CoreException e) { | |
StatusHandler.log(new Status(IStatus.WARNING, TasksUiPlugin.ID_PLUGIN, | |
"Failed to migrate context state to new task", e)); //$NON-NLS-1$ | |
} | |
final TaskContextStoreEvent event = new TaskContextStoreEvent(Kind.MOVE, sourceTask, targetTask); | |
listeners.notify(new Notifier<TaskContextStoreListener>() { | |
@Override | |
public void run(TaskContextStoreListener listener) throws Exception { | |
listener.taskContextChanged(event); | |
} | |
}); | |
} | |
@Override | |
public void refactorRepositoryUrl(TaskRepository repository, String oldRepositoryUrl, String newRepositoryUrl) { | |
refactorMetaContextHandles(oldRepositoryUrl, newRepositoryUrl); | |
refactorContextFileNames(oldRepositoryUrl, newRepositoryUrl); | |
if (repository != null) { | |
refactorRepositoryLocation(repository, oldRepositoryUrl, newRepositoryUrl); | |
} | |
} | |
public void removeListener(TaskContextStoreListener listener) { | |
listeners.remove(listener); | |
} | |
@Override | |
public void saveActiveContext() { | |
ContextCorePlugin.getContextStore().saveActiveContext(); | |
ITask task = TasksUi.getTaskActivityManager().getActiveTask(); | |
if (task != null) { | |
stateHandler.saved(task); | |
final TaskContextStoreEvent event = new TaskContextStoreEvent(Kind.SAVE, task); | |
listeners.notify(new Notifier<TaskContextStoreListener>() { | |
@Override | |
public void run(TaskContextStoreListener listener) throws Exception { | |
listener.taskContextChanged(event); | |
} | |
}); | |
} | |
} | |
@Override | |
public synchronized void setDirectory(File directory) { | |
this.directory = directory; | |
if (taskStore != null) { | |
taskStore.setLocation(directory); | |
} | |
File contextDirectory = new File(directory.getParent(), ITasksCoreConstants.CONTEXTS_DIRECTORY); | |
if (!contextDirectory.exists()) { | |
contextDirectory.mkdirs(); | |
} | |
ContextCorePlugin.getContextStore().setContextDirectory(contextDirectory); | |
} | |
private IAdaptable asAdaptable(final IInteractionContext result) { | |
return new IAdaptable() { | |
public Object getAdapter(Class adapter) { | |
if (adapter == IInteractionContext.class) { | |
return result; | |
} | |
return null; | |
} | |
}; | |
} | |
private IInteractionContext copyContextInternal(ITask sourceTask, ITask targetTask) { | |
ContextCorePlugin.getContextStore().saveActiveContext(); | |
final IInteractionContext result = ContextCore.getContextStore().cloneContext(sourceTask.getHandleIdentifier(), | |
targetTask.getHandleIdentifier()); | |
return result; | |
} | |
private IPath getPath(ITask task) { | |
IPath path = new Path(""); //$NON-NLS-1$ | |
path = path.append(task.getConnectorKind() + "-" + CoreUtil.asFileName(task.getRepositoryUrl())); //$NON-NLS-1$ | |
path = path.append(FOLDER_DATA); | |
path = path.append(CoreUtil.asFileName(task.getTaskId())); | |
return path; | |
} | |
private synchronized CommonStore getTaskStore() { | |
if (taskStore == null) { | |
taskStore = new CommonStore(directory); | |
} | |
return taskStore; | |
} | |
@SuppressWarnings("restriction") | |
private void refactorContextFileNames(String oldUrl, String newUrl) { | |
File dataDir = new File(TasksUiPlugin.getDefault().getDataDirectory(), ITasksCoreConstants.CONTEXTS_DIRECTORY); | |
if (dataDir.exists() && dataDir.isDirectory()) { | |
File[] files = dataDir.listFiles(); | |
if (files != null) { | |
for (File file : dataDir.listFiles()) { | |
int dotIndex = file.getName().lastIndexOf(".xml"); //$NON-NLS-1$ | |
if (dotIndex != -1) { | |
String storedHandle; | |
try { | |
storedHandle = URLDecoder.decode(file.getName().substring(0, dotIndex), | |
InteractionContextManager.CONTEXT_FILENAME_ENCODING); | |
int delimIndex = storedHandle.lastIndexOf(RepositoryTaskHandleUtil.HANDLE_DELIM); | |
if (delimIndex != -1) { | |
String storedUrl = storedHandle.substring(0, delimIndex); | |
if (oldUrl.equals(storedUrl)) { | |
String id = RepositoryTaskHandleUtil.getTaskId(storedHandle); | |
String newHandle = RepositoryTaskHandleUtil.getHandle(newUrl, id); | |
File newFile = ContextCorePlugin.getContextStore().getFileForContext(newHandle); | |
file.renameTo(newFile); | |
} | |
} | |
} catch (Exception e) { | |
StatusHandler.log(new Status(IStatus.ERROR, TasksUiPlugin.ID_PLUGIN, | |
"Could not move context file: " + file.getName(), e)); //$NON-NLS-1$ | |
} | |
} | |
} | |
} | |
} | |
} | |
@SuppressWarnings("restriction") | |
private void refactorMetaContextHandles(String oldRepositoryUrl, String newRepositoryUrl) { | |
InteractionContext metaContext = ContextCorePlugin.getContextManager().getActivityMetaContext(); | |
ContextCorePlugin.getContextManager().resetActivityMetaContext(); | |
InteractionContext newMetaContext = ContextCorePlugin.getContextManager().getActivityMetaContext(); | |
for (InteractionEvent event : metaContext.getInteractionHistory()) { | |
if (event.getStructureHandle() != null) { | |
String storedUrl = RepositoryTaskHandleUtil.getRepositoryUrl(event.getStructureHandle()); | |
if (storedUrl != null) { | |
if (oldRepositoryUrl.equals(storedUrl)) { | |
String taskId = RepositoryTaskHandleUtil.getTaskId(event.getStructureHandle()); | |
if (taskId != null) { | |
String newHandle = RepositoryTaskHandleUtil.getHandle(newRepositoryUrl, taskId); | |
event = new InteractionEvent(event.getKind(), event.getStructureKind(), newHandle, | |
event.getOriginId(), event.getNavigation(), event.getDelta(), | |
event.getInterestContribution(), event.getDate(), event.getEndDate()); | |
} | |
} | |
} | |
} | |
newMetaContext.parseEvent(event); | |
} | |
} | |
private void refactorRepositoryLocation(TaskRepository repository, String oldRepositoryUrl, | |
String newRepositoryUrl) { | |
IPath oldPath = new Path(repository.getConnectorKind() + "-" + CoreUtil.asFileName(oldRepositoryUrl)) //$NON-NLS-1$ | |
.append(FOLDER_DATA); | |
IPath newPath = new Path(repository.getConnectorKind() + "-" + CoreUtil.asFileName(newRepositoryUrl)) //$NON-NLS-1$ | |
.append(FOLDER_DATA); | |
try { | |
getTaskStore().move(oldPath, newPath); | |
} catch (CoreException e) { | |
StatusHandler.log(new Status(IStatus.WARNING, TasksUiPlugin.ID_PLUGIN, | |
NLS.bind("Failed to migrate data store for repository {0}", newRepositoryUrl), e)); //$NON-NLS-1$ | |
} | |
} | |
public ContextStatePersistenceHandler getStateHandler() { | |
return stateHandler; | |
} | |
} |