/// <summary> /// Fill a collection of tasks by loading the content of a file /// </summary> public void LoadFromFile(string file) { //read the file line by line and gather these lines into groups String separator = GetTaskSeparator(); _allTasks.Clear(); StringBuilder currentGroup = null; List <StringBuilder> listOfGroups = new List <StringBuilder>(); using (StreamReader reader = new StreamReader(file)) { while (!reader.EndOfStream) { String line = reader.ReadLine(); if ((line == separator) || (currentGroup == null)) //begin of new group, add this group in the list { currentGroup = new StringBuilder(); listOfGroups.Add(currentGroup); } //add the line to the current group currentGroup.AppendLine(line); } } //convert each group of lines into tasks and add them into the collection TaskToStringConvertor convertor = new TaskToStringConvertor(); foreach (StringBuilder sb in listOfGroups) { TaskToDo task = convertor.StringToTask(sb.ToString()); _allTasks.Add(task); } }
/// <summary> /// Add a new subtask to the current task /// </summary> /// <param name="newSubtask"></param> /// <returns></returns> public TaskToDo AddSubtask(TaskToDo newSubtask) { newSubtask._parentTask = this; //create the uplink between this and the child _subtasks.Add(newSubtask); RefreshCompletedStatus(true); //refresh the completed status of current task and all its parents return(newSubtask); }
/// <summary> /// returns true if a given task can be moved down /// </summary> /// <param name="task"></param> /// <returns></returns> public Boolean CanMoveTaskDown(TaskToDo task) { int index = _allTasks.IndexOf(task); int lastIndex = _allTasks.Count - 1; return((index >= 0) && (index < lastIndex)); //case where index is -1 is integrated in this condition }
/// <summary> /// remove a given task from the collection /// </summary> /// <param name="taskToRemove"></param> public void RemoveTask(TaskToDo taskToRemove) { if (_allTasks.Contains(taskToRemove)) { _allTasks.Remove(taskToRemove); } }
/// <summary> /// replace a task in the collection by a new one /// </summary> /// <param name="taskToReplace"></param> /// <param name="taskToInsert"></param> public void ReplaceTask(TaskToDo taskToReplace, TaskToDo taskToInsert) { int index = _allTasks.IndexOf(taskToReplace); _allTasks.RemoveAt(index); _allTasks.Insert(index, taskToInsert); }
/// <summary> /// Add a task into the collection /// returns the task that was added /// </summary> /// <param name="newTask"></param> /// <returns></returns> public TaskToDo AddTask(TaskToDo newTask) { if (!_allTasks.Contains(newTask)) { _allTasks.Add(newTask); } return(newTask); }
/// <summary> /// Remove a subtask /// </summary> /// <param name="subtaskToRemove"></param> public void RemoveSubtask(TaskToDo subtaskToRemove) { if (_subtasks.Contains(subtaskToRemove)) { _subtasks.Remove(subtaskToRemove); RefreshCompletedStatus(true); //refresh the completed status of current task and all its parents } }
/// <summary> /// constructor /// </summary> public TaskToDo(String name, String description) { _parentTask = null; _name = name; _subtasks = new List <TaskToDo>(); _description = description; _completed = false; }
/// <summary> /// convert a task to a string /// </summary> /// <param name="task"></param> /// <returns></returns> public String TaskToString(TaskToDo task) { if (task == null) { return(String.Empty); } StringBuilder sb = new StringBuilder(); RecursiveTaskToString(task, 0, sb); return(sb.ToString()); }
/// <summary> /// Move a given task up -swap it with previous task) /// </summary> /// <param name="task"></param> public void MoveTaskUp(TaskToDo task) { int currentIndex = _allTasks.IndexOf(task); if (currentIndex < 1) { return; } _allTasks.RemoveAt(currentIndex); _allTasks.Insert(currentIndex - 1, task); }
/// <summary> /// Move a given task down (swap it with next task) /// </summary> /// <param name="task"></param> public void MoveTaskDown(TaskToDo task) { int currentIndex = _allTasks.IndexOf(task); int lastIndex = _allTasks.Count - 1; if (currentIndex < 0 || currentIndex >= lastIndex) { return; } _allTasks.RemoveAt(currentIndex); _allTasks.Insert(currentIndex + 1, task); }
/// <summary> /// returns the taskTodo that is just after the current task at the same level /// returns null if there is no next brother /// </summary> /// <returns></returns> public TaskToDo NextBrother() { TaskToDo result = null; if (_parentTask != null) { int currentIndex = _parentTask._subtasks.IndexOf(this); if (currentIndex < (_parentTask._subtasks.Count - 1)) { result = _parentTask._subtasks[currentIndex + 1]; } } return(result); }
/// <summary> /// returns the taskTodo that is just before the current task at the same level /// returns null if there is no previous brother /// </summary> /// <returns></returns> public TaskToDo PreviousBrother() { TaskToDo result = null; if (_parentTask != null) { int currentIndex = _parentTask._subtasks.IndexOf(this); if (currentIndex > 0) { result = _parentTask._subtasks[currentIndex - 1]; } } return(result); }
/// <summary> /// Count recursively the number of terminal tasks and the number of terminal tasks that are completed /// </summary> /// <param name="task"></param> private void RecursiveUpdate(TaskToDo task) { if (task.IsTerminalTask) { _totalNbTerminalTasks++; if (task.IsCompleted) { _totalNbCompletedTasks++; } } else { foreach (TaskToDo subtask in task.Subtasks) { RecursiveUpdate(subtask); } } }
/// <summary> /// write /// </summary> /// <param name="task"></param> /// <param name="indentationLevel"></param> /// <param name="strToWrite"></param> private void RecursiveTaskToString(TaskToDo task, int indentationLevel, StringBuilder strToWrite) { //write name strToWrite.AppendLine(String.Format("{0}- {1}{2}", new String('\t', indentationLevel), task.Name, task.IsCompleted ? " [OK]" : String.Empty)); //write description (possibly on several lines) if (!String.IsNullOrEmpty(task.Description)) { String[] lines = task.Description.SplitLines(false); foreach (String line in lines) { strToWrite.AppendLine(new string('\t', indentationLevel + 1) + line); } //add a blank line beetween description and subtasks strToWrite.AppendLine(); } //write subtasks foreach (TaskToDo subtask in task.Subtasks) { RecursiveTaskToString(subtask, indentationLevel + 1, strToWrite); } }
/// <summary> /// update the current progression from a given task /// </summary> /// <param name="task"></param> public void UpdateFromTask(TaskToDo task) { _totalNbCompletedTasks = 0; _totalNbTerminalTasks = 0; RecursiveUpdate(task); }
/// <summary> /// Constructor from a given task /// </summary> public TaskProgression(TaskToDo task) { UpdateFromTask(task); }
/// <summary> /// parse a string and convert it into a task /// </summary> /// <param name="strToParse"></param> /// <returns></returns> public TaskToDo StringToTask(String strToParse) { TaskToDo rootTask = null; TaskToDo lastTask = null; int lastIndentation = 0; Stack <Tuple <int, TaskToDo> > stack = new Stack <Tuple <int, TaskToDo> >(); String[] lines = strToParse.SplitLines(true); foreach (String line in lines) { int indentation = 0; String content; LineType type = ParseLine(line, out indentation, out content); switch (type) { case LineType.Empty: //ignore empty line break; case LineType.Comment: //comment, ignore it break; case LineType.Description: //Description of the current task (could be on several lines) if (lastTask != null) //if there is no current task, the description is ignored { if (!String.IsNullOrEmpty(lastTask.Description)) { lastTask.Description += Environment.NewLine; } lastTask.Description += content; } break; case LineType.Task: //task or subtask TaskToDo newTask = ParseTaskLine(content); if (lastTask == null) //this is the first task, use it as root { rootTask = newTask; lastTask = newTask; } else //not the first task, insert it in the existing tree { if (indentation > lastIndentation) //newTask is a subtask of last task { lastTask.AddSubtask(newTask); } else //we have to go back in tree to find a parent { Boolean parentFound = false; while (!parentFound) { if (stack.Count == 0) { throw new ApplicationException("Indentation smaller than root level"); } var upperLevel = stack.Peek(); if (upperLevel.Item1 < indentation) //parent found { parentFound = true; upperLevel.Item2.AddSubtask(newTask); } else //not found yet { stack.Pop(); //go to level up } } //end of search for parents } //end of "Not subtask of last task } //end of "Not root" //save state for next loop stack.Push(new Tuple <int, TaskToDo>(indentation, newTask)); lastTask = newTask; lastIndentation = indentation; break; default: //ignore all other lines break; } } //end foreach line return(rootTask); }
/// <summary> /// parse a task line and returns the corresponding task including its attributes /// </summary> /// <param name="contentToParse"></param> /// <returns></returns> private TaskToDo ParseTaskLine(String contentToParse) { //remove (and check) the '-' that starts the line if (!contentToParse.StartsWith("-")) { throw new ApplicationException("Task line must begin with a -"); } //browse the string to separate the name and all attributes (between []) StringBuilder sbName = new StringBuilder(); List <String> attributes = new List <string>(); StringBuilder sbCurrentAttribute = new StringBuilder(); Boolean inAttribute = false; for (int i = 1; i < contentToParse.Length; i++) //starts from 1 to ignore the '-' { char c = contentToParse[i]; switch (c) { case '[': //starts a new attribute if (inAttribute) { throw new ApplicationException("Recursive attribute ([[)"); //already in an attribute, not managed } inAttribute = true; sbCurrentAttribute.Clear(); break; case ']': //ends an attribute if (!inAttribute) { throw new ApplicationException("End of attribute without begin (])"); } attributes.Add(sbCurrentAttribute.ToString()); sbCurrentAttribute.Clear(); inAttribute = false; break; default: //any other character if (inAttribute) { sbCurrentAttribute.Append(c); } else { sbName.Append(c); } break; } } //check we have not begun an attribute and is not finished if (inAttribute) { throw new ApplicationException("Attribute started but without end ([)"); } //create the task with trimmed name TaskToDo result = new TaskToDo(sbName.ToString().Trim(), String.Empty); //check each attribute foreach (String attribute in attributes) { if (attribute == "OK") { result.SetCompleted(); } else { throw new ApplicationException("Unknwon attribute : " + attribute); } } return(result); }
/// <summary> /// returns true if a given task can be moved up /// </summary> /// <param name="task"></param> /// <returns></returns> public Boolean CanMoveTaskUp(TaskToDo task) { int index = _allTasks.IndexOf(task); return(index > 0); //case where index is -1 is integrated in this condition }
/// <summary> /// Create a task without subtasks /// </summary> /// <param name="name"></param> /// <param name="description"></param> /// <returns></returns> public static TaskToDo CreateTaskWithoutChild(String name, string description) { TaskToDo result = new TaskToDo(name, description); return(result); }