dorothyの魔法でクラス図を描く

[Graphviz](http://graphviz.org/)のClojureラッパーである[dorothy](https://github.com/daveray/dorothy)を使うと、こんな継承関係の図が簡単に描けます。

![WS000006.JPG](https://qiita-image-store.s3.amazonaws.com/0/40558/ad8e582b-3ca2-73e2-be5d-2396ff842134.jpeg)

dorothyを使うにはあらかじめGraphvizをインストールした後に、GraphvizのbinにPATHを通しておく必要があります。

project.cljの:dependenciesには`[dorothy "0.0.5"]`を書いとけばOKです。

# dorothyの基本

ノード（丸の部分）は`[:node1]`のような形式か、そのまま`:node2`と書けます。
エッジ（丸をつなぐ線の部分）は`[:a :b :c :d :a]`のようにベクタに複数のノード名を入れればよいです。
下記例では`:a`などはノードとして定義してないですが、表示してくれるようです。

```sample.clj
(ns classzu.sample
  (:require [dorothy.core :refer :all]))

(-> [[:node1] :node2
     [:node1 :node2]]
    graph dot show!)

(-> [[:node1] :node2
     [:a :b :c :d :a]]
    digraph dot show!)
```

![WS000004.JPG](https://qiita-image-store.s3.amazonaws.com/0/40558/84c19ccf-593d-75d8-9bed-cca5828499ed.jpeg)

![WS000000.JPG](https://qiita-image-store.s3.amazonaws.com/0/40558/3e6bd08b-c0f7-0437-efa7-6a0beb227c21.jpeg)

なお、`graph`（無向グラフ）と`digraph`（有向グラフ）が選択できます。
グラフをさらに入れ子にする`subgraph`などもあるようです。

#### ノードごと、エッジごとのスタイル
ノード定義、エッジ定義の最後にマップをつけることで、Graphvizの属性をつけることができます。

```clj
(-> [[:node1 {:style :filled :shape :record}] :node2
     [:a :b :c :d :a {:arrowhead :empty}]
     [:e :f]]
    digraph dot show!)
```

![WS000001.JPG](https://qiita-image-store.s3.amazonaws.com/0/40558/8facd0bf-d48c-c191-3cfa-c7db0ff5cdf2.jpeg)

#### 全体のスタイル
`node-attrs`, `edge-attrs`を使えば、全ノード・全エッジのスタイルを定義できます。

```clj
(-> [(node-attrs {:color :red})
     (edge-attrs {:style :dashed})
     [:node1 {:style :filled :shape :record}] :node2
     [:a :b :c :d :a {:arrowhead :empty}]
     [:e :f]]
    digraph dot show!)
```


![WS000002.JPG](https://qiita-image-store.s3.amazonaws.com/0/40558/5127924f-7c29-f03c-0dd2-0ec81961b421.jpeg)

`show!`だと別ウィンドウでグラフが表示されますが、画像ファイルとして保存したい場合は`save!`を使うことができます。

```clj
(-> [(node-attrs {:color :red})
     (edge-attrs {:style :dashed})
     [:node1 {:style :filled :shape :record}] :node2
     [:a :b :c :d :a {:arrowhead :empty}]
     [:e :f]]
    digraph dot (save! "out.png" {:format :png}))
```

# クラス図（継承関係のみ）を描く
まずは、手動でクラス図を描いてみます。
[クラス名、親クラス、実装インタフェース1, 2...]のような形式で複数`show-diagram`に渡せます。

```core.clj
(ns classzu.core
  (:require [dorothy.core :refer :all]
            [clojure.set :as set]
            [clojure.walk :as walk]))

(defn show-diagram [& class-defs]
  (->> (for [[class extends & implements] class-defs]
         (concat [class]
                 (when extends [[class extends]])
                 (map (fn [i] [class i {:style :dashed}]) implements)))
       (apply concat)
       (list* (node-attrs {:shape :record})
              (edge-attrs {:arrowhead :empty}))
       digraph dot show!))

(show-diagram [:classA :classB :interface1 :interface2] [:classZ nil :interface9])
```

![WS000007.JPG](https://qiita-image-store.s3.amazonaws.com/0/40558/0fddb0ff-7565-6132-a0f9-3313c918cdf8.jpeg)

####Classオブジェクトからグラフを描く
Classオブジェクトから`show-diagram`に渡す定義を自動生成してグラフを描いてみます。

```clj
(defn get-classdef [^Class c]
  (->> (list* c (.getSuperclass c) (apply set/difference (supers c) #{(.getSuperclass c)} (map supers (supers c))))
       (walk/postwalk #(if (or (nil? %) (sequential? %)) % (keyword (.getSimpleName %))))))

(defn get-all-classdefs [& classes]
  (distinct (concat (map get-classdef classes)
                    (mapcat #(apply get-all-classdefs (supers %)) classes))))

(import '[java.util ArrayList LinkedList])
(apply show-diagram (get-all-classdefs ArrayList LinkedList))

```

![WS000010.JPG](https://qiita-image-store.s3.amazonaws.com/0/40558/1fb48202-8649-9592-d06f-6e2092588cb9.jpeg)

Clojureのいろんなシーケンスをグラフにしてみるとこんな感じになります。

```clj
(apply show-diagram (apply get-all-classdefs (map class [() '(1) (range 1) (seq "a") (cons 1 [])
                                                         (seq {1 2}) (keys {1 2}) (vals {1 2})])))
```

![WS000011.JPG](https://qiita-image-store.s3.amazonaws.com/0/40558/7956857e-6cba-d2f1-9ae4-fe8dec5e5587.jpeg)

簡単にグラフ描けてすばらです～