alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Groovy example source code file (JsonLexer.java)

This example Groovy source code file (JsonLexer.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Groovy tags/keywords

an, io, io, ioexception, json, jsonexception, jsonexception, jsontoken, jsontoken, lexing, linecolumnreader, linecolumnreader, regex, string, string, stringbuilder, util

The Groovy JsonLexer.java source code

/*
 * Copyright 2003-2011 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package groovy.json;

import static groovy.json.JsonTokenType.*;

import groovy.io.LineColumnReader;

import java.io.IOException;
import java.io.Reader;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * The lexer reads JSON tokens in a streaming fashion from the underlying reader.
 *
 * @author Guillaume Laforge
 * @since 1.8.0
 */
public class JsonLexer implements Iterator<JsonToken> {
    private static final char SPACE    = ' ';
    private static final char DOT      = '.';
    private static final char MINUS    = '-';
    private static final char PLUS     = '+';
    private static final char LOWER_E  = 'e';
    private static final char UPPER_E  = 'E';
    private static final char ZERO     = '0';
    private static final char NINE     = '9';

    private static final Pattern p = Pattern.compile("\\\\u(\\p{XDigit}{4})");

    private LineColumnReader reader;

    /**
     * Underlying reader from which to read the JSON tokens.
     * This reader is an instance of <code>LineColumnReader,
     * to keep track of line and column positions.
     */
    public LineColumnReader getReader() {
        return reader;
    }

    private JsonToken currentToken = null;


    /**
     * Instanciates a lexer with a reader from which to read JSON tokens.
     * Under the hood, the reader is wrapped in a <code>LineColumnReader,
     * for line and column information, unless it's already an instance of that class.
     *
     * @param reader underlying reader
     */
    public JsonLexer(Reader reader) {
        this.reader = reader instanceof LineColumnReader ? (LineColumnReader)reader : new LineColumnReader(reader);
    }

    /**
     * @return the next token from the stream
     */
    public JsonToken nextToken() {
        try {
            int firstIntRead = skipWhitespace();
            if (firstIntRead == -1) return null;

            char firstChar = (char) firstIntRead;
            JsonTokenType possibleTokenType = startingWith((char) firstIntRead);

            if (possibleTokenType == null) {
                throw new JsonException(
                        "Lexing failed on line: " + reader.getLine() + ", column: " + reader.getColumn() +
                                ", while reading '" + firstChar + "', " +
                                "no possible valid JSON value or punctuation could be recognized."
                );
            }

            reader.reset();
            long startLine = reader.getLine();
            long startColumn = reader.getColumn();

            JsonToken token = new JsonToken();
            token.setStartLine(startLine);
            token.setStartColumn(startColumn);
            token.setEndLine(startLine);
            token.setEndColumn(startColumn + 1);
            token.setType(possibleTokenType);
            token.setText("" + firstChar);

            if (possibleTokenType.ordinal() >= OPEN_CURLY.ordinal() && possibleTokenType.ordinal() <= FALSE.ordinal()) {
                return readingConstant(possibleTokenType, token);
            } else if (possibleTokenType == STRING) {
                StringBuilder currentContent = new StringBuilder("\"");
                // consume the first double quote starting the string
                reader.read();
                for (;;) {
                    int read = reader.read();
                    if (read == -1) return null;
                    currentContent.append((char) read);

                    if (currentContent.charAt(currentContent.length() - 1) == '"' && currentContent.charAt(currentContent.length() - 2) != '\\' &&
                            possibleTokenType.matching(currentContent.toString())) {
                        token.setEndLine(reader.getLine());
                        token.setEndColumn(reader.getColumn());
                        token.setText(unescape(currentContent.toString()));
                        return token;
                    }
                }
            } else if (possibleTokenType == NUMBER) {
                StringBuilder currentContent = new StringBuilder();
                for (;;) {
                    reader.mark(1);
                    int read = reader.read();
                    if (read == -1) return null;
                    char lastCharRead = (char) read;

                    if (lastCharRead >= ZERO && lastCharRead <= NINE ||
                            lastCharRead == DOT || lastCharRead == MINUS || lastCharRead == PLUS ||
                            lastCharRead == LOWER_E || lastCharRead == UPPER_E) {
                        currentContent.append(lastCharRead);
                    } else {
                        reader.reset();
                        break;
                    }
                }

                String content = currentContent.toString();
                if (possibleTokenType.matching(content)) {
                    token.setEndLine(reader.getLine());
                    token.setEndColumn(reader.getColumn());
                    token.setText(currentContent.toString());

                    return token;
                } else {
                    throwJsonException(currentContent.toString(), possibleTokenType);
                }
            }
            return null;
        } catch (IOException ioe) {
            throw new JsonException("An IO exception occurred while reading the JSON payload", ioe);
        }
    }

    private void throwJsonException(String content, JsonTokenType type) {
        throw new JsonException(
                "Lexing failed on line: " +
                reader.getLine() + ", column: " + reader.getColumn() +
                ", while reading '" + content + "', " +
                "was trying to match " + type.getLabel()
        );
    }

    /**
     * Replace unicode escape and other control characters with real characters
     *
     * @param input text
     * @return input text without the escaping
     */
    public static String unescape(String input) {
        // presence of a backslash
        if (input.indexOf(92) > -1) {
            Matcher m = p.matcher(input
                    .replaceAll("\\\\b", "\b")
                    .replaceAll("\\\\f", "\f")
                    .replaceAll("\\\\n", "\n")
                    .replaceAll("\\\\r", "\r")
                    .replaceAll("\\\\t", "\t")
                    .replaceAll("\\\\\\\\", "\\\\")
                    .replaceAll("\\\\/", "/")
                    .replaceAll("\\\\\"", "\""));

            // replace unicode escapes
            StringBuffer sb = new StringBuffer();
            while (m.find()) {
                m.appendReplacement(sb, new String(new char[] { (char)Integer.valueOf(m.group(1), 16).intValue() }));
            }
            m.appendTail(sb);

            return sb.toString();
        } else {
            return input;
        }
    }

    /**
     * When a constant token type is expected, check that the expected constant is read,
     * and update the content of the token accordingly.
     *
     * @param type the token type
     * @param token the token
     * @return the token updated with end column and text updated
     */
    private JsonToken readingConstant(JsonTokenType type, JsonToken token) {
        try {
            int numCharsToRead = ((String)type.getValidator()).length();
            char[] chars = new char[numCharsToRead];
            reader.read(chars);
            String stringRead = new String(chars);

            if (stringRead.equals(type.getValidator())) {
                token.setEndColumn(token.getStartColumn() + numCharsToRead);
                token.setText(stringRead);
                return token;
            } else {
                throwJsonException(stringRead, type);
            }
        } catch (IOException ioe) {
            throw new JsonException("An IO exception occurred while reading the JSON payload", ioe);
        }
        return null;
    }

    /**
     * Skips all the whitespace characters and moves the cursor to the next non-space character.
     */
    public int skipWhitespace() {
        try {
            int readChar = 20;
            char c = SPACE;
            while(Character.isWhitespace(c)) {
                reader.mark(1);
                readChar = reader.read();
                c = (char)readChar;
            }
            reader.reset();
            return readChar;
        } catch (IOException ioe) {
            throw new JsonException("An IO exception occurred while reading the JSON payload", ioe);
        }
    }

    /**
     * Iterator method to know if another token follows,
     * or if we've reached the end of the stream.
     *
     * @return true if there are more tokens
     */
    public boolean hasNext() {
        currentToken = nextToken();
        return currentToken != null;
    }

    /**
     * Iterator method to get the next token of the stream.
     *
     * @return the next token
     */
    public JsonToken next() {
        return currentToken;
    }

    /**
     * Method not implemented.
     *
     * @throws UnsupportedOperationException
     */
    public void remove() {
        throw new UnsupportedOperationException("The method remove() is not supported on this lexer.");
    }

}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy JsonLexer.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.