diff options
Diffstat (limited to 'src/main/java/design/model')
| -rw-r--r-- | src/main/java/design/model/Golfer.java | 51 | ||||
| -rw-r--r-- | src/main/java/design/model/holeplay/HolePlayContext.java | 61 | ||||
| -rw-r--r-- | src/main/java/design/model/undo/Memento.java | 4 | ||||
| -rw-r--r-- | src/main/java/design/model/undo/Originator.java | 7 | ||||
| -rw-r--r-- | src/main/java/design/model/undo/UndoManager.java | 91 |
5 files changed, 212 insertions, 2 deletions
diff --git a/src/main/java/design/model/Golfer.java b/src/main/java/design/model/Golfer.java index 48daae8..0f4c914 100644 --- a/src/main/java/design/model/Golfer.java +++ b/src/main/java/design/model/Golfer.java @@ -6,8 +6,11 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import java.util.ArrayList; import java.util.List; +import design.model.undo.Memento; +import design.model.undo.Originator; + @JsonPropertyOrder({ "clubs", "nextClubId" }) -public class Golfer { +public class Golfer implements Originator { private String username; private int passwordHash; private String fullName; @@ -112,4 +115,50 @@ public class Golfer { public void removeClub(Club c) { clubs.remove(c); } + + // Takes a snapshot of the golfer + private static class GolferMemento implements Memento { + private final String username; + private final int passwordHash; + private final String fullName; + private final List<Course> courses; + private final List<Round> rounds; + private final List<Club> clubs; + private final int nextClubId; + + GolferMemento(Golfer g) { + this.username = g.username; + this.passwordHash = g.passwordHash; + this.fullName = g.fullName; + this.courses = new ArrayList<>(g.courses); + this.rounds = new ArrayList<>(g.rounds); + this.clubs = new ArrayList<>(g.clubs); + this.nextClubId = g.nextClubId; + } + } + + @Override + public Memento createMemento() { + return new GolferMemento(this); + } + + @Override + public void restore(Memento memento) { + GolferMemento gm = (GolferMemento) memento; + + this.username = gm.username; + this.passwordHash = gm.passwordHash; + this.fullName = gm.fullName; + + this.courses.clear(); + this.courses.addAll(gm.courses); + + this.rounds.clear(); + this.rounds.addAll(gm.rounds); + + this.clubs.clear(); + this.clubs.addAll(gm.clubs); + + this.nextClubId = gm.nextClubId; + } } diff --git a/src/main/java/design/model/holeplay/HolePlayContext.java b/src/main/java/design/model/holeplay/HolePlayContext.java index 7a5d8ef..71fba32 100644 --- a/src/main/java/design/model/holeplay/HolePlayContext.java +++ b/src/main/java/design/model/holeplay/HolePlayContext.java @@ -6,7 +6,10 @@ import java.util.ArrayList; import design.model.*; import design.persistence.PersonalDatabase; -public class HolePlayContext { +import design.model.undo.Memento; +import design.model.undo.Originator; + +public class HolePlayContext implements Originator { private final Golfer golfer; private final Round round; private final PersonalDatabase pdb; @@ -109,4 +112,60 @@ public class HolePlayContext { void setStrokes(int s) { this.strokes = s; } + + // Captures current State, current play (hole# + swings), and stroke counter + private static class HolePlayMemento implements Memento { + private final String stateName; + private final Play currentPlayCopy; + private final int strokesCopy; + + HolePlayMemento(HolePlayContext ctx) { + this.stateName = ctx.state.name(); + + // Copy currentPlay if it exists + Play src = ctx.currentPlay; + if (src != null) { + var copySwings = new ArrayList<Swing>(); + for (Swing s : src.getSwings()) { + copySwings.add(new Swing(s.getDistance(), s.getClubUsed())); + } + this.currentPlayCopy = new Play(src.getHoleNumber(), copySwings); + } else { + this.currentPlayCopy = null; + } + + this.strokesCopy = ctx.strokes; + } + } + + @Override + public Memento createMemento() { + return new HolePlayMemento(this); + } + + @Override + public void restore(Memento m) { + HolePlayMemento hm = (HolePlayMemento) m; + + // restore strokes + this.strokes = hm.strokesCopy; + + // restore currentPlay + if (hm.currentPlayCopy == null) { + this.currentPlay = null; + } else { + var copySwings = new ArrayList<Swing>(); + for (Swing s : hm.currentPlayCopy.getSwings()) { + copySwings.add(new Swing(s.getDistance(), s.getClubUsed())); + } + this.currentPlay = new Play(hm.currentPlayCopy.getHoleNumber(), copySwings); + } + + // restore state based on name + switch (hm.stateName) { + case "SetupState" -> this.state = new SetupState(); + case "PlayState" -> this.state = new PlayState(); + case "HoleCompleteState" -> this.state = new HoleCompleteState(); + } + } }
\ No newline at end of file diff --git a/src/main/java/design/model/undo/Memento.java b/src/main/java/design/model/undo/Memento.java new file mode 100644 index 0000000..162c3f3 --- /dev/null +++ b/src/main/java/design/model/undo/Memento.java @@ -0,0 +1,4 @@ +package design.model.undo; + +public interface Memento { +} diff --git a/src/main/java/design/model/undo/Originator.java b/src/main/java/design/model/undo/Originator.java new file mode 100644 index 0000000..63eac38 --- /dev/null +++ b/src/main/java/design/model/undo/Originator.java @@ -0,0 +1,7 @@ +package design.model.undo; + +public interface Originator { + Memento createMemento(); + + void restore(Memento memento); +}
\ No newline at end of file diff --git a/src/main/java/design/model/undo/UndoManager.java b/src/main/java/design/model/undo/UndoManager.java new file mode 100644 index 0000000..5c6a4a0 --- /dev/null +++ b/src/main/java/design/model/undo/UndoManager.java @@ -0,0 +1,91 @@ +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<Entry> undoStack = new ArrayDeque<>(); + private final Deque<Entry> 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<String> 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(); + } +} |
