All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.common.highlight.SQLScanner Maven / Gradle / Ivy

package org.bidib.wizard.common.highlight;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * SQLScanner
 */
public class SQLScanner extends Scanner {

    private static final Logger LOGGER = LoggerFactory.getLogger(SQLScanner.class);

    private final boolean debug = false;

    public SQLScanner() {
        super();

        setCaseInsensitive(true);
        initKind();
        initUniKind();
    }

    // Override initSymbolTable

    @Override
    protected void initSymbolTable() {
        lookup(KEYWORD, "select");
        lookup(KEYWORD, "from");
        lookup(KEYWORD, "where");
        lookup(KEYWORD, "join");
        lookup(KEYWORD, "left");
        lookup(KEYWORD, "outer");
        lookup(KEYWORD, "inner");
        lookup(KEYWORD, "as");
        lookup(KEYWORD, "is");
        lookup(KEYWORD, "on");
        lookup(KEYWORD, "not");
        lookup(KEYWORD, "null");
        lookup(KEYWORD, "convert");
        lookup(KEYWORD, "substring");
        lookup(KEYWORD, "trim");
        lookup(KEYWORD, "use");
        lookup(KEYWORD, "like");
        lookup(KEYWORD, "and");
        lookup(KEYWORD, "or");

        lookup(KEYWORD, "sum");
        lookup(KEYWORD, "count");
        lookup(KEYWORD, "max");

        lookup(KEYWORD, "order");
        lookup(KEYWORD, "by");
        lookup(KEYWORD, "asc");
        lookup(KEYWORD, "desc");

        lookup(KEYWORD2, "datetime");
    }

    /** Override the read method from the Scanner class. */
    @Override
    protected int read() {
        int type;
        int saveStart = 0;
        if (debug) {
            saveStart = start;
        }

        if (start >= end) {
            return WHITESPACE;
        }

        switch (state) {
            case MID_COMMENT:
            case END_COMMENT:
                type = readComment(MID_COMMENT);
                if (type == END_COMMENT) {
                    state = WHITESPACE;
                }
                else {
                    state = MID_COMMENT;
                }
                return type;
            default:
                char c = buffer[start];
                if (c == '\\') {
                    c = next();
                }
                if (c < 128) {
                    type = KIND[c];
                }
                else {
                    type = UNIKIND[Character.getType(c)];
                }
                switch (type) {
                    case WHITESPACE:
                        start = start + charlength;
                        charlength = 1;
                        while (start < end) {
                            c = buffer[start];
                            if (c == '\\') {
                                c = next();
                            }
                            int k;
                            if (c < 128) {
                                k = KIND[c];
                            }
                            else {
                                k = UNIKIND[Character.getType(c)];
                            }
                            if (k != WHITESPACE) {
                                break;
                            }
                            start = start + charlength;
                            charlength = 1;
                        }
                        break;
                    case UNRECOGNIZED:
                    case BRACKET:
                    case SEPARATOR:
                        start = start + charlength;
                        charlength = 1;
                        break;
                    case OPERATOR:
                        start = start + charlength;
                        charlength = 1;
                        type = readOperator(c);
                        break;
                    case CHARACTER:
                        start = start + charlength;
                        charlength = 1;
                        type = readCharLiteral();
                        break;
                    case STRING:
                        start = start + charlength;
                        charlength = 1;
                        type = readStringLiteral();
                        break;
                    case IDENTIFIER:
                        start = start + charlength;
                        charlength = 1;
                        while (start < end) {
                            c = buffer[start];
                            if (c == '\\') {
                                c = next();
                            }
                            int k;
                            if (c < 128) {
                                k = KIND[c];
                            }
                            else {
                                k = UNIKIND[Character.getType(c)];
                            }
                            if (k != IDENTIFIER && k != NUMBER) {
                                break;
                            }
                            start = start + charlength;
                            charlength = 1;
                        }
                        break;
                    case NUMBER:
                        start = start + charlength;
                        charlength = 1;
                        type = readNumber(c);
                        break;
                    case PUNCTUATION:
                        start = start + charlength;
                        charlength = 1;
                        type = readDot();
                        break;
                    case COMMENT:
                        start = start + charlength;
                        charlength = 1;
                        type = readSlash();
                        if (type == START_COMMENT) {
                            state = MID_COMMENT;
                        }
                        break;
                    case VARIABLE:
                        start = start + charlength;
                        charlength = 1;
                        type = readVariable();
                        break;
                    default:
                        break;
                }
        }
        if (LOGGER.isDebugEnabled()) {
            if (type > -1 && type < TokenTypes.TYPENAMES.length) {
                StringBuilder sb = new StringBuilder(TokenTypes.TYPENAMES[type]);
                sb.append(" ").append(saveStart).append(",").append(end).append("(").append((start - saveStart))
                    .append(")");
                LOGGER.debug(sb.toString());
            }
            else {
                LOGGER.debug("Invalid type: {}", type);
            }
        }
        return type;
    }

    private int readOperator(char c) {
        if (start >= end) {
            return OPERATOR;
        }
        char c2;

        switch (c) {
            case '~':
            case '?':
                break;
            case '+':
            case '-':
            case '&':
            case '|':
                c2 = buffer[start];
                if (c2 == '\\') {
                    c2 = next();
                }
                if (c2 != c && c2 != '=') {
                    break;
                }
                start = start + charlength;
                charlength = 1;
                break;
            case '=':
            case '*':
            case '!':
            case '^':
            case '%':
            case '/':
                c2 = buffer[start];
                if (c2 == '\\') {
                    c2 = next();
                }
                if (c2 != '=') {
                    break;
                }
                start = start + charlength;
                charlength = 1;
                break;
            case '<':
            case '>':
                c2 = buffer[start];
                if (c2 == '\\') {
                    c2 = next();
                }
                if (c2 == '=') {
                    start = start + charlength;
                    charlength = 1;
                }
                else if (c2 == c) {
                    start = start + charlength;
                    charlength = 1;
                    if (start >= end) {
                        break;
                    }
                    char c3 = buffer[start];
                    if (c3 == '\\') {
                        c3 = next();
                    }
                    if (c3 == '=') {
                        start = start + charlength;
                        charlength = 1;
                    }
                    else if (c == '>' && c3 == '>') { // >>>
                        start = start + charlength;
                        charlength = 1;
                        if (start >= end) {
                            break;
                        }
                        char c4 = buffer[start];
                        if (c4 == '\\') {
                            c4 = next();
                        }
                        if (c4 != '=') {
                            break;
                        }
                        start = start + charlength;
                        charlength = 1;
                    }
                }
                break;
            default:
                break;
        }
        return OPERATOR;
    }

    private int readCharLiteral() {
        if (start >= end) {
            return bad(CHARACTER);
        }
        char c2 = buffer[start];
        if (c2 == '\\') {
            c2 = next();
        }

        switch (c2) {
            case '\\':
                start = start + charlength;
                charlength = 1;
                boolean ok = readEscapeSequence();
                if (!ok) {
                    return bad(CHARACTER);
                }
                break;
            case '\'':
            case '\n':
                return bad(CHARACTER);
            default:
                start = start + charlength;
                charlength = 1;
                break;
        }
        if (start >= end) {
            return bad(CHARACTER);
        }
        char c3 = buffer[start];
        if (c3 == '\\') {
            c3 = next();
        }
        if (c3 != '\'') {
            return bad(CHARACTER);
        }
        start = start + charlength;
        charlength = 1;
        return CHARACTER;
    }

    private int readStringLiteral() {
        if (start >= end) {
            return bad(STRING);
        }
        char c = buffer[start];
        if (c == '\\') {
            c = next();
        }

        while (c != '"' && c != '\'') {
            switch (c) {
                case '\\':
                    start = start + charlength;
                    charlength = 1;
                    boolean ok = readEscapeSequence();
                    if (!ok) {
                        return bad(STRING);
                    }
                    break;
                case '\n':
                    return bad(STRING);
                default:
                    start = start + charlength;
                    charlength = 1;
                    if (start >= end) {
                        return bad(STRING);
                    }
                    break;
            }
            c = buffer[start];
            if (c == '\\') {
                c = next();
            }
        }
        if ((c != '"') && (c != '\'')) {
            return bad(STRING);
        }
        start = start + charlength;
        charlength = 1;
        return STRING;
    }

    private int readSlash() {
        if (start >= end) {
            return OPERATOR;
        }
        char c = buffer[start];
        if (c == '\\') {
            c = next();
        }
        if (c == '-') {
            while (c != '\n') {
                start = start + charlength;
                charlength = 1;
                if (start >= end) {
                    return COMMENT;
                }
                c = buffer[start];
                if (c == '\\') {
                    c = next();
                }
            }
            start = start + charlength;
            charlength = 1;
            return COMMENT;
        }
        else if (c == '*') {
            start = start + charlength;
            charlength = 1;
            return readComment(START_COMMENT);
        }
        return readOperator('/');
    }

    // Read one line of a /*...*/ comment, given the expected type
    int readComment(int type) {
        if (start >= end) {
            return type;
        }
        char c = buffer[start];
        if (c == '\\') {
            c = next();
        }

        while (true) {
            while (c != '*' && c != '\n') {
                start = start + charlength;
                charlength = 1;
                if (start >= end) {
                    return type;
                }
                c = buffer[start];
                if (c == '\\') {
                    c = next();
                }
            }
            start = start + charlength;
            charlength = 1;
            if (c == '\n') {
                return type;
            }
            if (start >= end) {
                return type;
            }
            c = buffer[start];
            if (c == '\\') {
                c = next();
            }
            if (c == '/') {
                start = start + charlength;
                charlength = 1;
                if (type == START_COMMENT) {
                    return COMMENT;
                }
                else {
                    return END_COMMENT;
                }
            }
        }
    }

    // Read a number, without checking whether it is out of range
    // Doesn't deal with e.g. 0777.9 or 07779f
    private int readNumber(char c) {
        if (c == '0') {
            int saveStart = start;
            int saveLength = charlength;
            start = start + charlength;
            charlength = 1;
            if (start >= end) {
                return NUMBER;
            }
            char c2 = buffer[start];
            if (c2 == '\\') {
                c2 = next();
            }
            switch (c2) {
                case 'x':
                case 'X':
                    start = start + charlength;
                    charlength = 1;
                    boolean ok = readDigits(16);
                    if (!ok) {
                        return bad(NUMBER);
                    }
                    readSuffix();
                    return NUMBER;
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    readDigits(8);
                    readSuffix();
                    return NUMBER;
                case '.':
                case 'e':
                case 'E':
                    start = saveStart;
                    charlength = saveLength;
                    break;
                case 'f':
                case 'F':
                case 'd':
                case 'D':
                    start = start + charlength; // NOSONAR
                    charlength = 1;
                    return NUMBER;
                case 'l':
                case 'L':
                    start = start + charlength; // NOSONAR
                    charlength = 1;
                    return NUMBER;
                default:
                    break;
            }
        }
        boolean hasDigits = false;
        if ('0' <= c && c <= '9') {
            hasDigits = true;
            readDigits(10);
            if (start >= end) {
                return NUMBER;
            }
            c = buffer[start];
            if (c == '\\') {
                c = next();
            }
            if (c == 'l' || c == 'L') {
                start = start + charlength;
                charlength = 1;
                return NUMBER;
            }
        }
        if (c == '.') {
            start = start + charlength;
            charlength = 1;
            if (start >= end) {
                return NUMBER;
            }
            c = buffer[start];
            if (c == '\\') {
                c = next();
            }
            if ('0' <= c && c <= '9') {
                hasDigits = true;
                readDigits(10);
                if (start >= end) {
                    return NUMBER;
                }
                c = buffer[start];
                if (c == '\\') {
                    c = next();
                }
            }
        }
        if (!hasDigits) {
            return bad(NUMBER);
        }
        switch (c) {
            case 'e':
            case 'E':
                start = start + charlength;
                charlength = 1;
                if (start >= end) {
                    return bad(NUMBER);
                }
                c = buffer[start];
                if (c == '\\') {
                    c = next();
                }
                if (c == '+' || c == '-') {
                    start = start + charlength;
                    charlength = 1;
                    if (start >= end) {
                        return bad(NUMBER);
                    }
                    c = buffer[start];
                    if (c == '\\') {
                        next();
                    }
                }
                readDigits(10);
                break;
            case 'f':
            case 'F':
            case 'd':
            case 'D':
                start = start + charlength;
                charlength = 1;
                return NUMBER;
            default:
                break;
        }
        return NUMBER;
    }

    boolean readDigits(int radix) {
        if (start >= end) {
            return false;
        }
        char c = buffer[start];
        if (c == '\\') {
            c = next();
        }
        if (Character.digit(c, radix) == -1) {
            return false;
        }
        while (Character.digit(c, radix) != -1) {
            start = start + charlength;
            charlength = 1;
            if (start >= end) {
                return true;
            }
            c = buffer[start];
            if (c == '\\') {
                c = next();
            }
        }
        return true;
    }

    void readSuffix() {
        if (start >= end) {
            return;
        }
        char c = buffer[start];
        if (c == '\\') {
            c = next();
        }
        switch (c) {
            case 'f':
            case 'F':
            case 'd':
            case 'D':
            case 'l':
            case 'L':
                start = start + charlength;
                charlength = 1;
            default:
                break;
        }
    }

    private int readDot() {
        if (start >= end) {
            return SEPARATOR;
        }
        char c2 = buffer[start];
        if (c2 == '\\') {
            c2 = next();
        }
        if (Character.isDigit(c2)) {
            return readNumber('.');
        }
        if (start + 1 >= end /* || version < 15 */) {
            return SEPARATOR;
        }
        if (c2 != '.' || buffer[start + 1] != '.') {
            return SEPARATOR;
        }
        start = start + 2;
        return SEPARATOR;
    }

    private boolean readEscapeSequence() {
        if (start >= end) {
            return false;
        }
        char c2 = buffer[start];
        if (c2 == '\\') {
            c2 = next();
        }

        switch (c2) {
            case 'b':
            case 't':
            case 'n':
            case 'f':
            case 'r':
            case '\"':
            case '\'':
            case '\\':
                start = start + charlength;
                charlength = 1;
                return true;
            case '0':
            case '1':
            case '2':
            case '3':
                return readOctal(3);
            case '4':
            case '5':
            case '6':
            case '7':
                return readOctal(2);
            default:
                return false;
        }
    }

    boolean readOctal(int maxlength) {
        if (start >= end) {
            return false;
        }
        char c = buffer[start];
        if (c == '\\') {
            c = next();
        }

        int i;
        int val = 0;
        for (i = 0; i < maxlength; i++) {
            if (Character.digit(c, 8) != -1) {
                val = 8 * val + Character.digit(c, 8);
                start = start + charlength;
                charlength = 1;
                if (start >= end) {
                    break;
                }
                c = buffer[start];
                if (c == '\\') {
                    c = next();
                }
            }
            else {
                break;
            }
        }
        if ((i == 0) || (val > 0xFF)) {
            return false;
        }
        return true;
    }

    int readVariable() {
        if (start >= end) {
            return bad(VARIABLE);
        }
        char c = buffer[start];
        if (c == '\\') {
            c = next();
        }

        while (c == '_' || Character.isLetterOrDigit(c)) {
            start = start + charlength;
            charlength = 1;
            if (start >= end) {
                return VARIABLE;
            }
            c = buffer[start];
            if (c == '\\') {
                c = next();
            }
        }
        return VARIABLE;
    }

    // A malformed or incomplete token has a negative type
    private int bad(int type) {
        return -type;
    }

    // Look ahead at the next character or unicode escape.
    // For efficiency, replace c = next(); with
    // c = buffer[start]; if (c == '\\') c = next();
    // To accept the character after looking at it, use:
    // start = start + charlength; charlength = 1;

    // Record the number of source code characters used up. To deal with an
    // odd or even number of backslashes preceding a unicode escape, whenever a
    // second backslash is coming up, mark its position as a pair.

    private int charlength = 1;

    private int pair = 0;

    private char next() {
        if (start >= end) {
            return 26; // EOF
        }
        char c = buffer[start];
        if (c != '\\') {
            return c;
        }
        if (start == pair) {
            pair = 0;
            return '\\';
        }
        if (start + 1 >= end) {
            return '\\';
        }

        c = buffer[start + 1];
        if (c == '\\') {
            pair = start + 1;
        }
        if (c != 'u') {
            return '\\';
        }

        int pos = start + 2;
        while (pos < end && buffer[pos] == 'u') {
            pos++;
        }
        if (pos + 4 > end) {
            charlength = end - start;
            return '\0';
        }

        c = 0;
        for (int j = 0; j < 4; j++) {
            int d = Character.digit(buffer[pos + j], 16);
            if (d < 0) {
                charlength = pos + j - start;
                return '\0';
            }
            c = (char) (c * 16 + d);
        }
        charlength = pos + 4 - start;
        return c;
    }

    // *** Override lookup, but what about unicode escape translation?

    private final Symbol temp = new Symbol(0, null);

    @Override
    protected Symbol lookup(int type, String name) {
        if (type != IDENTIFIER) {
            return super.lookup(type, name);
        }
        temp.type = KEYWORD;
        temp.name = name;
        Symbol sym = symbolTable.get(temp);
        if (sym != null) {
            return sym;
        }
        temp.type = KEYWORD2;
        temp.name = name;
        sym = symbolTable.get(temp);
        if (sym != null) {
            return sym;
        }
        temp.type = LITERAL;
        sym = symbolTable.get(temp);
        if (sym != null) {
            return sym;
        }
        return super.lookup(type, name);
    }

    // Classify the ascii characters using an array of kinds, and classify all
    // other unicode characters using an array indexed by unicode category.
    // See the source file java/lang/Character.java for the categories.
    // To find the classification of a character, use:
    // if (c < 128) k = kind[c]; else k = unikind[Character.getType(c)];

    private static final byte[] KIND = new byte[128];

    private static final byte[] UNIKIND = new byte[31];

    // Initialise the two classification arrays using static initializer code.
    // Token types from the TokenTypes class are used to classify characters.

    private void initKind() {
        for (char c = 0; c < 128; c++) {
            KIND[c] = -1;
        }
        for (char c = 0; c < 128; c++) {
            switch (c) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                case 8:
                case 11:
                    // case 13 : \\ '\r'
                case 14:
                case 15:
                case 16:
                case 17:
                case 18:
                case 19:
                case 20:
                case 21:
                case 22:
                case 23:
                case 24:
                case 25:
                case 27:
                case 28:
                case 29:
                case 30:
                case 31:
                case 127:
                case '#':
                case '@':
                case '`':
                case '\\':
                    KIND[c] = UNRECOGNIZED;
                    break;
                case '\t':
                case '\r':
                case '\n':
                case ' ':
                case '\f':
                case 26:
                    KIND[c] = WHITESPACE;
                    break;
                case '!':
                case '&':
                case '+':
                case '<':
                case '=':
                case '>':
                case '?':
                case '^':
                case '|':
                case '~':
                    KIND[c] = OPERATOR;
                    break;
                case '%':
                case '*':
                    KIND[c] = IDENTIFIER; // NOSONAR
                    break;
                case '"':
                    KIND[c] = STRING; // NOSONAR
                    break;
                case '\'':
                    KIND[c] = STRING; // NOSONAR
                    break;
                case '.':
                    KIND[c] = PUNCTUATION;
                    break;
                case '-':
                case '/':
                    KIND[c] = COMMENT;
                    break;
                case '$':
                case 'A':
                case 'B':
                case 'C':
                case 'D':
                case 'E':
                case 'F':
                case 'G':
                case 'H':
                case 'I':
                case 'J':
                case 'K':
                case 'L':
                case 'M':
                case 'N':
                case 'O':
                case 'P':
                case 'Q':
                case 'R':
                case 'S':
                case 'T':
                case 'U':
                case 'V':
                case 'W':
                case 'X':
                case 'Y':
                case 'Z':
                case '_':
                case 'a':
                case 'b':
                case 'c':
                case 'd':
                case 'e':
                case 'f':
                case 'g':
                case 'h':
                case 'i':
                case 'j':
                case 'k':
                case 'l':
                case 'm':
                case 'n':
                case 'o':
                case 'p':
                case 'q':
                case 'r':
                case 's':
                case 't':
                case 'u':
                case 'v':
                case 'w':
                case 'x':
                case 'y':
                case 'z':
                    KIND[c] = IDENTIFIER; // NOSONAR
                    break;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    KIND[c] = NUMBER;
                    break;
                case '(':
                case ')':
                case '[':
                case ']':
                case '{':
                case '}':
                    KIND[c] = BRACKET;
                    break;
                case ',':
                case ';':
                    KIND[c] = SEPARATOR;
                    break;
                case ':':
                    KIND[c] = VARIABLE;
                    break;
                default:
                    break;
            }
        }
        for (char c = 0; c < 128; c++) {
            if (KIND[c] == -1) {
                LOGGER.debug("Char " + ((int) c) + " hasn't been classified");
            }
        }
    }

    private void initUniKind() {
        for (byte b = 0; b < 31; b++) {
            UNIKIND[b] = -1;
        }
        for (byte b = 0; b < 31; b++) {
            switch (b) {
                case Character.UNASSIGNED:
                case Character.ENCLOSING_MARK:
                case Character.OTHER_NUMBER:
                case Character.SPACE_SEPARATOR:
                case Character.LINE_SEPARATOR:
                case Character.PARAGRAPH_SEPARATOR:
                case Character.CONTROL:
                case 17: // category 17 is unused
                case Character.PRIVATE_USE:
                case Character.SURROGATE:
                case Character.DASH_PUNCTUATION:
                case Character.START_PUNCTUATION:
                case Character.END_PUNCTUATION:
                case Character.OTHER_PUNCTUATION:
                case Character.MATH_SYMBOL:
                case Character.MODIFIER_SYMBOL:
                case Character.OTHER_SYMBOL:
                case Character.INITIAL_QUOTE_PUNCTUATION:
                case Character.FINAL_QUOTE_PUNCTUATION:
                    UNIKIND[b] = UNRECOGNIZED;
                    break;
                case Character.UPPERCASE_LETTER:
                case Character.LOWERCASE_LETTER:
                case Character.TITLECASE_LETTER:
                case Character.MODIFIER_LETTER:
                case Character.OTHER_LETTER:
                case Character.LETTER_NUMBER:
                case Character.CONNECTOR_PUNCTUATION: // maybe NUMBER
                case Character.CURRENCY_SYMBOL:
                    // Characters where Other_ID_Start is true
                    UNIKIND[b] = IDENTIFIER;
                    break;
                case Character.NON_SPACING_MARK:
                case Character.COMBINING_SPACING_MARK:
                case Character.DECIMAL_DIGIT_NUMBER:
                case Character.FORMAT:
                    UNIKIND[b] = NUMBER;
                    break;
                default:
                    break;
            }
        }
        for (byte b = 0; b < 31; b++) {
            if (UNIKIND[b] == -1) {
                LOGGER.debug("Unicode cat " + b + " hasn't been classified");
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy