diff --git a/build.gradle b/build.gradle index 1c1bc38c..698e549a 100644 --- a/build.gradle +++ b/build.gradle @@ -32,7 +32,12 @@ project(':faunaJava') { dependencies { implementation project(':common') + implementation "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}" + implementation "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}" + implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}" + testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}" + testImplementation 'junit:junit:4.13.2' testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junitVersion}" testImplementation "org.mockito:mockito-core:${mockitoVersion}" } diff --git a/faunaJava/src/main/java/com/fauna/exception/SerializationException.java b/faunaJava/src/main/java/com/fauna/exception/SerializationException.java new file mode 100644 index 00000000..dd43cab6 --- /dev/null +++ b/faunaJava/src/main/java/com/fauna/exception/SerializationException.java @@ -0,0 +1,12 @@ +package com.fauna.exception; + +public class SerializationException extends RuntimeException { + + public SerializationException(String message) { + super(message); + } + + public SerializationException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/faunaJava/src/main/java/com/fauna/serialization/FaunaParser.java b/faunaJava/src/main/java/com/fauna/serialization/FaunaParser.java new file mode 100644 index 00000000..533e9f51 --- /dev/null +++ b/faunaJava/src/main/java/com/fauna/serialization/FaunaParser.java @@ -0,0 +1,115 @@ +package com.fauna.serialization; + +import static com.fauna.common.enums.FaunaTokenType.END_ARRAY; +import static com.fauna.common.enums.FaunaTokenType.END_DOCUMENT; +import static com.fauna.common.enums.FaunaTokenType.END_OBJECT; +import static com.fauna.common.enums.FaunaTokenType.END_PAGE; +import static com.fauna.common.enums.FaunaTokenType.END_REF; +import static com.fauna.common.enums.FaunaTokenType.NONE; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonToken; +import com.fauna.common.enums.FaunaTokenType; +import com.fauna.exception.SerializationException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.Stack; + +/** + * Represents a reader that provides fast, non-cached, forward-only access to serialized data. + */ +public class FaunaParser { + + private static final String INT_TAG = "@int"; + private static final String LONG_TAG = "@long"; + private static final String DOUBLE_TAG = "@double"; + private static final String TIME_TAG = "@time"; + private static final String DATE_TAG = "@date"; + private static final String REF_TAG = "@ref"; + private static final String DOC_TAG = "@doc"; + private static final String MOD_TAG = "@mod"; + private static final String SET_TAG = "@set"; + private static final String OBJECT_TAG = "@object";//TODO Understand Module + private final JsonParser jsonParser; + private final Stack tokenStack = new Stack<>(); + private FaunaTokenType currentFaunaTokenType; + private FaunaTokenType bufferedFaunaTokenType; + private String taggedTokenValue; + + public FaunaParser(InputStream body) throws IOException { + JsonFactory factory = new JsonFactory(); + this.jsonParser = factory.createParser(body); + currentFaunaTokenType = NONE; + } + + public FaunaParser(JsonParser jsonParser) { + this.jsonParser = jsonParser; + currentFaunaTokenType = NONE; + } + + public JsonToken getCurrentTokenType() { + return jsonParser.currentToken(); + } + + private final Set closers = new HashSet<>(Arrays.asList( + END_OBJECT, + END_PAGE, + END_DOCUMENT, + END_REF, + END_ARRAY + )); + + public boolean read() { + taggedTokenValue = null; + + if (bufferedFaunaTokenType != null) { + currentFaunaTokenType = bufferedFaunaTokenType; + bufferedFaunaTokenType = null; + if (closers.contains(currentFaunaTokenType)) { + tokenStack.pop(); + } + return true; + } + + if (!advance()) { + return false; + } + + JsonToken currentToken = jsonParser.currentToken(); + if (currentToken != null) { + switch (currentToken) { + case VALUE_STRING: + currentFaunaTokenType = FaunaTokenType.STRING; + break; + default: + throw new SerializationException( + "Unhandled JSON token type " + currentToken + "."); + } + } else { + return false; + } + + return true; + } + + private boolean advance() { + try { + return Objects.nonNull(jsonParser.nextToken()); + } catch (IOException e) { + throw new SerializationException("Failed to advance underlying JSON reader.", e); + } + } + + public String getValueAsString() { + try { + return jsonParser.getValueAsString(); + } catch (IOException e) { + throw new RuntimeException("Error reading current token as String", e); + } + } +} \ No newline at end of file diff --git a/faunaJava/src/test/java/com/fauna/serialization/FaunaParserTest.java b/faunaJava/src/test/java/com/fauna/serialization/FaunaParserTest.java new file mode 100644 index 00000000..d2ba8acd --- /dev/null +++ b/faunaJava/src/test/java/com/fauna/serialization/FaunaParserTest.java @@ -0,0 +1,53 @@ +package com.fauna.serialization; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.fauna.common.enums.FaunaTokenType; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + + +class FaunaParserTest { + + @Test + public void testGetValueAsString() throws IOException { + InputStream inputStream = new ByteArrayInputStream("\"hello\"".getBytes()); + FaunaParser reader = new FaunaParser(inputStream); + + List> expectedTokens = List.of( + Map.entry(FaunaTokenType.STRING, "hello") + ); + + assertReader(reader, expectedTokens); + } + + private static void assertReader(FaunaParser reader, + List> tokens) { + for (Map.Entry entry : tokens) { + reader.read(); + assertNotNull(entry.getKey()); + assertNotNull(reader.getCurrentTokenType()); + assertEquals(entry.getKey(), FaunaTokenType.STRING); + + switch (entry.getKey()) { + case FIELD_NAME: + case STRING: + assertEquals(entry.getValue(), reader.getValueAsString()); + break; + default: + assertNull(entry.getValue() == null); + break; + } + } + + assertFalse(reader.read()); + } + +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index c42a4e49..372461a8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,5 @@ version=0.1.0 junitVersion=5.10.0 -mockitoVersion=5.6.0 \ No newline at end of file +mockitoVersion=5.6.0 +jacksonVersion=2.15.3 \ No newline at end of file