Understand APIチュートリアル5:グラフ

Understand

本記事は米国向けに公開されている開発元のブログ記事をベースとし、日本国内のUnderstand 7.0 APIの活用をご検討されているお客様向けに加筆・修正を加えたものとなります。

前回の記事はこちらをご確認ください。

※一部の記事の内容ではUnderstand 6.5が使用されていますが、基本的な機能はUnderstand 7.0でも同様にご利用いただけます。

チュートリアル目次

第1回:Understand APIチュートリアル1:Python API入門

第2回:Understand APIチュートリアル2:最初のAPIスクリプトの作成

第3回:Understand APIチュートリアル3:Entity、References、Kind Filterとは

第4回:Understand APIチュートリアル4:字句解析(Lexer、Lexemeとは)

第5回:Understand APIチュートリアル5:グラフ

(以降順次公開中)

カスタムグラフを描画する機能は、Understand Python APIの中核機能です。Understandプラグインシステムを使用することで、使い慣れたユーザーであれば独自のカスタムグラフを作成できます。

このチュートリアルでは、シンプルなアーキテクチャツリーグラフを生成する方法を説明します。この機能はUnderstandに既に組み込まれており、アーキテクチャブラウザーで任意のフォルダーまたはアーキテクチャを右クリックし、[グラフィカルビュー]-[Graph Architecture]を選択することで利用できます。しかし、このコードはグラフをニーズに合わせて拡張・調整するための優れたテンプレートとなります。 

※アーキテクチャブラウザーは[アーキテクチャ]-[アーキテクチャブラウザー]から表示できます。(図において厚みのあるノード(例:図右上のwincred)をダブルクリックすると、さらに内部を展開可能です。)

Understandプラグインのインストール

このチュートリアルでは、Understandに独自のスクリプトを追加する主な方法であるプラグインを紹介します。これまではコマンドラインからuPythonスクリプトを実行する方法のみを見てきましたが、Understandでは、.upyファイルをUnderstandのGUIにドラッグ&ドロップするだけで、あらゆるuPythonスクリプトをプラグインに変換できます。

ドラッグ&ドロップの代わりに、プラグインを次のいずれかのディレクトリに配置することでインストールすることもできます。

  • Windows – 例:%APPDATA%\SciTools\plugin
  • Mac – 例:/Users/username/Library/Application Support/SciTools/plugin/Graph
  • Linux – 例:/home/username/.config/SciTools/plugin/Graph

独自のプラグインファイルを作成するには、.upy拡張子の新しいファイルを作成するだけです。この例ではarch_tree.upyを使用します。
※本プラグインはUnderstand 7.0では[ツール]-[プラグイン]-[プラグインの管理]からプラグインマネージャーを開き、グラフの[すべて表示]-[Tutorial: Architecture Tree – Custom]を有効にすることで利用可能になります。なお、プラグインの実体は(Understandインストールフォルダー)\plugins\Graph\archTree.upyであり、ファイル名がarchTree.upyに変更されています。

グラフプラグインの基本

今回のプラグインは次のコードから始まります。

import understand

def name():
  return "Tutorial: Architecture Tree"

def description():
  return "Visually displays the hierarchical structure of a software architecture."

def test_architecture(arch):
  return True

def init(graph, target):
   graph.options().define("Fill", ["On","Off"], "Off")
   graph.legend().define("func", "roundedrect", "Function", "blue", "#FFFFFF")
  pass

上記のコードにおいて、name() はプラグインの名前(=グラフの名称)を設定し、description() はプラグインの説明を設定します。(ここで記載した説明は[プラグインの管理]に表示されます。)

test_architecture(arch) は選択したアーキテクチャのグラフが存在することをチェックし、init(graph, target) はグラフを初期化します。また、グラフのオプションと凡例も設定できます。Understand にプラグインを追加すると、設定した名前でグラフィカルビューリストに表示されます。
※test_architecture(arch)にてTrueを返すことで当プラグインのグラフがグラフィカルビューメニューに出現します。

アーキテクチャブラウザーにおいて、特定のフォルダーの右クリックメニューから表示した[グラフィカルビュー]リスト(英語版の画面)

グラフプラグインロジック

さて、実際のプラグインロジックについて見ていきましょう。Understandは、Graphvizを使ってグラフレイアウトを生成するため、プラグインでは描画するノード、エッジ、そしてサブグラフを定義する必要があります。まずは、後でノードを取得するために使用する関数を作成しましょう。各ノードはグラフの一部であり、このページ上部にあるサンプルグラフではボックスで表されています。

def grabNode(graph, nodes, arch):
  if arch in nodes:
    node = nodes[arch]
  else:
    node = graph.node(arch.name())
    node.sync(arch); # architectures must be synced instead of given at creation
    nodes[arch] = node

  return node

これは、draw関数を確認するとより理解しやすくなります。ここでロジックが実行されます。

def draw(graph, target):
  """
  Draw the graph

  The second argument can be a database or an entity depending on whether
  the graph was created at a project level or entity level.
  """

  graph.set("rankdir", "LR")

  # store the arch->graphviz node so that each arch node appears only
  # once no matter how many edges
  nodes = dict()

  curLevel = []
  curLevel.append(target)

  while curLevel:
    nextLevel = []
    for arch in curLevel:
      tail = grabNode(graph, nodes, arch)
      for child in arch.children():
        edge = graph.edge(tail,grabNode(graph,nodes,child))
        nextLevel.append(child)
    curLevel = nextLevel

関数「draw(graph, target)」では、両方の変数はスクリプトの冒頭でインポートしたunderstandライブラリから取得されます。graphは関数内で使用するオブジェクトであり、targetはユーザーが選択したエンティティです。既にアーキテクチャをフィルタリングしているので、この場合のtargetはアーキテクチャになります。

graph.set() は、さまざまなグラフオプションを設定するために使用されます。上記の例では、グラフを左から右にフローするように指定しています。これらのオプションはGraphvizから直接取得されます。

whileループでは、curLevelが最初に選択されたアーキテクチャに設定されます。これはコードベース内のフォルダー階層のようなものだと想像してください。例えばsrcというフォルダーがあり、その下に複数のフォルダーとサブフォルダーがある場合を考えます。srcにフォルダーが0個しかない場合、ループは1回だけ実行され、グラフには「src」というボックスが1つだけ表示されます。srcに実際には複数のフォルダーがある場合、次のループでは次のレベルのすべてのフォルダー(ノード)を検索し、それらとその子ノードの間にエッジを作成します。そして、それらの子ノードをnextLevel[]に追加します。これにより、次のループでそれらの子ノードを検索できるようになります。これを繰り返し、アーキテクチャ全体がグラフ化されるまで繰り返します。