diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/json/JsonNodeUnwrapper.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/json/JsonNodeUnwrapper.java new file mode 100644 index 0000000..80c803d --- /dev/null +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/json/JsonNodeUnwrapper.java @@ -0,0 +1,18 @@ +package uulm.teamname.marvelous.gamelibrary.json; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JsonNodeUnwrapper { + + private static final ObjectMapper mapper = new ObjectMapper(); + + public static T unwrap (JsonNode rootNode, String key, Class type) throws JsonProcessingException { + JsonNode subNode; + if ((subNode = rootNode.get(key)) != null) { + return mapper.readValue(subNode.toString(), type); + } + return null; + } +} diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/json/ingame/EventDeserializer.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/json/ingame/EventDeserializer.java index a288cc1..f86257f 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/json/ingame/EventDeserializer.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/json/ingame/EventDeserializer.java @@ -16,6 +16,8 @@ import uulm.teamname.marvelous.gamelibrary.events.EventBuilder; import uulm.teamname.marvelous.gamelibrary.events.EventType; import uulm.teamname.marvelous.gamelibrary.events.GamestateEvent; +import static uulm.teamname.marvelous.gamelibrary.json.JsonNodeUnwrapper.unwrap; + import java.io.IOException; import java.util.Arrays; import java.util.HashMap; @@ -74,40 +76,40 @@ public class EventDeserializer extends JsonDeserializer { // } // necessary because of exact type mismatch - var stoneTypeID = getIfNodeValid(node, "stoneType", EntityID.class); + var stoneTypeID = unwrap(node, "stoneType", EntityID.class); - builder.withTargetEntity(getIfNodeValid(node, "targetEntity", EntityID.class)) - .withTargetField(getIfNodeValid(node, "targetField", IntVector2.class)) - .withAmount(getIfNodeValid(node, "targetField", Integer.class)) - .withEntity(getIfNodeValid(node, "entity", Entity.class)) + builder.withTargetEntity(unwrap(node, "targetEntity", EntityID.class)) + .withTargetField(unwrap(node, "targetField", IntVector2.class)) + .withAmount(unwrap(node, "targetField", Integer.class)) + .withEntity(unwrap(node, "entity", Entity.class)) - .withOriginEntity(getIfNodeValid(node, "originEntity", EntityID.class)) - .withOriginField(getIfNodeValid(node, "originField", IntVector2.class)) + .withOriginEntity(unwrap(node, "originEntity", EntityID.class)) + .withOriginField(unwrap(node, "originField", IntVector2.class)) .withStoneType(stoneTypeID != null ? StoneType.valueOf(stoneTypeID.id) : null) - .withRoundCount(getIfNodeValid(node, "roundCount", Integer.class)) - .withTurnCount(getIfNodeValid(node, "turnCount", Integer.class)) - .withCharacterOrder(getIfNodeValid(node, "characterOrder", EntityID[].class)) - .withNextCharacter(getIfNodeValid(node, "nextCharacter", EntityID.class)) + .withRoundCount(unwrap(node, "roundCount", Integer.class)) + .withTurnCount(unwrap(node, "turnCount", Integer.class)) + .withCharacterOrder(unwrap(node, "characterOrder", EntityID[].class)) + .withNextCharacter(unwrap(node, "nextCharacter", EntityID.class)) - .withPlayerWon(getIfNodeValid(node, "playerWon", Integer.class)) + .withPlayerWon(unwrap(node, "playerWon", Integer.class)) - .withMessage(getIfNodeValid(node, "message", String.class)) - .withTimeLeft(getIfNodeValid(node, "timeLeft", Integer.class)) + .withMessage(unwrap(node, "message", String.class)) + .withTimeLeft(unwrap(node, "timeLeft", Integer.class)) - .withEntities(getIfNodeValid(node, "entities", Entity[].class)) - .withTurnOrder(getIfNodeValid(node, "turnOrder", EntityID[].class)) - .withMapSize(getIfNodeValid(node, "mapSize", IntVector2.class)) - .withActiveCharacter(getIfNodeValid(node, "activeCharacter", EntityID.class)) - .withStoneCooldowns(getIfNodeValid(node, "stoneCooldowns",Integer[].class)) - .withWinCondition(getIfNodeValid(node, "winCondition",Boolean.class)) + .withEntities(unwrap(node, "entities", Entity[].class)) + .withTurnOrder(unwrap(node, "turnOrder", EntityID[].class)) + .withMapSize(unwrap(node, "mapSize", IntVector2.class)) + .withActiveCharacter(unwrap(node, "activeCharacter", EntityID.class)) + .withStoneCooldowns(unwrap(node, "stoneCooldowns",Integer[].class)) + .withWinCondition(unwrap(node, "winCondition",Boolean.class)) - .withTeamIdentifier(getIfNodeValid(node, "teamIdentifier", String.class)) - .withCustomContent((HashMap) getIfNodeValid(node,"customContent", HashMap.class)); + .withTeamIdentifier(unwrap(node, "teamIdentifier", String.class)) + .withCustomContent(unwrap(node,"customContent", HashMap.class)); - builder.withTargetEntity(getIfNodeValid(node, "targetEntity", EntityID.class)); - builder.withTargetField(getIfNodeValid(node, "targetField", IntVector2.class)); + builder.withTargetEntity(unwrap(node, "targetEntity", EntityID.class)); + builder.withTargetField(unwrap(node, "targetField", IntVector2.class)); switch (eventType) { case Ack, Nack, GamestateEvent -> { return builder.buildGameStateEvent(); } @@ -136,11 +138,5 @@ public class EventDeserializer extends JsonDeserializer { return null; } - private T getIfNodeValid (JsonNode rootNode, String key, Class type) throws JsonProcessingException { - JsonNode subNode; - if ((subNode = rootNode.get(key)) != null) { - return mapper.readValue(subNode.toString(), type); - } - return null; - } + } diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/json/ingame/RequestDeserializer.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/json/ingame/RequestDeserializer.java new file mode 100644 index 0000000..7ad3ca4 --- /dev/null +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/json/ingame/RequestDeserializer.java @@ -0,0 +1,67 @@ +package uulm.teamname.marvelous.gamelibrary.json.ingame; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import uulm.teamname.marvelous.gamelibrary.IntVector2; +import uulm.teamname.marvelous.gamelibrary.entities.Entity; +import uulm.teamname.marvelous.gamelibrary.entities.EntityID; +import uulm.teamname.marvelous.gamelibrary.entities.StoneType; +import uulm.teamname.marvelous.gamelibrary.events.Event; +import uulm.teamname.marvelous.gamelibrary.requests.*; + +import java.io.IOException; +import java.util.Optional; + +import static uulm.teamname.marvelous.gamelibrary.json.JsonNodeUnwrapper.unwrap; + +public class RequestDeserializer extends JsonDeserializer { + + // static so that no reinitializations are needed + private static final ObjectMapper mapper = new ObjectMapper(); + + @Override + public Request deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { + ObjectCodec codec = p.getCodec(); + JsonNode node = codec.readTree(p); + + JsonNode requestTypeNode; + RequestType requestType; + if ((requestTypeNode = node.get("requestType")) != null) { + requestType = RequestType.valueOf(requestTypeNode.textValue()); + } else { + throw new IOException("No RequestType found. JSON does not represent a request"); + } + + var requestBuilder = new RequestBuilder(requestType) + .withValue(unwrap(node, "value", int.class)) + .withOriginEntity(unwrap(node, "originEntity", EntityID.class)) + .withTargetEntity(unwrap(node, "targetEntity", EntityID.class)) + .withOriginField(unwrap(node, "originField", IntVector2.class)) + .withTargetField(unwrap(node, "targetField", IntVector2.class)) + .withStoneType(unwrap(node, "stoneType", StoneType.class)); + + switch (requestType) { + case DisconnectRequest, + Req, + PauseStartRequest, + PauseStopRequest, + EndRoundRequest -> { + return requestBuilder.buildGameRequest(); + } + case ExchangeInfinityStoneRequest, + MeleeAttackRequest, + MoveRequest, + RangedAttackRequest, + UseInfinityStoneRequest -> { + return requestBuilder.buildCharacterRequest(); + } + } + + return null; // if there is no request type + } +} diff --git a/src/main/java/uulm/teamname/marvelous/gamelibrary/requests/Request.java b/src/main/java/uulm/teamname/marvelous/gamelibrary/requests/Request.java index 82f6adf..ace94f9 100644 --- a/src/main/java/uulm/teamname/marvelous/gamelibrary/requests/Request.java +++ b/src/main/java/uulm/teamname/marvelous/gamelibrary/requests/Request.java @@ -1,10 +1,13 @@ package uulm.teamname.marvelous.gamelibrary.requests; -import uulm.teamname.marvelous.gamelibrary.json.ingame.MessageStructure; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import uulm.teamname.marvelous.gamelibrary.json.basic.EventMessage; +import uulm.teamname.marvelous.gamelibrary.json.ingame.RequestDeserializer; import java.util.Objects; -/** Represents an abstract request sent inside a {@link MessageStructure} between client and server. */ +/** Represents an abstract request sent inside a {@link EventMessage} between client and server. */ +@JsonDeserialize(using = RequestDeserializer.class) public abstract class Request { public RequestType type; diff --git a/src/test/java/uulm/teamname/marvelous/gamelibrary/json/ingame/RequestDeserializerTest.java b/src/test/java/uulm/teamname/marvelous/gamelibrary/json/ingame/RequestDeserializerTest.java new file mode 100644 index 0000000..5e5028a --- /dev/null +++ b/src/test/java/uulm/teamname/marvelous/gamelibrary/json/ingame/RequestDeserializerTest.java @@ -0,0 +1,31 @@ +package uulm.teamname.marvelous.gamelibrary.json.ingame; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.assertj.core.api.Assertions.*; +import uulm.teamname.marvelous.gamelibrary.requests.GameRequest; +import uulm.teamname.marvelous.gamelibrary.requests.Request; +import uulm.teamname.marvelous.gamelibrary.requests.RequestType; + +public class RequestDeserializerTest { + + private ObjectMapper mapper; + + @BeforeEach + void beforeEach() { + mapper = new ObjectMapper(); + } + + @Test + void gameRequestDeserialization() throws JsonProcessingException { + var request = new GameRequest(); + request.type = RequestType.DisconnectRequest; + + var jsonRepresentingRequest = "{ \"requestType\": \"DisconnectRequest\" }"; + + assertThat((GameRequest) (mapper.readValue(jsonRepresentingRequest, Request.class))) + .isEqualTo(request); + } +}