Skip to content

Commit

Permalink
Feature/java 18 update (#5)
Browse files Browse the repository at this point in the history
* Updated Java version to 18

* StringNotClosedException now shows the startIndex of the expected string

* InvalidCharacterException now shows the startIndex of the invalid character.

* Specific exceptions thrown for invalid negative and invalid decimal numbers with index of where the error is found.

* InvalidKeywordException now shows index the invalid keyword starts

* InvalidControlCharacterException now shows index where the control character starts
Added InvalidUnicodeCharacterException to specifically call out those exceptions

* InvalidSyntaxException now says the expected token types and where the invalid actual token was found.

* readme update

* Added javadoc for JsonMapper

* JsonElement serialize now works for line separator and paragraph separator
  • Loading branch information
iamdudeman authored Jun 10, 2022
1 parent eb55cc2 commit 699c2ca
Show file tree
Hide file tree
Showing 18 changed files with 272 additions and 93 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up JDK 16
uses: actions/setup-java@v2
- uses: actions/checkout@v3
- name: Set up JDK 18
uses: actions/setup-java@v3
with:
java-version: 16
java-version: 18
distribution: 'temurin'
cache: gradle
- name: Build with Gradle
Expand Down
9 changes: 2 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,11 @@ value : STRING|NUMBER|object|array|TRUE|FALSE|NULL

## TODO List

* Error handling
* Line + character count for where bad characters found
* Better messages for why parsing fails
* Performance
* Continue to improve Tokenizer#tokenString method
* Research improvements to Tokenizer#tokenString method
* Research improvements to Tokenizer#tokenNumber method
* Improve Parser performance
* Research improving Parser performance
* Research alternative ways of getting initial character array for Tokenizer (maybe not using String#toCharArray())
* API
* Pretty serializing of JsonElement, JsonObject and JsonArray
* JavaDoc all the things
* Bugs
* JsonElement#toString (and SolaJson#serialize) doesn't support unicode characters
7 changes: 4 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ plugins {
id("java-library")
}

version = "1.0.2"
version = "2.0.0"

java {
sourceCompatibility = JavaVersion.VERSION_16
targetCompatibility = JavaVersion.VERSION_16
toolchain {
languageVersion.set(JavaLanguageVersion.of(18))
}
}

repositories {
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
7 changes: 6 additions & 1 deletion src/main/java/technology/sola/json/JsonElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,12 @@ private static String escapeNonUnicode(String s){
.replace("\n", "\\n")
.replace("\r", "\\r")
.replace("\f", "\\f")
.replace("\"", "\\\"");
.replace("\"", "\\\"")
// line separator
.replace("\u2028", "\\u2028")
// paragraph separator
.replace("\u2029", "\\u2029")
;
}

private void assertType(JsonValueType assertionType) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/technology/sola/json/JsonMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import java.util.List;

/**
* JsonMapper defines how a class will be converted to and from {@link JsonObject}s.
*
* @param <T> the type to define a JSON mapping for
*/
public interface JsonMapper<T> {
/**
* Converts object of type T into a {@link JsonObject}.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
package technology.sola.json.exception;

public class InvalidCharacterException extends RuntimeException {
public InvalidCharacterException(char invalidCharacter) {
super(String.format("Invalid character [%s]", invalidCharacter));
private final char invalidCharacter;
private final int startIndex;

public InvalidCharacterException(char invalidCharacter, int startIndex) {
super(String.format("Invalid character [%s] at [%s]", invalidCharacter, startIndex));

this.invalidCharacter = invalidCharacter;
this.startIndex = startIndex;
}

public char getInvalidCharacter() {
return invalidCharacter;
}

public int getStartIndex() {
return startIndex;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
package technology.sola.json.exception;

public class InvalidControlCharacterException extends RuntimeException {
private final int startIndex;

public InvalidControlCharacterException(int startIndex) {
super("Invalid control character at [" + startIndex + "]");
this.startIndex = startIndex;
}

public int getStartIndex() {
return startIndex;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package technology.sola.json.exception;

public class InvalidDecimalNumberException extends RuntimeException {
private final int startIndex;

public InvalidDecimalNumberException(int startIndex) {
super("Number for decimal expected starting at [" + startIndex + "]");
this.startIndex = startIndex;
}

public int getStartIndex() {
return startIndex;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
package technology.sola.json.exception;

public class InvalidKeywordException extends RuntimeException {
public InvalidKeywordException(String keyword, String current, char invalidChar) {
super("Expected keyword [" + keyword + "] but have [" + current + invalidChar + "]");
private final String expectedKeyword;
private final String receivedKeyword;
private final int startIndex;

public InvalidKeywordException(String keyword, String current, char invalidChar, int startIndex) {
super("Expected keyword [" + keyword + "] but received [" + current + invalidChar + "] at [" + startIndex + "]");
this.expectedKeyword = keyword;
this.receivedKeyword = current + invalidChar;
this.startIndex = startIndex;
}

public String getExpectedKeyword() {
return expectedKeyword;
}

public String getReceivedKeyword() {
return receivedKeyword;
}

public int getStartIndex() {
return startIndex;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package technology.sola.json.exception;

public class InvalidNegativeNumberException extends RuntimeException {
private final int startIndex;

public InvalidNegativeNumberException(int startIndex) {
super("Negative number expected following '-' at [" + startIndex + "]");
this.startIndex = startIndex;
}

public int getStartIndex() {
return startIndex;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
package technology.sola.json.exception;

import technology.sola.json.token.TokenType;

import java.util.Arrays;
import java.util.stream.Collectors;

public class InvalidSyntaxException extends RuntimeException {
public InvalidSyntaxException(TokenType actual, int index, TokenType ...expected) {
super("Expected [" + Arrays.stream(expected).map(TokenType::name).collect(Collectors.joining(" or ")) + "] but received [" + actual.name() + "] at [" + index + "]");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package technology.sola.json.exception;

public class InvalidUnicodeCharacterException extends RuntimeException {
private final int startIndex;

public InvalidUnicodeCharacterException(int startIndex) {
super("Invalid unicode character must be 4 numbers in length at [" + startIndex + "]");
this.startIndex = startIndex;
}

public int getStartIndex() {
return startIndex;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
package technology.sola.json.exception;

public class StringNotClosedException extends RuntimeException {
private final int startIndex;

public StringNotClosedException(int startIndex) {
super("String starting at [" + startIndex + "] not closed");

this.startIndex = startIndex;
}

public int getStartIndex() {
return startIndex;
}
}
11 changes: 7 additions & 4 deletions src/main/java/technology/sola/json/parser/Parser.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public AstNode parse() {
AstNode node = ruleRoot();

if (currentToken.type() != TokenType.EOF) {
throw new InvalidSyntaxException();
throw new InvalidSyntaxException(currentToken.type(), tokenizer.getTextIndex(), TokenType.EOF);
}

return node;
Expand All @@ -31,7 +31,7 @@ private AstNode ruleRoot() {
return switch (currentToken.type()) {
case L_BRACKET -> ruleArray();
case L_CURLY -> ruleObject();
default -> throw new InvalidSyntaxException();
default -> throw new InvalidSyntaxException(currentToken.type(), tokenizer.getTextIndex(), TokenType.L_BRACKET, TokenType.L_CURLY);
};
}

Expand Down Expand Up @@ -103,15 +103,18 @@ private AstNode ruleValue() {
eat(TokenType.NUMBER);
yield AstNode.value(token);
}
default -> throw new RuntimeException("Unrecognized value type " + token.type());
default -> throw new InvalidSyntaxException(
token.type(), tokenizer.getTextIndex(),
TokenType.L_BRACKET, TokenType.L_CURLY, TokenType.TRUE, TokenType.FALSE, TokenType.NULL, TokenType.STRING, TokenType.NUMBER
);
};
}

private void eat(TokenType tokenType) {
if (currentToken.type() == tokenType) {
currentToken = tokenizer.getNextToken();
} else {
throw new InvalidSyntaxException();
throw new InvalidSyntaxException(currentToken.type(), tokenizer.getTextIndex(), tokenType);
}
}
}
41 changes: 24 additions & 17 deletions src/main/java/technology/sola/json/token/Tokenizer.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public Tokenizer(String text) {
currentChar = characters[textIndex];
}

public int getTextIndex() {
return textIndex;
}

public Token getNextToken() {
if (currentChar == null) {
return new Token(TokenType.EOF);
Expand Down Expand Up @@ -75,7 +79,7 @@ public Token getNextToken() {
return new Token(TokenType.NULL);
}

throw new InvalidCharacterException(currentChar);
throw new InvalidCharacterException(currentChar, textIndex);
}

private Token tokenString() {
Expand All @@ -102,7 +106,7 @@ private Token tokenString() {

localPos++;
if (localPos >= buffer.length) {
throw new StringNotClosedException();
throw new StringNotClosedException(start);
}
localChar = buffer[localPos];
}
Expand All @@ -128,41 +132,44 @@ private Token tokenNumber() {
int characterCount = textIndex - startIndex;

if (characterCount == 1 && characters[startIndex] == '-') {
throw new InvalidNumberException();
throw new InvalidNegativeNumberException(startIndex);
}

return new Token(TokenType.NUMBER, new String(characters, startIndex, characterCount));
}

private void advanceKeywordTrue() {
int keywordStartIndex = textIndex;
advance();
if (currentChar != 'r') throw new InvalidKeywordException("true", "t", currentChar);
if (currentChar != 'r') throw new InvalidKeywordException("true", "t", currentChar, keywordStartIndex);
advance();
if (currentChar != 'u') throw new InvalidKeywordException("true", "tr", currentChar);
if (currentChar != 'u') throw new InvalidKeywordException("true", "tr", currentChar, keywordStartIndex);
advance();
if (currentChar != 'e') throw new InvalidKeywordException("true", "tru", currentChar);
if (currentChar != 'e') throw new InvalidKeywordException("true", "tru", currentChar, keywordStartIndex);
advance();
}

private void advanceKeywordNull() {
int keywordStartIndex = textIndex;
advance();
if (currentChar != 'u') throw new InvalidKeywordException("null", "n", currentChar);
if (currentChar != 'u') throw new InvalidKeywordException("null", "n", currentChar, keywordStartIndex);
advance();
if (currentChar != 'l') throw new InvalidKeywordException("null", "nu", currentChar);
if (currentChar != 'l') throw new InvalidKeywordException("null", "nu", currentChar, keywordStartIndex);
advance();
if (currentChar != 'l') throw new InvalidKeywordException("null", "nul", currentChar);
if (currentChar != 'l') throw new InvalidKeywordException("null", "nul", currentChar, keywordStartIndex);
advance();
}

private void advanceKeywordFalse() {
int keywordStartIndex = textIndex;
advance();
if (currentChar != 'a') throw new InvalidKeywordException("false", "f", currentChar);
if (currentChar != 'a') throw new InvalidKeywordException("false", "f", currentChar, keywordStartIndex);
advance();
if (currentChar != 'l') throw new InvalidKeywordException("false", "fa", currentChar);
if (currentChar != 'l') throw new InvalidKeywordException("false", "fa", currentChar, keywordStartIndex);
advance();
if (currentChar != 's') throw new InvalidKeywordException("false", "fal", currentChar);
if (currentChar != 's') throw new InvalidKeywordException("false", "fal", currentChar, keywordStartIndex);
advance();
if (currentChar != 'e') throw new InvalidKeywordException("false", "fals", currentChar);
if (currentChar != 'e') throw new InvalidKeywordException("false", "fals", currentChar, keywordStartIndex);
advance();
}

Expand All @@ -183,18 +190,18 @@ private int advanceEscapeCharacter(char[] buffer, int pos, StringBuilder stringB
localPos++;

if (localPos + 4 > buffer.length) {
throw new InvalidControlCharacterException();
throw new InvalidUnicodeCharacterException(localPos);
}

try {
int codePoint = Integer.parseInt(new String(buffer, localPos, 4), 16);
localPos += 3;
yield (char) codePoint;
} catch (NumberFormatException ex) {
throw new InvalidControlCharacterException();
throw new InvalidUnicodeCharacterException(localPos);
}
}
default -> throw new InvalidControlCharacterException();
default -> throw new InvalidControlCharacterException(localPos);
};

stringBuilder.append(result);
Expand All @@ -219,7 +226,7 @@ private void advanceFraction() {
}
}
if (textIndex - startFraction == 1) {
throw new InvalidNumberException();
throw new InvalidDecimalNumberException(startFraction);
}
}

Expand Down
Loading

0 comments on commit 699c2ca

Please sign in to comment.