//===============================================================================
// Goal Book.
// Copyright � 2009 Mark Brownsword.
//===============================================================================
#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Csla;
using GoalBook.Infrastructure.Interfaces;
#endregion
namespace GoalBook.Infrastructure.ObjectModel
{
[Serializable]
public sealed class TaskList : BusinessListBase<TaskList, Task>, IXDocSerializable
{
#region Constants and Enums
private const string SERIALIZATION_ROOT = "TaskList";
private const string SERIALIZATION_TASKS = "Tasks";
private const string SERIALIZATION_DELETED_TASKS = "DeletedTasks";
private const string SERIALIZATION_LAST_SERVER_TASK_EDIT = "LastServerTaskEdit";
#endregion
#region Inner Classes and Structures
#endregion
#region Delegates and Events
#endregion
#region Instance and Shared Fields
private List<Task> _deleted;
private DateTime _lastServerTaskEdit;
private bool _lastServerTaskEditChanged = false;
#endregion
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
public TaskList()
{
AllowEdit =
AllowNew =
AllowRemove = true;
}
#endregion
#region Properties
/// <summary>
/// Expose the EditLevel so can call ApplyEdit the
/// correct number of times.
/// </summary>
public new int EditLevel { get { return base.EditLevel; } }
/// <summary>
/// Reference to LastServerTaskEdit. A timestamp that indicates
/// the last time an edit occurred on the server.
/// </summary>
public DateTime LastServerTaskEdit
{
get { return _lastServerTaskEdit;}
set { _lastServerTaskEdit = value; _lastServerTaskEditChanged = true; }
}
/// <summary>
/// Reference to SyncRequired. A flag indicating if any
/// notes have been edited and require synchronisation.
/// </summary>
public bool SyncRequired
{
get
{
foreach (Task task in this)
{
if (task.SyncRequired) { return true; }
}
foreach (Task task in this.DeletedList)
{
if (task.ExternalIdentifier > 0) { return true; }
}
if (this.DeletedPendingSync.Count > 0) { return true; }
return false;
}
}
/// <summary>
/// Deleted tasks. For synchronisation.
/// </summary>
public List<Task> DeletedPendingSync
{
get
{
if (_deleted == null) { DeletedPendingSync = new List<Task>(); }
return _deleted;
}
set { _deleted = value; }
}
#endregion
#region Private and Protected Methods
/// <summary>
/// Get Task FromElement.
/// </summary>
private Task GetTaskFromElement(XElement element)
{
Guid taskID = new Guid(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_TASKID));
int parentTask = int.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_PARENTTASK));
int children = int.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_CHILDREN));
string title = GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_TITLE);
string tag = GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_TAG);
Guid folderID = new Guid(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_FOLDERID));
Guid contextID = new Guid(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_CONTEXTID));
Guid goalID = new Guid(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_GOALID));
DateTime? added = GetDateTime(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_ADDED));
DateTime? modified = GetDateTime(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_MODIFIED));
DateTime? start = GetDateTime(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_START));
DateTime? due = GetDateTime(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_DUE));
string dueModifier = GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_DUEMODIFIER);
TimeInfo? dueTime = GetDueTime(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_DUETIME));
string startTime = GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_STARTTIME);
DateTime? completed = GetDateTime(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_COMPLETED));
int repeat = int.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_REPEAT));
string repeatAdvanced = GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_REPEATADVANCED);
int status = int.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_STATUS));
bool star = bool.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_STAR));
int priority = int.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_PRIORITY));
int length = int.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_LENGTH));
int timer = int.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_TIMER));
string note = GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_NOTE);
int externalIdentifier = int.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_EXTERNALIDENTIFIER));
Task task = new Task(taskID, parentTask, children, title, tag, folderID, contextID, goalID, added, modified, start, due,
dueModifier, dueTime, startTime, completed, repeat, repeatAdvanced, status, star, priority, length, timer, note, externalIdentifier);
task.SyncRequired = bool.Parse(GetAttributeFromElement(element, Constants.TaskSerializationConstants.SERIALIZATION_SYNCREQUIRED));
return task;
}
/// <summary>
/// Get DueTime.
/// </summary>
/// <param name="input">input parameter</param>
/// <returns>TwelveHourTimeInfo instance</returns>
private TimeInfo? GetDueTime(string input)
{
TimeInfo result;
if (TimeInfo.TryParse(input, out result))
{
return result;
}
return null;
}
/// <summary>
/// Get DateTime.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
private DateTime? GetDateTime(string input)
{
DateTime result;
if (DateTime.TryParse(input, out result))
{
return result;
}
return null;
}
/// <summary>
/// Get Task with the specified id.
/// </summary>
private Task GetTask(TaskList taskList, Guid id)
{
var targetList = from g in taskList where g.TaskID == id select g;
foreach (Task task in targetList)
{
return task; //One only can exist.
}
return null;
}
/// <summary>
/// Get Attribute From Element.
/// </summary>
private string GetAttributeFromElement(XElement element, string attribute)
{
if (element.Attribute(attribute) == null) { return null; }
return element.Attribute(attribute).Value;
}
#endregion
#region Public and internal Methods
#region IXDocSerializable Members
/// <summary>
/// Get Goals List as XDocument.
/// </summary>
public XDocument GetXDocument()
{
//Serialize the NotesList Element.
XElement tasksElement = new XElement(SERIALIZATION_TASKS, new XAttribute(SERIALIZATION_LAST_SERVER_TASK_EDIT,
LastServerTaskEdit.ToString()));
//Serialize the Tasks.
foreach (Task task in this)
{
tasksElement.Add(task.SerializeAsXElement());
}
XElement deletedPendingSyncElement = new XElement(SERIALIZATION_DELETED_TASKS);
//Serialize the deleted Tasks.
foreach (Task task in this.DeletedPendingSync)
{
deletedPendingSyncElement.Add(task.SerializeAsXElement());
}
XElement rootElement = new XElement(SERIALIZATION_ROOT, tasksElement, deletedPendingSyncElement);
return new XDocument(rootElement);
}
/// <summary>
/// Create TasksList From XDocument.
/// </summary>
public void CreateFromXDocument(XDocument xDoc)
{
if (xDoc == null) return;
XElement root = xDoc.Element(SERIALIZATION_ROOT);
XElement tasks = root.Element(SERIALIZATION_TASKS);
XElement deletedTasks = root.Element(SERIALIZATION_DELETED_TASKS);
//LastServerTaskEdit
if (!string.IsNullOrEmpty(GetAttributeFromElement(tasks, SERIALIZATION_LAST_SERVER_TASK_EDIT)))
{
this._lastServerTaskEdit = Convert.ToDateTime(GetAttributeFromElement(tasks, SERIALIZATION_LAST_SERVER_TASK_EDIT));
}
//Load the Tasks.
foreach (XElement element in tasks.Descendants(Constants.TaskSerializationConstants.SERIALIZATION_TASK))
{
this.Add(GetTaskFromElement(element));
}
//Load the deletedPendingSync Tasks.
this.DeletedPendingSync.Clear();
foreach (XElement element in deletedTasks.Descendants(Constants.TaskSerializationConstants.SERIALIZATION_TASK))
{
this.DeletedPendingSync.Add(GetTaskFromElement(element));
}
}
/// <summary>
/// MarkListOld. Mark all list items as old.
/// </summary>
public void MarkListOld()
{
//Wind back the EditLevel.
while (this.EditLevel > 0) { this.ApplyEdit(); }
//Mark each child as old (clean).
foreach (Task task in this) { task.MarkTaskOld(); }
//Move the Deleted items with an ExternalIdentifier to the Deleted list. The DeletedList
//holds tasks that have been deleted on the client but have not yet been synchronised.
var deleted = from g in DeletedList where g.ExternalIdentifier > 0 select g;
foreach (Task task in deleted)
{
if (DeletedPendingSync.Contains(task)) { continue; }
DeletedPendingSync.Add(task);
}
//The list remains dirty until the DeletedList is clear, so clear it now.
DeletedList.Clear();
//Set to true after sync, so set false during save.
_lastServerTaskEditChanged = false;
}
#endregion
/// <summary>
/// Merge tasks. Synchronisation occurs in a cloned TaskList,
/// so need to merge changes back into the primary TaskList.
/// </summary>
public void Merge(TaskList syncTasks)
{
foreach (Task task in syncTasks)
{
Task target = GetTask(this, task.TaskID);
if (target == null)
{
//Add
this.BeginEdit();
this.Add(task);
this.EndNew(this.IndexOf(task));
}
else
{
//Edit
if (task.IsDirty) //Dirty means changes occured during sync.
{
this.BeginEdit();
target.MapFields(task);
}
//Set SyncRequired flag (so client edits only get pushed to server once).
target.SyncRequired = task.SyncRequired;
}
}
foreach (Task task in syncTasks.DeletedList)
{
//Remove
Task target = GetTask(this, task.TaskID);
if (target != null)
{
this.Remove(target);
}
}
//Clear Deleted. Sync of Deleted items is complete.
if (this.DeletedList.Count > 0) { this.DeletedList.Clear(); }
this.DeletedPendingSync.Clear();
foreach (Task task in syncTasks.DeletedPendingSync)
{
//Tasks remaining in syncTasks.DeletedPendingSync were not
//successfully deleted during the client changes synchronisation.
//These need to be retained so can attempt delete again.
this.DeletedPendingSync.Add(task);
}
//Map LastServerTaskEdit.
if (syncTasks.LastServerTaskEdit > this.LastServerTaskEdit)
{
this.LastServerTaskEdit = syncTasks.LastServerTaskEdit;
}
}
#endregion
#region Event Handlers
#endregion
#region Base Class Overrides
/// <summary>
/// AddNewCore. Initialises and adds a new Task item to the List.
/// </summary>
protected override object AddNewCore()
{
Guid guid = Guid.NewGuid();
Task Task = new Task(guid);
Add(Task);
return Task;
}
/// <summary>
/// IsSavable.
/// </summary>
public override bool IsSavable
{
get
{
if (_lastServerTaskEditChanged)
{
return base.IsValid;
}
return base.IsSavable;
}
}
#endregion
}
}