blob: acedaa230c185703938a0fd1c753b7b3abc8ec67 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.mylyn.util;
import java.util.EventListener;
import java.util.EventObject;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.mylyn.internal.tasks.core.data.TaskAttributeDiff;
import org.eclipse.mylyn.internal.tasks.core.data.TaskDataState;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.data.ITaskDataWorkingCopy;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.eclipse.mylyn.tasks.core.data.TaskDataModel;
import org.eclipse.mylyn.tasks.core.data.TaskDataModelEvent;
import org.eclipse.mylyn.tasks.core.data.TaskDataModelListener;
/**
*
* @author Ondrej Vrabec
*/
public final class NbTaskDataModel {
private final ITaskDataWorkingCopy workingCopy;
private final TaskDataModel delegateModel;
private final List<NbTaskDataModelListener> listeners = new CopyOnWriteArrayList<NbTaskDataModelListener>();
private final NbTask task;
private final Set<TaskAttribute> unsavedChangedAttributes = new HashSet<TaskAttribute>();
NbTaskDataModel (TaskRepository taskRepository, NbTask task, ITaskDataWorkingCopy workingCopy) {
this.task = task;
this.delegateModel = new TaskDataModel(taskRepository, task.getDelegate(), workingCopy);
this.workingCopy = workingCopy;
delegateModel.addModelListener(new TaskDataModelListener() {
@Override
public void attributeChanged (TaskDataModelEvent modelEvent) {
NbTaskDataModelEvent event = new NbTaskDataModelEvent(NbTaskDataModel.this, modelEvent);
for (NbTaskDataModelListener list : listeners.toArray(new NbTaskDataModelListener[0])) {
list.attributeChanged(event);
}
}
});
}
public boolean hasIncomingChanges (TaskAttribute taskAttribute, boolean includeConflicts) {
TaskData repositoryData = workingCopy.getRepositoryData();
if (repositoryData == null) {
return false;
}
taskAttribute = repositoryData.getRoot().getMappedAttribute(taskAttribute.getPath());
if (taskAttribute == null) {
return false;
}
boolean incoming = delegateModel.hasIncomingChanges(taskAttribute);
if (includeConflicts && !incoming && delegateModel.hasOutgoingChanges(taskAttribute)) {
TaskData lastReadData = workingCopy.getLastReadData();
if (lastReadData == null) {
return true;
}
TaskAttribute oldAttribute = lastReadData.getRoot().getMappedAttribute(taskAttribute.getPath());
if (oldAttribute == null) {
return true;
}
return !repositoryData.getAttributeMapper().equals(taskAttribute, oldAttribute);
}
return incoming;
}
public boolean hasOutgoingChanges (TaskAttribute ta) {
return delegateModel.hasOutgoingChanges(ta);
}
public TaskData getLocalTaskData () {
return delegateModel.getTaskData();
}
public TaskData getLastReadTaskData () {
return workingCopy.getLastReadData();
}
public TaskData getRepositoryTaskData () {
return workingCopy.getRepositoryData();
}
public void addNbTaskDataModelListener (NbTaskDataModelListener listener) {
listeners.add(listener);
}
public void removeNbTaskDataModelListener (NbTaskDataModelListener listener) {
listeners.remove(listener);
}
public boolean isDirty () {
return delegateModel.isDirty();
}
public void attributeChanged (TaskAttribute a) {
synchronized (unsavedChangedAttributes) {
// replace the task attribute in unsaved changes
unsavedChangedAttributes.remove(a);
unsavedChangedAttributes.add(a);
delegateModel.attributeChanged(a);
}
}
public Set<TaskAttribute> getChangedAttributes () {
return delegateModel.getChangedAttributes();
}
public Set<TaskAttribute> getChangedOldAttributes () {
return delegateModel.getChangedOldAttributes();
}
/**
* Updates the model and all taskdata to keep track with external taskdata
* changes (such as a task refresh)
* @throws CoreException
*/
public void refresh () throws CoreException {
// refresh reverts all taskdata to the state on disk
delegateModel.refresh(null);
// also clear unsaved changes in the mylyn model
// this is needed because after refresh() the unsaved changes no longer
// belong to the local taskdata (they're reinstantiated)
delegateModel.revert();
if (workingCopy instanceof TaskDataState && workingCopy.getLastReadData() == null) {
((TaskDataState) workingCopy).setLastReadData(workingCopy.getRepositoryData());
}
Set<TaskAttribute> changedAttributes;
synchronized (unsavedChangedAttributes) {
changedAttributes = new HashSet<TaskAttribute>(unsavedChangedAttributes);
}
for (TaskAttribute ta : changedAttributes) {
// there are still local unsaved changes, keep them in local taskdata
TaskData td = getLocalTaskData();
td.getRoot().deepAddCopy(ta);
TaskAttribute attribute = td.getRoot().getAttribute(ta.getId());
// now refill the unsaved changes so they belong to the correct local TD
attributeChanged(attribute);
}
}
public void save () throws CoreException {
save(null);
}
public void save (IProgressMonitor monitor) throws CoreException {
// clear delegate model changes
delegateModel.save(monitor);
// now bit of hacking
// task attributes with same values as local changes must be reverted
TaskData editsData = workingCopy.getEditsData();
TaskData repositoryData = workingCopy.getRepositoryData();
synchronized (unsavedChangedAttributes) {
if (editsData != null && repositoryData != null) {
for (Iterator<TaskAttribute> it = unsavedChangedAttributes.iterator(); it.hasNext(); ) {
TaskAttribute ta = it.next();
if (!editsDiffer(ta, repositoryData)) {
editsData.getRoot().removeAttribute(ta.getId());
it.remove();
}
}
}
// are there any edits
boolean noChanges = unsavedChangedAttributes.isEmpty() && !hasOutgoingChanged();
if (noChanges) {
task.discardLocalEdits();
}
delegateModel.revert();
unsavedChangedAttributes.clear();
}
}
public boolean hasBeenRead () {
return delegateModel.hasBeenRead();
}
public TaskRepository getTaskRepository () {
return delegateModel.getTaskRepository();
}
public NbTask getTask () {
return task;
}
ITask getDelegateTask () {
return delegateModel.getTask();
}
public boolean hasOutgoingChanged () {
return isDirty() || !getChangedAttributes().isEmpty();
}
public void clearUnsavedChanges () {
if (isDirty()) {
delegateModel.revert();
unsavedChangedAttributes.clear();
}
}
private boolean editsDiffer (TaskAttribute ta, TaskData repositoryData) {
TaskAttribute repositoryTA = repositoryData.getRoot().getMappedAttribute(ta.getPath());
boolean changes = new TaskAttributeDiff(ta, repositoryTA).hasChanges();
if (!changes) {
// is a child attribue changed??
for (TaskAttribute childTA : ta.getAttributes().values()) {
if (editsDiffer(childTA, repositoryData)) {
changes = true;
break;
}
}
}
return changes;
}
public static interface NbTaskDataModelListener extends EventListener {
public void attributeChanged (NbTaskDataModelEvent event);
}
public static final class NbTaskDataModelEvent extends EventObject {
private final TaskDataModelEvent modelEvent;
private final NbTaskDataModel model;
private NbTaskDataModelEvent (NbTaskDataModel source, TaskDataModelEvent modelEvent) {
super(source);
this.model = source;
this.modelEvent = modelEvent;
}
public NbTaskDataModel getModel () {
return model;
}
public TaskAttribute getTaskAttribute () {
return modelEvent.getTaskAttribute();
}
}
}