2013年2月4日月曜日

[Clojure]リフレクションを使ってインターフェースの関係を図にしてみる

clojure.langあたりのコードを少し読もうとしてみましたが、各クラスやインターフェースの関係が分かりにくいので 図にしてみようと思いました。

clojure.reflectを使ってインターフェースの継承関係を取得し、DOTファイルを作って図にしてみます。

(require '[clojure.reflect :as r])

(defn class-found? [klass]
  (try (Class/forName (.getName klass))
       (catch Exception _ nil)
       (catch Error _ nil)))

(defn clj-class-name [klass]
  (let [name (.getName klass)]
    (if-let [clj-name (re-find #"clojure\.lang\.(.+)" name)]
      (nth clj-name 1)
      (.getName klass))))

(defn clj-class-name? [name]
  (.startsWith name "clojure.lang"))

(defn class-name-list []
  (let [f (doto (.getDeclaredField ClassLoader "classes")
            (.setAccessible true))
        loader (.getClassLoader clojure.lang.RT)]
    (try
      (for [klass (vec (.get f loader))
            :when (class-found? klass)]
        (.getName klass))
      (finally
        (.setAccessible f false)))))

(defn clojure-interface-list []
  (->> (class-name-list)
       (filter clj-class-name?)
       (map #(Class/forName %))
       (filter #(.isInterface %))))

(defn parent-class-set [klass]
  (:bases (r/reflect klass)))

(defn print-interface-tree []
  (printf "digraph \"interface-tree\" {\n")
  (doseq [klass (clojure-interface-list)]
    (let [name (clj-class-name klass)]
      (doseq [parent (parent-class-set klass)]
        (printf "\"%s\" -> \"%s\";\n" name (clj-class-name parent)))))
  (printf "}\n"))

;; (print-interface-tree)

print-interface-tree 関数を実行すると repl に DOTファイルの内容が出力されるので、 DOTファイルとして保存します。

DOTファイルをPNG画像にするには以下のコマンドを実行すれば良いです。

dot -Tpng interface-tree.dot > interface-tree.png

シーケンスに関連する部分が多いようです。

0 件のコメント:

コメントを投稿