diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java index ff351afcb4ad7f3a2c86760e719c20a69d73aeb3..1ad731facf94f00145337fc240fb207256c8dd62 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/App.java @@ -17,11 +17,11 @@ import tdt4140.gr1800.app.doc.IDocumentLoader; import tdt4140.gr1800.app.doc.IDocumentPersistence; import tdt4140.gr1800.app.doc.IDocumentStorage; import tdt4140.gr1800.app.gpx.GpxDocumentConverter; -import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence; +import tdt4140.gr1800.app.json.GeoLocationsPersistence; public class App { - private GeoLocationsStreamPersistence geoLocationsPersistence = new GeoLocationsJsonPersistence(); + private GeoLocationsStreamPersistence geoLocationsPersistence = new GeoLocationsPersistence(); private Collection<GeoLocations> geoLocations = null; diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java index 5c6d552702fccff1ef5acf04e9f15a00e1224cd3..82075bc204491feeee0680d45f1a99c13dc93b1e 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/core/GeoLocation.java @@ -18,6 +18,10 @@ public class GeoLocation extends TimedTaggedImpl implements GeoLocated, Timed, T return elevation; } + public void setElevation(int elevation) { + this.elevation = elevation; + } + private String name, description; public String getName() { diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/AbstractDbAccessImpl.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/AbstractDbAccessImpl.java index c4e335f698d4a694cf82976e313f4aec6dfaeda9..2281c980afd965e211dd6f4d5b0a1ae1f5863910 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/AbstractDbAccessImpl.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/AbstractDbAccessImpl.java @@ -15,53 +15,61 @@ public abstract class AbstractDbAccessImpl implements IDbAccess { protected IdMap<GeoLocations> geoLocationsIds = new IdMap<GeoLocations>(); protected IdMap<GeoLocation> geoLocationIds = new IdMap<GeoLocation>(); - public int getId(Person person) { + public IdProvider<Person> getPersonIdProvider() { + return personIds; + } + + public int getId(final Person person) { return personIds.getId(person); } - public int getId(GeoLocations geoLocations) { + public int getId(final GeoLocations geoLocations) { return geoLocationsIds.getId(geoLocations); } - public int getId(GeoLocation geoLocation) { + public int getId(final GeoLocation geoLocation) { return geoLocationIds.getId(geoLocation); } - + @Override - public Person createPerson(String name, String email) { - Person person = new Person(); + public Person createPerson(final String name, final String email) { + final Person person = new Person(); person.setName(name); person.setEmail(email); return person; } @Override - public GeoLocations createGeoLocations(Person owner) { - GeoLocations geoLocations = new GeoLocations(owner); + public GeoLocations createGeoLocations(final Person owner) { + final GeoLocations geoLocations = new GeoLocations(owner); owner.addGeolocations(geoLocations); return geoLocations; } @Override - public GeoLocation addGeoLocation(GeoLocations geoLocations, GeoLocated geoLoc, int elevation, LocalTime time) { - // TODO Auto-generated method stub - return null; + public GeoLocation addGeoLocation(final GeoLocations geoLocations, final GeoLocated geoLoc, final int elevation, final LocalTime time) { + final GeoLocation geoLocation = new GeoLocation(); + geoLocation.setLatLong(geoLoc.getLatLong()); + geoLocation.setElevation(elevation); + geoLocation.setTime(time); + geoLocations.addLocation(geoLoc); + return geoLocation; } @Override - public Collection<Person> getAllPersons(boolean refresh) { + public Collection<Person> getAllPersons(final boolean refresh) { return new ArrayList<Person>(personIds.get()); } @Override - public Person getPerson(int id, boolean refresh) { - Person person = personIds.get(id); + public Person getPerson(final int id, final boolean refresh) { + final Person person = personIds.get(id); return person; } @Override - public Person getPersonByName(String name, boolean refresh) { - for (Person person : personIds.get()) { + public Person getPersonByName(final String name, final boolean refresh) { + for (final Person person : personIds.get()) { if (name == person.getName() || (name != null && name.equals(person.getName()))) { return person; } @@ -70,8 +78,8 @@ public abstract class AbstractDbAccessImpl implements IDbAccess { } @Override - public Person getPersonByEmail(String email, boolean refresh) { - for (Person person : personIds.get()) { + public Person getPersonByEmail(final String email, final boolean refresh) { + for (final Person person : personIds.get()) { if (email == person.getEmail() || (email != null && email.equals(person.getEmail()))) { return person; } @@ -80,41 +88,23 @@ public abstract class AbstractDbAccessImpl implements IDbAccess { } @Override - public Collection<GeoLocations> getGeoLocations(Person owner, boolean refresh) { - Collection<GeoLocations> result = new ArrayList<>(); - for (GeoLocations geoLocations : geoLocationsIds.get()) { - if (geoLocations.getOwner() == owner) { - result.add(geoLocations); - } - } - return result; - } - - @Override - public void updateGeoLocationsData(GeoLocations geoLocations) { - // TODO Auto-generated method stub - - } - - @Override - public void updateGeoLocationData(GeoLocations geoLocations, GeoLocations geoLocation) { - // TODO Auto-generated method stub - + public Collection<GeoLocations> getGeoLocations(final Person owner, final boolean refresh) { + return owner.getGeoLocations((String[]) null); } @Override - public void deletePerson(Person person) { + public void deletePerson(final Person person) { personIds.remove(person); } @Override - public void deleteGeoLocations(GeoLocations geoLocations) { + public void deleteGeoLocations(final GeoLocations geoLocations) { geoLocations.getOwner().removeGeolocations(geoLocations); geoLocationsIds.remove(geoLocations); } @Override - public void deleteGeoLocation(GeoLocations geoLocations, GeoLocation geoLocation) { + public void deleteGeoLocation(final GeoLocations geoLocations, final GeoLocation geoLocation) { geoLocationIds.remove(geoLocation); } } diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessHelper.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessHelper.java index 0b6c0542d2852815d7b188651673646c2a2fac60..4435e4b628f34de613b002a87d4f142a180e4385 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessHelper.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessHelper.java @@ -1,13 +1,16 @@ package tdt4140.gr1800.app.db; import java.sql.Connection; +import java.sql.Date; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLType; import java.sql.Statement; +import java.sql.Time; import java.sql.Types; +import java.time.LocalDate; +import java.time.LocalTime; public class DbAccessHelper { @@ -64,24 +67,37 @@ public class DbAccessHelper { try { preparedStatement = dbConnection.prepareStatement(statement); for (int argNum = 1; argNum <= args.length; argNum++) { - Object arg = args[argNum - 1]; - if (arg == null) { - preparedStatement.setNull(argNum, Types.VARCHAR); - } else if (arg instanceof String) { - preparedStatement.setString(argNum, (String) arg); - } else if (arg instanceof Double) { - preparedStatement.setDouble(argNum, (Double) arg); - } else if (arg instanceof Integer) { - preparedStatement.setInt(argNum, (Integer) arg); - } else if (arg instanceof Boolean) { - preparedStatement.setBoolean(argNum, (Boolean) arg); - } + setStatementArgument(preparedStatement, args[argNum - 1], null, argNum); } } catch (SQLException e) { throwException(e); } return preparedStatement; } + + protected void setStatementArgument(PreparedStatement preparedStatement, Object arg, Class<?> type, int argNum) throws SQLException { + if (arg == null) { + int sqlType = Types.VARCHAR; + if (type == String.class) { sqlType = Types.VARCHAR; + } else if (type == Double.class) { sqlType = Types.DECIMAL; + } else if (type == Integer.class) { sqlType = Types.INTEGER; + } else if (type == Boolean.class) { sqlType = Types.BOOLEAN; + } else if (type == Date.class) { sqlType = Types.DATE; + } else if (type == LocalDate.class) { sqlType = Types.DATE; + } else if (type == Time.class) { sqlType = Types.TIME; + } else if (type == LocalTime.class) { sqlType = Types.TIME; + } + preparedStatement.setNull(argNum, sqlType); + } else if (arg instanceof String || type == String.class) { preparedStatement.setString(argNum, (String) arg); + } else if (arg instanceof Double || type == Double.class) { preparedStatement.setDouble(argNum, (Double) arg); + } else if (arg instanceof Integer || type == Integer.class) { preparedStatement.setInt (argNum, (Integer) arg); + } else if (arg instanceof Boolean || type == Boolean.class) { preparedStatement.setBoolean(argNum, (Boolean) arg); + } else if (arg instanceof Date || type == Date.class) { preparedStatement.setDate (argNum, (Date) arg); + } else if (arg instanceof LocalDate || type == LocalDate.class) { preparedStatement.setDate (argNum, Date.valueOf((LocalDate) arg)); + } else if (arg instanceof Time || type == Time.class) { preparedStatement.setTime (argNum, (Time) arg); + } else if (arg instanceof LocalTime || type == LocalTime.class) { preparedStatement.setTime (argNum, Time.valueOf((LocalTime) arg)); + } + } protected void executeDbStatement(String statement, Object... args) { PreparedStatement preparedStatement = prepareStatement(statement, args); diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessImpl.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessImpl.java index 88f3b905a2a2dfe33d658b50c7a60049be42f452..4122a67e3e89bb899798658b05f089e829b5bf84 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessImpl.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/DbAccessImpl.java @@ -1,11 +1,17 @@ package tdt4140.gr1800.app.db; import java.sql.Connection; +import java.sql.Date; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Time; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; import java.util.Collection; +import tdt4140.gr1800.app.core.GeoLocation; import tdt4140.gr1800.app.core.GeoLocations; import tdt4140.gr1800.app.core.Person; @@ -166,18 +172,23 @@ public class DbAccessImpl extends AbstractDbAccessImpl { geoLocationsIds.removeAll(existingGeoLocations); existingGeoLocations.clear(); int ownerId = getId(owner); - ResultSet result = helper.executeQuery("SELECT id, path, name, description, date, time FROM geoLocations WHERE ownerId = ?", ownerId); + ResultSet result = helper.executeQuery("SELECT id, path, name, description, date, time, zone FROM geoLocations WHERE ownerId = ?", ownerId); try { while (result.next()) { int id = result.getInt(1); boolean path = result.getBoolean(2); - String name = result.getString(3), description = result.getString(3); + String name = result.getString(3), description = result.getString(4); GeoLocations geoLocations = new GeoLocations(owner); owner.addGeolocations(geoLocations); geoLocations.setPath(path); geoLocations.setName(name); geoLocations.setDescription(description); - // TODO: date and time + Date date = result.getDate(5); + Time time = result.getTime(6); + String zone = result.getString(7); + geoLocations.setDate(date != null ? date.toLocalDate() : null); + geoLocations.setTime(time != null ? time.toLocalTime() : null); + geoLocations.setZone(zone != null ? ZoneId.of(zone) : null); existingGeoLocations.add(geoLocations); geoLocationsIds.set(geoLocations, id); ResultSet tagResults = helper.executeQuery(String.format("SELECT tag FROM tag WHERE ownerId = ? AND ownerType = '%s'", TagOwnerType.GLS), id); @@ -209,8 +220,11 @@ public class DbAccessImpl extends AbstractDbAccessImpl { public void updateGeoLocationsData(GeoLocations geoLocations) { boolean path = geoLocations.isPath(); String name = geoLocations.getName(), desc = geoLocations.getDescription(); + LocalDate date = geoLocations.getDate(); + LocalTime time = geoLocations.getTime(); + String zone = (geoLocations.getZone() != null ? geoLocations.getZone().getId() : null); int ownerId = getId(geoLocations); - helper.executeDbStatement("UPDATE geoLocations SET path = ?, name = ?, description = ? WHERE id = ?", path, name, desc, ownerId); + helper.executeDbStatement("UPDATE geoLocations SET path = ?, name = ?, description = ?, date = ?, time = ?, zone = ? WHERE id = ?", path, name, desc, Date.valueOf(date), Time.valueOf(time), zone, ownerId); deleteTags(ownerId, TagOwnerType.GLS); String insertStatement = "INSERT INTO tag (ownerId, ownerType, tag) VALUES "; String[] tags = geoLocations.getTags(); @@ -234,4 +248,11 @@ public class DbAccessImpl extends AbstractDbAccessImpl { helper.executeDbStatement("DELETE FROM geoLocations WHERE id = ?", ownerId); deleteTags(ownerId, TagOwnerType.GLS); } + + // + + @Override + public void updateGeoLocationData(GeoLocations geoLocations, GeoLocation geoLocation) { + throw new UnsupportedOperationException("NYI"); + } } diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IDbAccess.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IDbAccess.java index 1a0be1aba4cac9226b2677f26ba6d33c9290cb1f..f95822761946826245501bdc18f8362b291da391 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IDbAccess.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IDbAccess.java @@ -6,12 +6,11 @@ import java.util.Collection; import tdt4140.gr1800.app.core.GeoLocated; import tdt4140.gr1800.app.core.GeoLocation; import tdt4140.gr1800.app.core.GeoLocations; -import tdt4140.gr1800.app.core.GeoLocationsOwner; import tdt4140.gr1800.app.core.Person; /* * CRUD interface for our domain: - * + * * @startuml * class GeoLocationsOwner { * String id @@ -37,7 +36,9 @@ import tdt4140.gr1800.app.core.Person; * @enduml */ public interface IDbAccess { - + + public IdProvider<Person> getPersonIdProvider(); + // Create public Person createPerson(String name, String email); public GeoLocations createGeoLocations(Person owner); @@ -48,14 +49,14 @@ public interface IDbAccess { public Person getPerson(int id, boolean refresh); public Person getPersonByName(String name, boolean refresh); public Person getPersonByEmail(String email, boolean refresh); - + public Collection<GeoLocations> getGeoLocations(Person owner, boolean refresh); - + // Update public void updatePersonData(Person person); public void updateGeoLocationsData(GeoLocations geoLocations); - public void updateGeoLocationData(GeoLocations geoLocations, GeoLocations geoLocation); - + public void updateGeoLocationData(GeoLocations geoLocations, GeoLocation geoLocation); + // Delete public void deletePerson(Person person); public void deleteGeoLocations(GeoLocations geoLocations); diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdMap.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdMap.java index e83e889afdbb13c7aca0142ed333826fd35f53ab..7eb2a5b2afad9d90fd4a5d14a185cc498e02ca0e 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdMap.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdMap.java @@ -4,31 +4,41 @@ import java.util.Collection; import java.util.HashMap; import java.util.Map; -public class IdMap<T> { +public class IdMap<T> implements IdProvider<T> { protected Map<Integer, T> id2o = new HashMap<Integer, T>(); protected Map<T, Integer> o2id = new HashMap<T, Integer>(); - public T get(int id) { + public int size() { + return o2id.size(); + } + + public T get(final int id) { return id2o.get(id); } - public int getId(T o) { - Integer id = o2id.get(o); + @Override + public boolean hasId(final T t) { + return o2id.containsKey(t); + } + + @Override + public int getId(final T o) { + final Integer id = o2id.get(o); return (id != null ? id : -1); } - + public Collection<T> get() { return o2id.keySet(); } - + public Collection<Integer> getIds() { return id2o.keySet(); } // - - void set(T o, int id) { + + public void set(final T o, final int id) { if (o2id.containsKey(o)) { throw new IllegalStateException(o + " already has the id " + o2id.get(o)); } @@ -38,32 +48,32 @@ public class IdMap<T> { id2o.put(id, o); o2id.put(o, id); } - - void clear() { + + public void clear() { o2id.clear(); - id2o.clear(); + id2o.clear(); } - - private void remove(T o, int id) { + + private void remove(final T o, final int id) { o2id.remove(o); - id2o.remove(id); + id2o.remove(id); } - void remove(T o) { + public void remove(final T o) { if (o2id.containsKey(o)) { remove(o, o2id.get(o)); } } - void removeAll(Iterable<T> os) { - for (T o : os) { + public void removeAll(final Iterable<T> os) { + for (final T o : os) { remove(o); } } - void remove(int id) { + public void remove(final int id) { if (id2o.containsKey(id)) { remove(id2o.get(id), id); - } + } } } diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdProvider.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..7f3af84efa972a82b15472dbc27ab6a8f1b33c5b --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/db/IdProvider.java @@ -0,0 +1,6 @@ +package tdt4140.gr1800.app.db; + +public interface IdProvider<T> { + public boolean hasId(T t); + public int getId(T t); +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/gpx/GpxDocumentConverter.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/gpx/GpxDocumentConverter.java index e8c65d0a5edc1fceb409147d4d53b12c83aadde3..a019497879322f734c82499cbbde94892c641756 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/gpx/GpxDocumentConverter.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/gpx/GpxDocumentConverter.java @@ -50,7 +50,7 @@ public class GpxDocumentConverter implements IDocumentLoader<Collection<GeoLocat public void setRouteCountFormat(String routeCountFormat) { this.routeCountFormat = routeCountFormat; } - + public Collection<GeoLocations> convert(GPX gpx) throws Exception { Collection<GeoLocations> geoLocations = new ArrayList<GeoLocations>(); int trackCount = 1; diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocatedSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocatedSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..6bf5acfea6682c076f751d642b16a00067919597 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocatedSerializer.java @@ -0,0 +1,31 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import tdt4140.gr1800.app.core.GeoLocated; + +public class GeoLocatedSerializer<T extends GeoLocated> extends StdSerializer<T> { + + public static final String LONGITUDE_FIELD_NAME = "longitude"; + public static final String LATITUDE_FIELD_NAME = "latitude"; + + public GeoLocatedSerializer(Class<T> clazz) { + super(clazz); + } + + protected void serialize(GeoLocated geoLocated, JsonGenerator jsonGen) throws IOException { + jsonGen.writeFieldName(LATITUDE_FIELD_NAME); + jsonGen.writeNumber(geoLocated.getLatitude()); + jsonGen.writeFieldName(LONGITUDE_FIELD_NAME); + jsonGen.writeNumber(geoLocated.getLongitude()); + } + + @Override + public void serialize(T value, JsonGenerator jsonGen, SerializerProvider provider) throws IOException { + serialize(value, jsonGen); + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..c728cf14a97c5906b07db9e72a882d09035997a1 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationDeserializer.java @@ -0,0 +1,62 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import tdt4140.gr1800.app.core.GeoLocation; +import tdt4140.gr1800.app.core.GeoLocations; + +public class GeoLocationDeserializer extends StdDeserializer<GeoLocation> { + + public GeoLocationDeserializer() { + super(GeoLocations.class); + } + + @Override + public GeoLocation deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException { + final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + return deserialize(jsonNode); + } + + private final TimedTaggedImplDeserializer timedTaggedImplDeserializer = new TimedTaggedImplDeserializer(); + private final LatLongDeserializer latLongDeserializer = new LatLongDeserializer(); + + GeoLocation deserialize(final JsonNode jsonNode) throws JsonProcessingException { + if (jsonNode instanceof ObjectNode) { + final ObjectNode objectNode = (ObjectNode) jsonNode; + final GeoLocation geoLocation = new GeoLocation(); + if (objectNode.has(GeoLocationsSerializer.NAME_FIELD_NAME)) { + final String name = objectNode.get(GeoLocationsSerializer.NAME_FIELD_NAME).asText(); + geoLocation.setName(name); + } + if (objectNode.has(GeoLocationsSerializer.DESCRIPTION_FIELD_NAME)) { + final String description = objectNode.get(GeoLocationsSerializer.DESCRIPTION_FIELD_NAME).asText(); + geoLocation.setDescription(description); + } + if (objectNode.has("location")) { + final JsonNode locationNode = objectNode.get("location"); + geoLocation.setLatLong(latLongDeserializer.deserialize(locationNode)); + } else { + geoLocation.setLatLong(latLongDeserializer.deserialize(objectNode)); + } + if (objectNode.has(GeoLocationSerializer.ELEVATION_FIELD_NAME)) { + final int elevation = objectNode.get(GeoLocationSerializer.ELEVATION_FIELD_NAME).asInt(); + geoLocation.setElevation(elevation); + } + timedTaggedImplDeserializer.deserialize(jsonNode, geoLocation); + return geoLocation; + } else if (jsonNode instanceof ArrayNode) { + final GeoLocation geoLocation = new GeoLocation(); + geoLocation.setLatLong(latLongDeserializer.deserialize(jsonNode)); + return geoLocation; + } + return null; + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..304c52816f462070384876611dfa01ac44fd1563 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationSerializer.java @@ -0,0 +1,39 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; + +import tdt4140.gr1800.app.core.GeoLocation; + +public class GeoLocationSerializer extends TimedTaggedSerializer<GeoLocation> { + + public static final String ELEVATION_FIELD_NAME = "elevation"; + + public GeoLocationSerializer() { + super(GeoLocation.class); + } + + private GeoLocatedSerializer<GeoLocation> geoLocatedSerializer = new GeoLocatedSerializer<>(GeoLocation.class); + + @Override + public void serialize(GeoLocation geoLocation, JsonGenerator jsonGen, SerializerProvider serProvider) throws IOException { + jsonGen.writeStartObject(); + super.serialize(geoLocation, jsonGen); + geoLocatedSerializer.serialize(geoLocation, jsonGen); + if (geoLocation.getElevation() != 0) { + jsonGen.writeFieldName(ELEVATION_FIELD_NAME); + jsonGen.writeNumber(geoLocation.getElevation()); + } + if (geoLocation.getName() != null) { + jsonGen.writeFieldName(GeoLocationsSerializer.NAME_FIELD_NAME); + jsonGen.writeString(geoLocation.getName()); + } + if (geoLocation.getDescription() != null) { + jsonGen.writeFieldName(GeoLocationsSerializer.DESCRIPTION_FIELD_NAME); + jsonGen.writeString(geoLocation.getDescription()); + } + jsonGen.writeEndObject(); + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..bc3a5137d5a466f9ce26066de4452eabec10e82a --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsDeserializer.java @@ -0,0 +1,48 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import tdt4140.gr1800.app.core.GeoLocation; +import tdt4140.gr1800.app.core.GeoLocations; + +public class GeoLocationsDeserializer extends StdDeserializer<GeoLocations> { + + public GeoLocationsDeserializer() { + super(GeoLocations.class); + } + + @Override + public GeoLocations deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException { + final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + return deserialize(jsonNode, deserContext); + } + + private final GeoLocationDeserializer geoLocationDeserializer = new GeoLocationDeserializer(); + + GeoLocations deserialize(final JsonNode jsonNode, final DeserializationContext deserContext) throws JsonProcessingException { + if (jsonNode instanceof ObjectNode) { + final ObjectNode objectNode = (ObjectNode) jsonNode; + final String name = objectNode.get(GeoLocationsSerializer.NAME_FIELD_NAME).asText(); + final GeoLocations geoLocations = new GeoLocations(name); + final JsonNode pathNode = objectNode.get(GeoLocationsSerializer.PATH_FIELD_NAME); + geoLocations.setPath(pathNode != null && pathNode.asBoolean(false)); + final JsonNode locationsNode = objectNode.get(GeoLocationsSerializer.LOCATIONS_FIELD_NAME); + if (locationsNode instanceof ArrayNode) { + for (final JsonNode locationNode : (ArrayNode) locationsNode) { + final GeoLocation geoLocation = geoLocationDeserializer.deserialize(locationNode); + geoLocations.addLocation(geoLocation); + } + } + return geoLocations; + } + return null; + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonDeserializer.java deleted file mode 100644 index c5f0e3dd4e844e3eb6803d31181fd24d746ba2dd..0000000000000000000000000000000000000000 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonDeserializer.java +++ /dev/null @@ -1,69 +0,0 @@ -package tdt4140.gr1800.app.json; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import tdt4140.gr1800.app.core.GeoLocations; -import tdt4140.gr1800.app.core.LatLong; - -public class GeoLocationsJsonDeserializer extends StdDeserializer<GeoLocations> { - - public GeoLocationsJsonDeserializer() { - super(GeoLocations.class); - } - - @Override - public GeoLocations deserialize(JsonParser jsonParser, DeserializationContext deserContext) throws IOException, JsonProcessingException { - JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); - return deserialize(jsonNode); - } - - private GeoLocations deserialize(JsonNode jsonNode) throws IOException, JsonProcessingException { - if (jsonNode instanceof ObjectNode) { - ObjectNode objectNode = (ObjectNode) jsonNode; - String name = objectNode.get(GeoLocationsJsonSerializer.NAME_FIELD_NAME).asText(); - GeoLocations geoLocations = new GeoLocations(name); - JsonNode pathNode = objectNode.get(GeoLocationsJsonSerializer.PATH_FIELD_NAME); - geoLocations.setPath(pathNode != null && pathNode.asBoolean(false)); - JsonNode locationsNode = objectNode.get(GeoLocationsJsonSerializer.LOCATIONS_FIELD_NAME); - if (locationsNode instanceof ArrayNode) { - for (JsonNode locationNode : (ArrayNode) locationsNode) { - LatLong latLong = decodeLatLong(locationNode); - geoLocations.addLocation(latLong); - } - } else { - LatLong latLong = decodeLatLong(locationsNode); - geoLocations.addLocation(latLong); - } - return geoLocations; - } - return null; - } - - private LatLong decodeLatLong(JsonNode locationNode) { - LatLong latLong = null; - if (locationNode instanceof ObjectNode) { - ObjectNode objectNode = (ObjectNode) locationNode; - if (objectNode.has(GeoLocationsJsonSerializer.LATITUDE_FIELD_NAME) && objectNode.has(GeoLocationsJsonSerializer.LONGITUDE_FIELD_NAME)) { - double lat = objectNode.get(GeoLocationsJsonSerializer.LATITUDE_FIELD_NAME).asDouble(); - double lon = objectNode.get(GeoLocationsJsonSerializer.LONGITUDE_FIELD_NAME).asDouble(); - latLong = new LatLong(lat, lon); - } - } else if (locationNode instanceof ArrayNode) { - ArrayNode arrayNode = (ArrayNode) locationNode; - if (arrayNode.size() == 2) { - double lat = arrayNode.get(0).asDouble(); - double lon = arrayNode.get(1).asDouble(); - latLong = new LatLong(lat, lon); - } - } - return latLong; - } -} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java deleted file mode 100644 index 66a078eca89a05489d52a219554a910463781b8f..0000000000000000000000000000000000000000 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonPersistence.java +++ /dev/null @@ -1,35 +0,0 @@ -package tdt4140.gr1800.app.json; - -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Collection; -import java.util.List; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; - -import tdt4140.gr1800.app.core.GeoLocations; -import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence; - -public class GeoLocationsJsonPersistence implements GeoLocationsStreamPersistence { - - private final ObjectMapper objectMapper; - - public GeoLocationsJsonPersistence() { - objectMapper = new ObjectMapper(); - SimpleModule module = new SimpleModule(); - module.addSerializer(new GeoLocationsJsonSerializer()); - module.addDeserializer(GeoLocations.class, new GeoLocationsJsonDeserializer()); - objectMapper.registerModule(module); - } - - @Override - public Collection<GeoLocations> loadLocations(InputStream inputStream) throws Exception { - return objectMapper.readValue(inputStream, objectMapper.getTypeFactory().constructCollectionType(List.class, GeoLocations.class)); - } - - @Override - public void saveLocations(Collection<GeoLocations> geoLocations, OutputStream outputStream) throws Exception { - objectMapper.writeValue(outputStream, geoLocations); - } -} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsPersistence.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsPersistence.java new file mode 100644 index 0000000000000000000000000000000000000000..8ceb613910fa764ed2bef2ec6d70241f514bacb2 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsPersistence.java @@ -0,0 +1,44 @@ +package tdt4140.gr1800.app.json; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import tdt4140.gr1800.app.core.GeoLocation; +import tdt4140.gr1800.app.core.GeoLocations; +import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence; +import tdt4140.gr1800.app.core.LatLong; +import tdt4140.gr1800.app.core.Person; + +public class GeoLocationsPersistence implements GeoLocationsStreamPersistence { + + private final ObjectMapper objectMapper; + + public GeoLocationsPersistence() { + objectMapper = new ObjectMapper(); + final SimpleModule module = new SimpleModule(); + module.addSerializer(new LatLongSerializer()); + module.addSerializer(new GeoLocationSerializer()); + module.addSerializer(new GeoLocationsSerializer()); + module.addSerializer(new PersonSerializer()); + module.addDeserializer(LatLong.class, new LatLongDeserializer()); + module.addDeserializer(GeoLocation.class, new GeoLocationDeserializer()); + module.addDeserializer(GeoLocations.class, new GeoLocationsDeserializer()); + module.addDeserializer(Person.class, new PersonDeserializer()); + objectMapper.registerModule(module); + } + + @Override + public Collection<GeoLocations> loadLocations(final InputStream inputStream) throws Exception { + return objectMapper.readValue(inputStream, objectMapper.getTypeFactory().constructCollectionType(List.class, GeoLocations.class)); + } + + @Override + public void saveLocations(final Collection<GeoLocations> geoLocations, final OutputStream outputStream) throws Exception { + objectMapper.writeValue(outputStream, geoLocations); + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsSerializer.java similarity index 66% rename from tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonSerializer.java rename to tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsSerializer.java index 2f08083a2f4bea94a40612fc1357a0ef034a568b..276d6ce6f8e00189991e1817d481699f05ca5895 100644 --- a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsJsonSerializer.java +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/GeoLocationsSerializer.java @@ -9,34 +9,37 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer; import tdt4140.gr1800.app.core.GeoLocated; import tdt4140.gr1800.app.core.GeoLocations; -public class GeoLocationsJsonSerializer extends StdSerializer<GeoLocations> { +public class GeoLocationsSerializer extends StdSerializer<GeoLocations> { - public static final String LONGITUDE_FIELD_NAME = "longitude"; - public static final String LATITUDE_FIELD_NAME = "latitude"; public static final String LOCATIONS_FIELD_NAME = "locations"; public static final String PATH_FIELD_NAME = "path"; public static final String NAME_FIELD_NAME = "name"; + public static final String DESCRIPTION_FIELD_NAME = "description"; - public GeoLocationsJsonSerializer() { + public GeoLocationsSerializer() { super(GeoLocations.class); } + private TimedTaggedSerializer<GeoLocations> timedTaggedSerializer = new TimedTaggedSerializer<>(GeoLocations.class); + @Override public void serialize(GeoLocations geoLocations, JsonGenerator jsonGen, SerializerProvider serProvider) throws IOException { jsonGen.writeStartObject(); jsonGen.writeFieldName(NAME_FIELD_NAME); jsonGen.writeString(geoLocations.getName()); + if (geoLocations.getDescription() != null) { + jsonGen.writeFieldName(DESCRIPTION_FIELD_NAME); + jsonGen.writeString(geoLocations.getDescription()); + } jsonGen.writeFieldName(PATH_FIELD_NAME); jsonGen.writeBoolean(geoLocations.isPath()); jsonGen.writeFieldName(LOCATIONS_FIELD_NAME); jsonGen.writeStartArray(); for (GeoLocated geoLoc : geoLocations) { - jsonGen.writeStartArray(); - jsonGen.writeNumber(geoLoc.getLatitude()); - jsonGen.writeNumber(geoLoc.getLongitude()); - jsonGen.writeEndArray(); + jsonGen.writeObject(geoLoc); } jsonGen.writeEndArray(); + timedTaggedSerializer.serialize(geoLocations, jsonGen); jsonGen.writeEndObject(); } } diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/IdSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/IdSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..3df8e3383890dbd780261bf50a692d46c8f7cd4f --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/IdSerializer.java @@ -0,0 +1,30 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import tdt4140.gr1800.app.db.IdProvider; + +public abstract class IdSerializer<T> extends StdSerializer<T> { + + public static final String ID_FIELD_NAME = "id"; + + public IdSerializer(final Class<T> clazz) { + super(clazz); + } + + private IdProvider<T> idProvider = null; + + public void setIdProvider(final IdProvider<T> idProvider) { + this.idProvider = idProvider; + } + + protected void serializeId(final T t, final JsonGenerator jsonGen) throws IOException { + if (idProvider != null && idProvider.hasId(t)) { + jsonGen.writeFieldName(ID_FIELD_NAME); + jsonGen.writeNumber(idProvider.getId(t)); + } + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..7f32025f4371d5e23acc2c842ae733173dbb7292 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongDeserializer.java @@ -0,0 +1,43 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import tdt4140.gr1800.app.core.LatLong; + +public class LatLongDeserializer extends StdDeserializer<LatLong> { + + public LatLongDeserializer() { + super(LatLong.class); + } + + @Override + public LatLong deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException { + final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + return deserialize(jsonNode); + } + + public LatLong deserialize(final JsonNode jsonNode) throws JsonProcessingException { + if (jsonNode instanceof ObjectNode) { + final ObjectNode objectNode = (ObjectNode) jsonNode; + final double latitude = objectNode.get(GeoLocatedSerializer.LATITUDE_FIELD_NAME).asDouble(); + final double longitude = objectNode.get(GeoLocatedSerializer.LONGITUDE_FIELD_NAME).asDouble(); + return new LatLong(latitude, longitude); + } else if (jsonNode instanceof ArrayNode) { + final ArrayNode locationArray = (ArrayNode) jsonNode; + if (locationArray.size() == 2) { + final double latitude = locationArray.get(0).asDouble(); + final double longitude = locationArray.get(1).asDouble(); + return new LatLong(latitude, longitude); + } + } + return null; + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..c1d911417f7a7a089d11f468f95aed28299597e2 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/LatLongSerializer.java @@ -0,0 +1,22 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; + +import tdt4140.gr1800.app.core.LatLong; + +public class LatLongSerializer extends GeoLocatedSerializer<LatLong> { + + public LatLongSerializer() { + super(LatLong.class); + } + + @Override + public void serialize(LatLong geoLocated, JsonGenerator jsonGen, SerializerProvider serProvider) throws IOException { + jsonGen.writeStartObject(); + super.serialize(geoLocated, jsonGen); + jsonGen.writeEndObject(); + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..7c7f32a61139b4478af722e6cc3427969ce1686c --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonDeserializer.java @@ -0,0 +1,51 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import tdt4140.gr1800.app.core.GeoLocations; +import tdt4140.gr1800.app.core.Person; + +public class PersonDeserializer extends StdDeserializer<Person> { + + public PersonDeserializer() { + super(Person.class); + } + + @Override + public Person deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException { + final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + return deserialize(jsonNode, deserContext); + } + + private final GeoLocationsDeserializer geoLocationsDeserializer = new GeoLocationsDeserializer(); + + Person deserialize(final JsonNode jsonNode, final DeserializationContext deserContext) throws JsonProcessingException { + if (jsonNode instanceof ObjectNode) { + final ObjectNode objectNode = (ObjectNode) jsonNode; + final Person person = new Person(); + if (objectNode.has(GeoLocationsSerializer.NAME_FIELD_NAME)) { + final String name = objectNode.get(GeoLocationsSerializer.NAME_FIELD_NAME).asText(); + person.setName(name); + } + if (objectNode.has(GeoLocationsSerializer.LOCATIONS_FIELD_NAME)) { + final JsonNode locationsNode = objectNode.get(GeoLocationsSerializer.LOCATIONS_FIELD_NAME); + if (locationsNode instanceof ArrayNode) { + for (final JsonNode childNode : ((ArrayNode) locationsNode)) { + final GeoLocations geoLocations = geoLocationsDeserializer.deserialize(childNode, deserContext); + person.addGeolocations(geoLocations); + } + } + } + return person; + } + return null; + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..9a556febbb96d81b037aaf9cb411d6748db121e1 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/PersonSerializer.java @@ -0,0 +1,40 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; + +import tdt4140.gr1800.app.core.GeoLocations; +import tdt4140.gr1800.app.core.Person; + +public class PersonSerializer extends IdSerializer<Person> { + + public static final String ID_FIELD_NAME = "id"; + public static final String NAME_FIELD_NAME = "name"; + public static final String EMAIL_FIELD_NAME = "email"; + public static final String GEOLOCATIONS_FIELD_NAME = "geoLocations"; + + public PersonSerializer() { + super(Person.class); + } + + @Override + public void serialize(final Person person, final JsonGenerator jsonGen, final SerializerProvider serProvider) throws IOException { + jsonGen.writeStartObject(); + serializeId(person, jsonGen); + jsonGen.writeFieldName(NAME_FIELD_NAME); + jsonGen.writeString(person.getName()); + if (person.getEmail() != null) { + jsonGen.writeFieldName(EMAIL_FIELD_NAME); + jsonGen.writeString(person.getEmail()); + } + jsonGen.writeFieldName(GEOLOCATIONS_FIELD_NAME); + jsonGen.writeStartArray(); + for (final GeoLocations geoLocations : person.getGeoLocations((String[]) null)) { + jsonGen.writeObject(geoLocations); + } + jsonGen.writeEndArray(); + jsonGen.writeEndObject(); + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedImplDeserializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedImplDeserializer.java new file mode 100644 index 0000000000000000000000000000000000000000..66f68fdf0b7ab971110ae618aacd67fdcd636e12 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedImplDeserializer.java @@ -0,0 +1,44 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import tdt4140.gr1800.app.core.TimedTaggedImpl; + +public class TimedTaggedImplDeserializer extends StdDeserializer<TimedTaggedImpl> { + + public TimedTaggedImplDeserializer() { + super(TimedTaggedImpl.class); + } + + @Override + public TimedTaggedImpl deserialize(final JsonParser jsonParser, final DeserializationContext deserContext) throws IOException, JsonProcessingException { + final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + return deserialize(jsonNode, new TimedTaggedImpl()); + } + + public TimedTaggedImpl deserialize(final JsonNode jsonNode, final TimedTaggedImpl timedTagged) throws JsonProcessingException { + if (jsonNode instanceof ObjectNode) { + final ObjectNode objectNode = (ObjectNode) jsonNode; + if (objectNode.has(TimedTaggedSerializer.DATE_FIELD_NAME)) { + timedTagged.setDate(LocalDate.parse(objectNode.get(TimedTaggedSerializer.DATE_FIELD_NAME).asText())); + } + if (objectNode.has(TimedTaggedSerializer.TIME_FIELD_NAME)) { + timedTagged.setTime(LocalTime.parse(objectNode.get(TimedTaggedSerializer.TIME_FIELD_NAME).asText())); + } + if (objectNode.has(TimedTaggedSerializer.ZONE_FIELD_NAME)) { + timedTagged.setZone(ZoneId.of(objectNode.get(TimedTaggedSerializer.ZONE_FIELD_NAME).asText())); + } + } + return timedTagged; + } +} diff --git a/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedSerializer.java b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedSerializer.java new file mode 100644 index 0000000000000000000000000000000000000000..859fb39a5c7b9dc94d65b9d36b020f984299f542 --- /dev/null +++ b/tdt4140-gr1800/app.core/src/main/java/tdt4140/gr1800/app/json/TimedTaggedSerializer.java @@ -0,0 +1,46 @@ +package tdt4140.gr1800.app.json; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import tdt4140.gr1800.app.core.TimedTaggedImpl; + +public class TimedTaggedSerializer<T extends TimedTaggedImpl> extends StdSerializer<T> { + + public static final String DATE_FIELD_NAME = "date"; + public static final String TIME_FIELD_NAME = "time"; + public static final String ZONE_FIELD_NAME = "zone"; + public static final String TAGS_FIELD_NAME = "tags"; + + public TimedTaggedSerializer(Class<T> clazz) { + super(clazz); + } + + protected void serialize(TimedTaggedImpl timedTagged, JsonGenerator jsonGen) throws IOException { + if (timedTagged.getDate() != null) { + jsonGen.writeFieldName(DATE_FIELD_NAME); + jsonGen.writeString(timedTagged.getDate().toString()); + } + if (timedTagged.getTime() != null) { + jsonGen.writeFieldName(TIME_FIELD_NAME); + jsonGen.writeString(timedTagged.getTime().toString()); + } + String[] tags = timedTagged.getTags(); + if (tags != null && tags.length > 0) { + jsonGen.writeFieldName(TAGS_FIELD_NAME); + jsonGen.writeStartArray(); + for (int i = 0; i < tags.length; i++) { + jsonGen.writeString(tags[i]); + } + jsonGen.writeEndArray(); + } + } + + @Override + public void serialize(T value, JsonGenerator jsonGen, SerializerProvider provider) throws IOException { + serialize(value, jsonGen); + } +} diff --git a/tdt4140-gr1800/app.core/src/main/resources/tdt4140/gr1800/app/db/schema.sql b/tdt4140-gr1800/app.core/src/main/resources/tdt4140/gr1800/app/db/schema.sql index 321c749e8b043366e55297301b6bc7ce820ac08d..3b16fc7bc69be19be34addd3ff03e65985c1e984 100644 --- a/tdt4140-gr1800/app.core/src/main/resources/tdt4140/gr1800/app/db/schema.sql +++ b/tdt4140-gr1800/app.core/src/main/resources/tdt4140/gr1800/app/db/schema.sql @@ -21,6 +21,7 @@ CREATE TABLE geoLocations ( date date NULL, time time NULL, + zone varchar(20) NULL, ); CREATE TABLE geoLocation ( @@ -36,6 +37,7 @@ CREATE TABLE geoLocation ( date date NULL, time time NULL, + zone varchar(20) NULL, ); CREATE TABLE tag ( diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java index fc5e13c0da1df6808f2a142d304e164161f9242b..ab6e089a7ab4463e5b4519ac728c45edbaad21de 100644 --- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java +++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/AppTest.java @@ -9,6 +9,7 @@ import org.junit.Before; import org.junit.Test; import tdt4140.gr1800.app.doc.IDocumentStorage; +import tdt4140.gr1800.app.json.GeoLocationsStreamPersistenceTest; public class AppTest { diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java deleted file mode 100644 index 2eaa8a872919dcf63b5bbb89f03cd0b4cbdfe53d..0000000000000000000000000000000000000000 --- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsStreamPersistenceTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package tdt4140.gr1800.app.core; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence; - -public class GeoLocationsStreamPersistenceTest { - - private GeoLocationsStreamPersistence persistence; - - @Before - public void setUp() { - persistence = new GeoLocationsJsonPersistence(); - } - - @Test - public void testLoadLocations() { - try { - Collection<GeoLocations> geoLocations = persistence.loadLocations(getClass().getResourceAsStream("geoLocations.json")); - testGeoLocationsDotJson(geoLocations); - } catch (Exception e) { - Assert.fail(e.getMessage()); - } - } - - public static Collection<GeoLocations> createGeoLocationsDotJson() { - return Arrays.asList( - new GeoLocations("1", new LatLong(63, 10), new LatLong(63.1, 10.1)), - new GeoLocations("2", new LatLong(64, 11), new LatLong(64.1, 11.1)) - ); - } - - public static void testGeoLocationsDotJson(Collection<GeoLocations> geoLocations) { - Assert.assertEquals(2, geoLocations.size()); - Iterator<GeoLocations> it = geoLocations.iterator(); - GeoLocationsTest.assertGeoLocations(it.next(), new LatLong(63, 10), new LatLong(63.1, 10.1)); - GeoLocationsTest.assertGeoLocations(it.next(), new LatLong(64, 11), new LatLong(64.1, 11.1)); - } - - @Test - public void testSaveLocations() { - Collection<GeoLocations> geoLocations = createGeoLocationsDotJson(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try { - persistence.saveLocations(geoLocations, outputStream); - outputStream.close(); - Collection<GeoLocations> geoLocations2 = persistence.loadLocations(new ByteArrayInputStream(outputStream.toByteArray())); - testGeoLocationsDotJson(geoLocations2); - } catch (Exception e) { - Assert.fail(e.getMessage()); - } - } -} diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java index c03b9e50612ce3a969a1a42e4c7957b29e3455c3..1081bb79c54449c557ec3b0000795cf980687e73 100644 --- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java +++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GeoLocationsTest.java @@ -10,7 +10,8 @@ import org.junit.Test; public class GeoLocationsTest extends TimedTaggedTest { private GeoLocations geoLocations; - + + @Override @Before public void setUp() { setUp(geoLocations = new GeoLocations()); @@ -24,12 +25,12 @@ public class GeoLocationsTest extends TimedTaggedTest { Assert.assertEquals(2, new GeoLocations(new LatLong(0, 0), new LatLong(1, 1)).size()); } - private void assertGeoLocations(LatLong...latLongs) { + private void assertGeoLocations(final LatLong...latLongs) { assertGeoLocations(geoLocations, latLongs); } - public static void assertGeoLocations(GeoLocations geoLocations, LatLong...latLongs) { - Iterator<GeoLocated> it = geoLocations.iterator(); + public static void assertGeoLocations(final GeoLocations geoLocations, final LatLong...latLongs) { + final Iterator<GeoLocated> it = geoLocations.iterator(); Assert.assertEquals(latLongs.length, geoLocations.size()); int pos = 0; while (it.hasNext()) { @@ -38,8 +39,8 @@ public class GeoLocationsTest extends TimedTaggedTest { } } - public static void assertGeoLocations(GeoLocations geoLocations, GeoLocations geoLocations2) { - Iterator<GeoLocated> it = geoLocations.iterator(), it2 = geoLocations2.iterator(); + public static void assertGeoLocations(final GeoLocations geoLocations, final GeoLocations geoLocations2) { + final Iterator<GeoLocated> it = geoLocations.iterator(), it2 = geoLocations2.iterator(); while (it.hasNext()) { Assert.assertTrue(it2.hasNext()); checkGeoLocated(it2.next(), it.next()); @@ -50,50 +51,50 @@ public class GeoLocationsTest extends TimedTaggedTest { @Test public void testAddLocation() { Assert.assertEquals(0, geoLocations.size()); - LatLong latLong1 = new LatLong(0, 0); + final LatLong latLong1 = new LatLong(0, 0); geoLocations.addLocation(latLong1); assertGeoLocations(latLong1); geoLocations.addLocation(new LatLong(0, 0)); assertGeoLocations(latLong1, latLong1); - LatLong latLong2 = new LatLong(1, 1); + final LatLong latLong2 = new LatLong(1, 1); geoLocations.addLocation(latLong2); assertGeoLocations(latLong1, latLong1, latLong2); } - static void checkGeoLocated(GeoLocated geoLoc1, GeoLocated geoLoc2) { - Assert.assertTrue(geoLoc1.equalsLatLong(geoLoc2)); + static void checkGeoLocated(final GeoLocated geoLoc1, final GeoLocated geoLoc2) { + Assert.assertTrue(geoLoc1.getLatLong().toString() + " expected, but was " + geoLoc2.getLatLong(), geoLoc1.equalsLatLong(geoLoc2)); } - + @Test - public void testFindLocationsNearby() { - LatLong latLong = new LatLong(0, 0); + public void testFindLocationsNearby() { + final LatLong latLong = new LatLong(0, 0); Assert.assertTrue(geoLocations.findLocationsNearby(latLong, 0).isEmpty()); geoLocations.addLocation(latLong); - Collection<GeoLocated> locationsNearby = geoLocations.findLocationsNearby(latLong, 0); + final Collection<GeoLocated> locationsNearby = geoLocations.findLocationsNearby(latLong, 0); Assert.assertEquals(1, locationsNearby.size()); - checkGeoLocated(latLong, geoLocations.iterator().next()); + checkGeoLocated(latLong, geoLocations.iterator().next()); } - + @Test public void testFindNearestLocation() { - LatLong latLong = new LatLong(0, 0); + final LatLong latLong = new LatLong(0, 0); Assert.assertNull(geoLocations.findNearestLocation(latLong)); geoLocations.addLocation(latLong); - GeoLocated nearestlocations = geoLocations.findNearestLocation(latLong); + final GeoLocated nearestlocations = geoLocations.findNearestLocation(latLong); checkGeoLocated(latLong, nearestlocations); } - + @Test public void testRemoveSameLocations() { - GeoLocations geoLocations1 = new GeoLocations(new LatLong(0, 0), new LatLong(1, 1)); + final GeoLocations geoLocations1 = new GeoLocations(new LatLong(0, 0), new LatLong(1, 1)); Assert.assertEquals(2, geoLocations1.size()); geoLocations1.removeLocations(new LatLong(0, 0), 0.0); Assert.assertEquals(1, geoLocations1.size()); geoLocations1.removeLocations(new LatLong(1, 1), 0.0); - Assert.assertEquals(0, geoLocations1.size()); - GeoLocations geoLocations2 = new GeoLocations(new LatLong(0, 0), new LatLong(0, 0)); + Assert.assertEquals(0, geoLocations1.size()); + final GeoLocations geoLocations2 = new GeoLocations(new LatLong(0, 0), new LatLong(0, 0)); Assert.assertEquals(2, geoLocations2.size()); geoLocations2.removeLocations(new LatLong(0, 0), 0.0); Assert.assertEquals(0, geoLocations2.size()); diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/db/HsqldbAccessTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/db/HsqldbAccessTest.java index 20c0f0b0f0acc5f0b324071d38f1b02bfca5808e..b585b22a67d23f26d82cd21b27a1ff7e138d4aee 100644 --- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/db/HsqldbAccessTest.java +++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/db/HsqldbAccessTest.java @@ -4,6 +4,8 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; +import java.time.LocalDate; +import java.time.LocalTime; import java.util.Collection; import java.util.Scanner; @@ -149,6 +151,10 @@ public class HsqldbAccessTest { Person hal = dbAccess.createPerson("hal", "hal@ntnu.no"); GeoLocations geoLocations1 = dbAccess.createGeoLocations(hal); geoLocations1.setName("geoLocs1"); + geoLocations1.setDescription("my first geo-location"); + geoLocations1.setDate(LocalDate.of(2018, 3, 14)); + geoLocations1.setTime(LocalTime.of(11, 14)); + geoLocations1.setZone("Europe/Oslo"); String[] tags = {"tag1", "tag2"}; geoLocations1.addTags(tags); dbAccess.updateGeoLocationsData(geoLocations1); @@ -158,6 +164,11 @@ public class HsqldbAccessTest { Assert.assertEquals(1, hal.getGeoLocations((String[]) null).size()); Assert.assertEquals(1, dbGeoLocations.size()); GeoLocations dbGeoLocations1 = dbGeoLocations.iterator().next(); + Assert.assertEquals(geoLocations1.getName(), dbGeoLocations1.getName()); + Assert.assertEquals(geoLocations1.getDescription(), dbGeoLocations1.getDescription()); + Assert.assertEquals(geoLocations1.getDate(), dbGeoLocations1.getDate()); + Assert.assertEquals(geoLocations1.getTime(), dbGeoLocations1.getTime()); + Assert.assertEquals(geoLocations1.getZone(), dbGeoLocations1.getZone()); Assert.assertEquals(2, dbGeoLocations1.getTags().length); Assert.assertTrue(dbGeoLocations1.hasTags(tags)); } diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GpxPersistenceTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/gpx/GpxPersistenceTest.java similarity index 97% rename from tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GpxPersistenceTest.java rename to tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/gpx/GpxPersistenceTest.java index 67a6fb87b40eec0f9e4cbba8b98875a593492a59..80b4db20d8094b21f95f45309da5c6a293cf6483 100644 --- a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/core/GpxPersistenceTest.java +++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/gpx/GpxPersistenceTest.java @@ -1,4 +1,4 @@ -package tdt4140.gr1800.app.core; +package tdt4140.gr1800.app.gpx; import java.util.Collection; import java.util.Iterator; @@ -8,6 +8,7 @@ import org.junit.Before; import org.junit.Test; import io.jenetics.jpx.GPX; +import tdt4140.gr1800.app.core.GeoLocations; import tdt4140.gr1800.app.gpx.GpxDocumentConverter; import tdt4140.gr1800.app.gpx.GpxDocumentLoader; diff --git a/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/json/GeoLocationsStreamPersistenceTest.java b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/json/GeoLocationsStreamPersistenceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fd72569325a6a936de4c6cba3fa16bd5a8f6253b --- /dev/null +++ b/tdt4140-gr1800/app.core/src/test/java/tdt4140/gr1800/app/json/GeoLocationsStreamPersistenceTest.java @@ -0,0 +1,67 @@ +package tdt4140.gr1800.app.json; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import tdt4140.gr1800.app.core.GeoLocations; +import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence; +import tdt4140.gr1800.app.core.GeoLocationsTest; +import tdt4140.gr1800.app.core.LatLong; + +public class GeoLocationsStreamPersistenceTest { + + private GeoLocationsStreamPersistence persistence; + + @Before + public void setUp() { + persistence = new GeoLocationsPersistence(); + } + + @Test + public void testLoadLocations() { + Collection<GeoLocations> geoLocations = null; + try { + geoLocations = persistence.loadLocations(getClass().getResourceAsStream("geoLocations.json")); + } catch (final Exception e) { + Assert.fail(e.getMessage()); + } + testGeoLocationsDotJson(geoLocations); + } + + public static Collection<GeoLocations> createGeoLocationsDotJson() { + return Arrays.asList( + new GeoLocations("1", new LatLong(63, 10), new LatLong(63.1, 10.1)), + new GeoLocations("2", new LatLong(64, 11), new LatLong(64.1, 11.1)) + ); + } + + public static void testGeoLocationsDotJson(final Collection<GeoLocations> geoLocations) { + Assert.assertEquals(2, geoLocations.size()); + final Iterator<GeoLocations> it = geoLocations.iterator(); + GeoLocationsTest.assertGeoLocations(it.next(), new LatLong(63, 10), new LatLong(63.1, 10.1)); + GeoLocationsTest.assertGeoLocations(it.next(), new LatLong(64, 11), new LatLong(64.1, 11.1)); + } + + @Test + public void testSaveLocations() { + final Collection<GeoLocations> geoLocations = createGeoLocationsDotJson(); + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + Collection<GeoLocations> geoLocations2 = null; + try { + persistence.saveLocations(geoLocations, outputStream); + outputStream.close(); + geoLocations2 = persistence.loadLocations(new ByteArrayInputStream(outputStream.toByteArray())); + } catch (final Exception e) { + e.printStackTrace(); + Assert.fail(e.getMessage()); + } + testGeoLocationsDotJson(geoLocations2); + } +} diff --git a/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/Achterbroek-route.gpx b/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/Achterbroek-route.gpx similarity index 100% rename from tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/Achterbroek-route.gpx rename to tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/Achterbroek-route.gpx diff --git a/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/Achterbroek-track.gpx b/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/Achterbroek-track.gpx similarity index 100% rename from tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/Achterbroek-track.gpx rename to tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/Achterbroek-track.gpx diff --git a/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/sample1.gpx b/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/sample1.gpx similarity index 100% rename from tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/sample1.gpx rename to tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/gpx/sample1.gpx diff --git a/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/geoLocations.json b/tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/json/geoLocations.json similarity index 100% rename from tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/core/geoLocations.json rename to tdt4140-gr1800/app.core/src/test/resources/tdt4140/gr1800/app/json/geoLocations.json diff --git a/tdt4140-gr1800/web.server/pom.xml b/tdt4140-gr1800/web.server/pom.xml index 57286633848e6cc761df3a89dd42d172e82c385e..785a7c64f47beff3d4cca72c84dd7191cecea7e9 100644 --- a/tdt4140-gr1800/web.server/pom.xml +++ b/tdt4140-gr1800/web.server/pom.xml @@ -50,6 +50,26 @@ <build> <plugins> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>0.8.0</version> + <executions> + <execution> + <id>pre-unit-test</id> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <execution> + <id>post-unit-test</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + </execution> + </executions> + </plugin> <plugin> <!-- https://mvnrepository.com/artifact/org.eclipse.jetty/jetty-maven-plugin --> <groupId>org.eclipse.jetty</groupId> diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/DeleteMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/DeleteMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..496abed0d98b9dc8e6616da9d1492d6a611a243e --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/DeleteMethod.java @@ -0,0 +1,23 @@ +package tdt4140.gr1800.web.server; + +public class DeleteMethod extends HttpMethod { + + public DeleteMethod(RestEntity<?> rootEntity) { + super(rootEntity); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation, int num) { + relation.deleteRelatedObject(parentEntity, num); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation, String qualifier) { + relation.deleteRelatedObject(parentEntity, qualifier); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation) { + relation.deleteRelatedObject(parentEntity, getPayload()); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/EntityJsonConverter.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/EntityJsonConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..cfb4fd8e225d4f5ca478fca777a7b29cee80cc2a --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/EntityJsonConverter.java @@ -0,0 +1,13 @@ +package tdt4140.gr1800.web.server; + +import com.fasterxml.jackson.databind.JsonNode; + +public interface EntityJsonConverter<T> { + + public static enum JsonDepth { + ID, CONTENTS, CONTAINED + } + + public boolean supportsEntity(Object entity, JsonDepth depth); + public JsonNode convert2Json(T entity, JsonDepth depth); +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoLocationsGeoLocationsRelation.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoLocationsGeoLocationsRelation.java new file mode 100644 index 0000000000000000000000000000000000000000..d5474987c69502677da73f22b168e60c40b27443 --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoLocationsGeoLocationsRelation.java @@ -0,0 +1,35 @@ +package tdt4140.gr1800.web.server; + +import java.util.Collection; + +import com.fasterxml.jackson.databind.JsonNode; + +import tdt4140.gr1800.app.core.GeoLocation; +import tdt4140.gr1800.app.core.GeoLocations; + +public class GeoLocationsGeoLocationsRelation extends RestRelation<GeoLocations, GeoLocation> { + + public GeoLocationsGeoLocationsRelation(String name, RestEntity<GeoLocation> relatedEntity) { + super(name, relatedEntity); + } + + @Override + public Collection<GeoLocation> getRelated(GeoLocations entity) { + throw new UnsupportedOperationException("NYI"); + } + + @Override + public GeoLocation createRelated(GeoLocations owner, JsonNode payload) { + throw new UnsupportedOperationException("NYI"); + } + + @Override + public void updateRelated(GeoLocations owner, GeoLocation related, JsonNode payload) { + getDbAccess().updateGeoLocationData(owner, related); + } + + @Override + public void deleteRelated(GeoLocations owner, GeoLocation related) { + getDbAccess().deleteGeoLocation(owner, related); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java index 9c00904e66aaf7471f0f74c93500c946aa579f1c..0e99b506c013fae43f9f0cc222e6b19203af822c 100644 --- a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GeoServlet.java @@ -2,121 +2,182 @@ package tdt4140.gr1800.web.server; import java.io.IOException; import java.io.OutputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; -import java.util.stream.Collectors; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.Iterator; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import io.jenetics.jpx.Person; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import tdt4140.gr1800.app.core.GeoLocation; import tdt4140.gr1800.app.core.GeoLocations; -import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence; -import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence; +import tdt4140.gr1800.app.core.LatLong; +import tdt4140.gr1800.app.core.Person; +import tdt4140.gr1800.app.db.DbAccessImpl; +import tdt4140.gr1800.app.db.IDbAccess; +import tdt4140.gr1800.app.json.GeoLocationDeserializer; +import tdt4140.gr1800.app.json.GeoLocationSerializer; +import tdt4140.gr1800.app.json.GeoLocationsDeserializer; +import tdt4140.gr1800.app.json.GeoLocationsSerializer; +import tdt4140.gr1800.app.json.LatLongDeserializer; +import tdt4140.gr1800.app.json.LatLongSerializer; +import tdt4140.gr1800.app.json.PersonDeserializer; +import tdt4140.gr1800.app.json.PersonSerializer; public class GeoServlet extends HttpServlet { - private GeoLocationsStreamPersistence persistence = new GeoLocationsJsonPersistence(); + private IDbAccess dbAccess; + private RestEntity<Void> rootEntity; + private ObjectMapper objectMapper; - private Collection<GeoLocations> allGeoLocations = new ArrayList<GeoLocations>(); - @Override public void init() throws ServletException { - String dataLocationsProp = System.getProperty("data.locations"); - if (dataLocationsProp != null) { - String[] dataLocations = dataLocationsProp.split(","); - for (int i = 0; i < dataLocations.length; i++) { - try { - Collection<GeoLocations> geoLocations = persistence.loadLocations(new URL(dataLocations[i]).openStream()); - allGeoLocations.addAll(geoLocations); - } catch (Exception e) { - System.err.println(e.getMessage()); - } - } + String dbConnectionUrl = "jdbc:hsqldb:mem:" + getClass().getName(); + final String dbConnectionParam = getInitParameter("dbConnectionURL"); + if (dbConnectionParam != null) { + dbConnectionUrl = dbConnectionParam; + } + try { + dbAccess = new DbAccessImpl(dbConnectionUrl); + } catch (final SQLException e) { + throw new ServletException("Error when initializing database connection (" + dbConnectionUrl + "): " + e, e); } - super.init(); + + rootEntity = new RestEntity<>(); + final RestEntity<Person> personEntity = new RestEntity<>(); + final RestEntity<GeoLocations> geoLocationsEntity = new RestEntity<>(); + final RestEntity<GeoLocation> geoLocationEntity = new RestEntity<>(); + + final RestRelation<Void, Person> rootPersonRelation = new RootPersonsRelation("persons", personEntity); + rootEntity.addRelation(rootPersonRelation); + final RestRelation<Person, ?> personGeoLocationsRelation = new PersonGeoLocationsRelation("geoLocations", geoLocationsEntity); + personGeoLocationsRelation.setDbAccess(dbAccess); + personEntity.addRelation(personGeoLocationsRelation); + final RestRelation<GeoLocations, ?> geoLocationsGeoLocationsRelation = new GeoLocationsGeoLocationsRelation("geoLocations", geoLocationEntity); + geoLocationsGeoLocationsRelation.setDbAccess(dbAccess); + geoLocationsEntity.addRelation(geoLocationsGeoLocationsRelation); + + objectMapper = new ObjectMapper(); + final SimpleModule module = new SimpleModule(); + module.addSerializer(new LatLongSerializer()); + module.addSerializer(new GeoLocationSerializer()); + module.addSerializer(new GeoLocationsSerializer()); + final PersonSerializer personSerializer = new PersonSerializer(); + personSerializer.setIdProvider(dbAccess.getPersonIdProvider()); + module.addSerializer(personSerializer); + module.addDeserializer(LatLong.class, new LatLongDeserializer()); + module.addDeserializer(GeoLocation.class, new GeoLocationDeserializer()); + module.addDeserializer(GeoLocations.class, new GeoLocationsDeserializer()); + module.addDeserializer(Person.class, new PersonDeserializer()); + objectMapper.registerModule(module); } - + // REST URL structure, according to https://blog.mwaysolutions.com/2014/06/05/10-best-practices-for-better-restful-api/ // persons/<id>/geoLocations/<num>/geoLocations/<num> // GET variants - // persons: Get all Person objects. Do we allow that? Should we return a list of <id> values or all the person entities (with some subset of properties) + // persons: Get all Person objects. Do we allow that? Should we return a list of <id> values or all the person entities (with some subset of properties) // persons/<id>: Get a specific Person object // persons/name or email: Get a specific Person object, with the provided name or email (with a '@') // persons/<id>/geoLocations: Get all the GeoLocations objects, with (some subset of) properties // persons/<id>/geoLocations/<num>: Get a specific GeoLocations object // persons/<id>/geoLocations/<num>/geoLocations: Get all GeoLocation objects, with (some subset of) properties // persons/<id>/geoLocations/<num>/geoLocations/<num>: Get a specific GeoLocations object - - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - String path = request.getPathInfo(); - Collection<GeoLocations> geoLocations = null; - if (path != null) { - if (path.startsWith("/")) { - path = path.substring(1); - } - String[] segments = path.split("\\/"); - if (segments.length == 1) { - geoLocations = allGeoLocations.stream().filter(geoLocation -> segments[0].equals(geoLocation.getName())).collect(Collectors.toList()); - } - } else { - geoLocations = allGeoLocations; - } - if (geoLocations == null) { + + @Override + protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { + final HttpMethod method = new GetMethod(rootEntity); + final Object result = method.doMethod(getPathSegments(request)); + if (result == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); - } else { - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_OK); - try (OutputStream output = response.getOutputStream()) { - persistence.saveLocations(geoLocations, output); - } catch (Exception e) { - response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } - } + } else { + respondWithJson(response, result); + } + } + + protected void respondWithJson(final HttpServletResponse response, final Object result) { + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_OK); + try (OutputStream output = response.getOutputStream()) { + objectMapper.writeValue(output, result); + } catch (final Exception e) { + response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + protected Iterator<String> getPathSegments(final HttpServletRequest request) throws ServletException { + final String path = request.getPathInfo(); + if (path == null) { + throw new ServletException("Path cannot be empty"); + } + final Iterator<String> segments = Arrays.asList(path.split("\\/")).iterator(); + return segments; + } // POST variants // persons: Create a new Person object, with properties in the payload // persons/<id>: Not allowed - // persons/<id>/geoLocations: Create a new GeoLocations object, with properties in the payload + // persons/<id>/geoLocations: Create a new GeoLocations object, with properties in the payload // persons/<id>/geoLocations/<num>: Not allowed // persons/<id>/geoLocations/<num>/geoLocations: Create a new GeoLocation object, with properties in the payload // persons/<id>/geoLocations/<num>/geoLocations/<num>: Not allowed - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - super.doPost(req, resp); - } - + @Override + protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { + final HttpMethod method = new PostMethod(rootEntity); + final JsonNode jsonNode = objectMapper.readTree(request.getInputStream()); + method.setPayload(jsonNode); + final Object result = method.doMethod(getPathSegments(request)); + if (result == null) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } else { + respondWithJson(response, result); + } + } + // PUT variants // persons: Not allowed // persons/<id>: Update specific Person object - // persons/<id>/geoLocations: Not allowed + // persons/<id>/geoLocations: Not allowed // persons/<id>/geoLocations/<num>: Update specific GeoLocations object // persons/<id>/geoLocations/<num>/geoLocations: Not allowed // persons/<id>/geoLocations/<num>/geoLocations/<num>: Update specific GeoLocations object - @Override - protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // TODO Auto-generated method stub - super.doPut(req, resp); - } - - // DELETE variants - // persons: Not allowed - // persons/<id>: Delete specific Person object - // persons/<id>/geoLocations: Delete all GeoLocations objects? - // persons/<id>/geoLocations/<num>: Delete specific GeoLocations object - // persons/<id>/geoLocations/<num>/geoLocations: Delete all GeoLocation objects? - // persons/<id>/geoLocations/<num>/geoLocations/<num>: Delete specific GeoLocation object - - @Override - protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - // TODO Auto-generated method stub - super.doDelete(req, resp); - } + @Override + protected void doPut(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { + final HttpMethod method = new PutMethod(rootEntity); + final JsonNode jsonNode = objectMapper.readTree(request.getInputStream()); + method.setPayload(jsonNode); + final Object result = method.doMethod(getPathSegments(request)); + if (result == null) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } else { + respondWithJson(response, result); + } + } + + // DELETE variants + // persons: Not allowed + // persons/<id>: Delete specific Person object + // persons/<id>/geoLocations: Delete all GeoLocations objects? + // persons/<id>/geoLocations/<num>: Delete specific GeoLocations object + // persons/<id>/geoLocations/<num>/geoLocations: Delete all GeoLocation objects? + // persons/<id>/geoLocations/<num>/geoLocations/<num>: Delete specific GeoLocation object + + @Override + protected void doDelete(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { + final HttpMethod method = new DeleteMethod(rootEntity); + final Object result = method.doMethod(getPathSegments(request)); + if (result == null) { + response.setStatus(HttpServletResponse.SC_NOT_FOUND); + } else { + respondWithJson(response, result); + } + } } diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GetMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GetMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..8a0ebead3fb99093c000bcf2006bb57e5a7a301c --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/GetMethod.java @@ -0,0 +1,23 @@ +package tdt4140.gr1800.web.server; + +public class GetMethod extends HttpMethod { + + public GetMethod(RestEntity<?> rootEntity) { + super(rootEntity); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation, int num) { + doRelated(relation, num); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation, String qualifier) { + doRelated(relation, qualifier); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation) { + doRelated(relation); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/HttpMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/HttpMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..8b34ad2b6e2797e254b317b0f2f0cbc452f5567d --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/HttpMethod.java @@ -0,0 +1,161 @@ +package tdt4140.gr1800.web.server; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +public abstract class HttpMethod { + + private final RestEntity<?> rootEntity; + + public HttpMethod(final RestEntity<?> rootEntity) { + this.rootEntity = rootEntity; + } + + protected Object parentEntity = null, entity = null; + protected Collection<?> entities = null; + + private Map<String, Object> params; + private JsonNode payload; + + public Map<String, Object> getParameters() { + return params; + } + + public void setParameters(final Map<String, ? extends Object> params) { + this.params = new HashMap<String, Object>(params); + } + + public void setParameters(final Object... params) { + this.params = new HashMap<String, Object>(); + for (int i = 0; i < params.length; i += 2) { + this.params.put(String.valueOf(params[i]), params[i + 1]); + } + } + + protected Object getParameter(final String param) { + if (params.containsKey(param)) { + return params.get(param); + } + if (payload instanceof ObjectNode) { + return ((ObjectNode) payload).get(param); + } + return null; + } + + private final JsonNodeFactory jsonNodeFactory = new JsonNodeFactory(false); + + public void setPayload(final JsonNode payload) { + this.payload = payload; + } + + public void setPayload(final Object... params) { + final ObjectNode objectNode = jsonNodeFactory.objectNode(); + for (int i = 0; i < params.length; i += 2) { + setPayloadValue(objectNode, String.valueOf(params[i]), params[i + 1]); + } + this.payload = objectNode; + } + + public void setPayload(final Map<String, ? extends Object> params) { + final ObjectNode objectNode = jsonNodeFactory.objectNode(); + for (final Map.Entry<String, ? extends Object> entry : params.entrySet()) { + setPayloadValue(objectNode, entry.getKey(), entry.getValue()); + } + this.payload = objectNode; + } + + protected void setPayloadValue(final ObjectNode objectNode, final String key, final Object value) { + if (value instanceof String) { + objectNode.put(key, (String) value); + } else if (value instanceof Number) { + objectNode.put(key, ((Number) value).doubleValue()); + } else if (value instanceof Boolean) { + objectNode.put(key, (Boolean) value); + } + } + + public JsonNode getPayload() { + return payload; + } + + public Object doMethod(final String... segments) { + return doMethod(Arrays.asList(segments)); + } + + public Object doMethod(final Iterable<String> segments) { + return doMethod(segments.iterator()); + } + + public Object doMethod(final Iterator<String> segments) { + RestEntity<?> restEntity = rootEntity; + entity = null; + while (segments.hasNext()) { + final String segment = segments.next(); + if (segment.length() == 0) { + continue; + } + final RestRelation<?, ?> relation = restEntity.getRelation(segment); + parentEntity = entity; + entity = null; + entities = null; + if (segments.hasNext()) { + final String qualifier = segments.next(); + try { + final int num = Integer.valueOf(qualifier); + if (segments.hasNext()) { + doRelated(relation, num); + } else { + doMethod(relation, num); + } + } catch (final NumberFormatException e) { + if (segments.hasNext()) { + doRelated(relation, qualifier); + } else { + doMethod(relation, qualifier); + } + } + restEntity = relation.getRelatedEntity(); + } else { + if (segments.hasNext()) { + doRelated(relation); + } else { + doMethod(relation); + } + } + if (entity == null) { + break; + } + } + if (entities != null) { + return entities; + } + return entity; + } + + protected abstract void doMethod(RestRelation<?, ?> relation, int num); + + protected abstract void doMethod(RestRelation<?, ?> relation, String qualifier); + + protected abstract void doMethod(RestRelation<?, ?> relation); + + // + + protected void doRelated(final RestRelation<?, ?> relation) { + entities = relation.getRelatedObjects(parentEntity); + } + + protected void doRelated(final RestRelation<?, ?> relation, final String qualifier) { + entity = relation.getRelatedObject(parentEntity, qualifier); + } + + protected void doRelated(final RestRelation<?, ?> relation, final int num) { + entity = relation.getRelatedObject(parentEntity, num); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PersonGeoLocationsRelation.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PersonGeoLocationsRelation.java new file mode 100644 index 0000000000000000000000000000000000000000..f60eea9ca12ae218a3bbbd27a6f5862ef7847c0d --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PersonGeoLocationsRelation.java @@ -0,0 +1,97 @@ +package tdt4140.gr1800.web.server; + +import java.time.DateTimeException; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeParseException; +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Consumer; +import java.util.function.Function; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.node.TextNode; + +import tdt4140.gr1800.app.core.GeoLocations; +import tdt4140.gr1800.app.core.Person; + +public class PersonGeoLocationsRelation extends RestRelation<Person, GeoLocations> { + + public PersonGeoLocationsRelation(String name, RestEntity<GeoLocations> relatedEntity) { + super(name, relatedEntity); + } + + @Override + public Collection<GeoLocations> getRelated(Person entity) { + return getDbAccess().getGeoLocations(entity, false); + } + + @Override + public GeoLocations createRelated(Person owner, JsonNode payload) { + GeoLocations geoLocations = getDbAccess().createGeoLocations(owner); + updateRelated(owner, geoLocations, payload); + return geoLocations; + } + + private final static Collection<String> NULL_STRINGS = Arrays.asList("", "\0"); + + protected boolean isNull(String s) { + return NULL_STRINGS.contains(s); + } + + protected <T, V> void set(T t, ObjectNode jsonNode, String fieldName, Consumer<String> setter) { + if (jsonNode.has(fieldName)) { + String s = jsonNode.get(fieldName).asText(); + setter.accept(isNull(s) ? null : s); + } + } + protected <T, V> void set(T t, ObjectNode jsonNode, String fieldName, Consumer<V> setter, Function<String, V> parser, Class<? extends Exception> exClass) { + if (jsonNode.has(fieldName)) { + String s = jsonNode.get(fieldName).asText(); + try { + setter.accept(isNull(s) ? null : parser.apply(s)); + } catch (RuntimeException e) { + if (exClass != null && (! exClass.isAssignableFrom(e.getClass()))) { + throw e; + } + } + } + } + + @Override + public void updateRelated(Person owner, GeoLocations related, JsonNode payload) { + if (payload instanceof ObjectNode) { + ObjectNode objectNode = (ObjectNode) payload; + set(related, objectNode, "name", related::setName); + set(related, objectNode, "description", related::setDescription); + set(related, objectNode, "date", related::setDate, LocalDate::parse, DateTimeParseException.class); + set(related, objectNode, "time", related::setTime, LocalTime::parse, DateTimeParseException.class); + set(related, objectNode, "zone", related::setZone, ZoneId::of, DateTimeException.class); + if (objectNode.has("tags")) { + JsonNode tagsNode = objectNode.get("tags"); + String[] tags = null; + if (tagsNode instanceof ArrayNode) { + ArrayNode tagsArray = (ArrayNode) tagsNode; + tags = new String[tagsArray.size()]; + for (int i = 0; i < tags.length; i++) { + tags[i] = tagsArray.get(i).asText(); + } + } else if (tagsNode instanceof TextNode) { + tags = ((TextNode) tagsNode).asText().split("[ ,;]"); + } + if (tags != null) { + related.setTags(tags); + } + } + } + getDbAccess().updateGeoLocationsData(related); + } + + @Override + public void deleteRelated(Person owner, GeoLocations related) { + getDbAccess().deleteGeoLocations(related); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PostMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PostMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..0c6558487922c129674be4238a3be9b7051a60c6 --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PostMethod.java @@ -0,0 +1,23 @@ +package tdt4140.gr1800.web.server; + +public class PostMethod extends HttpMethod { + + public PostMethod(RestEntity<?> rootEntity) { + super(rootEntity); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation, int num) { + throw new UnsupportedOperationException(); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation, String qualifier) { + throw new UnsupportedOperationException(); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation) { + entity = relation.createRelatedObject(parentEntity, getPayload()); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PutMethod.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PutMethod.java new file mode 100644 index 0000000000000000000000000000000000000000..37198d0d537e079a1f151ab436491ba671574b39 --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/PutMethod.java @@ -0,0 +1,25 @@ +package tdt4140.gr1800.web.server; + +public class PutMethod extends HttpMethod { + + public PutMethod(RestEntity<?> rootEntity) { + super(rootEntity); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation, int num) { + doRelated(relation, num); + relation.updateRelatedObject(parentEntity, entity, getPayload()); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation, String qualifier) { + doRelated(relation, qualifier); + relation.updateRelatedObject(parentEntity, entity, getPayload()); + } + + @Override + protected void doMethod(RestRelation<?, ?> relation) { + throw new UnsupportedOperationException(); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestEntity.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..984a4609d4469bf3fed6714495d683396e4e95cf --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestEntity.java @@ -0,0 +1,22 @@ +package tdt4140.gr1800.web.server; + +import java.util.ArrayList; +import java.util.Collection; + +public class RestEntity<T> { + + private final Collection<RestRelation<T, ?>> relations = new ArrayList<RestRelation<T,?>>(); + + public RestRelation<T, ?> getRelation(String relationName) { + for (RestRelation<T, ?> relation : relations) { + if (relationName.equals(relation.getName())) { + return relation; + } + } + return null; + } + + public void addRelation(RestRelation<T, ?> relation) { + this.relations.add(relation); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestRelation.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestRelation.java new file mode 100644 index 0000000000000000000000000000000000000000..b8f290360dd66f85bd18ddd000a6db1aa7ae4558 --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RestRelation.java @@ -0,0 +1,142 @@ +package tdt4140.gr1800.web.server; + +import java.util.Collection; +import java.util.NoSuchElementException; + +import com.fasterxml.jackson.databind.JsonNode; + +import tdt4140.gr1800.app.db.IDbAccess; + +public abstract class RestRelation<OE, RE> { + + private final String name; + + private RestEntity<RE> relatedEntity; + + protected RestRelation(String name, RestEntity<RE> relatedEntity) { + super(); + this.name = name; + this.relatedEntity = relatedEntity; + } + protected RestRelation(String name) { + this(name, null); + } + + public String getName() { + return name; + } + + public RestEntity<RE> getRelatedEntity() { + return relatedEntity; + } + + private IDbAccess dbAccess; + + public IDbAccess getDbAccess() { + return dbAccess; + } + + public void setDbAccess(IDbAccess dbAccess) { + this.dbAccess = dbAccess; + } + + public void setRelatedEntity(RestEntity<RE> relatedEntity) { + this.relatedEntity = relatedEntity; + } + + // GET method: /name + protected abstract Collection<RE> getRelated(OE entity); + + protected Collection<RE> getRelatedObjects(Object entity) { + return getRelated((OE) entity); + } + + // GET method: /name/<num> + protected RE getRelated(OE entity, int num) { + Collection<RE> col = getRelated(entity); + if (num < 0) { + num = col.size() + num; + } + for (RE related : col) { + if (num == 0) { + return related; + } + num--; + } + return null; + } + public RE getRelatedObject(Object entity, int num) { + return getRelated((OE) entity, num); + } + + protected boolean isRelated(RE related, String qualifier) { + return false; + } + + // GET method: /name/<qualifier> + protected RE getRelated(OE entity, String qualifier) { + for (RE related : getRelated(entity)) { + if (isRelated(related, qualifier)) { + return related; + } + } + return null; + } + public RE getRelatedObject(Object entity, String qualifier) { + return getRelated((OE) entity, qualifier); + } + + // POST method + protected abstract RE createRelated(OE owner, JsonNode payload); + + public Object createRelatedObject(Object owner, JsonNode payload) { + return createRelated((OE) owner, payload); + } + + // PUT method + protected abstract void updateRelated(OE owner, RE related, JsonNode payload); + + public void updateRelatedObject(Object owner, Object related, JsonNode payload) { + updateRelated((OE) owner, (RE) related, payload); + } + + // DELETE method + protected abstract void deleteRelated(OE owner, RE related); + + public void deleteRelatedObject(Object owner, Object related) { + deleteRelated((OE) owner, (RE) related); + } + + // DELETE method: /name/<num> + protected void deleteRelated(OE owner, int num) { + Collection<RE> col = getRelated(owner); + int count = (num < 0 ? col.size() + num : num); + for (RE related : col) { + if (count == 0) { + deleteRelated(owner, related); + return; + } + count--; + } + throw new NoSuchElementException("No element with '" + num + "' qualifier"); + } + + public void deleteRelatedObject(Object owner, int num) { + deleteRelated((OE) owner, num); + } + + // DELETE method: /name/<qualifier> + protected void deleteRelated(OE owner, String qualifier) { + for (RE related : getRelated(owner)) { + if (isRelated(related, qualifier)) { + deleteRelated(owner, related); + return; + } + } + throw new NoSuchElementException("No element with '" + qualifier + "' qualifier"); + } + + public void deleteRelatedObject(Object owner, String qualifier) { + deleteRelated((OE) owner, qualifier); + } +} diff --git a/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RootPersonsRelation.java b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RootPersonsRelation.java new file mode 100644 index 0000000000000000000000000000000000000000..f4e3aba7d2ea01a82d5eb71030f9cf0b7511eb79 --- /dev/null +++ b/tdt4140-gr1800/web.server/src/main/java/tdt4140/gr1800/web/server/RootPersonsRelation.java @@ -0,0 +1,64 @@ +package tdt4140.gr1800.web.server; + +import java.util.Collection; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import tdt4140.gr1800.app.core.Person; + +public class RootPersonsRelation extends RestRelation<Void, Person> { + + public RootPersonsRelation(String name, RestEntity<Person> relatedEntity) { + super(name, relatedEntity); + } + + @Override + public Collection<Person> getRelated(Void entity) { + return getDbAccess().getAllPersons(false); + } + + @Override + protected Person getRelated(Void entity, int num) { + return getDbAccess().getPerson(num, false); + } + + @Override + protected boolean isRelated(Person related, String qualifier) { + String propertyValue = qualifier.contains("@") ? related.getEmail() : related.getName(); + return qualifier.equals(propertyValue); + } + + @Override + public Person createRelated(Void owner, JsonNode payload) { + String name = null, email = null; + if (payload instanceof ObjectNode) { + ObjectNode objectNode = (ObjectNode) payload; + name = objectNode.get("name").asText(); + email = objectNode.get("email").asText(); + } + if (name == null || email == null) { + throw new IllegalArgumentException("name and email must be set: " + payload); + } + return getDbAccess().createPerson(name, email); + } + + @Override + public void updateRelated(Void owner, Person related, JsonNode payload) { + if (payload instanceof ObjectNode) { + ObjectNode objectNode = (ObjectNode) payload; + if (objectNode.has("name")) { + related.setName(objectNode.get("name").asText()); + } + if (objectNode.has("email")) { + related.setEmail(objectNode.get("email").asText()); + } + } + getDbAccess().updatePersonData(related); + } + + @Override + public void deleteRelated(Void owner, Person related) { + getDbAccess().deletePerson(related); + } +} diff --git a/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java index a230c446ad7269721f7913f58b793a31851f129c..1b2fd7c292ac7ba7ea971f8e64d7af33e2e97efa 100644 --- a/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java +++ b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/GeoLocationsServerIT.java @@ -1,57 +1,79 @@ package tdt4140.gr1800.web.server; +import java.io.ByteArrayOutputStream; import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; import java.net.URL; -import java.util.Collection; -import java.util.Iterator; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; -import tdt4140.gr1800.app.core.GeoLocated; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import tdt4140.gr1800.app.core.GeoLocation; import tdt4140.gr1800.app.core.GeoLocations; -import tdt4140.gr1800.app.core.GeoLocationsStreamPersistence; import tdt4140.gr1800.app.core.LatLong; -import tdt4140.gr1800.app.json.GeoLocationsJsonPersistence; +import tdt4140.gr1800.app.core.Person; +import tdt4140.gr1800.app.json.GeoLocationDeserializer; +import tdt4140.gr1800.app.json.GeoLocationSerializer; +import tdt4140.gr1800.app.json.GeoLocationsDeserializer; +import tdt4140.gr1800.app.json.GeoLocationsSerializer; +import tdt4140.gr1800.app.json.LatLongDeserializer; +import tdt4140.gr1800.app.json.LatLongSerializer; +import tdt4140.gr1800.app.json.PersonDeserializer; +import tdt4140.gr1800.app.json.PersonSerializer; public class GeoLocationsServerIT { - private GeoLocationsStreamPersistence persistence = new GeoLocationsJsonPersistence(); - - private Collection<GeoLocations> get(String path, int size) throws Exception { - URL url = new URL("http://localhost:8080/geo" + path); - try (InputStream inputStream = url.openStream()) { - Collection<GeoLocations> geoLocations = persistence.loadLocations(inputStream); - Assert.assertEquals(size, geoLocations.size()); - return geoLocations; - } - } + private ObjectMapper objectMapper; - private LatLong[][] latLongs = { - { new LatLong(63, 10), new LatLong(63.1, 10.1)}, - { new LatLong(64, 11), new LatLong(64.1, 11.1)} - }; - - @Test - public void testGet() throws Exception { - Collection<GeoLocations> geoLocations = get("", 2); - Iterator<GeoLocations> it = geoLocations.iterator(); - checkGeoLocations(it.next(), "1", latLongs[0]); - checkGeoLocations(it.next(), "2", latLongs[1]); - - Collection<GeoLocations> geoLocations1 = get("/1", 1); - checkGeoLocations(geoLocations1.iterator().next(), "1", latLongs[0]); - Collection<GeoLocations> geoLocations2 = get("/2", 1); - checkGeoLocations(geoLocations2.iterator().next(), "2", latLongs[1]); + @Before + public void setUp() { + objectMapper = new ObjectMapper(); + final SimpleModule module = new SimpleModule(); + module.addSerializer(new LatLongSerializer()); + module.addSerializer(new GeoLocationSerializer()); + module.addSerializer(new GeoLocationsSerializer()); + module.addSerializer(new PersonSerializer()); + module.addDeserializer(LatLong.class, new LatLongDeserializer()); + module.addDeserializer(GeoLocation.class, new GeoLocationDeserializer()); + module.addDeserializer(GeoLocations.class, new GeoLocationsDeserializer()); + module.addDeserializer(Person.class, new PersonDeserializer()); + objectMapper.registerModule(module); } - private void checkGeoLocations(GeoLocations geoLocations, String name, LatLong... latLongs) { - Assert.assertEquals(name, geoLocations.getName()); - Iterator<GeoLocated> it = geoLocations.iterator(); - for (int i = 0; i < latLongs.length; i++) { - Assert.assertTrue(it.hasNext()); - latLongs[i].equalsLatLong(it.next()); + @Test + public void testPostPerson() throws Exception { + final Person person = new Person(); + person.setName("Hallvard"); + person.setEmail("hal@ntnu.no"); + final URL url = new URL("http://localhost:8080/geo/persons"); + final HttpURLConnection con = ((HttpURLConnection) url.openConnection()); + con.setRequestMethod("POST"); + con.setDoOutput(true); + con.setDoInput(true); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + objectMapper.writeValue(out, person); + out.close(); + final byte[] bytes = out.toByteArray(); + con.setFixedLengthStreamingMode(bytes.length); + con.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + con.connect(); + try (OutputStream conOut = con.getOutputStream()) { + conOut.write(bytes); + } + try (InputStream conIn = url.openStream()) { + final JsonNode jsonTree = objectMapper.readTree(conIn); + Assert.assertTrue(jsonTree instanceof ObjectNode); + Assert.assertTrue(((ObjectNode) jsonTree).has("id")); + final Person conPerson = objectMapper.treeToValue(jsonTree, Person.class); + Assert.assertEquals(person.getName(), conPerson.getName()); + Assert.assertEquals(person.getEmail(), conPerson.getEmail()); } - Assert.assertFalse(it.hasNext()); } } diff --git a/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/HttpMethodTest.java b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/HttpMethodTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6bf4d06618f5748bc7a1a6395d6cf068e564cc0a --- /dev/null +++ b/tdt4140-gr1800/web.server/src/test/java/tdt4140/gr1800/web/server/HttpMethodTest.java @@ -0,0 +1,301 @@ +package tdt4140.gr1800.web.server; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import tdt4140.gr1800.app.core.GeoLocation; +import tdt4140.gr1800.app.core.GeoLocations; +import tdt4140.gr1800.app.core.LatLong; +import tdt4140.gr1800.app.core.Person; +import tdt4140.gr1800.app.db.AbstractDbAccessImpl; +import tdt4140.gr1800.app.db.IDbAccess; +import tdt4140.gr1800.app.json.GeoLocationDeserializer; +import tdt4140.gr1800.app.json.GeoLocationSerializer; +import tdt4140.gr1800.app.json.GeoLocationsDeserializer; +import tdt4140.gr1800.app.json.GeoLocationsSerializer; +import tdt4140.gr1800.app.json.LatLongDeserializer; +import tdt4140.gr1800.app.json.LatLongSerializer; +import tdt4140.gr1800.app.json.PersonDeserializer; +import tdt4140.gr1800.app.json.PersonSerializer; + +public class HttpMethodTest { + + private IDbAccess dbAccess; + private RestEntity<Void> rootEntity; + + private Map<String, Object> dbAccessRecord; + private ObjectMapper objectMapper; + + @Before + public void setUpEntities() { + + objectMapper = new ObjectMapper(); + final SimpleModule module = new SimpleModule(); + module.addSerializer(new LatLongSerializer()); + module.addSerializer(new GeoLocationSerializer()); + module.addSerializer(new GeoLocationsSerializer()); + module.addSerializer(new PersonSerializer()); + module.addDeserializer(LatLong.class, new LatLongDeserializer()); + module.addDeserializer(GeoLocation.class, new GeoLocationDeserializer()); + module.addDeserializer(GeoLocations.class, new GeoLocationsDeserializer()); + module.addDeserializer(Person.class, new PersonDeserializer()); + objectMapper.registerModule(module); + + dbAccessRecord = new HashMap<>(); + dbAccess = new AbstractDbAccessImpl() { + + @Override + public Person createPerson(final String name, final String email) { + final Person person = super.createPerson(name, email); + personIds.set(person, personIds.size() + 1); + dbAccessRecord.put("createPerson", "name=" + name + ";email=" + email); + return person; + } + + @Override + public Collection<Person> getAllPersons(final boolean refresh) { + final Collection<Person> persons = super.getAllPersons(refresh); + dbAccessRecord.put("getAllPersons", refresh); + return persons; + } + + @Override + public Person getPerson(final int id, final boolean refresh) { + final Person person = super.getPerson(id, refresh); + dbAccessRecord.put("getPerson", id); + return person; + } + + @Override + public void updatePersonData(final Person person) { + dbAccessRecord.put("updatePersonData", "name=" + person.getName() + ";email=" + person.getEmail()); + } + + @Override + public void deletePerson(final Person person) { + super.deletePerson(person); + dbAccessRecord.put("deletePerson", person.getName()); + } + + // + + @Override + public GeoLocations createGeoLocations(final Person owner) { + final GeoLocations geoLocations = super.createGeoLocations(owner); + geoLocationsIds.set(geoLocations, geoLocationsIds.size() + 1); + dbAccessRecord.put("createGeoLocations", owner.getGeoLocations((String[]) null).size()); + return geoLocations; + } + + @Override + public Collection<GeoLocations> getGeoLocations(final Person owner, final boolean refresh) { + final Collection<GeoLocations> geoLocations = super.getGeoLocations(owner, refresh); + dbAccessRecord.put("getGeoLocations", refresh); + return geoLocations; + } + + @Override + public void updateGeoLocationsData(final GeoLocations geoLocations) { + dbAccessRecord.put("updateGeoLocationsData", geoLocations.getName()); + } + + @Override + public void deleteGeoLocations(final GeoLocations geoLocations) { + super.deleteGeoLocations(geoLocations); + dbAccessRecord.put("deleteGeoLocations", geoLocations.getName()); + } + + @Override + public void updateGeoLocationData(final GeoLocations geoLocations, final GeoLocation geoLocation) { + } + }; + + rootEntity = new RestEntity<>(); + final RestEntity<Person> personEntity = new RestEntity<>(); + final RestEntity<GeoLocations> geoLocationsEntity = new RestEntity<>(); + final RestEntity<GeoLocation> geoLocationEntity = new RestEntity<>(); + + final RestRelation<Void, Person> rootPersonRelation = new RootPersonsRelation("persons", personEntity); + rootEntity.addRelation(rootPersonRelation); + rootPersonRelation.setDbAccess(dbAccess); + + final RestRelation<Person, ?> personGeoLocationsRelation = new PersonGeoLocationsRelation("geoLocations", geoLocationsEntity); + personGeoLocationsRelation.setDbAccess(dbAccess); + personEntity.addRelation(personGeoLocationsRelation); + + final RestRelation<GeoLocations, ?> geoLocationsGeoLocationsRelation = new GeoLocationsGeoLocationsRelation("geoLocations", geoLocationEntity); + geoLocationsGeoLocationsRelation.setDbAccess(dbAccess); + geoLocationsEntity.addRelation(geoLocationsGeoLocationsRelation); + } + + @Test + // create Person + public void testPostPerson() { + final HttpMethod method = new PostMethod(rootEntity); + method.setPayload("name" , "Hallvard", "email" , "hal@ntnu.no"); + final Object result = method.doMethod("persons"); + Assert.assertEquals("name=Hallvard;email=hal@ntnu.no", dbAccessRecord.get("createPerson")); + Assert.assertTrue(result instanceof Person); + } + + @Test + // read Person + public void testGetPerson() { + final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no"); + final HttpMethod method = new GetMethod(rootEntity); + final Object result = method.doMethod("persons"); + Assert.assertEquals(false, dbAccessRecord.get("getAllPersons")); + Assert.assertTrue(result instanceof Collection); + final Collection<?> persons = (Collection<?>) result; + Assert.assertEquals(1, persons.size()); + Assert.assertEquals(person, persons.iterator().next()); + } + + @Test + // update Person + public void testPutPerson() { + final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no"); + final HttpMethod method = new PutMethod(rootEntity); + final int id = 1; + method.setPayload("name" , "Hallvard Trætteberg", "email" , "hallvard.traetteberg@gmail.com"); + final Object result = method.doMethod("persons", String.valueOf(id)); + Assert.assertEquals("name=Hallvard Trætteberg;email=hallvard.traetteberg@gmail.com", dbAccessRecord.get("updatePersonData")); + Assert.assertTrue(result instanceof Person); + Assert.assertEquals("Hallvard Trætteberg", person.getName()); + Assert.assertEquals("hallvard.traetteberg@gmail.com", person.getEmail()); + } + + @Test + // delete Person + public void testDeletePerson() { + dbAccess.createPerson("Hallvard", "hal@ntnu.no"); + final HttpMethod method = new DeleteMethod(rootEntity); + method.doMethod("persons", "Hallvard"); + Assert.assertEquals("Hallvard", dbAccessRecord.get("deletePerson")); + Assert.assertTrue(dbAccess.getAllPersons(false).isEmpty()); + } + + @Test + // create GeoLocations + public void testPostGeoLocations() { + dbAccess.createPerson("Hallvard", "hal@ntnu.no"); + final HttpMethod method = new PostMethod(rootEntity); + method.setPayload( + "name" , "geoLocs1", + "description" , "my first geo-location", + "date", LocalDate.of(2018, 3, 14).toString(), + "time", LocalTime.of(11, 14).toString(), + "zone", "Europe/Oslo", + "tags", "tag1,tag2" + ); + final Object result = method.doMethod("persons", "Hallvard", "geoLocations"); + Assert.assertEquals(1, dbAccessRecord.get("createGeoLocations")); + Assert.assertTrue(result instanceof GeoLocations); + final GeoLocations geoLocations = (GeoLocations) result; + Assert.assertEquals("geoLocs1", geoLocations.getName()); + Assert.assertEquals("my first geo-location", geoLocations.getDescription()); + Assert.assertEquals(LocalDate.of(2018, 3, 14), geoLocations.getDate()); + Assert.assertEquals(LocalTime.of(11, 14), geoLocations.getTime()); + Assert.assertEquals(ZoneId.of("Europe/Oslo"), geoLocations.getZone()); + Assert.assertEquals("tag1,tag2", geoLocations.getTags(null, ",", null)); + } + + @Test + // read GeoLocations + public void testGetGeoLocations() { + final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no"); + dbAccess.createGeoLocations(person); + dbAccess.createGeoLocations(person); + final HttpMethod method = new GetMethod(rootEntity); + final Object result = method.doMethod("persons", "Hallvard", "geoLocations"); + Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations")); + Assert.assertTrue(result instanceof Collection<?>); + Assert.assertEquals(2, ((Collection<?>) result).size()); + + try { + objectMapper.writerWithDefaultPrettyPrinter().writeValue(System.out, person); + } catch (final Exception e) { + e.printStackTrace(); + } + } + + @Test + // read GeoLocations + public void testGetGeoLocations1() { + final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no"); + final GeoLocations geoLocations1 = dbAccess.createGeoLocations(person); + final GeoLocations geoLocations2 = dbAccess.createGeoLocations(person); + final HttpMethod method = new GetMethod(rootEntity); + final Object result0 = method.doMethod("persons", "Hallvard", "geoLocations", "0"); + Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations")); + Assert.assertSame(geoLocations1, result0); + final Object result1 = method.doMethod("persons", "Hallvard", "geoLocations", "1"); + Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations")); + Assert.assertSame(geoLocations2, result1); + final Object result2m = method.doMethod("persons", "Hallvard", "geoLocations", "-2"); + Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations")); + Assert.assertSame(geoLocations1, result2m); + final Object result1m = method.doMethod("persons", "Hallvard", "geoLocations", "-1"); + Assert.assertEquals(false, dbAccessRecord.get("getGeoLocations")); + Assert.assertSame(geoLocations2, result1m); + } + + @Test + // update GeoLocations + public void testUpdateGeoLocations() { + final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no"); + final GeoLocations geoLocations1 = dbAccess.createGeoLocations(person); + geoLocations1.setName("geoLocs1"); + geoLocations1.setDescription("my first geo-location"); + geoLocations1.setDate(LocalDate.of(2018, 3, 14)); + geoLocations1.setTime(LocalTime.of(11, 14)); + geoLocations1.setZone("Europe/Oslo"); + geoLocations1.setTags("tag1"); + final HttpMethod method = new PutMethod(rootEntity); + method.setPayload( + "name" , "geoLocs2", + "description" , "my second geo-location", + "date", LocalDate.of(2018, 3, 15).toString(), + "time", LocalTime.of(11, 15).toString(), + "zone", "Europe/Paris", + "tags", "tag2" + ); + method.doMethod("persons", "Hallvard", "geoLocations", "0"); + Assert.assertEquals("geoLocs2", dbAccessRecord.get("updateGeoLocationsData")); + Assert.assertEquals("geoLocs2", geoLocations1.getName()); + Assert.assertEquals("my second geo-location", geoLocations1.getDescription()); + Assert.assertEquals(LocalDate.of(2018, 3, 15), geoLocations1.getDate()); + Assert.assertEquals(LocalTime.of(11, 15), geoLocations1.getTime()); + Assert.assertEquals(ZoneId.of("Europe/Paris"), geoLocations1.getZone()); + Assert.assertEquals("tag2", geoLocations1.getTags(null, ",", null)); + } + + + @Test + // delete Person + public void testDeleteGeoLocations() { + final Person person = dbAccess.createPerson("Hallvard", "hal@ntnu.no"); + final GeoLocations geoLocations1 = dbAccess.createGeoLocations(person); + geoLocations1.setName("geoLoc1"); + final GeoLocations geoLocations2 = dbAccess.createGeoLocations(person); + geoLocations2.setName("geoLoc2"); + final HttpMethod method = new DeleteMethod(rootEntity); + method.doMethod("persons", "Hallvard", "geoLocations", "-2"); + Assert.assertEquals("geoLoc1", dbAccessRecord.get("deleteGeoLocations")); + Assert.assertEquals(1, person.getGeoLocations((String[]) null).size()); + method.doMethod("persons", "Hallvard", "geoLocations", "0"); + Assert.assertEquals("geoLoc2", dbAccessRecord.get("deleteGeoLocations")); + Assert.assertEquals(0, person.getGeoLocations((String[]) null).size()); + } +}