// Holy mother of god this code is so f*****g terrible. private static void DoImplicitLinkingEventIfRequired(Task task, GenericTodoEvent e, TaskList tasklist, Stack <UndoAction> undoStack) { // CASE: Task is not started but we want to do 'completed'. We can therefore implicitly link the states by first doing a "taskStarted" operation. if (e.EventType == EventTypes.TaskCompleted && task.ProgressStatus == ProgressStatusVals.NotStarted) { Handlers[EventTypes.TaskStarted](e, tasklist, undoStack); return; } // CASE: We want to do 'task revived' but the "original task" is not failed yet. In this case, we should implicitly 'fail' the original task first. if (e.EventType == EventTypes.TaskRevived) { Task original = tasklist.ActiveTaskReader(e.Original.Value); if (original != null && original.Category != CategoryVals.Deferred) { tasklist.FailTask(original, e.Timestamp); undoStack.Clear(); return; } } }
public static TaskList Replay(GenericTodoEvent e, TaskList tasklist, Stack <UndoAction> undoStack, out bool saveEvent, out bool triggerRefresh) { saveEvent = true; triggerRefresh = false; // Acquire the task from the global task list, to determine it's current state. Task task = tasklist.AllTaskReader(e.Id); try { // Handle cases where the task does not yet exist. If the event is a creation event, (or undo deletion event) then this is obviously expected. // If the event is an 'undo' of a creation event, then we can safely dispose of the event, since it would just erase the expected task anyway. We will still save it though, in case of an out-or-order arrival // If the event is a 'Task activated' or 'Task Started' or 'Task Edited' event, then we will create the event implicitly, since we can do so with no harm done. // If the event is a 'Task revived' event, then this is expected, but only if the 'original' task acutally existed and is a failed state. Otherwise, we will have to implicitly 'fail' the original one first. if (task == null) { if (e.EventType == EventTypes.TaskAdded || e.EventType == EventTypes.ChildTaskAdded || e.EventType == EventTypes.TaskDeletedUndo || (e.EventType == EventTypes.TaskRevived && tasklist.FailedTaskReader(e.Original.Value) != null)) { return(Handlers[e.EventType](e, tasklist, undoStack)); } else if (e.EventType == EventTypes.TaskRevived && tasklist.ActiveTaskReader(e.Original.Value) != null && tasklist.ActiveTaskReader(e.Original.Value).Category != CategoryVals.Deferred) { // Implicitly 'fail' the original task first tasklist.FailTask(tasklist.AllTaskReader(e.Original.Value), e.Timestamp); undoStack.Clear(); return(Handlers[e.EventType](e, tasklist, undoStack)); } else if (e.EventType == EventTypes.TaskAddedUndo || e.EventType == EventTypes.ChildTaskAddedUndo || e.EventType == EventTypes.TaskEdited || e.EventType == EventTypes.TaskDeleted) { // Save the event, but do not actually do anything. We will still save this event incase a subsequent request 'fills in the gap' so to speak (I.e. out of order arrival). saveEvent = true; triggerRefresh = false; return(tasklist); } else if (e.EventType == EventTypes.TaskActivated || e.EventType == EventTypes.TaskStarted) { // If the task does not exist yet, we are happy to create it implicitly. if (e.Parent.HasValue) { tasklist.CreateNewSubtask(e.Name, tasklist.AllTaskReader(e.Parent.Value), e.Category, e.Timestamp, e.Id); undoStack.Push(new UndoAction(EventTypes.ChildTaskAdded, e.Id)); } else { tasklist.CreateNewIndependentTask(e.Name, e.Category, e.Timestamp, e.ColourId, e.Id); undoStack.Push(new UndoAction(EventTypes.TaskAdded, e.Id)); } return(Handlers[e.EventType](e, tasklist, undoStack)); } else if (e.EventType == EventTypes.TaskCompleted && e.Category != CategoryVals.Deferred) { // Create the task implicitly, then apply the 'linking' event, and then perform the taskCompleted handler itself. This is the only case of a "double link". if (e.Parent.HasValue) { tasklist.CreateNewSubtask(e.Name, tasklist.AllTaskReader(e.Parent.Value), e.Category, e.Timestamp, e.Id); undoStack.Push(new UndoAction(EventTypes.ChildTaskAddedUndo, e.Id)); } else { tasklist.CreateNewIndependentTask(e.Name, e.Category, e.Timestamp, e.ColourId, e.Id); undoStack.Push(new UndoAction(EventTypes.TaskAdded, e.Id)); } Handlers[EventTypes.TaskStarted](e, tasklist, undoStack); return(Handlers[e.EventType](e, tasklist, undoStack)); } else { throw new InvalidOperationException("Illegal event applied to a task id with no-corresponding-existing task. Eventtype: " + e.EventType); } } // Handle cases where the task is deferred. // If the event is a creation event, we can safely dispose of the event without saving it, because the task has already progressed to a state beyond the state that those incoming events cause. // If the event is an undo-activation event, or undo-deletion event, we can skip execution of the event, but it must still be saved in order to handle out-of-order arrival; for example an activation event could 'fill the gap'. // If the event is a 'start task' or 'fail task', we are happy to fill in the implicit activation action. else if (task.Category == CategoryVals.Deferred) { if (e.EventType == EventTypes.TaskAdded || e.EventType == EventTypes.ChildTaskAdded) { // Throw this event away, it is harmless and uneeded in this case. saveEvent = false; triggerRefresh = false; return(tasklist); } else if (e.EventType == EventTypes.TaskActivatedUndo || e.EventType == EventTypes.TaskDeletedUndo) { // Save this event, but do not execute the replay of this event, since it would have no effect. We still save it to handle out of order arrival. saveEvent = true; triggerRefresh = false; return(tasklist); } else if (e.EventType == EventTypes.TaskStarted || e.EventType == EventTypes.TaskFailed) { tasklist.ActivateTask(task, e.Category, e.Timestamp); return(Handlers[e.EventType](e, tasklist, undoStack)); } else { return(Handlers[e.EventType](e, tasklist, undoStack)); } } // Handle the cases where the state is denoted by progress status of the task (the only remaining option) else { if (IncomingEventsWhichTriggerRefreshForProgressStatusMappings[task.ProgressStatus].Contains(e.EventType)) { triggerRefresh = true; } if (IncomingEventsToIgnoreForProgressStatusMappings[task.ProgressStatus].Contains(e.EventType)) { saveEvent = false; return(tasklist); } if (IncomingEventsToSaveButNotExecuteForProgressStatusMappings[task.ProgressStatus].Contains(e.EventType)) { saveEvent = true; return(tasklist); } DoImplicitLinkingEventIfRequired(task, e, tasklist, undoStack); return(Handlers[e.EventType](e, tasklist, undoStack)); } } catch (InvalidOperationException exception) { // Add additional loggable information about the error that was thrown. throw new InvalidOperationException(exception.Message + ". When this error was thrown, the task was in state: " + ((task == null) ? "NON-EXISTENT (NULL)" : (task.Category == CategoryVals.Deferred) ? "Deferred" : ProgressStatusVals.getString(task.ProgressStatus)), exception); } }