blob: d003eed99a97cf6809b8b4090c7bb1e943f19509 [file] [log] [blame]
/*******************************************************************************
* 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 refactorContext(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;
}
}