JSONScanner.java

package com.alibaba.fastjson.parser;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.util.DateUtils;

import java.io.Closeable;
import java.math.BigDecimal;
import java.util.Calendar;

public class JSONScanner
        extends JSONLexerBase
        implements Closeable {
    private final JSONReader reader;
    private boolean orderedField;

    protected int token;
    private String strVal;
    protected Calendar calendar;
    protected String str;

    public JSONScanner(JSONReader reader) {
        this.reader = reader;
    }

    public JSONScanner(String str) {
        this.reader = JSONReader.of(str);
        this.str = str;
    }

    public JSONScanner(String str, int features) {
        this.reader = JSONReader.of(str, JSON.createReadContext(features));
    }

    public Calendar getCalendar() {
        return calendar;
    }

    public boolean scanISO8601DateIfMatch() {
        return scanISO8601DateIfMatch(true);
    }

    public boolean scanISO8601DateIfMatch(boolean strict) {
        if (str != null) {
            try {
                long millis = DateUtils.parseMillis(str);
                Calendar calendar = Calendar.getInstance();
                calendar.setTimeInMillis(millis);
                this.calendar = calendar;
                return true;
            } catch (Exception ignored) {
                return false;
            }
        }

        throw new JSONException("UnsupportedOperation");
    }

    @Override
    public JSONReader getReader() {
        return reader;
    }

    public boolean isOrderedField() {
        return orderedField;
    }

    @Override
    public String stringVal() {
        return strVal;
    }

    public BigDecimal decimalValue() {
        return reader.getBigDecimal();
    }

    public int token() {
        return token;
    }

    public void config(Feature feature, boolean state) {
        JSONReader.Feature rawFeature = null;

        boolean not = false;
        switch (feature) {
            case AllowUnQuotedFieldNames:
                rawFeature = JSONReader.Feature.AllowUnQuotedFieldNames;
                break;
            case SupportArrayToBean:
                rawFeature = JSONReader.Feature.SupportArrayToBean;
                break;
            case DisableFieldSmartMatch:
                rawFeature = JSONReader.Feature.SupportSmartMatch;
                not = true;
                break;
            case SupportAutoType:
                rawFeature = JSONReader.Feature.SupportAutoType;
                break;
            case NonStringKeyAsString:
                rawFeature = JSONReader.Feature.NonStringKeyAsString;
                break;
            case ErrorOnEnumNotMatch:
                rawFeature = JSONReader.Feature.ErrorOnEnumNotMatch;
                break;
            case SupportClassForName:
                rawFeature = JSONReader.Feature.SupportClassForName;
                break;
            case ErrorOnNotSupportAutoType:
                rawFeature = JSONReader.Feature.ErrorOnNotSupportAutoType;
                break;
            case UseNativeJavaObject:
                rawFeature = JSONReader.Feature.UseNativeObject;
                break;
            case UseBigDecimal:
                rawFeature = JSONReader.Feature.UseDoubleForDecimals;
                not = true;
                break;
            case OrderedField:
                orderedField = state;
                break;
            default:
                break;
        }

        if (rawFeature == null) {
            return;
        }

        if (not) {
            state = !state;
        }

        JSONReader.Context context = reader.getContext();
        context.config(rawFeature, state);
    }

    public boolean isEnabled(Feature feature) {
        JSONReader.Feature rawFeature = null;

        switch (feature) {
            case AllowUnQuotedFieldNames:
                rawFeature = JSONReader.Feature.AllowUnQuotedFieldNames;
                break;
            case SupportArrayToBean:
                rawFeature = JSONReader.Feature.SupportArrayToBean;
                break;
            case DisableFieldSmartMatch:
                return !reader.isEnabled(JSONReader.Feature.SupportSmartMatch);
            case SupportAutoType:
                rawFeature = JSONReader.Feature.SupportAutoType;
                break;
            case NonStringKeyAsString:
                rawFeature = JSONReader.Feature.NonStringKeyAsString;
                break;
            case ErrorOnEnumNotMatch:
                rawFeature = JSONReader.Feature.ErrorOnEnumNotMatch;
                break;
            case SupportClassForName:
                rawFeature = JSONReader.Feature.SupportClassForName;
                break;
            case ErrorOnNotSupportAutoType:
                rawFeature = JSONReader.Feature.ErrorOnNotSupportAutoType;
                break;
            case UseNativeJavaObject:
                rawFeature = JSONReader.Feature.UseNativeObject;
                break;
            case UseBigDecimal:
                return !reader.isEnabled(JSONReader.Feature.UseDoubleForDecimals);
            default:
                break;
        }

        if (rawFeature == null) {
            return true;
        }

        return reader.isEnabled(rawFeature);
    }

    @Override
    public boolean isBlankInput() {
        return reader.isEnd();
    }

    @Override
    public int intValue() {
        return reader.getInt32Value();
    }

    @Override
    public long longValue() {
        return reader.getInt64Value();
    }

    public final void nextToken() {
        strVal = null;
        char ch = reader.current();
        switch (ch) {
            case '[':
                reader.next();
                token = JSONToken.LBRACKET;
                return;
            case ']':
                reader.next();
                token = JSONToken.RBRACKET;
                return;
            case '{':
                reader.next();
                token = JSONToken.LBRACE;
                return;
            case '}':
                reader.next();
                token = JSONToken.RBRACE;
                return;
            case ':':
                reader.next();
                token = JSONToken.COLON;
                return;
            case '"':
            case '\'':
                strVal = reader.readString();
                token = JSONToken.LITERAL_STRING;
                return;
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case '-':
            case '+':
                Number number = reader.readNumber();
                if (number instanceof BigDecimal || number instanceof Float || number instanceof Double) {
                    token = JSONToken.LITERAL_FLOAT;
                } else {
                    token = JSONToken.LITERAL_INT;
                }
                return;
            case 't':
            case 'f':
                boolean boolValue = reader.readBoolValue();
                token = boolValue ? JSONToken.TRUE : JSONToken.FALSE;
                return;
            case 'n':
                reader.readNull();
                token = JSONToken.NULL;
                return;
            case EOI:
                token = JSONToken.EOF;
                return;
            default:
                break;
        }

        if (reader.nextIfNull()) {
            return;
        }

        throw new JSONException("not support operation");
    }

    @Override
    public char getCurrent() {
        return reader.current();
    }

    @Override
    public final void nextToken(int expect) {
        nextToken();
    }

    @Override
    public boolean isEOF() {
        return reader.isEnd();
    }

    @Override
    public void close() {
    }
}