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; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; public class JSONLeagueDatabase implements LeagueDatabase { private static JSONLeagueDatabase INSTANCE; public static JSONLeagueDatabase instance() { if (INSTANCE == null) { INSTANCE = new JSONLeagueDatabase("data/leaguedb.json"); } return INSTANCE; } private final Map cache; private final ObjectMapper mapper; private final File file; private int nextLeagueID; 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()); 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()); try { load(); } catch (IOException ex) { throw new RuntimeException(ex); } this.nextLeagueID = cache.values().stream().mapToInt(League::getId).max().orElse(0) + 1; } private void load() throws IOException { League[] data = mapper.readValue(file, League[].class); cache.clear(); Arrays.stream(data).forEach(i -> cache.put(i.getId(), i)); } private void save() throws IOException { League[] data = cache.values().toArray(League[]::new); mapper.writer(new Serializers.CustomPrettyPrinter()).writeValue(file, data); } @Override public League getLeague(int id) { return cache.get(id); } @Override public League[] getLeagues() { return cache.values().toArray(League[]::new); } @Override public void addLeague(League league) throws IOException { league.setId(nextLeagueID++); cache.putIfAbsent(league.getId(), league); save(); } @Override public void removeLeague(League league) throws IOException { cache.remove(league.getId()); save(); } @Override public void updateLeague(League league) throws IOException { cache.put(league.getId(), league); save(); } }