bson/src/main/org/bson/json/JsonScanner.java
public JsonToken nextToken() {

        int c = buffer.read();
        while (c != -1 && Character.isWhitespace(c)) {
            c = buffer.read();
        }
        if (c == -1) {
            return new JsonToken(JsonTokenType.END_OF_FILE, "<eof>");
        }

        switch (c) {
            case '{':
                return new JsonToken(JsonTokenType.BEGIN_OBJECT, "{");
            case '}':
                return new JsonToken(JsonTokenType.END_OBJECT, "}");
            case '[':
                return new JsonToken(JsonTokenType.BEGIN_ARRAY, "[");
            case ']':
                return new JsonToken(JsonTokenType.END_ARRAY, "]");
            case '(':
                return new JsonToken(JsonTokenType.LEFT_PAREN, "(");
            case ')':
                return new JsonToken(JsonTokenType.RIGHT_PAREN, ")");
            case ':':
                return new JsonToken(JsonTokenType.COLON, ":");
            case ',':
                return new JsonToken(JsonTokenType.COMMA, ",");
            case '\'':
            case '"':
                return scanString((char) c);
            case '/':
                return scanRegularExpression();
            default:
                if (c == '-' || Character.isDigit(c)) {
                    return scanNumber((char) c);
                } else if (c == '$' || c == '_' || Character.isLetter(c)) {
                    return scanUnquotedString((char) c);
                } else {
                    int position = buffer.getPosition();
                    buffer.unread(c);
                    throw new JsonParseException("Invalid JSON input. Position: %d. Character: '%c'.", position, c);
                }
        }
    }
public JsonToken nextToken() {

        int c = buffer.read();
        while (c != -1 && Character.isWhitespace(c)) {
            c = buffer.read();
        }
        if (c == -1) {
            return new JsonToken(JsonTokenType.END_OF_FILE, "<eof>");
        }

        switch (c) {
            case '{':
                return new JsonToken(JsonTokenType.BEGIN_OBJECT, "{");
            case '}':
                return new JsonToken(JsonTokenType.END_OBJECT, "}");
            case '[':
                return new JsonToken(JsonTokenType.BEGIN_ARRAY, "[");
            case ']':
                return new JsonToken(JsonTokenType.END_ARRAY, "]");
            case '(':
                return new JsonToken(JsonTokenType.LEFT_PAREN, "(");
            case ')':
                return new JsonToken(JsonTokenType.RIGHT_PAREN, ")");
            case ':':
                return new JsonToken(JsonTokenType.COLON, ":");
            case ',':
                return new JsonToken(JsonTokenType.COMMA, ",");
            case '\'':
            case '"':
                return scanString((char) c);
            case '/':
                return scanRegularExpression();
            default:
                if (c == '-' || Character.isDigit(c)) {
                    return scanNumber((char) c);
                } else if (c == '$' || c == '_' || Character.isLetter(c)) {
                    return scanUnquotedString((char) c);
                } else {
                    int position = buffer.getPosition();
                    buffer.unread(c);
                    throw new JsonParseException("Invalid JSON input. Position: %d. Character: '%c'.", position, c);
                }
        }
    }
Finds and returns the next complete token from this scanner. If scanner reached the end of the source, it will return a token with
{@code JSONTokenType.END_OF_FILE} type.

@return The next token.
@throws JsonParseException if source is invalid.