Groovyで、テーブル一覧、テーブル定義をHTML出力してみる2

2019-03-25Groovy

前回作成した Groovyで、テーブル一覧、テーブル定義をHTML出力してみる の改良版。

もう少しテーブル定義を見やすくしてみました。

  • 左側にテーブル一覧が表示される
  • 右側にテーブル定義が表示される
  • テーブル一覧、テーブル定義それぞれ日本語名称を表示できる
  • 簡単なSelect SQLを作ってくれる

ソースは以下のとおり。

MarkupBuilder拡張クラス

#!/usr/bin/env groovy
/**
 * @author genzou
 */
class XhtmlBuilder extends groovy.xml.MarkupBuilder {

    def charset = "UTF8"
    def lang = "ja"
    def title = ""
    def cssList = ["style.css"]

    public XhtmlBuilder(map, writer) {
        super(writer)
        map.keySet().each {
            this."$it" = map."$it"
        }
    }

    def body = { closure ->
        delegate.print("""n""")
        delegate.print("""n""")
        html(lang: "${this.lang}", "xml:lang": "${this.lang}") {
            head() {
                meta("http-equiv": "Content-Type", content: "text/html;charset=${this.charset}")
                meta("http-equiv": "Content-Style-Type", content: "text/css;charset=${this.charset}")
                title("${this.title}")
                cssList.each {
                    link(rel: "stylesheet", href: it)
                }
            }
            body closure
        }
    }
}

日本語名称マッパークラス

#!/usr/bin/env groovy

/**
 * @author genzou
 */
public class Mapper {
    // テーブルID(大文字)と日本語名称の対応を記述(IDがキー)
    public static def tables = [:]

    // カラムID(大文字)と日本語名称の対応を記述(IDがキー)
    public static def columns = [:]

}

メイン処理

#!/usr/bin/env groovy
def props = [
        user    : "ゆーざーめい!",
        password: "ぱすわーど!",
] as Properties
def url = "JDBCのURL!"
def driver = "てきとうなどらいば!"

def dir = new File("entities")
if (!dir.exists() || dir.file)
    dir.mkdirs()

def gsql
try {
    gsql = Sql.newInstance(
            url
            , props
            , driver)

    def rs = gsql.connection.metaData.getColumns(null, props.user, "%", "%")

    def rows = []
    while (rs.next()) {
        rows << rs.toRowResult()
    }
    rs.close()
    def entities = rows.groupBy { it.TABLE_NAME }.findAll { !(it.key =~ /.*BIN$.*/) }
    println entities

    // 出力処理
    new File("$dir/index.html").withWriter("UTF8") {
        new XhtmlBuilder(it, title: "テーブル定義").body() {
            div(class: "container") {
                div(class: "left") {
                    h2("テーブル一覧")

                    table() {
                        tr() {
                            th "ID"
                            th "名称"
                        }

                        def i = 0
                        entities.keySet().sort().each { tableName ->
                            tr('class': (i++ % 2) ? "odd" : "") {
                                td() {

                                    a(href: "${tableName}.html", tableName)
                                }
                                td Mapper.tables[tableName]
                            }
                        }
                    }
                }
                div(class: "right") {
                }
            }
        }
    }

    entities.each { entity ->
        def columns = entity.value
        new File("$dir/${entity.key}.html").withWriter("UTF8") {
            new XhtmlBuilder(it, title: "テーブル定義 - ${entity.key}").body() {
                div(class: "container") {
                    div(class: "left") {
                        h2("テーブル一覧")

                        table() {
                            tr() {
                                th "ID"
                                th "名称"
                            }

                            def i = 0
                            entities.keySet().sort().each { tableName ->
                                tr('class': (i++ % 2) ? "odd" : "") {
                                    td() {

                                        a(href: "${tableName}.html", tableName)
                                    }
                                    td Mapper.tables[tableName]
                                }
                            }
                        }
                    }
                    div(class: "right") {
                        h1 "テーブル定義"

                        h2 "ID"
                        p "${entity.key}"

                        h2 "名称"
                        p "${Mapper.tables[entity.key]}"

                        h2 "カラム情報"
                        table() {
                            tr() {
                                th "ID"
                                th "名称"
                                th "型"
                                th "サイズ"
                                th "NOT NULL"
                            }

                            def i = 0
                            columns.each { column ->
                                tr('class': (i++ % 2) ? "odd" : "") {
                                    td column.COLUMN_NAME
                                    td(Mapper.columns[column.COLUMN_NAME])
                                    td column.TYPE_NAME
                                    td(class: "num", column.COLUMN_SIZE)
                                    td(column.IS_NULLABLE?.toString())
                                }
                            }
                        }

                        h2 "sql"
                        def sql = """
    SELECT
        *
    FROM
        ${entity.key}
--    WHERE""" + columns.collect { "n--        " + it.COLUMN_NAME + " = '' " }.join("AND")
                        textarea(sql)
                    }
                }
            }
        }
    }
} finally {
    gsql?.close()
}

new File("$dir/style.css").write("""
div.container
{
    width:99%;
}

h2
{
    border:2px solid gray;
    border-left:10px solid gray;
    padding-left:20px;
}

body
{
    background-color:#fbfbfb;
}

div.left
{
    float:left;
    width:30%;
    border-right:2px double gray;
    padding-right:0;
    margin-right:5px;
    height:800px;
    overflow:scroll;
}

div.left table
{
    font-size:75%;
}

div.right
{
    width: 65%;
    float:left;
}

div.right p
{
    padding-left:40px;
}

div.left td
{
    word-break:break-all;
    word-wrap:break-word;
}

div.right th,div.right td
{
    white-space:nowrap;
}

table
{
    width:90%;
    border:2px solid gray;
    border-collapse:collapse;
    margin:1px auto;
}

tr th
{
    background:#d7dbff;
}

th,td
{
    border-bottom:1px solid #c5cfc8;
    border-left:1px solid #e5eff8;
    padding:1px;
}

tr.odd td
{
    background:#f7fbe0;
}
textarea{
    width: 450px;
    height:200px;
} """, "UTF8")

結構長くなってしまった。

わからなかったこと1

MarkupBuilderの拡張クラスとしてXhtmlBuilderというクラスを作成したけれども、 body というクロージャを作成しそいつを呼び出したときにbuilder処理が行われる作りにしたけれども、 body ではなく任意のメソッド名でも実行されるようにしたかった。

わからなかったこと2

index.htmlとエンティティ名.htmlの両方で、左側のテーブル一覧出力処理は同じにも関わらず、同一の処理を記述している。

ここをうまいこと共通化できないものかなぁ。

作ってみてよかったこと

すごくビルダーの勉強になった。

ちなみにアンカー出力の処理は以下のような記述方法があるみたい。

  • a(href:"url"){ yield "テキスト" }
  • a(href:"url", "テキスト")

いずれも テキスト になるぽい。

2019-03-25Groovy