summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorsowgro <tpoke.ferrari@gmail.com>2025-11-16 02:01:58 -0500
committersowgro <tpoke.ferrari@gmail.com>2025-11-16 02:01:58 -0500
commit969b8071dd799dfaed939e10bf77b8a1ad42f9dc (patch)
tree214016dc742a155512fbb2f851cf589509f3b361 /src/main
parentcf59d52cba70742f1d4098c38b4c7a798b3d89fa (diff)
parent43530df067b1132b944e9619bdf60b72264829ec (diff)
downloaddesignproject-design-6-969b8071dd799dfaed939e10bf77b8a1ad42f9dc.tar.gz
designproject-design-6-969b8071dd799dfaed939e10bf77b8a1ad42f9dc.tar.bz2
designproject-design-6-969b8071dd799dfaed939e10bf77b8a1ad42f9dc.zip
Merge branch 'main' into import-exportimport-export
# Conflicts: # data/personaldb.json # src/main/java/design/controller/userinput/menus/MainMenu.java # src/main/java/design/persistence/JSONLeagueDatabase.java # src/main/java/design/persistence/JSONPersonalDatabase.java # src/test/java/design/model/ClubTest.java # src/test/java/design/model/GolferTest.java
Diffstat (limited to '')
-rw-r--r--src/main/java/design/controller/userinput/UndoActions.java51
-rw-r--r--src/main/java/design/controller/userinput/menus/CourseSearch.java8
-rw-r--r--src/main/java/design/controller/userinput/menus/HolePlayMenu.java46
-rw-r--r--src/main/java/design/controller/userinput/menus/LeageMenu.java30
-rw-r--r--src/main/java/design/controller/userinput/menus/MainMenu.java39
-rw-r--r--src/main/java/design/controller/userinput/menus/ManageClubs.java9
-rw-r--r--src/main/java/design/controller/userinput/menus/ManageCourses.java7
-rw-r--r--src/main/java/design/controller/userinput/menus/SelectLeague.java83
-rw-r--r--src/main/java/design/controller/userinput/menus/SelectUser.java8
-rw-r--r--src/main/java/design/controller/userinput/menus/UserSettings.java58
-rw-r--r--src/main/java/design/model/Golfer.java58
-rw-r--r--src/main/java/design/model/League.java30
-rw-r--r--src/main/java/design/model/ScrambleLeague.java20
-rw-r--r--src/main/java/design/model/StrokeLeague.java10
-rw-r--r--src/main/java/design/model/holeplay/HolePlayContext.java61
-rw-r--r--src/main/java/design/model/undo/Memento.java4
-rw-r--r--src/main/java/design/model/undo/Originator.java7
-rw-r--r--src/main/java/design/model/undo/UndoManager.java91
-rw-r--r--src/main/java/design/runtime/Session.java11
19 files changed, 560 insertions, 71 deletions
diff --git a/src/main/java/design/controller/userinput/UndoActions.java b/src/main/java/design/controller/userinput/UndoActions.java
new file mode 100644
index 0000000..6417284
--- /dev/null
+++ b/src/main/java/design/controller/userinput/UndoActions.java
@@ -0,0 +1,51 @@
+package design.controller.userinput;
+
+import design.model.undo.UndoManager;
+import design.persistence.PersonalDatabase;
+import design.runtime.Session;
+import java.io.IOException;
+
+public final class UndoActions {
+
+ private UndoActions() {
+ }
+
+ public static void undoWithSave() {
+ UndoManager um = UndoManager.instance();
+ if (!um.canUndo()) {
+ System.out.println("Nothing to undo.");
+ return;
+ }
+
+ String label = um.peekUndoLabel();
+ um.undo();
+ System.out.println("Undo: " + label);
+
+ saveCurrentGolfer();
+ }
+
+ public static void redoWithSave() {
+ UndoManager um = UndoManager.instance();
+ if (!um.canRedo()) {
+ System.out.println("Nothing to redo.");
+ return;
+ }
+
+ String label = um.peekRedoLabel();
+ um.redo();
+ System.out.println("Redo: " + label);
+
+ saveCurrentGolfer();
+ }
+
+ private static void saveCurrentGolfer() {
+ try {
+ var g = Session.getCurrentGolfer();
+ if (g != null) {
+ PersonalDatabase.instance().updateGolfer(g);
+ }
+ } catch (IOException e) {
+ System.err.println("Failed to save after undo/redo: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/design/controller/userinput/menus/CourseSearch.java b/src/main/java/design/controller/userinput/menus/CourseSearch.java
index b84247a..4dd2cad 100644
--- a/src/main/java/design/controller/userinput/menus/CourseSearch.java
+++ b/src/main/java/design/controller/userinput/menus/CourseSearch.java
@@ -9,6 +9,7 @@ import design.model.course_search.CurrentSearchQuery;
import design.model.course_search.ICourse;
import design.persistence.PersonalDatabase;
import design.runtime.Session;
+import design.model.undo.UndoManager;
import java.io.IOException;
import java.util.ArrayList;
@@ -71,7 +72,7 @@ public class CourseSearch extends Menu {
}
// recursively go through tree structure of courselist to make menu options.
- // this is all for displaying the menu options, not the actual sorting.
+ // this is all for displaying the menu options, not the actual sorting.
private void addCoursesRecursive(List<MenuOption> menuOptions, CourseList list) {
for (ICourse icourse : list.getCourses()) {
// if we find a leaf (course), display it as a menu option
@@ -79,6 +80,8 @@ public class CourseSearch extends Menu {
var name = String.format("%s, %s, Difficulty: %s, %s holes, %s total par",
c.getName(), c.getLocation(), c.getDifficultyRating(), c.getHoleCount(), c.getTotalPar());
menuOptions.add(new MenuOption(name, () -> {
+ UndoManager.instance().capture(golfer, "Add course " + c.getName());
+
// add the course, try to save to DB.
golfer.addCourse(c);
try {
@@ -89,8 +92,7 @@ public class CourseSearch extends Menu {
System.out.println("\n Course added to profile. \n");
new MainMenu().present();
- }
- ));
+ }));
}
// if not, we need to traverse another courselist
else if (icourse instanceof CourseList sublist) {
diff --git a/src/main/java/design/controller/userinput/menus/HolePlayMenu.java b/src/main/java/design/controller/userinput/menus/HolePlayMenu.java
index e0d1572..6651500 100644
--- a/src/main/java/design/controller/userinput/menus/HolePlayMenu.java
+++ b/src/main/java/design/controller/userinput/menus/HolePlayMenu.java
@@ -3,12 +3,15 @@ package design.controller.userinput.menus;
import design.controller.userinput.Menu;
import design.controller.userinput.MenuOption;
import design.model.Course;
+import design.model.Golfer;
import design.model.Hole;
import design.runtime.Session;
import design.model.Club;
import design.model.Round;
import design.model.holeplay.HolePlayContext;
import design.persistence.PersonalDatabase;
+import design.model.undo.UndoManager;
+import design.controller.userinput.UndoActions;
import java.util.ArrayList;
import java.util.List;
@@ -18,10 +21,11 @@ public class HolePlayMenu extends Menu {
private final Round round;
private final HolePlayContext ctx;
+ private final Golfer golfer = Session.getCurrentGolfer();
public HolePlayMenu() {
this.round = createRound();
- this.ctx = new HolePlayContext(Session.getCurrentGolfer(), round, PersonalDatabase.instance());
+ this.ctx = new HolePlayContext(this.golfer, round, PersonalDatabase.instance());
}
@Override
@@ -34,17 +38,37 @@ public class HolePlayMenu extends Menu {
public List<MenuOption> getMenuOptions() {
List<MenuOption> opts = new ArrayList<>();
+ // End round (always shown) 1
+ opts.add(new MenuOption("end round", () -> {
+ UndoManager.instance().capture(golfer, "End round on " + round.getCourse().getName());
+ ctx.endRoundNow();
+ System.out.println("Round ended.");
+ new MainMenu().present();
+ }));
+
+ // 2
+ opts.add(new MenuOption("undo", () -> {
+ UndoActions.undoWithSave();
+ this.present();
+ }));
+
+ // 3
+ opts.add(new MenuOption("redo", () -> {
+ UndoActions.redoWithSave();
+ this.present();
+ }));
+
boolean inSetup = (ctx.getCurrentPlay() == null);
if (inSetup) {
- // 0) Start hole
+ // 4) Start hole
opts.add(new MenuOption("start hole", () -> {
ctx.startHole();
System.out.println("Started hole " + round.getCurrentHole().getNumber() + ".");
this.present();
}));
} else {
- // 0) Take a shot
+ // 4) Take a shot
opts.add(new MenuOption("take a shot", () -> {
var selector = new SelectClub();
selector.present();
@@ -60,6 +84,8 @@ public class HolePlayMenu extends Menu {
dist = Integer.parseInt(ds);
}
+ UndoManager.instance().capture(ctx, "Shot with " + club.getNickname() + " for " + dist + " yds");
+
// Records shot
ctx.recordShot(club, dist);
System.out.println("Shot recorded: " + dist + " yds with " + club.getNickname());
@@ -67,23 +93,19 @@ public class HolePlayMenu extends Menu {
this.present();
}));
- // 1) Hole out
+ // 5) Hole out
opts.add(new MenuOption("hole out", () -> {
// Precedes to next hole
int prev = round.getCurrentHole().getNumber();
+
+ UndoManager.instance().capture(golfer, "Hole out on " + prev + " at " + round.getCourse().getName());
+
ctx.holeOut();
System.out.println("Holed out on " + prev + ". Next: " + round.getCurrentHole().getNumber());
this.present();
}));
}
- // End round (always shown)
- opts.add(new MenuOption("end round", () -> {
- ctx.endRoundNow();
- System.out.println("Round ended.");
- new MainMenu().present();
- }));
-
return opts;
}
@@ -118,6 +140,8 @@ public class HolePlayMenu extends Menu {
// Starts round and sends user to HolePlayMenu
Hole startHole = course.getHoles().get(startHoleNum - 1);
Round r = new Round(course, Session.getDateTime(), startHole);
+ // Undo puts golfer back to before round existed.
+ UndoManager.instance().capture(golfer, "Start round on " + course.getName() + " (hole " + startHoleNum + ")");
golfer.addRound(r);
return r;
}
diff --git a/src/main/java/design/controller/userinput/menus/LeageMenu.java b/src/main/java/design/controller/userinput/menus/LeageMenu.java
new file mode 100644
index 0000000..d9afa91
--- /dev/null
+++ b/src/main/java/design/controller/userinput/menus/LeageMenu.java
@@ -0,0 +1,30 @@
+package design.controller.userinput.menus;
+
+import design.controller.userinput.Menu;
+import design.controller.userinput.MenuOption;
+import design.model.League;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LeageMenu extends Menu {
+ private final League league;
+
+ public LeageMenu(League league) {
+ this.league = league;
+ }
+
+ @Override
+ public String getTitle() {
+ return "League Menu";
+ }
+
+ @Override
+ public List<MenuOption> getMenuOptions() {
+ List<MenuOption> options = new ArrayList<>();
+ options.add(new MenuOption("statistics...", () -> {}));
+ options.add(new MenuOption("leaderboard...", () -> {}));
+ options.add(new MenuOption("log round...", () -> {}));
+ return options;
+ }
+}
diff --git a/src/main/java/design/controller/userinput/menus/MainMenu.java b/src/main/java/design/controller/userinput/menus/MainMenu.java
index 9d5204f..0c42422 100644
--- a/src/main/java/design/controller/userinput/menus/MainMenu.java
+++ b/src/main/java/design/controller/userinput/menus/MainMenu.java
@@ -2,6 +2,8 @@ package design.controller.userinput.menus;
import design.controller.userinput.Menu;
import design.controller.userinput.MenuOption;
+import design.controller.userinput.UndoActions;
+import design.runtime.Session;
import java.util.List;
@@ -13,14 +15,33 @@ public class MainMenu extends Menu {
@Override
public List<MenuOption> getMenuOptions() {
- return List.of(
- new MenuOption("quit", () -> System.exit(0)),
- new MenuOption("user settings...", () -> new UserSettings().present()),
- new MenuOption("manage courses...", () -> new ManageCourses().present()),
- new MenuOption("manage clubs...", () -> new ManageClubs().present()),
- new MenuOption("statistics...", () -> new StatisticsMenu().present()),
- new MenuOption("log round...", () -> new HolePlayMenu().present()),
- new MenuOption("manage data...", () -> new ImportExportMenu().present())
- );
+ boolean guest = Session.isGuest();
+ if (guest) {
+ return List.of(
+ new MenuOption("quit", () -> System.exit(0)),
+ new MenuOption("guest settings...", () -> new UserSettings().present()),
+ new MenuOption("browse courses...", () -> new SearchMenu().present())
+
+ );
+ } else {
+ return List.of(
+ new MenuOption("quit", () -> System.exit(0)),
+ new MenuOption("undo", () -> {
+ UndoActions.undoWithSave();
+ this.present();
+ }),
+ new MenuOption("redo", () -> {
+ UndoActions.redoWithSave();
+ this.present();
+ }),
+ new MenuOption("user settings...", () -> new UserSettings().present()),
+ new MenuOption("manage courses...", () -> new ManageCourses().present()),
+ new MenuOption("manage clubs...", () -> new ManageClubs().present()),
+ new MenuOption("statistics...", () -> new StatisticsMenu().present()),
+ new MenuOption("log round...", () -> new HolePlayMenu().present()),
+ new MenuOption("League play...", () -> new SelectLeague().present()),
+ new MenuOption("manage data...", () -> new ImportExportMenu().present())
+ );
+ }
}
}
diff --git a/src/main/java/design/controller/userinput/menus/ManageClubs.java b/src/main/java/design/controller/userinput/menus/ManageClubs.java
index 6b6811b..9c23433 100644
--- a/src/main/java/design/controller/userinput/menus/ManageClubs.java
+++ b/src/main/java/design/controller/userinput/menus/ManageClubs.java
@@ -6,6 +6,7 @@ import design.model.Club;
import design.model.Golfer;
import design.persistence.PersonalDatabase;
import design.runtime.Session;
+import design.model.undo.UndoManager;
import java.io.IOException;
import java.util.Arrays;
@@ -36,7 +37,11 @@ public class ManageClubs extends Menu {
opts.add(new MenuOption("remove club...", () -> {
var selector = new SelectClub();
selector.present();
- golfer.removeClub(selector.getResult());
+ Club toRemove = selector.getResult();
+
+ UndoManager.instance().capture(golfer, "Remove club " + toRemove);
+
+ golfer.removeClub(toRemove);
try {
personalDB.updateGolfer(golfer);
} catch (IOException ex) {
@@ -46,6 +51,8 @@ public class ManageClubs extends Menu {
}));
opts.add(new MenuOption("add club...", () -> {
+ UndoManager.instance().capture(golfer, "Add club");
+
Scanner sc = new Scanner(System.in);
System.out.print("Manufacturer: ");
diff --git a/src/main/java/design/controller/userinput/menus/ManageCourses.java b/src/main/java/design/controller/userinput/menus/ManageCourses.java
index b592cd2..4f3efe5 100644
--- a/src/main/java/design/controller/userinput/menus/ManageCourses.java
+++ b/src/main/java/design/controller/userinput/menus/ManageCourses.java
@@ -6,6 +6,7 @@ import design.model.Course;
import design.model.Golfer;
import design.persistence.PersonalDatabase;
import design.runtime.Session;
+import design.model.undo.UndoManager;
import java.io.IOException;
import java.util.List;
@@ -34,7 +35,11 @@ public class ManageCourses extends Menu {
opts.add(new MenuOption("remove course...", () -> {
var selector = new SelectCourse();
selector.present();
- golfer.removeCourse(selector.getResult());
+ Course course = selector.getResult();
+
+ UndoManager.instance().capture(golfer, "Remove course " + course.getName());
+
+ golfer.removeCourse(course);
try {
personalDB.updateGolfer(golfer);
} catch (IOException ex) {
diff --git a/src/main/java/design/controller/userinput/menus/SelectLeague.java b/src/main/java/design/controller/userinput/menus/SelectLeague.java
new file mode 100644
index 0000000..08f6c91
--- /dev/null
+++ b/src/main/java/design/controller/userinput/menus/SelectLeague.java
@@ -0,0 +1,83 @@
+package design.controller.userinput.menus;
+
+import design.controller.userinput.Menu;
+import design.controller.userinput.MenuOption;
+import design.model.Golfer;
+import design.model.League;
+import design.model.ScrambleLeague;
+import design.model.StrokeLeague;
+import design.persistence.LeagueDatabase;
+import design.runtime.Session;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class SelectLeague extends Menu {
+ private final Golfer golfer = Session.getCurrentGolfer();
+
+ @Override
+ public String getTitle() {
+ return "Select League";
+ }
+
+ @Override
+ public List<MenuOption> getMenuOptions() {
+ List<MenuOption> options = new ArrayList<>();
+ for (League l : LeagueDatabase.instance().getLeagues()) {
+ options.add(new MenuOption(l.getName(), () -> {
+ if (l instanceof ScrambleLeague sl && sl.locateTeam(golfer) == null) {
+ Scanner sc = new Scanner(System.in);
+ System.out.print("You are not a member of a team for this league. Would you like to create one (Y/n): ");
+ if (sc.next().equals("n")) {
+ new MainMenu().present();
+ } else {
+ // create team
+ }
+ } else {
+ new LeageMenu(l).present();
+ }
+ }));
+ }
+ options.add(new MenuOption("<create>", this::createLeagueWizard));
+ return options;
+ }
+
+ private void createLeagueWizard() {
+ try {
+ Scanner scanner = new Scanner(System.in);
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+
+ System.out.print("Enter league name: ");
+ String name = scanner.nextLine();
+
+ System.out.print("Enter registration date (yyyy-MM-dd): ");
+ Date registrationDate = dateFormat.parse(scanner.nextLine());
+
+ System.out.print("Enter start date (yyyy-MM-dd): ");
+ Date startDate = dateFormat.parse(scanner.nextLine());
+
+ System.out.print("Enter end date (yyyy-MM-dd): ");
+ Date endDate = dateFormat.parse(scanner.nextLine());
+
+ Golfer owner = Session.getCurrentGolfer();
+
+ System.out.print("Enter format (stroke/scramble): ");
+ String scramble = scanner.nextLine();
+
+ League league;
+ switch (scramble) {
+ case "scramble" -> league = new ScrambleLeague(name, registrationDate, startDate, endDate, owner);
+ case "stroke" -> league = new StrokeLeague(name, registrationDate, startDate, endDate, owner);
+ default -> throw new RuntimeException();
+ }
+ LeagueDatabase.instance().addLeague(league);
+ } catch (ParseException ex) {
+ System.out.println("Invalid format");
+ } catch (IOException ex) {
+ System.out.println("Error writing to file");
+ }
+ this.present();
+ }
+}
diff --git a/src/main/java/design/controller/userinput/menus/SelectUser.java b/src/main/java/design/controller/userinput/menus/SelectUser.java
index 4b7928c..6931a4b 100644
--- a/src/main/java/design/controller/userinput/menus/SelectUser.java
+++ b/src/main/java/design/controller/userinput/menus/SelectUser.java
@@ -6,6 +6,7 @@ import design.controller.userinput.MenuOption;
import design.model.Golfer;
import design.runtime.Session;
import design.persistence.PersonalDatabase;
+import design.model.undo.UndoManager;
import java.io.IOException;
import java.util.ArrayList;
@@ -52,6 +53,12 @@ public class SelectUser extends Menu {
this.present();
}));
+ opts.add(new MenuOption("<guest login>", () -> {
+ Session.setCurrentGolfer(null);
+ Session.setGuest(true);
+ doAfter.onAction();
+ }));
+
return opts;
}
@@ -61,6 +68,7 @@ public class SelectUser extends Menu {
if (g.checkPassword(password)) {
// Sets user in Session too
Session.setCurrentGolfer(g);
+ UndoManager.instance().purge();
System.out.printf("Loaded user: %s%n", g);
doAfter.onAction();
} else {
diff --git a/src/main/java/design/controller/userinput/menus/UserSettings.java b/src/main/java/design/controller/userinput/menus/UserSettings.java
index 47b0a5b..1b2082d 100644
--- a/src/main/java/design/controller/userinput/menus/UserSettings.java
+++ b/src/main/java/design/controller/userinput/menus/UserSettings.java
@@ -12,6 +12,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
+import design.model.undo.UndoManager;
+
public class UserSettings extends Menu {
private final Golfer golfer = Session.getCurrentGolfer();
@@ -25,6 +27,8 @@ public class UserSettings extends Menu {
@Override
public List<MenuOption> getMenuOptions() {
+ boolean guest = Session.isGuest();
+
var opts = new ArrayList<MenuOption>();
opts.add(new MenuOption("return to main menu", () -> new MainMenu().present()));
@@ -34,31 +38,37 @@ public class UserSettings extends Menu {
new SelectUser(doAfter).present();
}));
- opts.add(new MenuOption("set password...", () -> {
- System.out.print("Enter password: ");
- var password = scanner.nextLine();
-
- golfer.setPassword(password);
- try {
- personalDatabase.updateGolfer(golfer);
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
- this.present();
- }));
+ if (!guest) {
+ opts.add(new MenuOption("set password...", () -> {
+ UndoManager.instance().capture(golfer, "Change password for @" + golfer.getUsername());
- opts.add(new MenuOption("set full name...", () -> {
- System.out.print("Enter full name: ");
- var fullName = scanner.nextLine();
-
- golfer.setFullName(fullName);
- try {
- personalDatabase.updateGolfer(golfer);
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
- this.present();
- }));
+ System.out.print("Enter password: ");
+ var password = scanner.nextLine();
+
+ golfer.setPassword(password);
+ try {
+ personalDatabase.updateGolfer(golfer);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ this.present();
+ }));
+
+ opts.add(new MenuOption("set full name...", () -> {
+ UndoManager.instance().capture(golfer, "Change full name for @" + golfer.getUsername());
+
+ System.out.print("Enter full name: ");
+ var fullName = scanner.nextLine();
+
+ golfer.setFullName(fullName);
+ try {
+ personalDatabase.updateGolfer(golfer);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ this.present();
+ }));
+ }
return opts;
}
diff --git a/src/main/java/design/model/Golfer.java b/src/main/java/design/model/Golfer.java
index 1c4e669..84f7396 100644
--- a/src/main/java/design/model/Golfer.java
+++ b/src/main/java/design/model/Golfer.java
@@ -7,8 +7,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;
@@ -16,20 +19,18 @@ public class Golfer {
private final List<Round> rounds;
private final List<Club> clubs; // Keep track of golfer's clubs
private int nextClubId;
- private final List<Invite> invites;
@JsonCreator
private Golfer(String username, int passwordHash, String fullName, List<Course> courses, List<Round> rounds,
- List<Club> clubs, List<Invite> invites) {
+ List<Club> clubs) {
this.username = username;
this.passwordHash = passwordHash;
this.fullName = fullName;
this.courses = courses;
this.rounds = rounds;
this.clubs = clubs;
- this.invites = invites != null ? invites : new ArrayList<>();
this.nextClubId = this.clubs.stream().mapToInt(Club::getId).max().orElse(0) + 1;
}
@@ -40,7 +41,6 @@ public class Golfer {
this.courses = new ArrayList<>();
this.rounds = new ArrayList<>();
this.clubs = new ArrayList<>();
- this.invites = new ArrayList<>();
this.nextClubId = 1;
}
@@ -93,8 +93,8 @@ public class Golfer {
}
public Club addClub(Club c) {
- c.setId(nextClubId++);
clubs.add(c);
+ c.setId(nextClubId++);
return c;
}
@@ -119,15 +119,49 @@ public class Golfer {
clubs.remove(c);
}
- public boolean addInvite(Invite invite) {
- return invites.add(invite);
+ // 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;
+ }
}
- public boolean removeInvite(Invite o) {
- return invites.remove(o);
+ @Override
+ public Memento createMemento() {
+ return new GolferMemento(this);
}
- public Invite[] getInvites() {
- return invites.toArray(Invite[]::new);
+ @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/League.java b/src/main/java/design/model/League.java
index 0252f89..881e25f 100644
--- a/src/main/java/design/model/League.java
+++ b/src/main/java/design/model/League.java
@@ -1,9 +1,22 @@
package design.model;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
+@JsonTypeInfo(
+ use = JsonTypeInfo.Id.NAME,
+ include = JsonTypeInfo.As.PROPERTY,
+ property = "type"
+)
+@JsonSubTypes({
+ @JsonSubTypes.Type(value = ScrambleLeague.class, name = "scramble"),
+ @JsonSubTypes.Type(value = StrokeLeague.class, name = "stroke")
+})
public abstract class League {
private int id;
private final String name;
@@ -11,15 +24,17 @@ public abstract class League {
private final Date startDate;
private final Date endDate;
private final Golfer owner;
+ private final List<Match> schedule;
@JsonCreator
- protected League(int id, String name, Date registrationDate, Date startDate, Date endDate, Golfer owner) {
+ protected League(int id, String name, Date registrationDate, Date startDate, Date endDate, Golfer owner, List<Match> schedule) {
this.id = id;
this.name = name;
this.registrationDate = registrationDate;
this.startDate = startDate;
this.endDate = endDate;
this.owner = owner;
+ this.schedule = schedule != null ? schedule : new ArrayList<>();
}
public League(String name, Date registrationDate, Date startDate, Date endDate, Golfer owner) {
@@ -29,6 +44,7 @@ public abstract class League {
this.startDate = startDate;
this.endDate = endDate;
this.owner = owner;
+ this.schedule = new ArrayList<>();
}
public int getId() {
@@ -55,6 +71,18 @@ public abstract class League {
return owner;
}
+ public Match[] getSchedule() {
+ return schedule.toArray(Match[]::new);
+ }
+
+ public void addMatchToSchedule(Match match) {
+ Date date = match.getDateScheduled();
+ if(date.after(endDate)){
+ throw new IllegalArgumentException("Cannot create match after league has ended");
+ }
+ schedule.add(match);
+ }
+
public void setId(int id) {
assert this.id == -1;
this.id = id;
diff --git a/src/main/java/design/model/ScrambleLeague.java b/src/main/java/design/model/ScrambleLeague.java
index 7ff966e..d372264 100644
--- a/src/main/java/design/model/ScrambleLeague.java
+++ b/src/main/java/design/model/ScrambleLeague.java
@@ -1,22 +1,25 @@
package design.model;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonTypeName;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.List;
+@JsonTypeName("scramble")
public class ScrambleLeague extends League {
private final List<Team> participants;
@JsonCreator
- private ScrambleLeague(int id, String name, Date registrationDate, Date startDate, Date endDate, Golfer owner, List<Team> participants) {
- super(id, name, registrationDate, startDate, endDate, owner);
+ private ScrambleLeague(int id, String name, Date registrationDate, Date startDate, Date endDate, Golfer owner, List<Team> participants, List<Match> schedule) {
+ super(id, name, registrationDate, startDate, endDate, owner, schedule);
this.participants = participants;
}
- public ScrambleLeague(int id, String name, Date registrationDate, Date startDate, Date endDate, Golfer owner) {
- super(id, name, registrationDate, startDate, endDate, owner);
+ public ScrambleLeague(String name, Date registrationDate, Date startDate, Date endDate, Golfer owner) {
+ super(name, registrationDate, startDate, endDate, owner);
this.participants = new ArrayList<>();
}
@@ -31,4 +34,13 @@ public class ScrambleLeague extends League {
public Team[] getParticipants() {
return participants.toArray(Team[]::new);
}
+
+ public Team locateTeam(Golfer golfer) {
+ for (Team participant : participants) {
+ if (List.of(participant.getMembers()).contains(golfer)) {
+ return participant;
+ }
+ }
+ return null;
+ }
}
diff --git a/src/main/java/design/model/StrokeLeague.java b/src/main/java/design/model/StrokeLeague.java
index 6bdde0a..2b787b5 100644
--- a/src/main/java/design/model/StrokeLeague.java
+++ b/src/main/java/design/model/StrokeLeague.java
@@ -1,22 +1,24 @@
package design.model;
import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonTypeName;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+@JsonTypeName("stroke")
public class StrokeLeague extends League {
private final List<Golfer> participants;
@JsonCreator
- private StrokeLeague(int id, String name, Date registrationDate, Date startDate, Date endDate, Golfer owner, List<Golfer> participants) {
- super(id, name, registrationDate, startDate, endDate, owner);
+ private StrokeLeague(int id, String name, Date registrationDate, Date startDate, Date endDate, Golfer owner, List<Golfer> participants, List<Match> schedule) {
+ super(id, name, registrationDate, startDate, endDate, owner, schedule);
this.participants = participants;
}
- public StrokeLeague(int id, String name, Date registrationDate, Date startDate, Date endDate, Golfer owner) {
- super(id, name, registrationDate, startDate, endDate, owner);
+ public StrokeLeague(String name, Date registrationDate, Date startDate, Date endDate, Golfer owner) {
+ super(name, registrationDate, startDate, endDate, owner);
this.participants = new ArrayList<>();
}
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();
+ }
+}
diff --git a/src/main/java/design/runtime/Session.java b/src/main/java/design/runtime/Session.java
index 59d31d8..5b00e67 100644
--- a/src/main/java/design/runtime/Session.java
+++ b/src/main/java/design/runtime/Session.java
@@ -7,12 +7,14 @@ import java.time.LocalDateTime;
public final class Session {
private static Golfer currentGolfer;
private static LocalDateTime timeOverride;
+ private static boolean isGuest = false;
public static Golfer getCurrentGolfer() {
return currentGolfer;
}
public static void setCurrentGolfer(Golfer currentGolfer) {
+ Session.setGuest(false);
Session.currentGolfer = currentGolfer;
}
@@ -23,4 +25,13 @@ public final class Session {
public static void setTimeOverride(LocalDateTime timeOverride) {
Session.timeOverride = timeOverride;
}
+
+ public static boolean isGuest() {
+ return isGuest;
+ }
+
+ public static void setGuest(boolean g) {
+ isGuest = g;
+ }
+
}