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

com.github.ltsopensource.store.jdbc.SQLFormatter Maven / Gradle / Ivy

package com.github.ltsopensource.store.jdbc;

import java.util.*;

/**
 * 格式化SQL
 * 来自hibernate更改
 *
 * @author Robert HG ([email protected]) on 3/9/16.
 */
public class SQLFormatter {

    private static final String WHITESPACE = " \n\r\f\t";

    private static final Set BEGIN_CLAUSES = new HashSet();
    private static final Set END_CLAUSES = new HashSet();
    private static final Set LOGICAL = new HashSet();
    private static final Set QUANTIFIERS = new HashSet();
    private static final Set DML = new HashSet();
    private static final Set MISC = new HashSet();
    private static final Set UPPER_CASE = new HashSet();

    static {

        BEGIN_CLAUSES.add("left");
        BEGIN_CLAUSES.add("right");
        BEGIN_CLAUSES.add("inner");
        BEGIN_CLAUSES.add("outer");
        BEGIN_CLAUSES.add("group");
        BEGIN_CLAUSES.add("order");

        END_CLAUSES.add("where");
        END_CLAUSES.add("set");
        END_CLAUSES.add("having");
        END_CLAUSES.add("join");
        END_CLAUSES.add("from");
        END_CLAUSES.add("by");
        END_CLAUSES.add("join");
        END_CLAUSES.add("into");
        END_CLAUSES.add("union");

        LOGICAL.add("and");
        LOGICAL.add("or");
        LOGICAL.add("when");
        LOGICAL.add("else");
        LOGICAL.add("end");

        QUANTIFIERS.add("in");
        QUANTIFIERS.add("all");
        QUANTIFIERS.add("exists");
        QUANTIFIERS.add("some");
        QUANTIFIERS.add("any");

        DML.add("insert");
        DML.add("update");
        DML.add("delete");

        MISC.add("select");
        MISC.add("on");
        //MISC.add("values");

        // 这个是mysql关键字列表, 会转化为大写 (需要的时候添加)
        UPPER_CASE.addAll(Arrays.asList("select", "insert", "update", "delete", "on", "left", "right", "inner", "outer", "group",
                "order", "where", "set", "having", "join", "from", "by", "join", "into",
                "union", "and", "or", "when", "else", "end", "in", "all", "exists", "if", "some", "any", "distinct", "count", "sum", "as", "asc", "desc", "between",
                "alter", "table", "else", "elseif", "index", "ignore", "limit", "like", "offset"
        ));

    }

    String indentString = "    ";
    String initial = "\n    ";

    boolean beginLine = true;
    boolean afterBeginBeforeEnd = false;
    boolean afterByOrSetOrFromOrSelect = false;
    boolean afterValues = false;
    boolean afterOn = false;
    boolean afterBetween = false;
    boolean afterInsert = false;
    int inFunction = 0;
    int parensSinceSelect = 0;
    private LinkedList parenCounts = new LinkedList();
    private LinkedList afterByOrFromOrSelects = new LinkedList();

    int indent = 1;

    StringBuffer result = new StringBuffer();
    StringTokenizer tokens;
    String lastToken;
    String token;
    String lcToken;

    public SQLFormatter(String sql) {
        tokens = new StringTokenizer(
                sql,
                "()+*/-=<>'`\"[]," + WHITESPACE,
                true
        );
    }

    public SQLFormatter setInitialString(String initial) {
        this.initial = initial;
        return this;
    }

    public SQLFormatter setIndentString(String indent) {
        this.indentString = indent;
        return this;
    }

    public String format() {

        result.append(initial);

        while (tokens.hasMoreTokens()) {
            token = tokens.nextToken();
            lcToken = token.toLowerCase();

            if ("'".equals(token)) {
                String t;
                do {
                    t = tokens.nextToken();
                    token += t;
                }
                while (!"'".equals(t) && tokens.hasMoreTokens()); // cannot handle single quotes
            } else if ("\"".equals(token)) {
                String t;
                do {
                    t = tokens.nextToken();
                    token += t;
                }
                while (!"\"".equals(t));
            }

            if (afterByOrSetOrFromOrSelect && ",".equals(token)) {
                commaAfterByOrFromOrSelect();
            } else if (afterOn && ",".equals(token)) {
                commaAfterOn();
            } else if ("(".equals(token)) {
                openParen();
            } else if (")".equals(token)) {
                closeParen();
            } else if (BEGIN_CLAUSES.contains(lcToken)) {
                beginNewClause();
            } else if (END_CLAUSES.contains(lcToken)) {
                endNewClause();
            } else if ("select".equals(lcToken)) {
                select();
            } else if (DML.contains(lcToken)) {
                updateOrInsertOrDelete();
            } else if ("values".equals(lcToken)) {
                values();
            } else if ("on".equals(lcToken)) {
                on();
            } else if (afterBetween && lcToken.equals("and")) {
                misc();
                afterBetween = false;
            } else if (LOGICAL.contains(lcToken)) {
                logical();
            } else if (isWhitespace(token)) {
                white();
            } else {
                misc();
            }

            if (!isWhitespace(token)) lastToken = lcToken;

        }
        return result.toString();
    }

    private void commaAfterOn() {
        out();
        indent--;
        newline();
        afterOn = false;
        afterByOrSetOrFromOrSelect = true;
    }

    private void commaAfterByOrFromOrSelect() {
        out();
        newline();
    }

    private void logical() {
        if ("end".equals(lcToken)) indent--;
        newline();
        out();
        beginLine = false;
    }

    private void on() {
        indent++;
        afterOn = true;
        newline();
        out();
        beginLine = false;
    }

    private void misc() {
        out();
        if ("between".equals(lcToken)) {
            afterBetween = true;
        }
        if (afterInsert) {
            newline();
            afterInsert = false;
        } else {
            beginLine = false;
            if ("case".equals(lcToken)) {
                indent++;
            }
        }
    }

    private void white() {
        if (!beginLine) {
            result.append(" ");
        }
    }

    private void updateOrInsertOrDelete() {
        out();
        indent++;
        beginLine = false;
        if ("update".equals(lcToken)) newline();
        if ("insert".equals(lcToken)) afterInsert = true;
    }

    private void select() {
        out();
        indent++;
        newline();
        parenCounts.addLast(parensSinceSelect);
        afterByOrFromOrSelects.addLast(afterByOrSetOrFromOrSelect);
        parensSinceSelect = 0;
        afterByOrSetOrFromOrSelect = true;
    }

    private void out() {
        if (UPPER_CASE.contains(token.toLowerCase())) {
            token = token.toUpperCase();
        }
        result.append(token);
    }

    private void endNewClause() {
        if (!afterBeginBeforeEnd) {
            indent--;
            if (afterOn) {
                indent--;
                afterOn = false;
            }
            newline();
        }
        out();
        if (!"union".equals(lcToken)) indent++;
        newline();
        afterBeginBeforeEnd = false;
        afterByOrSetOrFromOrSelect = "by".equals(lcToken)
                || "set".equals(lcToken)
                || "from".equals(lcToken);
    }

    private void beginNewClause() {
        if (!afterBeginBeforeEnd) {
            if (afterOn) {
                indent--;
                afterOn = false;
            }
            indent--;
            newline();
        }
        out();
        beginLine = false;
        afterBeginBeforeEnd = true;
    }

    private void values() {
        indent--;
        newline();
        out();
        indent++;
        newline();
        afterValues = true;
    }

    private void closeParen() {
        parensSinceSelect--;
        if (parensSinceSelect < 0) {
            indent--;
            parensSinceSelect = parenCounts.removeLast();
            afterByOrSetOrFromOrSelect = afterByOrFromOrSelects.removeLast();
        }
        if (inFunction > 0) {
            inFunction--;
            out();
        } else {
            if (!afterByOrSetOrFromOrSelect) {
                indent--;
                newline();
            }
            out();
        }
        beginLine = false;
    }

    private void openParen() {
        if (isFunctionName(lastToken) || inFunction > 0) {
            inFunction++;
        }
        beginLine = false;
        if (inFunction > 0) {
            out();
        } else {
            out();
            if (!afterByOrSetOrFromOrSelect) {
                indent++;
                newline();
                beginLine = true;
            }
        }
        parensSinceSelect++;
    }

    private static boolean isFunctionName(String tok) {
        final char begin = tok.charAt(0);
        final boolean isIdentifier = Character.isJavaIdentifierStart(begin) || '"' == begin;
        return isIdentifier &&
                !LOGICAL.contains(tok) &&
                !END_CLAUSES.contains(tok) &&
                !QUANTIFIERS.contains(tok) &&
                !DML.contains(tok) &&
                !MISC.contains(tok);
    }

    private static boolean isWhitespace(String token) {
        return WHITESPACE.contains(token);
    }

    private void newline() {
        result.append("\n");
        for (int i = 0; i < indent; i++) {
            result.append(indentString);
        }
        beginLine = true;
    }


    public static String format(String sql) {
        try {
            return new SQLFormatter(sql).format();
        } catch (Throwable t) {
            return sql;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy