diff options
| author | jrshi <jrs9538@g.rit.edu> | 2025-11-16 16:02:38 -0500 |
|---|---|---|
| committer | jrshi <jrs9538@g.rit.edu> | 2025-11-16 16:02:38 -0500 |
| commit | 0b43e258054b450f5007ef4d4fa34dacba2d8a9c (patch) | |
| tree | af8f64e4bdb6074fe2fb34c993850b6d81ce61de /src/main/java | |
| parent | 343d0baaaf718bfc9959484d187c4df1e171335e (diff) | |
| parent | af9f559a2ee427905c39363643bac2e7878fb10c (diff) | |
| download | designproject-design-6-league-play-refactoring.tar.gz designproject-design-6-league-play-refactoring.tar.bz2 designproject-design-6-league-play-refactoring.zip | |
Merge branch 'league-play-refactoring' of https://github.com/RIT-SWEN-262/designproject-design-6 into league-play-refactoringleague-play-refactoring
Merging?
Diffstat (limited to '')
14 files changed, 357 insertions, 77 deletions
diff --git a/src/main/java/design/controller/userinput/menus/CourseSearch.java b/src/main/java/design/controller/userinput/menus/CourseSearch.java index 4dd2cad..8fd1963 100644 --- a/src/main/java/design/controller/userinput/menus/CourseSearch.java +++ b/src/main/java/design/controller/userinput/menus/CourseSearch.java @@ -80,6 +80,10 @@ 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, () -> { + if(Session.isGuest()) { + System.out.println("\nYou cannot add Courses as a Guest. Please login for this functionality."); + new MainMenu().present(); + } UndoManager.instance().capture(golfer, "Add course " + c.getName()); // add the course, try to save to DB. diff --git a/src/main/java/design/controller/userinput/menus/HistoryMenu.java b/src/main/java/design/controller/userinput/menus/HistoryMenu.java new file mode 100644 index 0000000..226b331 --- /dev/null +++ b/src/main/java/design/controller/userinput/menus/HistoryMenu.java @@ -0,0 +1,30 @@ +package design.controller.userinput.menus; + +import design.controller.userinput.Menu; +import design.controller.userinput.MenuOption; +import design.controller.userinput.UndoActions; + +import java.util.List; + +public class HistoryMenu extends Menu { + + @Override + public String getTitle() { + return "History"; + } + + @Override + public List<MenuOption> getMenuOptions() { + return List.of( + new MenuOption("return to main menu", () -> new MainMenu().present()), + new MenuOption("undo", () -> { + UndoActions.undoWithSave(); + this.present(); + }), + new MenuOption("redo", () -> { + UndoActions.redoWithSave(); + this.present(); + }) + ); + } +} diff --git a/src/main/java/design/controller/userinput/menus/ImportExportMenu.java b/src/main/java/design/controller/userinput/menus/ImportExportMenu.java new file mode 100644 index 0000000..21ffa6c --- /dev/null +++ b/src/main/java/design/controller/userinput/menus/ImportExportMenu.java @@ -0,0 +1,81 @@ +package design.controller.userinput.menus; + +import design.controller.userinput.Menu; +import design.controller.userinput.MenuOption; +import design.persistence.*; +import design.persistence.importexport.DataHandler; +import design.persistence.importexport.DataSource; +import design.persistence.importexport.JSONHandler; +import design.persistence.importexport.XMLHandler; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Scanner; + +public class ImportExportMenu extends Menu { + + @Override + public String getTitle() { + return "import export menu"; + } + + @Override + public List<MenuOption> getMenuOptions() { + List<MenuOption> opts = new java.util.ArrayList<>(); + + + opts.add(new MenuOption("return to main menu", () -> new MainMenu().present())); + + opts.add(new MenuOption("import leagues...", () -> promptForPath(true, false))); + opts.add(new MenuOption("export leagues...", () -> promptForPath(false, false))); + opts.add(new MenuOption("import profiles...", () -> promptForPath(true, true))); + opts.add(new MenuOption("export profiles...", () -> promptForPath(false, true))); + + return opts; + } + + private void promptForPath(boolean isImporting, boolean isPersonalProfile) + { + System.out.print("Enter file path: "); + Scanner sc = new Scanner(System.in); + String filePath = sc.nextLine(); + File file = new File(filePath); + + DataSource source; + if (isPersonalProfile) { + source = PersonalDatabase.instance(); + } else { + source = LeagueDatabase.instance(); + } + + DataHandler handler; + String ext = getExtension(filePath); + switch (ext) { + case "json" -> handler = new JSONHandler(source); + case "xml" -> handler = new XMLHandler(source); + default -> { + System.out.println("Unsupported file type: " + ext); + this.present(); + return; + } + } + + try { + if (isImporting) { + handler.importData(file); + } else { + handler.exportData(file); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + this.present(); + } + + private static String getExtension(String fileName) { + int i = fileName.lastIndexOf('.'); + return (i >= 0) ? fileName.substring(i + 1).toLowerCase() : "unknown"; + } +} diff --git a/src/main/java/design/controller/userinput/menus/MainMenu.java b/src/main/java/design/controller/userinput/menus/MainMenu.java index 75ad88f..3f520e4 100644 --- a/src/main/java/design/controller/userinput/menus/MainMenu.java +++ b/src/main/java/design/controller/userinput/menus/MainMenu.java @@ -2,7 +2,6 @@ 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; @@ -15,32 +14,21 @@ public class MainMenu extends Menu { @Override public List<MenuOption> getMenuOptions() { - 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()) - - ); + List<MenuOption> options = new java.util.ArrayList<>(); + options.add(new MenuOption("quit", () -> System.exit(0))); + if (Session.isGuest()) { + options.add(new MenuOption("guest settings...", () -> new UserSettings().present())); + options.add(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()) - ); + options.add(new MenuOption("manage history...", () -> new HistoryMenu().present())); + options.add(new MenuOption("user settings...", () -> new UserSettings().present())); + options.add(new MenuOption("manage courses...", () -> new ManageCourses().present())); + options.add(new MenuOption("manage clubs...", () -> new ManageClubs().present())); + options.add(new MenuOption("statistics...", () -> new StatisticsMenu().present())); + options.add(new MenuOption("log round...", () -> new HolePlayMenu().present())); + options.add(new MenuOption("league play...", () -> new SelectLeague().present())); + options.add(new MenuOption("manage data...", () -> new ImportExportMenu().present())); } + return options; } } diff --git a/src/main/java/design/model/Golfer.java b/src/main/java/design/model/Golfer.java index 3b4770d..4fb521e 100644 --- a/src/main/java/design/model/Golfer.java +++ b/src/main/java/design/model/Golfer.java @@ -1,6 +1,7 @@ package design.model; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import java.util.ArrayList; @@ -21,6 +22,8 @@ public class Golfer implements Originator { private final List<Invite> invites; private Team joinedTeam; + + @JsonCreator private Golfer(String username, int passwordHash, String fullName, List<Course> courses, List<Round> rounds, List<Club> clubs, List<Invite> invites, Team joinedTeam) { diff --git a/src/main/java/design/persistence/JSONLeagueDatabase.java b/src/main/java/design/persistence/JSONLeagueDatabase.java index 6f3f2d2..6446b34 100644 --- a/src/main/java/design/persistence/JSONLeagueDatabase.java +++ b/src/main/java/design/persistence/JSONLeagueDatabase.java @@ -1,14 +1,7 @@ package design.persistence; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import design.model.Course; import design.model.Golfer; import design.model.League; @@ -30,6 +23,12 @@ public class JSONLeagueDatabase implements LeagueDatabase { return INSTANCE; } + // static instance strictly for testing, to not add data to leaguedb.json + static JSONLeagueDatabase testInstance(String filename) { + INSTANCE = new JSONLeagueDatabase(filename); + return INSTANCE; + } + private final Map<Integer, League> cache; private final ObjectMapper mapper; private final File file; @@ -38,20 +37,10 @@ public class JSONLeagueDatabase implements LeagueDatabase { public JSONLeagueDatabase(String filename) { this.file = new File(filename); this.cache = new HashMap<>(); - this.mapper = JsonMapper.builder().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION).build(); - - SimpleModule module = new SimpleModule(); - module.addDeserializer(Course.class, new Serializers.CourseIdDeserializer()); - module.addSerializer(Course.class, new Serializers.CourseIdSerializer()); - module.addDeserializer(Golfer.class, new Serializers.GolferUsernameDeserializer()); - module.addSerializer(Golfer.class, new Serializers.GolferUsernameSerializer()); - module.addSerializer(Map.class, new Serializers.MapListSerializer()); - module.addDeserializer(Map.class, new Serializers.MapListDeserializer()); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.registerModule(module); - mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - mapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES)); - mapper.registerModule(new JavaTimeModule()); + this.mapper = new ObjectMapper(); + + Serializers.configureMapper(mapper); + mapper.registerModule(this.getJacksonModule()); try { load(); @@ -101,4 +90,35 @@ public class JSONLeagueDatabase implements LeagueDatabase { cache.put(league.getId(), league); save(); } + + @Override + public void importData(Object rawData) throws IOException { + if (!(rawData instanceof League[] data)) { + throw new ClassCastException(); + } + cache.clear(); + for (League league : data) { + cache.put(league.getId(), league); + } + save(); + } + + @Override + public League[] exportData() { + return getLeagues(); + } + + @Override + public SimpleModule getJacksonModule() { + return new SimpleModule() + .addSerializer(Course.class, new Serializers.CourseIdSerializer()) + .addDeserializer(Course.class, new Serializers.CourseIdDeserializer()) + .addSerializer(Golfer.class, new Serializers.GolferUsernameSerializer()) + .addDeserializer(Golfer.class, new Serializers.GolferUsernameDeserializer()); + } + + @Override + public Class<?> getTargetClass() { + return League[].class; + } } diff --git a/src/main/java/design/persistence/JSONPersonalDatabase.java b/src/main/java/design/persistence/JSONPersonalDatabase.java index a2908f3..2f021d7 100644 --- a/src/main/java/design/persistence/JSONPersonalDatabase.java +++ b/src/main/java/design/persistence/JSONPersonalDatabase.java @@ -1,14 +1,7 @@ package design.persistence; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.StreamReadFeature; import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import design.model.Course; import design.model.Golfer; import design.model.League; @@ -29,6 +22,12 @@ public class JSONPersonalDatabase implements PersonalDatabase { return INSTANCE; } + // static instance strictly for testing, to not add data to personaldb.json + static JSONPersonalDatabase testInstance(String filename) { + INSTANCE = new JSONPersonalDatabase(filename); + return INSTANCE; + } + private final Map<String, Golfer> cache; private final ObjectMapper mapper; private final File file; @@ -36,23 +35,10 @@ public class JSONPersonalDatabase implements PersonalDatabase { private JSONPersonalDatabase(String filename) { this.file = new File(filename); this.cache = new HashMap<>(); - this.mapper = JsonMapper.builder().enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION).build(); - - // TODO: Once the saved JSON matches the model, consider removing. - // TEMP: tolerate unknown props while the model stabilizes - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - - SimpleModule module = new SimpleModule(); - module.addDeserializer(Course.class, new Serializers.CourseIdDeserializer()); - module.addSerializer(Course.class, new Serializers.CourseIdSerializer()); - module.addSerializer(League.class, new Serializers.LeagueIDSerializer()); - module.addDeserializer(League.class, new Serializers.LeagueIDDeserializer()); - module.addSerializer(Map.class, new Serializers.MapListSerializer()); - module.addDeserializer(Map.class, new Serializers.MapListDeserializer()); - mapper.registerModule(module); - mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - mapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES)); - mapper.registerModule(new JavaTimeModule()); + this.mapper = new ObjectMapper(); + + Serializers.configureMapper(mapper); + mapper.registerModule(this.getJacksonModule()); try { load(); @@ -102,4 +88,35 @@ public class JSONPersonalDatabase implements PersonalDatabase { cache.put(golfer.getUsername(), golfer); save(); } + + @Override + public void importData(Object rawData) throws IOException { + if (!(rawData instanceof Golfer[] data)) { + throw new ClassCastException(); + } + cache.clear(); + for (Golfer golfer : data) { + cache.put(golfer.getUsername(), golfer); + } + save(); + } + + @Override + public Golfer[] exportData() { + return getGolfers(); + } + + @Override + public SimpleModule getJacksonModule() { + return new SimpleModule() + .addSerializer(Course.class, new Serializers.CourseIdSerializer()) + .addDeserializer(Course.class, new Serializers.CourseIdDeserializer()) + .addSerializer(League.class, new Serializers.LeagueIDSerializer()) + .addDeserializer(League.class, new Serializers.LeagueIDDeserializer()); + } + + @Override + public Class<?> getTargetClass() { + return Golfer[].class; + } } diff --git a/src/main/java/design/persistence/LeagueDatabase.java b/src/main/java/design/persistence/LeagueDatabase.java index 953624d..ed6ddae 100644 --- a/src/main/java/design/persistence/LeagueDatabase.java +++ b/src/main/java/design/persistence/LeagueDatabase.java @@ -1,10 +1,11 @@ package design.persistence; import design.model.League; +import design.persistence.importexport.DataSource; import java.io.IOException; -public interface LeagueDatabase { +public interface LeagueDatabase extends DataSource { static LeagueDatabase instance() { return JSONLeagueDatabase.instance(); } @@ -18,4 +19,10 @@ public interface LeagueDatabase { void removeLeague(League league) throws IOException; void updateLeague(League league) throws IOException; + + @Override + void importData(Object data) throws IOException; + + @Override + Object exportData() throws IOException; } diff --git a/src/main/java/design/persistence/PersonalDatabase.java b/src/main/java/design/persistence/PersonalDatabase.java index adb865d..70dd37d 100644 --- a/src/main/java/design/persistence/PersonalDatabase.java +++ b/src/main/java/design/persistence/PersonalDatabase.java @@ -1,9 +1,11 @@ package design.persistence; import design.model.Golfer; +import design.persistence.importexport.DataSource; + import java.io.IOException; -public interface PersonalDatabase { +public interface PersonalDatabase extends DataSource { static PersonalDatabase instance() { return JSONPersonalDatabase.instance(); @@ -18,4 +20,10 @@ public interface PersonalDatabase { void removeGolfer(Golfer golfer) throws IOException; void updateGolfer(Golfer golfer) throws IOException; + + @Override + void importData(Object data) throws IOException; + + @Override + Object exportData() throws IOException; } diff --git a/src/main/java/design/persistence/Serializers.java b/src/main/java/design/persistence/Serializers.java index d00dcbf..225ec7c 100644 --- a/src/main/java/design/persistence/Serializers.java +++ b/src/main/java/design/persistence/Serializers.java @@ -1,21 +1,36 @@ package design.persistence; +import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import design.model.Course; import design.model.Golfer; import design.model.League; import java.io.IOException; import java.util.Map; +import java.time.LocalDateTime; +import java.util.List; public class Serializers { + public static void configureMapper(ObjectMapper mapper) { + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + mapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES)); + mapper.configOverride(List.class).setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY)); + mapper.registerModule(new JavaTimeModule()); + mapper.registerModule(new SimpleModule() + .addSerializer(Map.class, new Serializers.MapListSerializer()) + .addDeserializer(Map.class, new Serializers.MapListDeserializer()) + ); + } + public static class CustomPrettyPrinter extends DefaultPrettyPrinter { public CustomPrettyPrinter() { super._arrayIndenter = new DefaultIndenter(); @@ -78,6 +93,13 @@ public class Serializers { } } + public static class DateTimeStringSerializer extends JsonSerializer<LocalDateTime> { + @Override + public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeString(value.toString()); + } + } + @SuppressWarnings("rawtypes") public static class MapListSerializer extends JsonSerializer<Map> { diff --git a/src/main/java/design/persistence/importexport/DataHandler.java b/src/main/java/design/persistence/importexport/DataHandler.java new file mode 100644 index 0000000..59df9be --- /dev/null +++ b/src/main/java/design/persistence/importexport/DataHandler.java @@ -0,0 +1,11 @@ +package design.persistence.importexport; + +import java.io.File; +import java.io.IOException; + +public interface DataHandler { + + void importData(File file) throws IOException; + + void exportData(File file) throws IOException; +} diff --git a/src/main/java/design/persistence/importexport/DataSource.java b/src/main/java/design/persistence/importexport/DataSource.java new file mode 100644 index 0000000..e735c7e --- /dev/null +++ b/src/main/java/design/persistence/importexport/DataSource.java @@ -0,0 +1,16 @@ +package design.persistence.importexport; + +import com.fasterxml.jackson.databind.module.SimpleModule; + +import java.io.IOException; + +public interface DataSource { + + void importData(Object data) throws IOException; + + Object exportData() throws IOException; + + SimpleModule getJacksonModule(); + + Class<?> getTargetClass(); +}
\ No newline at end of file diff --git a/src/main/java/design/persistence/importexport/JSONHandler.java b/src/main/java/design/persistence/importexport/JSONHandler.java new file mode 100644 index 0000000..9c04281 --- /dev/null +++ b/src/main/java/design/persistence/importexport/JSONHandler.java @@ -0,0 +1,35 @@ +package design.persistence.importexport; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import design.persistence.Serializers; + +import java.io.File; +import java.io.IOException; + +public class JSONHandler implements DataHandler { + + private final DataSource dataSource; + private final ObjectMapper jsonMapper = new JsonMapper(); + + public JSONHandler(DataSource dataSource) { + this.dataSource = dataSource; + + Serializers.configureMapper(jsonMapper); + jsonMapper.registerModule(dataSource.getJacksonModule()); + } + + @Override + public void importData(File file) throws IOException { + Object data = jsonMapper.readValue(file, dataSource.getTargetClass()); + dataSource.importData(data); + } + + @Override + public void exportData(File file) throws IOException{ + Object data = dataSource.exportData(); + jsonMapper.writer(new Serializers.CustomPrettyPrinter()) + .writeValue(file, data); + } +} + + diff --git a/src/main/java/design/persistence/importexport/XMLHandler.java b/src/main/java/design/persistence/importexport/XMLHandler.java new file mode 100644 index 0000000..0a07d6e --- /dev/null +++ b/src/main/java/design/persistence/importexport/XMLHandler.java @@ -0,0 +1,38 @@ +package design.persistence.importexport; +import java.io.File; +import java.io.IOException; +import java.time.LocalDateTime; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import design.persistence.Serializers; + +public class XMLHandler implements DataHandler { + + private final DataSource dataSource; + private final XmlMapper xmlMapper = new XmlMapper(); + + public XMLHandler(DataSource dataSource) { + this.dataSource = dataSource; + + Serializers.configureMapper(xmlMapper); + SimpleModule module = dataSource.getJacksonModule(); + module.addSerializer(LocalDateTime.class, new Serializers.DateTimeStringSerializer()); + xmlMapper.registerModule(module); + } + + @Override + public void importData(File file) throws IOException { + Object data = xmlMapper.readValue(file, dataSource.getTargetClass()); + dataSource.importData(data); + } + + @Override + public void exportData(File file) throws IOException { + Object data = dataSource.exportData(); + xmlMapper.writerWithDefaultPrettyPrinter() + .writeValue(file, data); + } +} + + |
