Skip to content

io_nats_jparse_parser_indexoverlay

Rick Hightower edited this page Jul 18, 2023 · 1 revision

io.nats.jparse.parser.indexoverlay

class diagram

JsonStrictParser

The JsonStrictParser class is an implementation of the JsonParser interface. It uses a strict JSON parsing algorithm and does not accept JSON strings that are not strictly compliant with the JSON RFC.

private List scan(final CharSource source, TokenList tokens)

private List<Token> scan(final CharSource source, TokenList tokens) {
    nestLevel = 0;
    int ch = source.nextSkipWhiteSpace();
    switch(ch) {
        case OBJECT_START_TOKEN:
            parseObject(source, tokens);
            break;
        case ARRAY_START_TOKEN:
            parseArray(source, tokens);
            break;
        case TRUE_BOOLEAN_START:
            parseTrue(source, tokens);
            break;
        case FALSE_BOOLEAN_START:
            parseFalse(source, tokens);
            break;
        case NULL_START:
            parseNull(source, tokens);
            break;
        case STRING_START_TOKEN:
            parseString(source, tokens);
            break;
        case NUM_0:
        case NUM_1:
        case NUM_2:
        case NUM_3:
        case NUM_4:
        case NUM_5:
        case NUM_6:
        case NUM_7:
        case NUM_8:
        case NUM_9:
        case MINUS:
        case PLUS:
            parseNumber(source, tokens);
            break;
        default:
            throw new UnexpectedCharacterException("Scanning JSON", "Unexpected character", source, (char) ch);
    }
    source.checkForJunk();
    return tokens;
}

The scan method is defined in the JsonStrictParser class in the package io.nats.jparse.parser.indexoverlay. It takes two parameters: a CharSource object and a TokenList object.

Here is a step-by-step description of what the scan method does based on its body:

  1. It initializes the nestLevel variable to 0.

  2. It calls the nextSkipWhiteSpace method on the source object to get the next non-whitespace character and assigns it to the ch variable.

  3. It uses a switch statement to perform different actions based on the value of ch:

    • If ch is equal to OBJECT_START_TOKEN, it calls the parseObject method, passing the source and tokens objects.

    • If ch is equal to ARRAY_START_TOKEN, it calls the parseArray method, passing the source and tokens objects.

    • If ch is equal to TRUE_BOOLEAN_START, it calls the parseTrue method, passing the source and tokens objects.

    • If ch is equal to FALSE_BOOLEAN_START, it calls the parseFalse method, passing the source and tokens objects.

    • If ch is equal to NULL_START, it calls the parseNull method, passing the source and tokens objects.

    • If ch is equal to STRING_START_TOKEN, it calls the parseString method, passing the source and tokens objects.

    • If ch is equal to any of the number-related tokens (NUM_0, NUM_1, ..., NUM_9, MINUS, PLUS), it calls the parseNumber method, passing the source and tokens objects.

    • If none of the above cases match, it throws an UnexpectedCharacterException with a message indicating that an unexpected character was encountered.

  4. After the switch statement, it calls the checkForJunk method on the source object to check if there is any remaining junk after parsing the JSON.

  5. Finally, it returns the tokens object.

That's the step-by-step description of what the scan method does based on its body. sequence diagram

private void parseArray(final CharSource source, final TokenList tokens)

private void parseArray(final CharSource source, final TokenList tokens) {
    levelCheck(source);
    final int startSourceIndex = source.getIndex();
    final int tokenListIndex = tokens.getIndex();
    tokens.placeHolder();
    boolean done = false;
    while (!done) {
        done = parseArrayItem(source, tokens);
        if (!done) {
            done = source.findCommaOrEndForArray();
        }
    }
    final Token arrayToken = new Token(startSourceIndex, source.getIndex(), TokenTypes.ARRAY_TOKEN);
    tokens.set(tokenListIndex, arrayToken);
}

The parseArray method in class io.nats.jparse.parser.indexoverlay.JsonStrictParser is responsible for parsing an array from a given character source.

Here is a step-by-step description of what this method does:

  1. It starts by calling the levelCheck method, which verifies that the parser is at the correct level before parsing the array.
  2. It stores the current index of the character source and the index of the token list.
  3. It adds a placeholder token to the token list using the tokens.placeHolder() method.
  4. It enters a loop which continues until the parsing of the array is done.
  5. Inside the loop, it calls the parseArrayItem method to parse each item in the array. The method returns a boolean indicating whether the parsing is done or not.
  6. If the parsing is not done, it calls the source.findCommaOrEndForArray() method to find the next comma or the end of the array in the character source.
  7. The loop continues until the parsing of the array is done.
  8. After the loop, it creates a Token object representing the parsed array, using the start index and the current index of the character source. The token type is set to TokenTypes.ARRAY_TOKEN.
  9. Finally, it updates the placeholder token in the token list with the newly created array token using the tokens.set(tokenListIndex, arrayToken) method.

In summary, the parseArray method iterates over the items in the array, parses each item using the parseArrayItem method, and then creates a token representing the parsed array. sequence diagram

private boolean parseArrayItem(CharSource source, TokenList tokens)

private boolean parseArrayItem(CharSource source, TokenList tokens) {
    char startChar = source.getCurrentChar();
    int ch = source.nextSkipWhiteSpace();
    switch(ch) {
        case OBJECT_START_TOKEN:
            parseObject(source, tokens);
            break;
        case ARRAY_START_TOKEN:
            parseArray(source, tokens);
            break;
        case TRUE_BOOLEAN_START:
            parseTrue(source, tokens);
            break;
        case FALSE_BOOLEAN_START:
            parseFalse(source, tokens);
            break;
        case NULL_START:
            parseNull(source, tokens);
            break;
        case STRING_START_TOKEN:
            parseString(source, tokens);
            break;
        case NUM_0:
        case NUM_1:
        case NUM_2:
        case NUM_3:
        case NUM_4:
        case NUM_5:
        case NUM_6:
        case NUM_7:
        case NUM_8:
        case NUM_9:
        case MINUS:
        case PLUS:
            parseNumber(source, tokens);
            if (source.getCurrentChar() == ARRAY_END_TOKEN || source.getCurrentChar() == ARRAY_SEP) {
                if (source.getCurrentChar() == ARRAY_END_TOKEN) {
                    source.next();
                    return true;
                }
            }
            break;
        case ARRAY_END_TOKEN:
            if (startChar == ARRAY_SEP) {
                throw new UnexpectedCharacterException("Parsing Array Item", "Trailing comma", source, (char) ch);
            }
            source.next();
            return true;
        default:
            throw new UnexpectedCharacterException("Parsing Array Item", "Unexpected character", source, (char) ch);
    }
    return false;
}

The parseArrayItem method is defined in the JsonStrictParser class in the io.nats.jparse.parser.indexoverlay package. It takes two parameters, CharSource source and TokenList tokens, and returns a boolean value.

Here is a step-by-step description of what the parseArrayItem method does based on its body:

  1. It retrieves the current character from the source using the getCurrentChar method and assigns it to the startChar variable.
  2. It reads the next character from the source while skipping any whitespaces using the nextSkipWhiteSpace method and assigns it to the ch variable.
  3. It performs a switch-case statement based on the value of ch to determine the action to take:
    • If ch is equal to OBJECT_START_TOKEN, it calls the parseObject method passing source and tokens as arguments.
    • If ch is equal to ARRAY_START_TOKEN, it calls the parseArray method passing source and tokens as arguments.
    • If ch is equal to TRUE_BOOLEAN_START, it calls the parseTrue method passing source and tokens as arguments.
    • If ch is equal to FALSE_BOOLEAN_START, it calls the parseFalse method passing source and tokens as arguments.
    • If ch is equal to NULL_START, it calls the parseNull method passing source and tokens as arguments.
    • If ch is equal to STRING_START_TOKEN, it calls the parseString method passing source and tokens as arguments.
    • If ch is equal to any of the numeric characters (0-9) or the symbols '-' or '+', it calls the parseNumber method passing source and tokens as arguments.
      • After parsing the number, if the current character is either ARRAY_END_TOKEN or ARRAY_SEP, it checks if the current character is ARRAY_END_TOKEN.
        • If it is, it advances the source using the next method and returns true.
    • If ch is equal to ARRAY_END_TOKEN, it first checks if the startChar is ARRAY_SEP.
      • If it is, it throws an UnexpectedCharacterException with the message "Parsing Array Item: Trailing comma", source and the character as arguments.
      • If it is not, it advances the source using the next method and returns true.
    • If none of the above cases match, it throws an UnexpectedCharacterException with the message "Parsing Array Item: Unexpected character", source, and the character as arguments.
  4. If none of the cases match, it returns false.

The purpose of the parseArrayItem method is to parse an item within a JSON array and add tokens to the TokenList for further processing.

private boolean parseKey(final CharSource source, final TokenList tokens)

private boolean parseKey(final CharSource source, final TokenList tokens) {
    final char startChar = source.getCurrentChar();
    int ch = source.nextSkipWhiteSpace();
    final int startIndex = source.getIndex() - 1;
    final int tokenListIndex = tokens.getIndex();
    tokens.placeHolder();
    boolean found = false;
    switch(ch) {
        case STRING_START_TOKEN:
            final int strStartIndex = startIndex + 1;
            final int strEndIndex;
            if (objectsKeysCanBeEncoded) {
                strEndIndex = source.findEndOfEncodedString();
            } else {
                strEndIndex = source.findEndString();
            }
            tokens.add(new Token(strStartIndex + 1, strEndIndex, TokenTypes.STRING_TOKEN));
            found = true;
            break;
        case OBJECT_END_TOKEN:
            if (startChar == OBJECT_ATTRIBUTE_SEP) {
                throw new UnexpectedCharacterException("Parsing key", "Unexpected character found", source);
            }
            tokens.undoPlaceholder();
            return true;
        default:
            throw new UnexpectedCharacterException("Parsing key", "Unexpected character found", source);
    }
    boolean done = source.findObjectEndOrAttributeSep();
    if (!done && found) {
        tokens.set(tokenListIndex, new Token(startIndex + 1, source.getIndex(), TokenTypes.ATTRIBUTE_KEY_TOKEN));
    } else if (found && done) {
        throw new UnexpectedCharacterException("Parsing key", "Not found", source);
    }
    return done;
}

The parseKey method in the JsonStrictParser class is responsible for parsing a key from a CharSource and adding it to a TokenList. Here is a step-by-step description of this method:

  1. The method starts by getting the current character from the CharSource.
  2. It then skips any whitespace characters and gets the next character from the CharSource.
  3. The current index of the CharSource is stored as the starting index of the key.
  4. The current index of the TokenList is stored as the token list index.
  5. A placeholder token is added to the TokenList.
  6. A switch statement is used to handle different cases based on the value of the next character:
    • If the next character is a STRING_START_TOKEN, the method proceeds to extract the string key.
    • If the next character is an OBJECT_END_TOKEN, the method checks if the start character is an OBJECT_ATTRIBUTE_SEP and throws an exception if so. Otherwise, it undoes the placeholder token and returns true.
    • If none of the above cases match, an exception is thrown.
  7. If a string key is found, the method determines the end index of the string and adds a new token to the TokenList representing the string key.
  8. A boolean variable done is set based on whether the method can find the end of the object or the attribute separator.
  9. If the parsing is not done (!done) and a key is found (found is true), the method updates the placeholder token in the TokenList with the start and end indices of the key.
  10. If a key is found (found is true) and the parsing is done (done is true), an exception is thrown.
  11. Finally, the method returns the value of done.

This method is responsible for parsing a key from a JSON object and updating the TokenList with the parsed key token. sequence diagram

private boolean parseValue(final CharSource source, TokenList tokens)

private boolean parseValue(final CharSource source, TokenList tokens) {
    int ch = source.nextSkipWhiteSpace();
    final int startIndex = source.getIndex();
    final int tokenListIndex = tokens.getIndex();
    tokens.placeHolder();
    switch(ch) {
        case OBJECT_START_TOKEN:
            parseObject(source, tokens);
            break;
        case ARRAY_START_TOKEN:
            parseArray(source, tokens);
            break;
        case TRUE_BOOLEAN_START:
            parseTrue(source, tokens);
            break;
        case FALSE_BOOLEAN_START:
            parseFalse(source, tokens);
            break;
        case NULL_START:
            parseNull(source, tokens);
            break;
        case STRING_START_TOKEN:
            parseString(source, tokens);
            break;
        case NUM_0:
        case NUM_1:
        case NUM_2:
        case NUM_3:
        case NUM_4:
        case NUM_5:
        case NUM_6:
        case NUM_7:
        case NUM_8:
        case NUM_9:
        case MINUS:
        case PLUS:
            parseNumber(source, tokens);
            break;
        default:
            throw new UnexpectedCharacterException("Parsing Value", "Unexpected character", source, ch);
    }
    source.skipWhiteSpace();
    switch(source.getCurrentChar()) {
        case OBJECT_END_TOKEN:
            if (source.getIndex() == tokenListIndex) {
                throw new UnexpectedCharacterException("Parsing Value", "Key separator before value", source);
            }
            tokens.set(tokenListIndex, new Token(startIndex, source.getIndex(), TokenTypes.ATTRIBUTE_VALUE_TOKEN));
            return true;
        case OBJECT_ATTRIBUTE_SEP:
            if (source.getIndex() == tokenListIndex) {
                throw new UnexpectedCharacterException("Parsing Value", "Key separator before value", source);
            }
            tokens.set(tokenListIndex, new Token(startIndex, source.getIndex(), TokenTypes.ATTRIBUTE_VALUE_TOKEN));
            return false;
        default:
            throw new UnexpectedCharacterException("Parsing Value", "Unexpected character", source, source.getCurrentChar());
    }
}

The parseValue method in the JsonStrictParser class is responsible for parsing a single JSON value from a given character source (source) and adding the corresponding token to a token list (tokens). Here is a step-by-step description of what the method does:

  1. Read the next character from the character source while skipping any white space characters.
  2. Get the current index of the character source (startIndex) and the current index of the token list (tokenListIndex).
  3. Add a placeholder token to the token list at the current index.
  4. Use a switch statement to check the value of the character read in step 1.
    • If the character is the start of an object (OBJECT_START_TOKEN), call the parseObject method to parse the object and add the corresponding token(s) to the token list.
    • If the character is the start of an array (ARRAY_START_TOKEN), call the parseArray method to parse the array and add the corresponding token(s) to the token list.
    • If the character is the start of the boolean value true (TRUE_BOOLEAN_START), call the parseTrue method to parse the boolean value and add the corresponding token(s) to the token list.
    • If the character is the start of the boolean value false (FALSE_BOOLEAN_START), call the parseFalse method to parse the boolean value and add the corresponding token(s) to the token list.
    • If the character is the start of the value null (NULL_START), call the parseNull method to parse the null value and add the corresponding token(s) to the token list.
    • If the character is the start of a string (STRING_START_TOKEN), call the parseString method to parse the string value and add the corresponding token(s) to the token list.
    • If the character is a digit (NUM_0 to NUM_9) or a sign (MINUS or PLUS), call the parseNumber method to parse the number value and add the corresponding token(s) to the token list.
    • If the character does not match any of the above cases, throw an UnexpectedCharacterException with an appropriate error message.
  5. Skip any white space characters after parsing the value.
  6. Use a switch statement to check the current character in the character source.
    • If the character is the end of an object (OBJECT_END_TOKEN), check if the current index of the character source is the same as the tokenListIndex. If they are equal, throw an UnexpectedCharacterException with an error message indicating "Key separator before value". Otherwise, set the token at the tokenListIndex to be a new token representing the parsed value and return true from the method.
    • If the character is the attribute separator (OBJECT_ATTRIBUTE_SEP), check if the current index of the character source is the same as the tokenListIndex. If they are equal, throw an UnexpectedCharacterException with an error message indicating "Key separator before value". Otherwise, set the token at the tokenListIndex to be a new token representing the parsed value and return false from the method.
    • If the current character does not match any of the above cases, throw an UnexpectedCharacterException with an appropriate error message. sequence diagram

private void parseObject(final CharSource source, TokenList tokens)

private void parseObject(final CharSource source, TokenList tokens) {
    levelCheck(source);
    final int startSourceIndex = source.getIndex();
    final int tokenListIndex = tokens.getIndex();
    tokens.placeHolder();
    boolean done = false;
    while (!done) {
        done = parseKey(source, tokens);
        if (!done)
            done = parseValue(source, tokens);
    }
    source.next();
    tokens.set(tokenListIndex, new Token(startSourceIndex, source.getIndex(), TokenTypes.OBJECT_TOKEN));
}

The parseObject method is a private method defined in the class io.nats.jparse.parser.indexoverlay.JsonStrictParser. This method takes two parameters: source, which is an instance of the CharSource class, and tokens, which is an instance of the TokenList class.

The purpose of this method is to parse an object from the given source and add the corresponding token to the tokens list.

Here is a step-by-step description of what the parseObject method is doing based on its body:

  1. The method starts by calling the levelCheck method passing the source as a parameter, which ensures that the nesting level of the JSON object is within certain bounds.

  2. The next two lines of code store the current index of the source and the index of the tokens list.

  3. The method then adds a placeholder token to the tokens list using the placeHolder method.

  4. The method enters a loop that continues until the parsing is done.

  5. Inside the loop, the method first calls the parseKey method passing the source and tokens as parameters. The parseKey method is not shown in the provided code, but it is assumed to return a boolean value indicating whether the key parsing is done or not. The return value is stored in the done variable.

  6. If the key parsing is not done (i.e., done is false), the method calls the parseValue method passing the source and tokens as parameters. The parseValue method is not shown in the provided code, but it is assumed to return a boolean value indicating whether the value parsing is done or not. The return value is also stored in the done variable.

  7. The loop continues until both the key and value parsing is done.

  8. After the loop is done, the next line of code calls the next method on the source to advance to the next character in the input.

  9. Finally, the last line of code sets the token at the tokenListIndex in the tokens list to a new Token object. This Token object is created with the startSourceIndex, which is the starting index of the source, the current index of the source, and the TokenTypes.OBJECT_TOKEN type.

Overall, the parseObject method is responsible for parsing a JSON object from the source and adding a token representing that object to the tokens list. sequence diagram

private void levelCheck(CharSource source)

private void levelCheck(CharSource source) {
    nestLevel++;
    if (nestLevel > NEST_LEVEL) {
        throw new UnexpectedCharacterException("Next level violation", "Too many levels " + nestLevel, source);
    }
}

Method: levelCheck

This method is defined in the JsonStrictParser class, which resides in the io.nats.jparse.parser.indexoverlay package. It performs the following steps:

  1. Increments the value of the nestLevel variable by 1.
  2. Checks if the value of nestLevel is greater than NEST_LEVEL.
  3. If the condition evaluates to true, it throws an UnexpectedCharacterException.
    • The exception message is set to "Next level violation".
    • The exception details include "Too many levels" followed by the value of nestLevel.
    • The source parameter is also passed to the exception constructor.

This method is used to ensure that the nesting level in a JSON input does not exceed a predefined threshold (NEST_LEVEL). If the nesting level exceeds this threshold, an exception is thrown. sequence diagram

JsonFastParser

The JsonFastParser class is an implementation of the JsonParser interface that provides methods for scanning and parsing JSON strings. It offers functionality for scanning a character source and returning a list of tokens, as well as parsing a character source and returning a root node representing the parsed JSON. The class also includes default methods for parsing and scanning strings and extends the ParseConstants interface, which defines constants used for parsing JSON strings.

private List scan(final CharSource source, TokenList tokens)

private List<Token> scan(final CharSource source, TokenList tokens) {
    int ch = source.nextSkipWhiteSpace();
    switch(ch) {
        case OBJECT_START_TOKEN:
            parseObject(source, tokens);
            break;
        case ARRAY_START_TOKEN:
            parseArray(source, tokens);
            break;
        case TRUE_BOOLEAN_START:
            parseTrue(source, tokens);
            break;
        case FALSE_BOOLEAN_START:
            parseFalse(source, tokens);
            break;
        case NULL_START:
            parseNull(source, tokens);
            break;
        case STRING_START_TOKEN:
            parseString(source, tokens);
            break;
        case NUM_0:
        case NUM_1:
        case NUM_2:
        case NUM_3:
        case NUM_4:
        case NUM_5:
        case NUM_6:
        case NUM_7:
        case NUM_8:
        case NUM_9:
        case MINUS:
        case PLUS:
            parseNumber(source, tokens);
            break;
        default:
            throw new UnexpectedCharacterException("Scanning JSON", "Unexpected character", source, (char) ch);
    }
    return tokens;
}

The method scan is defined in the JsonFastParser class in the package io.nats.jparse.parser.indexoverlay. It takes two parameters, a CharSource object named source and a TokenList object named tokens. The purpose of this method is to scan the given input source character by character and determine the appropriate action to take based on the encountered character.

Here is a step-by-step description of what the scan method does based on its body:

  1. The method starts by calling the nextSkipWhiteSpace method on the source object, which returns the next non-whitespace character from the input source. This character is stored in the variable ch.

  2. A switch statement is used to determine the action to take based on the value of ch.

  3. If ch is equal to OBJECT_START_TOKEN, the parseObject method is called with the source and tokens objects as parameters. This method parses an object from the input source and adds the corresponding tokens to the tokens list.

  4. If ch is equal to ARRAY_START_TOKEN, the parseArray method is called with the source and tokens objects as parameters. This method parses an array from the input source and adds the corresponding tokens to the tokens list.

  5. If ch is equal to TRUE_BOOLEAN_START, the parseTrue method is called with the source and tokens objects as parameters. This method parses a true boolean value from the input source and adds the corresponding token to the tokens list.

  6. If ch is equal to FALSE_BOOLEAN_START, the parseFalse method is called with the source and tokens objects as parameters. This method parses a false boolean value from the input source and adds the corresponding token to the tokens list.

  7. If ch is equal to NULL_START, the parseNull method is called with the source and tokens objects as parameters. This method parses a null value from the input source and adds the corresponding token to the tokens list.

  8. If ch is equal to STRING_START_TOKEN, the parseString method is called with the source and tokens objects as parameters. This method parses a string from the input source and adds the corresponding token to the tokens list.

  9. If ch is equal to NUM_0, NUM_1, NUM_2, NUM_3, NUM_4, NUM_5, NUM_6, NUM_7, NUM_8, NUM_9, MINUS, or PLUS, the parseNumber method is called with the source and tokens objects as parameters. This method parses a number from the input source and adds the corresponding token to the tokens list.

  10. If none of the above cases match, an UnexpectedCharacterException is thrown with an error message indicating that the character is unexpected.

  11. Finally, the tokens list is returned.

In summary, the scan method scans the input source character by character and performs different parsing actions based on the encountered character, such as parsing objects, arrays, booleans, strings, numbers, or throwing an exception for unexpected characters. The method then returns the list of tokens that were parsed.

sequence diagram

private void parseArray(final CharSource source, final TokenList tokens)

private void parseArray(final CharSource source, final TokenList tokens) {
    final int startSourceIndex = source.getIndex();
    final int tokenListIndex = tokens.getIndex();
    tokens.placeHolder();
    boolean done = false;
    while (!done) {
        done = parseArrayItem(source, tokens);
    }
    final Token arrayToken = new Token(startSourceIndex, source.getIndex(), TokenTypes.ARRAY_TOKEN);
    tokens.set(tokenListIndex, arrayToken);
}

The parseArray method in the JsonFastParser class is responsible for parsing an array from a given CharSource and storing the resulting tokens in a TokenList object.

Here is a step-by-step description of what the method does:

  1. It starts by storing the current index of the CharSource in a variable called startSourceIndex.
  2. It also stores the current index of the TokenList in a variable called tokenListIndex.
  3. It adds a placeholder token to the TokenList using the placeHolder() method. This placeholder token will be replaced with the actual array token later.
  4. It initializes a boolean variable done to false. This variable will be used to determine when to stop parsing the array.
  5. It enters a while loop that continues until the done variable becomes true.
  6. Inside the while loop, it calls the parseArrayItem method, passing the source and tokens objects. This method is responsible for parsing a single item of the array.
  7. The return value of the parseArrayItem method is then used to update the done variable. If the parseArrayItem method returns true, it means that it has successfully parsed an array item. If it returns false, it means that there are no more array items to parse, and the loop should be terminated.
  8. After the while loop finishes, it creates a Token object called arrayToken with the start and end indices of the array in the CharSource, and the type TokenTypes.ARRAY_TOKEN.
  9. Finally, it replaces the placeholder token in the TokenList at the tokenListIndex with the arrayToken using the set method.

This completes the step-by-step description of the parseArray method in the JsonFastParser class. sequence diagram

private boolean parseArrayItem(CharSource source, TokenList tokens)

private boolean parseArrayItem(CharSource source, TokenList tokens) {
    char ch = (char) source.nextSkipWhiteSpace();
    forLoop: for (; ch != ETX; ch = (char) source.nextSkipWhiteSpace()) {
        switch(ch) {
            case OBJECT_START_TOKEN:
                parseObject(source, tokens);
                break forLoop;
            case ARRAY_START_TOKEN:
                parseArray(source, tokens);
                break forLoop;
            case TRUE_BOOLEAN_START:
                parseTrue(source, tokens);
                break forLoop;
            case FALSE_BOOLEAN_START:
                parseFalse(source, tokens);
                break forLoop;
            case NULL_START:
                parseNull(source, tokens);
                break forLoop;
            case STRING_START_TOKEN:
                parseString(source, tokens);
                break forLoop;
            case NUM_0:
            case NUM_1:
            case NUM_2:
            case NUM_3:
            case NUM_4:
            case NUM_5:
            case NUM_6:
            case NUM_7:
            case NUM_8:
            case NUM_9:
            case MINUS:
            case PLUS:
                parseNumber(source, tokens);
                break forLoop;
            case ARRAY_END_TOKEN:
                source.next();
                return true;
            case ARRAY_SEP:
                source.next();
                return false;
            default:
                throw new UnexpectedCharacterException("Parsing Array Item", "Unexpected character", source, ch);
        }
    }
    if (source.getCurrentChar() == ARRAY_END_TOKEN) {
        source.next();
        return true;
    }
    return false;
}

The method parseArrayItem in the JsonFastParser class is used to parse an individual item within a JSON array. Here is a step-by-step description of what this method does:

  1. The method takes two parameters: source, which is the source of the characters to be parsed, and tokens, which is a list to store the parsed tokens.

  2. It starts by reading the next character from the source and assigns it to the variable ch. The whitespace characters are skipped.

  3. It enters a for loop, labeled as forLoop, which iterates until the character ch is ETX (End of Text).

  4. Inside the loop, there is a switch statement that checks the value of the character ch.

  5. If ch is equal to OBJECT_START_TOKEN, it calls the parseObject method to parse the JSON object, and then break forLoop is executed, which exits the for loop.

  6. If ch is equal to ARRAY_START_TOKEN, it calls the parseArray method to parse the JSON array, and then break forLoop is executed.

  7. If ch is equal to TRUE_BOOLEAN_START, it calls the parseTrue method to parse a boolean value of true, and then break forLoop is executed.

  8. If ch is equal to FALSE_BOOLEAN_START, it calls the parseFalse method to parse a boolean value of false, and then break forLoop is executed.

  9. If ch is equal to NULL_START, it calls the parseNull method to parse a null value, and then break forLoop is executed.

  10. If ch is equal to STRING_START_TOKEN, it calls the parseString method to parse a string value, and then break forLoop is executed.

  11. If ch is a numeric digit (NUM_0 to NUM_9) or a minus or plus sign (MINUS or PLUS), it calls the parseNumber method to parse a numeric value, and then break forLoop is executed.

  12. If ch is equal to ARRAY_END_TOKEN, it moves to the next character in the source and returns true, indicating that the parsing of the array item is complete.

  13. If ch is equal to ARRAY_SEP, it moves to the next character in the source and returns false, indicating that there are more items in the array to be parsed.

  14. If none of the above cases match, it throws an UnexpectedCharacterException with an error message indicating that an unexpected character was encountered while parsing the array item.

  15. After the for loop, it checks if the current character in the source is equal to ARRAY_END_TOKEN. If it is, the method moves to the next character, returns true, indicating that the parsing of the array item is complete.

  16. If the current character is not ARRAY_END_TOKEN, the method returns false, indicating that there are additional array items to be parsed.

That's the step-by-step description of the parseArrayItem method.

private boolean parseKey(final CharSource source, final TokenList tokens)

private boolean parseKey(final CharSource source, final TokenList tokens) {
    int ch = source.nextSkipWhiteSpace();
    final int startIndex = source.getIndex() - 1;
    final int tokenListIndex = tokens.getIndex();
    tokens.placeHolder();
    boolean found = false;
    switch(ch) {
        case STRING_START_TOKEN:
            final int strStartIndex = startIndex + 1;
            final int strEndIndex;
            if (objectsKeysCanBeEncoded) {
                strEndIndex = source.findEndOfEncodedString();
            } else {
                strEndIndex = source.findEndString();
            }
            tokens.add(new Token(strStartIndex + 1, strEndIndex, TokenTypes.STRING_TOKEN));
            found = true;
            break;
        case OBJECT_END_TOKEN:
            tokens.undoPlaceholder();
            return true;
        default:
            throw new UnexpectedCharacterException("Parsing key", "Unexpected character found", source);
    }
    boolean done = source.findObjectEndOrAttributeSep();
    if (!done && found) {
        tokens.set(tokenListIndex, new Token(startIndex + 1, source.getIndex(), TokenTypes.ATTRIBUTE_KEY_TOKEN));
    } else if (found && done) {
        throw new UnexpectedCharacterException("Parsing key", "Not found", source);
    }
    return done;
}

The parseKey method in the class io.nats.jparse.parser.indexoverlay.JsonFastParser is used to parse a key from a JSON object. Here is a step-by-step description of what the method does based on its body:

  1. It begins by reading the next character from the input source and skipping any whitespace characters.

  2. The method keeps track of the starting index of the key and the current index of the token list.

  3. It adds a placeholder token to the token list to reserve a position for the parsed key.

  4. The method then checks the value of the currently read character (ch) using a switch statement.

  5. If the character is the start of a string (STRING_START_TOKEN), the method proceeds to parse the string key. It determines the start position of the string key and then finds the end position by either decoding the string or finding the end of the string. It adds a new token to the token list with the start and end positions of the string key, marked as a STRING_TOKEN. It sets the "found" variable to true.

  6. If the character is an object end token (OBJECT_END_TOKEN), it means that the parsing of the current JSON object is complete. The method removes the placeholder token from the token list and returns true to indicate that the parsing of the key is done.

  7. If the character is neither a string start token nor an object end token, it throws an UnexpectedCharacterException with an appropriate error message indicating that an unexpected character was found while parsing the key.

  8. After handling the character, the method proceeds to find the end of the current JSON object or the separator between attributes.

  9. If the end of the object or attribute separator is not found and a key has been found, it updates the token list with the correct start and end positions of the key as an ATTRIBUTE_KEY_TOKEN.

  10. If a key has been found but the end of the object or attribute separator is found, it throws an UnexpectedCharacterException with an appropriate error message indicating that the end was not found.

  11. Finally, the method returns the value of the "done" variable, which indicates whether the parsing of the key is complete or not.

This method provides a step-by-step process for parsing a key from a JSON object and handles different scenarios like string keys, object ends, and unexpected characters. sequence diagram

private boolean parseValue(final CharSource source, TokenList tokens)

private boolean parseValue(final CharSource source, TokenList tokens) {
    int ch = source.nextSkipWhiteSpace();
    final int startIndex = source.getIndex();
    final int tokenListIndex = tokens.getIndex();
    tokens.placeHolder();
    switch(ch) {
        case OBJECT_START_TOKEN:
            parseObject(source, tokens);
            break;
        case ARRAY_START_TOKEN:
            parseArray(source, tokens);
            break;
        case TRUE_BOOLEAN_START:
            parseTrue(source, tokens);
            break;
        case FALSE_BOOLEAN_START:
            parseFalse(source, tokens);
            break;
        case NULL_START:
            parseNull(source, tokens);
            break;
        case STRING_START_TOKEN:
            parseString(source, tokens);
            break;
        case NUM_0:
        case NUM_1:
        case NUM_2:
        case NUM_3:
        case NUM_4:
        case NUM_5:
        case NUM_6:
        case NUM_7:
        case NUM_8:
        case NUM_9:
        case MINUS:
        case PLUS:
            parseNumber(source, tokens);
            break;
        default:
            throw new UnexpectedCharacterException("Parsing Value", "Unexpected character", source, ch);
    }
    ch = source.skipWhiteSpace();
    switch(ch) {
        case OBJECT_END_TOKEN:
            if (source.getIndex() == tokenListIndex) {
                throw new UnexpectedCharacterException("Parsing Value", "Key separator before value", source);
            }
            tokens.set(tokenListIndex, new Token(startIndex, source.getIndex(), TokenTypes.ATTRIBUTE_VALUE_TOKEN));
            return true;
        case OBJECT_ATTRIBUTE_SEP:
            if (source.getIndex() == tokenListIndex) {
                throw new UnexpectedCharacterException("Parsing Value", "Key separator before value", source);
            }
            tokens.set(tokenListIndex, new Token(startIndex, source.getIndex(), TokenTypes.ATTRIBUTE_VALUE_TOKEN));
            return false;
        default:
            throw new UnexpectedCharacterException("Parsing Value", "Unexpected character", source, source.getCurrentChar());
    }
}

The parseValue method in the JsonFastParser class is responsible for parsing a JSON value from the provided source and populating the given tokens list with the parsed data.

Here is a step-by-step description of what the method does based on the given code:

  1. Read the next character from the source while skipping any white space characters.

  2. Get the current index of the source and the index of the tokens list.

  3. Insert a placeholder token at the current index in the tokens list.

  4. Use a switch statement to handle different types of characters:

    • If the character is { (indicating the start of an object), call the parseObject method to parse the object.
    • If the character is [ (indicating the start of an array), call the parseArray method to parse the array.
    • If the character is t (indicating the start of a true boolean value), call the parseTrue method to parse the true value.
    • If the character is f (indicating the start of a false boolean value), call the parseFalse method to parse the false value.
    • If the character is n (indicating the start of a null value), call the parseNull method to parse the null value.
    • If the character is " (indicating the start of a string), call the parseString method to parse the string.
    • If the character is a digit (indicating the start of a number) or a minus or plus sign, call the parseNumber method to parse the number.
    • If none of the above cases match, throw an UnexpectedCharacterException.
  5. Skip any remaining white space characters in the source.

  6. Use another switch statement to handle different types of characters:

    • If the character is } (indicating the end of an object), check if the current index of the source is the same as the index of the tokens list. If they are the same, throw an UnexpectedCharacterException indicating that a key separator was found before a value. Otherwise, update the token at the tokenListIndex with the start index and end index of the parsed value and return true.
    • If the character is : (indicating a key separator), check if the current index of the source is the same as the index of the tokens list. If they are the same, throw an UnexpectedCharacterException indicating that a key separator was found before a value. Otherwise, update the token at the tokenListIndex with the start index and end index of the parsed value and return false.
    • If none of the above cases match, throw an UnexpectedCharacterException indicating that an unexpected character was found.

In summary, the parseValue method parses a JSON value from the source and updates the tokens list with the start and end indexes of the parsed value. It also checks for unexpected characters or incorrect placement of key separators before values. sequence diagram

private void parseObject(final CharSource source, TokenList tokens)

private void parseObject(final CharSource source, TokenList tokens) {
    final int startSourceIndex = source.getIndex();
    final int tokenListIndex = tokens.getIndex();
    tokens.placeHolder();
    boolean done = false;
    while (!done) {
        done = parseKey(source, tokens);
        if (!done)
            done = parseValue(source, tokens);
    }
    source.next();
    tokens.set(tokenListIndex, new Token(startSourceIndex, source.getIndex(), TokenTypes.OBJECT_TOKEN));
}

The parseObject method in the JsonFastParser class is responsible for parsing a JSON object. Below is a step-by-step breakdown of what this method does based on its body:

  1. The method starts by storing the current index of the input source and the index of the token list.

  2. It creates a placeholder token in the token list to mark the starting point of the object.

  3. It initiates a loop that continues until the parsing of the object is complete. The variable done is used as a flag to indicate if the parsing is done.

  4. Within the loop, the method calls the parseKey method, passing the input source and token list. The parseKey method is expected to parse the next key in the object and return a boolean value indicating whether the parsing is done.

  5. If the parseKey method returns false, indicating that the parsing of the key is not done, the method calls the parseValue method. The parseValue method is responsible for parsing the corresponding value of the key and returns a boolean value indicating whether the parsing is done.

  6. The loop continues until either the parseKey or parseValue methods indicate that the parsing is done.

  7. After the loop, the method calls source.next() to move the input source to the next character.

  8. Finally, the method updates the token in the token list with the index range of the parsed object, using the set method. The token is marked as an "OBJECT_TOKEN" to indicate that it represents a JSON object.

Note: The specific implementation details of the parseKey and parseValue methods are not provided in the given code snippet. These methods are likely responsible for parsing the key-value pairs within the JSON object.

Clone this wiki locally