summaryrefslogtreecommitdiff
path: root/src/main/java/design
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/main/java/design/controller/userinput/menus/ImportExportMenu.java81
-rw-r--r--src/main/java/design/controller/userinput/menus/MainMenu.java3
-rw-r--r--src/main/java/design/model/Golfer.java3
-rw-r--r--src/main/java/design/persistence/JSONLeagueDatabase.java53
-rw-r--r--src/main/java/design/persistence/JSONPersonalDatabase.java56
-rw-r--r--src/main/java/design/persistence/LeagueDatabase.java9
-rw-r--r--src/main/java/design/persistence/PersonalDatabase.java10
-rw-r--r--src/main/java/design/persistence/Serializers.java25
-rw-r--r--src/main/java/design/persistence/importexport/DataHandler.java11
-rw-r--r--src/main/java/design/persistence/importexport/DataSource.java16
-rw-r--r--src/main/java/design/persistence/importexport/JSONHandler.java35
-rw-r--r--src/main/java/design/persistence/importexport/XMLHandler.java38
12 files changed, 294 insertions, 46 deletions
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..0c42422 100644
--- a/src/main/java/design/controller/userinput/menus/MainMenu.java
+++ b/src/main/java/design/controller/userinput/menus/MainMenu.java
@@ -39,7 +39,8 @@ public class MainMenu extends Menu {
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("League play...", () -> new SelectLeague().present()),
+ new MenuOption("manage data...", () -> new ImportExportMenu().present())
);
}
}
diff --git a/src/main/java/design/model/Golfer.java b/src/main/java/design/model/Golfer.java
index 870b460..84f7396 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;
@@ -19,6 +20,8 @@ public class Golfer implements Originator {
private final List<Club> clubs; // Keep track of golfer's clubs
private int nextClubId;
+
+
@JsonCreator
private Golfer(String username, int passwordHash, String fullName, List<Course> courses, List<Round> rounds,
List<Club> clubs) {
diff --git a/src/main/java/design/persistence/JSONLeagueDatabase.java b/src/main/java/design/persistence/JSONLeagueDatabase.java
index 8a55065..977e162 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;
@@ -38,18 +31,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();
+ this.mapper = new ObjectMapper();
- 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());
- 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());
+ Serializers.configureMapper(mapper);
+ mapper.registerModule(this.getJacksonModule());
try {
load();
@@ -99,4 +84,36 @@ 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() {
+ 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());
+ return module;
+ }
+
+ @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 30c280a..b92dddf 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;
@@ -36,21 +29,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();;
+ this.mapper = new ObjectMapper();
- // 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());
- mapper.registerModule(module);
- mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
- mapper.registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES));
- mapper.registerModule(new JavaTimeModule());
+ Serializers.configureMapper(mapper);
+ mapper.registerModule(this.getJacksonModule());
try {
load();
@@ -100,4 +82,36 @@ 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() {
+ 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());
+ return module;
+ }
+
+ @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 3940b44..246dbf1 100644
--- a/src/main/java/design/persistence/Serializers.java
+++ b/src/main/java/design/persistence/Serializers.java
@@ -1,18 +1,20 @@
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.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.time.LocalDateTime;
+import java.util.List;
public class Serializers {
public static class CustomPrettyPrinter extends DefaultPrettyPrinter {
@@ -76,4 +78,19 @@ public class Serializers {
return personalDB.getGolfer(username);
}
}
+
+ public static class DateTimeStringSerializer extends JsonSerializer<LocalDateTime> {
+ @Override
+ public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+ gen.writeString(value.toString());
+ }
+ }
+
+ 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.registerModule(new JavaTimeModule());
+ mapper.configOverride(List.class).setSetterInfo(JsonSetter.Value.forValueNulls(Nulls.AS_EMPTY));
+ }
}
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);
+ }
+}
+
+