Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.netbeans.modules.mylyn.util.AbstractNbTaskWrapper Maven / Gradle / Ivy
/*
* 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.awt.EventQueue;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.tasks.core.data.TaskAttribute;
import org.eclipse.mylyn.tasks.core.data.TaskData;
import org.netbeans.modules.bugtracking.spi.IssueScheduleInfo;
import org.netbeans.modules.bugtracking.spi.IssueStatusProvider;
import org.netbeans.modules.bugtracking.commons.AttachmentsPanel;
import org.netbeans.modules.bugtracking.spi.IssueController;
import org.netbeans.modules.bugtracking.spi.IssueProvider;
import org.netbeans.modules.bugtracking.spi.IssueScheduleProvider;
import org.openide.util.RequestProcessor;
import org.openide.util.WeakListeners;
/**
*
* @author Ondrej Vrabec
*/
public abstract class AbstractNbTaskWrapper {
static final String ATTR_NEW_UNREAD = "NetBeans.task.markedNewUnread"; //NOI18N
private static final Object MODEL_LOCK = new Object();
private static final Logger LOG = Logger.getLogger(AbstractNbTaskWrapper.class.getName());
private static final RequestProcessor RP = new RequestProcessor("NBTasks"); //NOI18N
public static final String NEW_ATTACHMENT_ATTRIBUTE_ID = "nb.attachments.new"; //NOI18N
private static final String NB_NEW_ATTACHMENT_PATCH_ATTR_ID = "nb.newattachment.patch"; //NOI18N
private static final String NB_NEW_ATTACHMENT_FILE_ATTR_ID = "nb.newattachment.file"; //NOI18N
private static final String NB_NEW_ATTACHMENT_CONTENT_TYPE_ATTR_ID = "nb.newattachment.contentType"; //NOI18N
private static final String NB_NEW_ATTACHMENT_DESC_ATTR_ID = "nb.newattachment.desc"; //NOI18N
private static final String NB_NEW_ATTACHMENT_ATTR_ID = "nb.newattachment."; //NOI18N
private NbTask task;
private NbTaskDataModel model;
private NbTaskDataModel.NbTaskDataModelListener list;
private boolean readPending;
private final TaskDataListenerImpl taskDataListener;
private final TaskListenerImpl taskListener;
private Reference repositoryDataRef;
private final RequestProcessor.Task repositoryTaskDataLoaderTask;
private final PropertyChangeSupport support;
/** PRIVATE TASK ATTRIBUTES **/
private String privateNotes;
private Date dueDate;
private boolean dueDateModified;
private NbDateRange scheduleDate;
private boolean scheduleDateModified;
private Integer estimate;
/** PRIVATE TASK ATTRIBUTES **/
public AbstractNbTaskWrapper (NbTask task) {
this.task = task;
this.repositoryDataRef = new SoftReference(null);
support = new PropertyChangeSupport(this);
repositoryTaskDataLoaderTask = RP.create(new Runnable() {
@Override
public void run () {
loadRepositoryTaskData();
}
});
MylynSupport mylynSupp = MylynSupport.getInstance();
taskDataListener = new TaskDataListenerImpl();
mylynSupp.addTaskDataListener(WeakListeners.create(TaskDataListener.class, taskDataListener, mylynSupp));
taskListener = new TaskListenerImpl();
task.addNbTaskListener(WeakListeners.create(NbTaskListener.class, taskListener, mylynSupp));
}
/**
* Returns the id of the given task or null if task is new
* @param task
* @return id or null
*/
public static String getID (NbTask task) {
if (task.isUnsubmittedRepositoryTask()) {
return "-" + task.getTaskId();
}
return task.getTaskId();
}
protected static boolean attachmentAttributesDiffer (TaskAttribute ta1, TaskAttribute ta2) {
if (ta2 == null) {
return true;
}
String value1 = ta1.getValue();
String value2 = ta2.getValue();
boolean changes = !value1.equals(value2);
if (!changes) {
// is a child attribue changed??
for (TaskAttribute childTA : ta1.getAttributes().values()) {
if (attachmentAttributesDiffer(childTA, ta2.getAttribute(childTA.getId()))) {
changes = true;
break;
}
}
}
return changes;
}
public final void addPropertyChangeListener (PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
public final void removePropertyChangeListener (PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
protected final TaskData getRepositoryTaskData () {
TaskData taskData = repositoryDataRef.get();
if (taskData == null) {
if (EventQueue.isDispatchThread()) {
repositoryTaskDataLoaderTask.schedule(100);
} else {
return loadRepositoryTaskData();
}
}
return taskData;
}
private TaskData loadRepositoryTaskData () {
// this method is time consuming
assert !EventQueue.isDispatchThread();
TaskData td = repositoryDataRef.get();
if (td != null) {
return td;
}
try {
NbTaskDataState taskDataState = task.getTaskDataState();
if (taskDataState != null) {
td = taskDataState.getRepositoryData();
repositoryDataRef = new SoftReference(td);
repositoryTaskDataLoaded(td);
}
} catch (CoreException ex) {
LOG.log(Level.WARNING, null, ex);
}
return td;
}
protected final void deleteTask () {
assert task.isLocal() : "Only local tasks can be deleted: " + task.getDelegate().getConnectorKind();
synchronized (MODEL_LOCK) {
if (list != null) {
model.removeNbTaskDataModelListener(list);
list = null;
}
model = null;
}
MylynSupport mylynSupp = MylynSupport.getInstance();
mylynSupp.removeTaskDataListener(taskDataListener);
task.removeNbTaskListener(taskListener);
if (task.isLocal()) {
task.delete();
taskDeleted(task);
fireDeleted();
}
}
protected abstract void taskDeleted (NbTask task);
public final boolean isMarkedNewUnread () {
return task.isLocal() && Boolean.TRUE.toString().equals(task.getAttribute(ATTR_NEW_UNREAD));
}
public final boolean isNew () {
return task.isUnsubmittedRepositoryTask();
}
protected final void markNewRead () {
task.setAttribute(ATTR_NEW_UNREAD, null);
}
public final void setUpToDate (boolean seen, boolean markReadPending) {
synchronized (MODEL_LOCK) {
if (markReadPending) {
// this is a workaround to keep incoming changes visible in editor
NbTask.SynchronizationState syncState = task.getSynchronizationState();
readPending |= syncState == NbTask.SynchronizationState.INCOMING
|| syncState == NbTask.SynchronizationState.CONFLICT;
} else {
readPending = false;
}
task.markSeen(seen);
}
}
protected final boolean editorOpened () {
list = new NbTaskDataModel.NbTaskDataModelListener() {
@Override
public void attributeChanged (NbTaskDataModel.NbTaskDataModelEvent event) {
NbTaskDataModel m = model;
if (event.getModel() == m) {
AbstractNbTaskWrapper.this.attributeChanged(event, m);
}
}
};
if (task.getSynchronizationState() == NbTask.SynchronizationState.INCOMING_NEW) {
// mark as seen so no fields are highlighted
setUpToDate(true, false);
}
synchronized (MODEL_LOCK) {
synchronized (getNbTask()) {
// clear upon close
if (readPending) {
// make sure remote changes are not lost and still highlighted in the editor
setUpToDate(false, false);
if (task.getDelegate() instanceof AbstractTask) {
((AbstractTask) task.getDelegate()).setMarkReadPending(false);
}
}
model = task.getTaskDataModel();
if (model == null) {
if (!synchronizeTask()) {
return false;
}
model = task.getTaskDataModel();
}
if(model == null) {
// issue #247877
// something went wrong, not much we can do about it at this place.
// lets avoid at least the NPE
LOG.log(Level.WARNING, "no model even after synchronizing {0}", task.getTaskId());
return false;
}
model.addNbTaskDataModelListener(list);
}
try {
MylynSupport.getInstance().editorOpened(task.getDelegate());
} catch (CoreException ex) {
LOG.log(Level.WARNING, null, ex);
}
}
return true;
}
protected final void editorClosed () {
NbTaskDataModel m = model;
boolean markedAsNewUnread = isMarkedNewUnread();
if (m != null) {
if (list != null) {
m.removeNbTaskDataModelListener(list);
list = null;
}
readPending = false;
if (markedAsNewUnread) {
// was not modified by user and not yet saved
deleteTask();
} else {
if (m.isDirty()) {
try {
save();
} catch (CoreException ex) {
LOG.log(Level.WARNING, null, ex);
}
}
try {
MylynSupport.getInstance().editorClosing(task.getDelegate(), m.getRepositoryTaskData());
} catch (CoreException ex) {
LOG.log(Level.WARNING, null, ex);
}
synchronized (MODEL_LOCK) {
if (model == m) {
model = null;
}
}
}
}
}
protected final void runWithModelLoaded (Runnable runnable) {
synchronized (MODEL_LOCK) {
boolean closeModel = false;
try {
if (model == null) {
closeModel = true;
model = task.getTaskDataModel();
}
runnable.run();
} finally {
if (closeModel) {
if (model != null && model.isDirty()) {
try {
// let's not loose edits
model.save();
} catch (CoreException ex) {
LOG.log(Level.INFO, null, ex);
}
}
model = null;
}
}
}
}
protected abstract void attributeChanged (NbTaskDataModel.NbTaskDataModelEvent event, NbTaskDataModel model);
private void save () throws CoreException {
NbTaskDataModel m = this.model;
if(m == null) {
// haven't been opened yet so there is no model?
// might happen when e.g. closing a local task on commit (see issue #252222)
m = task.getTaskDataModel();
}
markNewRead();
boolean modified = persistPrivateTaskAttributes();
if (m.isDirty()) {
if (isNew()) {
String summary = task.getSummary();
String newSummary = getSummary(m.getLocalTaskData());
if (newSummary != null && !(newSummary.isEmpty() || newSummary.equals(summary))) {
task.setSummary(newSummary);
taskModified(false);
}
}
m.save();
modelSaved(m);
}
if (modified) {
taskModified(false);
}
}
protected final void taskSubmitted (NbTask task) {
if (task != null && task != this.task) {
this.task.removeNbTaskListener(taskListener);
this.task = task;
task.addNbTaskListener(taskListener);
synchronized (MODEL_LOCK) {
if (list != null) {
model.removeNbTaskDataModelListener(list);
}
model = task.getTaskDataModel();
repositoryDataRef.clear();
if (list != null) {
model.addNbTaskDataModelListener(list);
}
}
}
}
protected final boolean saveChanges () {
try {
save();
return true;
} catch (CoreException ex) {
LOG.log(Level.WARNING, null, ex);
}
return false;
}
public final boolean cancelChanges () {
try {
if (saveChanges()) {
task.discardLocalEdits();
model.refresh();
return true;
}
} catch (CoreException ex) {
LOG.log(Level.WARNING, null, ex);
}
return false;
}
public final void clearUnsavedChanges () {
clearPrivateTaskAttributeChanges();
model.clearUnsavedChanges();
}
public final boolean hasLocalEdits () {
NbTaskDataModel m = model;
return !(m == null || m.getChangedAttributes().isEmpty());
}
public final boolean hasUnsavedChanges () {
NbTaskDataModel m = model;
return m != null && m.isDirty()
|| hasUnsavedPrivateTaskAttributes();
}
protected final boolean updateModel () {
try {
model.refresh();
MylynSupport.getInstance().editorOpened(task.getDelegate());
return true;
} catch (CoreException ex) {
LOG.log(Level.INFO, null, ex);
}
return false;
}
protected abstract void modelSaved (NbTaskDataModel model);
protected abstract String getSummary (TaskData taskData);
protected abstract void taskDataUpdated ();
protected final boolean isSeen () {
NbTask.SynchronizationState syncState = task.getSynchronizationState();
return syncState == NbTask.SynchronizationState.OUTGOING
|| syncState == NbTask.SynchronizationState.OUTGOING_NEW
|| syncState == NbTask.SynchronizationState.SYNCHRONIZED;
}
protected abstract void taskModified (boolean syncStateChanged);
protected final NbTaskDataModel getModel () {
return model;
}
protected abstract void repositoryTaskDataLoaded (TaskData repositoryTaskData);
protected final NbTask getNbTask () {
return task;
}
public final long getCreated () {
Date createdDate = getCreatedDate();
if (createdDate != null) {
return createdDate.getTime();
} else {
return -1;
}
}
public final Date getCreatedDate () {
return task.getCreationDate();
}
public final long getLastModify () {
Date lastModifyDate = getLastModifyDate();
if (lastModifyDate != null) {
return lastModifyDate.getTime();
} else {
return -1;
}
}
public final Date getLastModifyDate () {
return task.getModificationDate();
}
public final String getSummary () {
return task.getSummary();
}
public final String getID () {
return getID(task);
}
public final boolean isFinished () {
return task.isCompleted();
}
protected final NbTask.SynchronizationState getSynchronizationState () {
return task.getSynchronizationState();
}
public final IssueStatusProvider.Status getStatus () {
switch (getSynchronizationState()) {
case CONFLICT:
return IssueStatusProvider.Status.CONFLICT;
case INCOMING:
return IssueStatusProvider.Status.INCOMING_MODIFIED;
case INCOMING_NEW:
return IssueStatusProvider.Status.INCOMING_NEW;
case OUTGOING:
return IssueStatusProvider.Status.OUTGOING_MODIFIED;
case OUTGOING_NEW:
return IssueStatusProvider.Status.OUTGOING_NEW;
case SYNCHRONIZED:
return IssueStatusProvider.Status.SEEN;
}
return null;
}
/**
* Called when the task does not yet contain all needed data. Needs to re-sync to get full taskdata.
* @return false when the sync fails for some reason
*/
protected abstract boolean synchronizeTask ();
protected final boolean setNewAttachments (List newAttachments) {
NbTaskDataModel m = getModel();
TaskData td = m.getLocalTaskData();
TaskAttribute ta = td.getRoot().getAttribute(AbstractNbTaskWrapper.NEW_ATTACHMENT_ATTRIBUTE_ID);
Map previousAttachments = Collections.emptyMap();
if (ta != null) {
previousAttachments = ta.getAttributes();
ta.clearAttributes();
}
boolean retval = false;
if (newAttachments.isEmpty() && !previousAttachments.isEmpty()) {
m.attributeChanged(ta);
retval = true;
} else if (!newAttachments.isEmpty()) {
if (ta == null) {
ta = td.getRoot().createAttribute(AbstractNbTaskWrapper.NEW_ATTACHMENT_ATTRIBUTE_ID);
}
boolean present = true;
for (int i = 0; i < newAttachments.size(); ++i) {
AttachmentsPanel.AttachmentInfo att = newAttachments.get(i);
TaskAttribute attAttr = ta.createAttribute(NB_NEW_ATTACHMENT_ATTR_ID + i);
if (att.getDescription() != null) {
attAttr.createAttribute(NB_NEW_ATTACHMENT_DESC_ATTR_ID).setValue(att.getDescription());
}
if (att.getContentType() != null) {
attAttr.createAttribute(NB_NEW_ATTACHMENT_CONTENT_TYPE_ATTR_ID).setValue(att.getContentType());
}
if (att.getFile() != null) {
attAttr.createAttribute(NB_NEW_ATTACHMENT_FILE_ATTR_ID).setValue(att.getFile().getAbsolutePath());
}
attAttr.createAttribute(NB_NEW_ATTACHMENT_PATCH_ATTR_ID).setValue(att.isPatch() ? "1" : "0");
if (present) {
present = false;
for (TaskAttribute previousAttachment : previousAttachments.values()) {
if (!attachmentAttributesDiffer(previousAttachment, ta)) {
present = true;
break;
}
}
}
}
if (!present || previousAttachments.size() != newAttachments.size()) {
m.attributeChanged(ta);
retval = true;
}
}
return retval;
}
protected final List getNewAttachments () {
NbTaskDataModel m = getModel();
TaskData td = m == null ? null : m.getLocalTaskData();
List attachments = Collections.emptyList();
if (td != null) {
TaskAttribute ta = td.getRoot().getAttribute(AbstractNbTaskWrapper.NEW_ATTACHMENT_ATTRIBUTE_ID);
if (ta != null) {
Map attributes = ta.getAttributes();
attachments = new ArrayList(attributes.size());
for (TaskAttribute attAttr : attributes.values()) {
AttachmentsPanel.AttachmentInfo attInfo = new AttachmentsPanel.AttachmentInfo();
attInfo.setDescription("");
attInfo.setFile(null);
attInfo.setIsPatch(false);
for (Map.Entry attSubAttr : attAttr.getAttributes().entrySet()) {
if (NB_NEW_ATTACHMENT_DESC_ATTR_ID.equals(attSubAttr.getKey())) {
attInfo.setDescription(attSubAttr.getValue().getValue());
} else if (NB_NEW_ATTACHMENT_CONTENT_TYPE_ATTR_ID.equals(attSubAttr.getKey())) {
attInfo.setContentType(attSubAttr.getValue().getValue());
} else if (NB_NEW_ATTACHMENT_FILE_ATTR_ID.equals(attSubAttr.getKey())) {
attInfo.setFile(new File(attSubAttr.getValue().getValue()));
} else if (NB_NEW_ATTACHMENT_PATCH_ATTR_ID.equals(attSubAttr.getKey())) {
attInfo.setIsPatch("1".equals(attSubAttr.getValue().getValue()));
}
}
attachments.add(attInfo);
}
}
}
return attachments;
}
protected final void setPrivateNotes (String text) {
privateNotes = text;
}
public final String getPrivateNotes () {
if (privateNotes == null) {
return getNbTask().getPrivateNotes();
} else {
return privateNotes;
}
}
public final Date getDueDate () {
return dueDateModified ? dueDate : getNbTask().getDueDate();
}
protected final void setDueDate (Date date, boolean persistentChange) {
dueDate = date;
dueDateModified = true;
if (persistentChange) {
persistDueDate();
}
}
public final NbDateRange getScheduleDate () {
return scheduleDateModified ? scheduleDate : getNbTask().getScheduleDate();
}
protected final void setScheduleDate (IssueScheduleInfo info, boolean persistentChange) {
scheduleDate = info == null ? null : new NbDateRange(info);
scheduleDateModified = true;
if (persistentChange) {
persistScheduleDate();
}
}
public final int getEstimate () {
return estimate == null ? getNbTask().getEstimate() : estimate;
}
protected final void setEstimate (int estimate, boolean persistentChange) {
this.estimate = estimate;
if (persistentChange) {
persistEstimate();
}
}
/**
* Returns only persistent value of due date, never the unsaved modification from the task editor
* @return current task's persistent due date value
*/
public final Date getPersistentDueDate () {
return getNbTask().getDueDate();
}
/**
* Returns only persistent value of estimate, never the unsaved modification from the task editor
* @return current task's persistent estimate value
*/
public final int getPersistentEstimate () {
return getNbTask().getEstimate();
}
/**
* Returns only persistent value of schedule date, never the unsaved modification from the task editor
* @return current task's persistent scheduled date value
*/
public final IssueScheduleInfo getPersistentScheduleInfo () {
NbDateRange scheduled = getNbTask().getScheduleDate();
return scheduled == null ? null : scheduled.toSchedulingInfo();
}
protected final boolean hasUnsavedPrivateTaskAttributes () {
return privateNotes != null || dueDateModified || scheduleDateModified || estimate != null;
}
private boolean persistPrivateTaskAttributes () {
boolean modified = false;
if (privateNotes != null) {
getNbTask().setPrivateNotes(privateNotes.isEmpty() ? null : privateNotes);
privateNotes = null;
modified = true;
}
boolean fireScheduleEvent = false;
if (persistEstimate()) {
modified = true;
fireScheduleEvent = true;
}
if (persistDueDate()) {
modified = true;
fireScheduleEvent = true;
}
if (persistScheduleDate()) {
modified = true;
fireScheduleEvent = true;
}
if (fireScheduleEvent) {
fireScheduleChanged();
}
return modified;
}
private boolean persistDueDate () {
boolean modified = false;
if (dueDateModified) {
getNbTask().setDueDate(dueDate);
dueDate = null;
dueDateModified = false;
modified = true;
}
return modified;
}
private boolean persistEstimate () {
boolean modified = false;
if (estimate != null) {
getNbTask().setEstimate(estimate);
estimate = null;
modified = true;
}
return modified;
}
private boolean persistScheduleDate () {
boolean modified = false;
if (scheduleDateModified) {
getNbTask().setScheduleDate(scheduleDate);
scheduleDate = null;
scheduleDateModified = false;
modified = true;
}
return modified;
}
private void clearPrivateTaskAttributeChanges () {
privateNotes = null;
dueDate = null;
dueDateModified = false;
scheduleDate = null;
scheduleDateModified = false;
estimate = null;
}
protected final String getDueDisplayString () {
Calendar cal = Calendar.getInstance();
Date date = getPersistentDueDate();
if (date == null) {
return "";
}
cal.setTime(date);
return formatDate(cal);
}
protected final String getEstimateDisplayString () {
int est = getPersistentEstimate();
if (est == 0) {
return "";
}
return "" + est;
}
protected final String getScheduleDisplayString () {
NbDateRange schedule = getNbTask().getScheduleDate();
if (schedule == null) {
return "";
}
int interval = schedule.getEndDate().get(Calendar.DAY_OF_YEAR) - schedule.getStartDate().get(Calendar.DAY_OF_YEAR);
if (interval == 1) {
return formatDate(schedule.getStartDate());
}
return formateDate(schedule.getStartDate(), schedule.getEndDate());
}
protected final void fireChanged () {
support.firePropertyChange(IssueController.PROP_CHANGED, null, null);
}
protected final void fireDataChanged () {
support.firePropertyChange(IssueProvider.EVENT_ISSUE_DATA_CHANGED, null, null);
}
protected final void fireScheduleChanged () {
support.firePropertyChange(IssueScheduleProvider.EVENT_ISSUE_SCHEDULE_CHANGED, null, null);
}
protected final void fireStatusChanged () {
support.firePropertyChange(IssueStatusProvider.EVENT_STATUS_CHANGED, null, null);
}
private void fireDeleted () {
support.firePropertyChange(IssueProvider.EVENT_ISSUE_DELETED, null, null);
}
private String formatDate (Calendar date) {
return DateFormat.getDateInstance(DateFormat.DEFAULT).format(date.getTime());
}
private String formateDate (Calendar start, Calendar end) {
DateFormat format = DateFormat.getDateInstance(DateFormat.DEFAULT);
return format.format(start.getTime()) + " - " + format.format(end.getTime()); //NOI18N
}
private class TaskDataListenerImpl implements TaskDataListener {
@Override
public void taskDataUpdated (TaskDataListener.TaskDataEvent event) {
if (event.getTask() == task) {
if (event.getTaskData() != null && !event.getTaskData().isPartial()) {
repositoryDataRef = new SoftReference(event.getTaskData());
}
if (event.getTaskDataUpdated()) {
NbTaskDataModel m = model;
if (m != null) {
try {
m.refresh();
} catch (CoreException ex) {
LOG.log(Level.INFO, null, ex);
}
}
AbstractNbTaskWrapper.this.taskDataUpdated();
}
}
}
}
private class TaskListenerImpl implements NbTaskListener {
@Override
public void taskModified (NbTaskListener.TaskEvent event) {
if (event.getTask() == task && event.getKind() == NbTaskListener.TaskEvent.Kind.MODIFIED) {
boolean syncStateChanged = event.taskStateChanged();
AbstractNbTaskWrapper.this.taskModified(syncStateChanged);
}
}
}
}