package todo; import java.util.ArrayList; import java.util.List; /** * Represents a list of named items on a To-Do list. The items are stored in * order. New items can only be added at the end. Existing items can be * completed. Completed items are removed after a little delay. *

* Think of this as a list of pairs (name, completed), where the latter is a * boolean value. */ public class TodoList { // AF: list with the given item names and completed = true if completedAt is // a non-negative number private final List items = new ArrayList<>(); private final List completedAt = new ArrayList<>(); private final int delaySec; // RI: delaySec is positive // RI: the two lists have the same length and contain no nulls // Note that the list may contain items that were completed more than // delaySec ago, which are no longer in the abstract state and should not be // shown. We could keep these forever and just not show them. However, we // will instead prune them out before returning the state to the client. (See // removeStaleItems below, which does this.) /** * Creates an empty to-do list where items are removed delaySec seconds after * they are completed. */ public TodoList(int delaySec) { assert delaySec >= 0; this.delaySec = delaySec; } /** * @return A description of all the items, with each one on its own line and * the text "\t(completed)" if it is completed. */ public String describe() { removeStaleItems(); // NOTE: doesn't change abstract state StringBuilder buf = new StringBuilder(); for (int i = 0; i < items.size(); i++) { if (i > 0) buf.append("\n"); buf.append(items.get(i)); if (completedAt.get(i) > 0) buf.append("\t(completed)"); } return buf.toString(); } /** @return Whether there is already an item with the given name. */ public boolean has(String name) { return items.indexOf(name) >= 0; } /** * @requires There is not already an item with this name. * @modifies this * @effects Adds an uncompleted item with the given name (at the end). */ public void add(String name) { assert name != null; assert !has(name); items.add(name); completedAt.add(-1L); } /** * @requires There is an item with this name. * @modifies this * @effects Marks the given item completed at this time. */ public void completed(String name) { int index = items.indexOf(name); assert index >= 0; completedAt.set(index, System.currentTimeMillis()); } // Removes any items that are no longer displayed in the list because they // were completed more than delaySec second ago. private void removeStaleItems() { long removeBefore = System.currentTimeMillis() - delaySec * 1000; // Inv: no stale items at index i+1 or later for (int i = items.size() - 1; i >= 0; i--) { if (0 <= completedAt.get(i) && completedAt.get(i) < removeBefore) { items.remove(i); completedAt.remove(i); } } } }