課題B1

課題B1では, ArgoUMLの図形の削除機能について見ていきます.
ArgoUMLはオープンソースのUMLモデリングツールで, Javaで書かれております.
また, ArgoUML内部では, グラフ編集用フレームワークであるGEFが使用されています.
ワークスペース内のargouml-*が、ArgoUMLを構成するプロジェクトであり, GEF-0.13.7がGEFのプロジェクトです.


実行手順

  1. デスクトップのEclipseを起動し, 指定されたワークスペースを開いてください.
  2. ワークスペース中にあるGEF-0.13.7プロジェクトを選択し, メニューバーの Search → Java で Fig という型の宣言を検索してください.
  3. Figクラスのソースファイルを開いて743行目 (Fig#removeFromDiagram()) にブレークポイントを入れてください.
  4. メニューバーの Window → Perspective → Open Perspective → Other... → トレースデバッガ(逆方向) でトレースデバッガのパースペクティブを開いてください.
  5. 開いたトレースデバッガのパースペクティブ右上のブレークポイントビュー上にあるフォルダアイコン(左から1番目)を
    クリックしてトレースファイル(ArgoUMLBenchMarkWithMoreStandardClasses.trace)を開いてください.
  6. トレースファイルの読み込みが終わるまで待ってから, ブレークポイントビュー上にある「ブレークポイントをEclipseから取り入れる」アイコン(左から2番目)を
    クリックしてトレースデバッガ用のブレークポイントを作成してください.
  7. ブレークポイントビュー上にあるデバッグアイコン(左から3番目)をクリックしてください.
    トレースデバッガによるデバッグ実行が起動し, ブレークポイントを入れた位置の行が選択された状態で一時停止します.


課題B1

Fig クラスはグラフィックエディタ上の図形を表すGEFの内部のクラスです.
また, FigClass クラスはクラス図上に配置された「クラス」を表す ArgoUML のクラスで, Fig クラスの子孫クラスにあたります.
ブレークポイントを置いた行は、図形を削除するたびに実行されます.

先ほどの実行手順にしたがって進めていくと, 下図のように Fig クラスの removeFromDiagram() メソッド内の743行目で一時停止している状態になっているはずです.
この状態になっていれば, ここから時間計測を開始してください.
ここで, 変数ビューを確認すると, このメソッド実行は FigClass クラスのインスタンス (id = 1675174935) に対する呼び出しだとわかります.
この FigClass クラスのインスタンス (id = 1675174935) がどこから来たのかを確認していきます.
まずは呼び出しスタック上で1つ呼び出し元をクリックしてください.

現在は, 呼び出し元である FigNode クラスの removeFromDiagram() メソッドを見ています.
ここで, 変数ビューを見ると, このメソッドも同一の FigClass クラスのインスタンス (id = 1675174935) に対して呼び出されていることがわかります.
これを確認したら, 呼び出しスタック上でさらに1つ呼び出し元をクリックしてください.

現在は, 呼び出し元である FigNodeModelElement クラスの removeFromDiagramImpl() メソッドを見ています.
ここで, 変数ビューを見ると, このメソッドも先ほどと同様に FigClass クラスのインスタンス (id = 1675174935) によって呼び出されていることがわかります.
これを確認したら, 呼び出しスタック上でさらに1つ呼び出し元をクリックしてください.

現在は, 呼び出し元である FigNodeModelElement クラスの removeFromDiagram() メソッドを見ています.
ここで, ソースコードを見ると, ローカル変数 delegate が参照しているインスタンスに対して removeFromDiagramImpl() メソッドが呼び出されていることがわかります.

このローカル変数 delegate で参照されるインスタンスはどこから来たのかを確認します.
ソースコードを見ると, 1965行目に delegate への代入文があることがわかるので,
ステップバックオーバーを1回押して, その代入文の実行直前の時点まで戻ってください.
1965行目の delegate への代入文の時点まで戻ったら, ステップインを1回押して getRemoveDelegate() メソッドの中に前から入ってください.

FigNodeModelElement クラスの getRemoveDelegate() メソッドに入りました.
ここで, ソースコードを見ると, このメソッドでは return this で自分自身を戻り値として返していることが確認できます.
これを確認したら, ステップバックリターンを1回押して呼び出し元へと戻ってください.

呼び出し元に戻ると, 先ほどの return this の値が delegate に代入されていることがわかります.
ここで, 再びステップバックリターンを1回押してさらに呼び出し元に戻ってください.

呼び出し元である ActionRemoveFromDiagram クラスの actionPerformed() メソッドの89行目に戻ってきました.
ここで, 「トレースデバッガ」パースペクティブの右下にある「実行時点の登録」ビューの追加ボタンをクリックしてください.
そうすると, 現在の実行時点がこのビュー上に登録されます.

89行目では, FigClass クラスのインスタンス (id = 1675174935) のメソッドを呼び出していることがわかります.
この FigClass クラスのインスタンスがどこから来たのかを確認するために, ステップバックオーバーを3回押して, 86行目の拡張for文まで戻ってください.
ここで FigClass クラスのインスタンスは Figクラスを保持するリストであるローカル変数 figs から拡張for文で取得されてきたことがわかります.
ここからは, FigClass クラスのインスタンスを保持していた figs がどこから来たのかと, figs にそのインスタンスがどこで追加されたのかを見ていきます.
まずは, ステップバックインを3回押して, 85行目の getFigs() メソッドの中に後ろから入ってください.

SelectionManager クラスの getFigs() メソッドに後ろから入りました.
ここではステップバックオーバーを1回押してください.
すると, 363行目にある Vector クラスの addElement(Object) メソッドの呼び出し前の時点に来ます.
ここで, 変数ビュー上にある「呼び出し前」を開くと, 引数として FigClass クラスのインスタンスが渡されていることがわかります.
また, この時点で「実行時点の登録」ビューの追加ボタンをクリックして実行時点の登録を行ってください.
Vector クラスの addElement(Object) メソッドの引数には Selection クラスの getContent() メソッドの戻り値が渡されています.
getContent() メソッドの戻り値を確認するために, ステップバックインを1回押して, このメソッドの中に後ろから入ってください.

Selection クラスの getContent() メソッドに入りました.
ここで, 変数ビュー上の this を開くと, Selection クラスの子孫クラスである SelectionClass クラスのインスタンスが
フィールド content として FigClass クラスのインスタンス (id = 1675174935) を保持していることがわかります.

先ほどの結果から, FigClass クラスのインスタンス (id = 1675174935) は SelectionClass クラスのインスタンス (id = 1672744985) が保持していることがわかりました.
次は, この SelectionClass クラスのインスタンスがどこから来たのかを見ていきます.
呼び出しスタック上で呼び出し元である SelectionManger クラスの getFigs() メソッドをクリックしてください.
ここで, ソースコードを見ると, SelectionClass クラスのインスタンスは selections から取得していることが確認でき,
それは SelectionManager クラスのインスタンス (id = 4310438) がフィールドとして保持していることがわかります.

先ほどの結果から, SelectionClass クラスのインスタンス (id = 1672744985) は
SelectionManager クラスのインスタンス (id = 4310438) が保持していることがわかりました.
次は, この SelectionManager クラスのインスタンスがどこから来たのかを見ていきます.
呼び出しスタック上でさらに呼び出し元である ActionRemoveFromDiagram クラスの actionPerformed(ActionEvent) メソッドをクリックしてください.
ここでは, ステップバックインを1回押して Editor クラスの getSelectionManager() メソッドの中に後ろから入ってください.

Editor クラスの getSelectionManager() メソッドに入りました.
ここで, 変数ビュー上の this を開くと, この Editor クラスがフィールド _selectionManager として
SelectionManager クラスのインスタンス (id = 4310438) を保持していることがわかります.

先ほどの結果から, SelectionManager クラスのインスタンス (id = 431048) は
Editor クラスのインスタンス (id = 371019845) が保持していることがわかりました.
次は, この Editor クラスのインスタンスがどこから来たのかを見ていきます.
呼び出しスタック上で呼び出し元をクリックしてください.

現在は, ActionRemoveFromDiagram クラスの actionPerformed(ActionEvent) メソッドの85行目を見ています.
ここで, ソースコードを見ると, この Editor はローカル変数 ce に代入されている値だとわかります.
ここでは, このローカル変数 ce の値がどこから来たのかを見ていきます.
ステップバックオーバーを2回押して83行目まで戻ってください.
83行目ではローカル変数 ce に Globals クラスの curEditor() メソッドの戻り値が代入されています.
ステップインを1回押してこのメソッドの中に前から入ってください.

Globals クラスの curEditor() メソッドに入りました.
ここで, ソースコードを見ると, Globalsクラスが static フィールド _curEditor として
Editor クラスのインスタンス (id = 371019845) を保持していることがわかります.

先ほどの結果から, Editor クラスのインスタンス (id = 371019845) は Globals クラスの static フィールド _curEditor から取得していることがわかりました.
呼び出しスタック上で呼び出し元をクリックしてください.
ここで, 課題B1は終了です. 時間計測を終了してください.