#25 Initial implementation of the JsonGardenPlan
Updated and renamed the Crop type to accommodate storage
This commit is contained in:
parent
aceb6aa1e6
commit
72b0d029d5
|
@ -1,12 +1,42 @@
|
||||||
package ch.zhaw.gartenverwaltung.io;
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
import ch.zhaw.gartenverwaltung.types.UserPlanting;
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface GardenPlan {
|
public interface GardenPlan {
|
||||||
List<UserPlanting> getPlantings();
|
/**
|
||||||
void savePlanting(UserPlanting planting) throws IOException;
|
* Yields a list of all {@link Crop}s in the database.
|
||||||
void removePlanting(UserPlanting planting) throws IOException;
|
*
|
||||||
|
* @return A list of all {@link Crop}s in the database
|
||||||
|
* @throws IOException If there is a problem reading from or writing to the database
|
||||||
|
*/
|
||||||
|
List<Crop> getCrops() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to retrieve the {@link Crop} with the specified cropId.
|
||||||
|
*
|
||||||
|
* @param id The {@link Crop#cropId} to look for
|
||||||
|
* @return {@link Optional} of the found {@link Crop}, {@link Optional#empty()} if no entry matched the criteria
|
||||||
|
* @throws IOException If there is a problem reading from or writing to the database
|
||||||
|
*/
|
||||||
|
Optional<Crop> getCropById(long id) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a Crop to the Database.
|
||||||
|
*
|
||||||
|
* @param crop The {@link Crop} to be saved
|
||||||
|
* @throws IOException If there is a problem reading from or writing to the database
|
||||||
|
*/
|
||||||
|
void saveCrop(Crop crop) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a Crop from the Database.
|
||||||
|
*
|
||||||
|
* @param crop The {@link Crop} to be removed
|
||||||
|
* @throws IOException If there is a problem reading from or writing to the database
|
||||||
|
*/
|
||||||
|
void removeCrop(Crop crop) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
class InvalidJsonException extends IOException {
|
||||||
|
public InvalidJsonException(String reason) {
|
||||||
|
super(reason);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.io;
|
||||||
|
|
||||||
|
import ch.zhaw.gartenverwaltung.types.Crop;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class JsonGardenPlan implements GardenPlan {
|
||||||
|
private final URL dataSource = getClass().getResource("user-crops.json");
|
||||||
|
|
||||||
|
private IdProvider idProvider;
|
||||||
|
private Map<Long, Crop> cropMap = Collections.emptyMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creating constant objects required to deserialize the {@link LocalDate} classes
|
||||||
|
*/
|
||||||
|
private final static JavaTimeModule timeModule = new JavaTimeModule();
|
||||||
|
private final static String INVALID_DATASOURCE_MSG = "Invalid datasource specified!";
|
||||||
|
static {
|
||||||
|
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yy-MM-dd");
|
||||||
|
LocalDateDeserializer dateDeserializer = new LocalDateDeserializer(dateFormat);
|
||||||
|
timeModule.addDeserializer(LocalDate.class, dateDeserializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see GardenPlan#getCrops()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<Crop> getCrops() throws IOException {
|
||||||
|
if (idProvider == null) {
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
return cropMap.values().stream().toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see GardenPlan#getCropById(long)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Optional<Crop> getCropById(long id) throws IOException {
|
||||||
|
if (idProvider == null) {
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(cropMap.get(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see GardenPlan#saveCrop(Crop)
|
||||||
|
*
|
||||||
|
* Saves a crop to the database.
|
||||||
|
* If no {@link Crop#cropId} is set, one will be generated and
|
||||||
|
* the Crop is stored as a new entry.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void saveCrop(Crop crop) throws IOException {
|
||||||
|
if (idProvider == null) {
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
Long cropId = crop.getCropId().orElse(idProvider.incrementAndGet());
|
||||||
|
cropMap.put(cropId, crop.withId(cropId));
|
||||||
|
writeCropList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see GardenPlan#removeCrop(Crop)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeCrop(Crop crop) throws IOException {
|
||||||
|
if (idProvider == null) {
|
||||||
|
loadCropList();
|
||||||
|
}
|
||||||
|
Optional<Long> cropId = crop.getCropId();
|
||||||
|
if (cropId.isPresent()) {
|
||||||
|
cropMap.remove(cropId.get());
|
||||||
|
writeCropList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the database from {@link #dataSource} and updates the cached data.
|
||||||
|
*
|
||||||
|
* @throws IOException If the database cannot be accessed or invalid data was read.
|
||||||
|
*/
|
||||||
|
private void loadCropList() throws IOException {
|
||||||
|
if (dataSource != null) {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
mapper.registerModule(timeModule);
|
||||||
|
|
||||||
|
List<Crop> result;
|
||||||
|
result = mapper.readerForListOf(Crop.class).readValue(dataSource);
|
||||||
|
|
||||||
|
// Turn list into a HashMap with structure cropId => Crop
|
||||||
|
cropMap = new HashMap<>(result.size());
|
||||||
|
for (Crop crop : result) {
|
||||||
|
Long id = crop.getCropId()
|
||||||
|
.orElseThrow(() -> new InvalidJsonException(String.format("Invalid value for \"cropId\" in %s!", crop)));
|
||||||
|
cropMap.put(id, crop);
|
||||||
|
}
|
||||||
|
Long maxId = cropMap.isEmpty() ? 0L : Collections.max(cropMap.keySet());
|
||||||
|
idProvider = new IdProvider(maxId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the values from {@link #cropMap} to the {@link #dataSource}
|
||||||
|
*
|
||||||
|
* @throws IOException If the database cannot be accessed
|
||||||
|
*/
|
||||||
|
private void writeCropList() throws IOException {
|
||||||
|
if (dataSource != null) {
|
||||||
|
try {
|
||||||
|
new ObjectMapper()
|
||||||
|
.registerModule(timeModule)
|
||||||
|
.writeValue(new File(dataSource.toURI()), cropMap.values());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
// TODO: Log
|
||||||
|
throw new IOException(INVALID_DATASOURCE_MSG, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ch.zhaw.gartenverwaltung.types;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class Crop {
|
||||||
|
private Long cropId = null;
|
||||||
|
private final long plantId;
|
||||||
|
private final LocalDate startDate;
|
||||||
|
private int area = 1;
|
||||||
|
|
||||||
|
public Crop(long plantId, LocalDate startDate) {
|
||||||
|
this.plantId = plantId;
|
||||||
|
this.startDate = startDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builder-Style setter
|
||||||
|
public Crop withId(long cropId) {
|
||||||
|
this.cropId = cropId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Crop withArea(int area) {
|
||||||
|
this.area = area;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
public Optional<Long> getCropId() {
|
||||||
|
return Optional.ofNullable(cropId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getPlantId() { return plantId; }
|
||||||
|
public LocalDate getStartDate() { return startDate; }
|
||||||
|
public int getArea() { return area; }
|
||||||
|
}
|
|
@ -1,10 +0,0 @@
|
||||||
package ch.zhaw.gartenverwaltung.types;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public record UserPlanting(
|
|
||||||
long plantId,
|
|
||||||
Date startDate,
|
|
||||||
int area
|
|
||||||
) {
|
|
||||||
}
|
|
Loading…
Reference in New Issue