package design.model.undo; import java.util.ArrayDeque; import java.util.Deque; import java.util.List; import java.util.stream.Collectors; // Singleton caretaker that handles undo and redo stacks. public final class UndoManager { private static final UndoManager INSTANCE = new UndoManager(); public static UndoManager instance() { return INSTANCE; } private UndoManager() { } // Stack entry private static class Entry { final Originator originator; final Memento memento; final String label; Entry(Originator originator, Memento memento, String label) { this.originator = originator; this.memento = memento; this.label = label; } } private final Deque undoStack = new ArrayDeque<>(); private final Deque redoStack = new ArrayDeque<>(); // Capture state public void capture(Originator originator, String label) { undoStack.push(new Entry(originator, originator.createMemento(), label)); redoStack.clear(); } // Undo / Redo ops public boolean canUndo() { return !undoStack.isEmpty(); } public boolean canRedo() { return !redoStack.isEmpty(); } public String peekUndoLabel() { return canUndo() ? undoStack.peek().label : null; } public String peekRedoLabel() { return canRedo() ? redoStack.peek().label : null; } public List getUndoHistoryLabels() { return undoStack.stream() .map(e -> e.label) .collect(Collectors.toList()); } public void undo() { if (!canUndo()) { System.out.println("Nothing to undo."); return; } Entry entry = undoStack.pop(); redoStack.push(new Entry(entry.originator, entry.originator.createMemento(), entry.label)); entry.originator.restore(entry.memento); } public void redo() { if (!canRedo()) { System.out.println("Nothing to redo."); return; } Entry entry = redoStack.pop(); undoStack.push(new Entry(entry.originator, entry.originator.createMemento(), entry.label)); entry.originator.restore(entry.memento); } public void purge() { undoStack.clear(); redoStack.clear(); } }