SQLのキーワードを強調表示

a-san2007-09-20

先日、強調表示のやり方がわかったので、VerticalViewerにSQLの強調表示をつけてみました。
http://d.hatena.ne.jp/a-san/20070908
SQLの強調表示は、ある程度の字句解析をやらなければならないのですが、そういえば昔やったのでそれが使えそう。
手元に当時のソースはあるけれど、その後、いがぴょんによって拡張されているのでそれを使ってみます。:−)
http://hp.vector.co.jp/authors/VA027994/blanco/blancosqleditorplugin.html
むむ。結構、手が入っているし、ANSI-SQL予約語も有用です。
ただそのままだとトークンの位置が取れないので、BlancoSqlTokenにfPosを追加。
あとはサクッと作れました。
強調部分は以下の通り。ついでに、SQL整形もメニューに追加。

    /** 強調表示を設定する. */
    void setHighlight(StyledDocument doc) {
        // SQL文を取り出す。
        String text = "";
        try {
            text = doc.getText(0, doc.getLength());
        } catch (BadLocationException e) {
            e.printStackTrace();
            return; // フツー、ココには来ないけれど、来るようならば色づけなどしない。
        }
        // SQL文を字句解析し、字句ごとにぶった切る。
        ArrayList tokens = null;
        try {
            tokens = fParser.parse(text);
        } catch (RuntimeException e) {
            //e.printStackTrace();
            return; // 不完全なSQLだと起こりえる。
        }
        // 一旦、すべての属性を消す
        doc.setCharacterAttributes(0, text.length(), PLANE, true);
        // トークンごとに色分けする。
        for (int i=0; i<tokens.size(); i++) {
            BlancoSqlToken t = (BlancoSqlToken) tokens.get(i);
            int pos = t.getPos();
            String str = t.getString();
            switch (t.getType()) {
            case BlancoSqlTokenConstants.SPACE:     // 空白、TAB、改行など
                break;
            case BlancoSqlTokenConstants.SYMBOL:    // 記号. " <="のような2つで1つの記号もある。
                break;
            case BlancoSqlTokenConstants.KEYWORD:   // キーワード. "SELECT", "ORDER"など.
                str = str.toUpperCase();
                if (Arrays.binarySearch(BlancoSqlConstants.SQL89_RESERVED_WORDS, str) >= 0) {
                    doc.setCharacterAttributes(pos, str.length(), SQL89, true);
                } else if (Arrays.binarySearch(BlancoSqlConstants.SQL92_RESERVED_WORDS, str) >= 0) {
                    doc.setCharacterAttributes(pos, str.length(), SQL92, true);
                } else if (Arrays.binarySearch(BlancoSqlConstants.SQL99_RESERVED_WORDS, str) >= 0) {
                    doc.setCharacterAttributes(pos, str.length(), SQL99, true);
                } else if (Arrays.binarySearch(BlancoSqlConstants.SQL_FAMOUS_WORDS, str) >= 0) {
                    doc.setCharacterAttributes(pos, str.length(), SQL_FAMOUS, true);
                } else {
                    doc.setCharacterAttributes(pos, str.length(), KEYWORD, true);
                    // ココには来ないはず
                    System.out.println("keyword="+str);
                }
                break;
            case BlancoSqlTokenConstants.NAME:      // 名前.テーブル名、列名など。ダブルクォーテーションが付く場合がある。
                doc.setCharacterAttributes(pos, str.length(), NAME, true);
                break;
            case BlancoSqlTokenConstants.VALUE:     // 値. 数値(整数、実数)、文字列など。
                doc.setCharacterAttributes(pos, str.length(), VALUE, true);
                break;
            case BlancoSqlTokenConstants.COMMENT:   // コメント. シングルラインコメントとマルチラインコメントがある。
                doc.setCharacterAttributes(pos, str.length(), COMMENT, true);
                break;
            case BlancoSqlTokenConstants.END:       // SQL文の終わり.
                break;
            case BlancoSqlTokenConstants.UNKNOWN:   // 解析不可能なトークン. 通常のSQLではありえない。
                doc.setCharacterAttributes(pos, str.length(), UNKNOWN, true);
                break;
            default:
                assert false: t.getType();  // ありえない
            }
        }
    }