課題B2 (機能理解)

課題B2では, JHotDrawの図形の選択機能について見ていきます.
具体的には,

「配置された図形がJHotDrawのシステム内部でどのように管理され,図形選択機能の実行によってどのように取り出されて,どのように選択図形として登録されるか?」
JHotDrawはオープンソースの図形描画ツールで, Javaで書かれております.
ワークスペース内のjhotdraw7が、JHotDrawを構成するプロジェクトです。


前準備

課題B1と同様に「トレースデバッガ(順方向)」のパースペクティブを開いてください.

ブレークポイントビュー上の「ブレークポイントをEclipseから取り入れる」アイコンを押してください.
ブレークポイントを入れたら, 全てのブレークポイントにチェックが入った状態にしてください.

デバッグ実行してください.
すると, まずは DefaultDragTracker クラスの mousePressed(MouseEvent) メソッドの98行目で実行時点が一時停止します.
そのまま, 再開ボタンを4回押して, 2回目の DefaultDragTracker クラスの mousePressed(MouseEvent) メソッドの116行目にまで進んでください.
ここで, ステップインを押して DefaultDrawingView クラスの getSelectedFigures() メソッドの中に入ってください.

先ほどの操作によって, 現在は DefaultDrawingView クラスの getSelectedFigures() メソッドの931行目にいます.
ここで, フィールド selectedFigures に RectangleFigure クラスのインスタンス (id = 1952912699) を実際に追加しているのはどこなのかを見ていきます.
ソースコード中の selectedFigures にカーソルを入れ, そのフィールドに対してオブジェクトを追加している全ての行にブレークポイントを入れてください.
今回は, DefaultDrawingView クラスの780行目と815行目と889行目の三か所が該当します.

DefaultDrawingView クラスの780行目と815行目と889行目にブレークポイントを入れた状態で,
ブレークポイントビュー上の「ブレークポイントをEclipseから取り入れる」アイコンを押してください.
すると, 実際にトレース上に実行された記録が残っている780行目にだけブレークポイントが新規追加されます.
そのあと, ブレークポイントビュー上で下記3つのブレークポイントのチェックを外してください.

  1. 159行目 DefaultDragTracker.mouseDragged(MouseEvent)
  2.   98行目 DefaultDragTracker.mousePressed(MouseEvent)
  3. 116行目 DefaultDragTracker.mousePressed(MouseEvent)
ここで, デバッグ実行をいったん停止してください.

デバッグ実行を押すと, DefaultDrawingView クラスの addToSelection(Figure) メソッドの780行目で止まります.
ここで, 再開ボタンを押して, このあと実行がどのように移り変わるかを確認していきます.
まずは再開ボタンを1回押すと, 再び780行目に止まることが確認できます.
そこからさらに再開ボタンを押していくと, 現在の実行時点は118行目に2回止まることが確認できます.
これを確認したら, デバッグ実行をいったん終了してから再び実行して, 再開ボタンを1回押して2回目の780行目に進めてください.
下図の状態になれば, ここで課題2の前準備は終了です.


機能理解

ブレークポイントを置いた行は、図形を選択するたびに実行されます.

先ほどの前準備にしたがって進めていくと, 下図のように DefaultDrawingView クラスの
addToSelection(Figure) メソッド内の780行目で一時停止している状態になっているはずです.
この状態になっていれば, ここから時間計測を開始してください. 時間計測はできる限り,1分以内の単位での計測をお願いします.
DefaultDrawingView クラスの addToSelection(Figures) メソッドの780行目では, フィールド selectedFigures に
仮引数 f で参照される RectangleFigure クラスのインスタンス (id = 1952912699) を追加していることが確認できます.

まずは, この仮引数 figure の値がどこから来たのかを見るために, 呼び出しスタック上で1つ呼び出し元をクリックして戻ってください.
ソースコードを見ると, ここではローカル変数 view で参照されているインスタンスに対して先ほどの addToSelection(Figure) メソッドが呼び出されていることがわかります.
ここで, 変数ビュー上で「呼び出し前」および this を開いてオブジェクトのIDを確認してください.
すると, view の値は DefaultDrawingView クラスのインスタンス (id = 150367587) であり, その引数には DefaultDragTracker クラスのインスタンス (id = 758826749) が
フィールド anchorFigure として参照している RectangleFigure クラスのインスタンス (id = 1952912699) が渡されていることがわかります.

ここでは, 先にローカル変数 view の値がどこから来たのかを見ていきます.
ブレークポイントビュー上の DefaultDragTracekr クラスの mousePressed(MouseEvent) メソッドの98行目のブレークポイントにチェックを入れてください.
ブレークポイントを入れたら, デバッグ実行をいったん終了してから, もう一度デバッグ実行してください.
すると, まずは DefaultDrawingView クラスの addToSelection(Figure) メソッドの780行目に止まります.
続けて, 現在の実行時点がどのように移り変わっていくかを確認するために再開ボタンを押していくと, 現在の実行が98行目 → 780行目 と移っていくことが確認できます.
これを確認したら, デバッグ実行をいったん終了してください.

再びデバッグ実行してから, 再開ボタンを1回押して DefaultDragTracekr クラスの mousePressed(MouseEvent) メソッドの98行目にまで進めてください.
ここで, ステップインを1回押して, getView() メソッドの中に入ってください.

AbstractTool クラスの getView() メソッドの中に入りました.
ここで, 変数ビュー上の this を開くと, DefaultDragTracker クラスのインスタンス(id = 758826749) が
フィールド editor として DefaultDrawingEditor クラスのインスタンス (id = 1859859960) を参照していることがわかります.
これを確認したら, ステップインを3回押して getActiveView() メソッドの中に入ってください.

DefaultDrawingView クラスの getActiveView() メソッドの中に入りました.
ここで, 変数ビュー上の this を開くと, DefaultDrawingEditor クラスのインスタンス (id = 1859859960) が
フィールド activeView として DefaultDrawingView クラスのインスタンス (id = 150367587) を参照していることがわかります.
これを確認したら, 呼び出しスタック上で2つ呼び出し元をクリックしてください.

現在は DefaultDragTracker クラスの mousePressed(MouseEvent) メソッドの98行目を見ています.
ここで, 変数ビュー上で this を開いて, フィールド anchorFigure と this 自体のIDを確認してください.
DefaultDragTracker クラスのインスタンス (id = 758826749) が anchorFigure として
RectangleFigure クラスのインスタンス (id = 1952912699) を参照していることが確認できます.

ここからは, このフィールド anchorFigure に RectangleFigure クラスのインスタンスが代入された時点を見ていきます.
ソースコード中の anchorFigure にカーソルを入れて, ソースコード中で代入しているところ全てにブレークポイントを入れてください.
今回は, DefaultDragTracker クラスの84行目と218行目の二か所が該当します.

DefaultDragTracker クラスの84行目と218行目にブレークポイントを入れたら, ブレークポイントビュー上の「ブレークポイントをEclipseから取り入れる」アイコンを押してください.
すると, 実際にトレース上に実行された記録が残っている218行目にだけブレークポイントが新規追加されます.
そのあと, ブレークポイントビュー上で780行目と218行目以外のブレークポイントのチェックを外してください.

デバッグ実行をいったん終了してから, 再びデバッグ実行してください.
すると, まずは DefaultDrawingView クラスの addToSelection(Figure) メソッドの780行目に止まります.
ここで, 再開ボタンを1回押すと, 今度は DefaultDragTracker クラスの setDraggedFigure(Figure) メソッドの218行目に止まることが確認できます.
さらに再開ボタンを1回押すと, DefaultDragTracker クラスの addToSelection(Figure) メソッドの780行目に止まることが確認できます.
これにより, setDraggedFigure(Figure) メソッドの218行目で フィールド anchorFigure に代入された値があとで取得され, それが780行目で追加されていることがわかります.

デバッグ実行をいったん終了してから, 再びデバッグ実行してください.
そのあと, 再開ボタンを1回押して DefaultDragTracker クラスの setDraggedFigure(Figure) メソッドの218行目にまで進んでください.
ここで, 変数ビューを見て this と 引数の f のIDをそれぞれ確認してください.
これを確認したら, 呼び出しスタック上で1つ呼び出し元をクリックしてください.

現在は, 呼び出し元である SelectionTool クラスの getDragTracker(Figure) メソッドを見ています.
ここで, ソースコードを見ると, DefaultDragTracker クラスのインスタンス (id = 758826749) が
フィールド dragTracker に代入されており, それが戻り値としても返されることがわかります.
これを確認したら, 呼び出しスタック上でさらに1つ呼び出し元をクリックしてください.

現在は, 呼び出し元である SelectionTool クラスの mousePressed(MouseEvent) メソッドを見ています.
ここで, 変数ビュー上で「呼び出し前」を開いて引数と, this を開いてフィールド dragTracker のIDをそれぞれ確認してください.
すると, 先ほどの DefaultDragTracker クラスのインスタンス (id = 758826749) を フィールド dragTracker として
DelegationSelectionTool クラスのインスタンス (id = 599587451) が参照していることが確認できます.

ソースコードを見ると, 278行目で呼び出されている getDragTracker() メソッドの戻り値をいったんローカル変数 newTracker に代入しておいてから,
289行目で setTracker(Tool) メソッドに引数として渡していることが確認できます.
ここで, ステップオーバーを2回押して, 実際に289行目に止まることを確認し, ステップインを2回押して, setTracker() メソッドの中に入ってください.

SelectionTool クラスの setTracker(Tool) メソッドに入りました.
ここでは, ステップオーバーを4回押して301行目まで進めてください.
そのあと, 変数ビュー上で this を開くと, フィールド tracekr の値が 仮引数 newTracker と同じ値に更新されていることがわかります.
これを確認したら, ステップリターンを1回押して呼び出し元に戻ってください.

先ほどの呼び出し先から戻ってきたことで, 現在は SelectionTool クラスの mousePressed() メソッドの291行目にいます.
ここでは, ステップインを4回押して, DefaultDragTracker クラスの mousePressed(MouseEvent) メソッドの中に入ってください.

DefaultDragTracker クラスの mousePressed(MouseEvent) メソッドに入りました.
ここで, 変数ビュー上で this を開いて, フィールド anchorFigure と this 自体のIDを確認してください.

呼び出しスタック上で呼び出し元をクリックして戻ってください.
なお, 呼び出しスタック上では呼び出し元の行番号が0になっていますが, 実際には291行目にいることに注意してください.
ソースコード上で291行目が見えるようにスクロールしてください.
ここからはローカル変数 figure の値がどこから来たのかを見ていきます.
ローカル変数 figure にカーソルを入れ, この figure に代入された箇所を探していきます.
ローカル変数 figure の宣言は234行目にあるため, ここではその次の行である235行目にブレークポイントを入れてください.
235行目にブレークポイントを入れたら, ブレークポイントビュー上の「ブレークポイントをEclipseから取り入れる」アイコンを押してください.

いったんデバッグ実行を終了してから, もう一度デバッグ実行してください.
すると, まずは DefaultDrawingView クラスの addToSelection(Figure) メソッドの780行目に止まります.
ここで, 再開ボタンを1回押すと, 235行目に止まることが確認できます.
このあと, 実際に実行時点がどのように移り変わっていくのかを確認していきます.
再開ボタンを1回押すと, 再び235行目に止まることが確認でき, それからもう一度再開ボタンを押すと今度は2回目の780行目に止まることが確認できます.

いったんデバッグ実行を終了してから, もう一度デバッグ実行してください.
そのあと, 再開ボタンを2回押して, 2回目の235行目にまで進んでください.

ローカル変数 figure に値が代入された時点がどこなのかを見ていきます.
ここでは, ステップオーバーを8回押して, 270行目にまで進んでください.
270行目では, ローカル変数 view が参照しているインスタンスに対して findFigure(Point) メソッドが呼び出され,
その戻り値がローカル変数 figure に代入されていることがわかります.
ここで, いったんステップオーバーを3回実行して, この戻り値の代入のあとに別の時点で figure が更新されることなく,
278行目で getDragTracker(Figure) メソッドの引数として渡されることを確認してください.

先ほどの操作によって, ローカル変数 figure に値を代入した箇所がわかりました.
ここで, いったんデバッグ実行を終了してから, 再びデバッグ実行してください.
まずは DefaultDrawingViewクラスの addToSelection(Figure) メソッドの780行目で止まるので, 再開ボタンを2回押して2回目の235行目にまで進んでください.
そのあと, ステップオーバーを8回押して270行目にまで進み, ステップインを3回押して findFigure(Point) メソッドの中に入ってください.

DefaultDrawingView クラスの findFigure(Point) メソッドに入りました.
ここでは, ステップインを2回押して, getDrawing() メソッドの中に入ってください.

DefaultDrawingView クラスの getDrawing() メソッドに入りました.
ここで, 変数ビュー上の this を開くと, DefaultDrawingView クラスのインスタンス (id = 150367587) が
フィールド drawing として QuadTreeDrawing クラスのインスタンス (id = 1583174451) のインスタンスを参照していることがわかります.
これを確認したら, ステップリターンを1回押して呼び出し元に戻ってください.

DefaultDrawingView クラスの findFigure(Point) メソッドに戻ってきました.
ソースコードを見ると, 先ほどの getDrawing() メソッドで取得してきた QuadTreeDrawing クラスのインスタンスに対して findFigure(Point2D#Double) メソッドが呼び出され,
その引数として, DefaultDrawingView クラスの viewToDrawing(Point) メソッドの戻り値が渡されていることが確認できます.
次は, ステップインを1回押して, viewToDrawing(Point) メソッドの中に入ってください.

DefaultDrawingView クラスの viewToDrawing(Point) メソッドに入りました.
そのままステップリターンを1回押して呼び出し元に戻ってください.

DefaultDrawingView クラスの findFigure(Point) メソッドに戻ってきました.
ステップインを1回押して QuadTreeDrawing クラスの findContains(Point2D#Double) メソッドの中に入ってください.

QuadTreeDrawing クラスの findContains(Double) メソッドに入りました.
ここで, 変数ビュー上の this を開くと, QuadTreeDrawing クラスのインスタンス (id = 1583174451) が,
フィールド quadTree として QuadTree クラスのインスタンス (id = 808853315) を参照していることがわかります.
これを確認したら, ステップインを3回押して, QuadTree クラスの findContains(Point2D#Double) メソッドの中に入ってください.

QuadTree クラスの findContains(Point2D#Double) メソッドに入りました.
ここで, 変数ビュー上の this を開くと, QuadTree クラスのインスタンス (id = 808853315) がフィールド root として
QuadTree クラスの内部クラスである QuadNode クラスのインスタンス (id = 2120356010) を参照していることがわかります.
また, ソースコードを見ると, 82行目で生成された HashSet がローカル変数 result に代入されたあと,
83行目ではいったん呼び出し先に引数として渡され, 最終的には89行目で戻り値として返されていることが確認できます.
ここでは, ステップオーバーを1回押したあと, ステップインを2回押して QuadNode クラスの findContains(Point2D#Double, HashSet) メソッドの中に入ってください.

QuadNode クラスの findContains(Point2D#Double, HashSet) メソッドに入りました.
ここで, 変数ビュー上の this を開くと, QuadNode クラスのインスタンス (id = 2120356010) が
フィールド objects として HashMap を参照していることがわかります.
また, ソースコードを見ると, 224行目の拡張for文でこの HashMap から取り出してきた要素を, 226行目で仮引数 result に追加していることが確認できます.
ここでは, ステップオーバーを3回押して226行目にまで進んでください.

先ほどの操作によって, 現在は226行目にいます.
ここでは, ステップインを2回押してください.
そのあと, 変数ビュー上の「呼び出し前」を開き, 引数のIDを確認してください.
すると, RectangleFigure クラスのインスタンス (id = 1952912699) が引数として渡されており,
それが 仮引数 result に対して追加されることがわかります.
これを確認したら, ステップオーバーを4回押して呼び出し元に戻ってください.

呼び出し元である QuadTree クラスの findContains(Point2D#Double) メソッドに戻ってきました.
ここで, ステップオーバーを1回押して, ローカル変数 result にそれ以上何も追加されずに
そのまま89行目の return result まで進むことを確認してください.
これを確認したら, ステップオーバーを1回押して呼び出し元に戻ってください.

呼び出し元である QuadTreeDrawing クラスの findContains(Double) メソッドに戻ってきました.
ここでは, ステップオーバーを2回押して130行目にまで進めてください
ここで, 変数ビュー上の「呼び出し後」を開いて戻り値のIDを確認してください.
すると, 先ほどの RectangleFigure クラスのインスタンス (id = 1952912699) を取得していることがわかります.
これを確認したら, 呼び出しスタック上で2つ呼び出し元をクリックしてください.

現在は, 呼び出し元である SelectionTool クラスの mousePressed(MouseEvent) メソッドの270行目を見ています.
ここで, ステップオーバーを1回押して271行目に進んでください.
そのあと, 変数ビュー上の「呼び出し後」を開いて戻り値のIDを確認してください.
すると, 実際に戻り値として先ほどの RectangleFigure クラスのインスタンス (id = 1952912699) が返されていることが確認でき,
したがって, その値がローカル変数 figure に代入されていることがわかります.

ここからは, ローカル変数 view の値がどこから来たのかを見ていきます.
ソースコード中のローカル変数 view にカーソルを入れて view に代入している箇所を探していきます.
今回は, 228行目に該当するのでブレークポイントを入れ, ブレークポイントビュー上の「ブレークポイントをEclipseから取り入れる」アイコンを押してください.

いったんデバッグ実行を終了してから, 再びデバッグ実行してください.
まずは, DefaultDrawingView クラスの addToSelection(Figure) メソッドの780行目に止まります.
ここで, 再開ボタンを1回押すと, 今度は SelectionToolクラスの mousePressed(MouseEvent) メソッドの228行目に止まります.
さらに, 現在の実行時点がどのように移り変わっていくかを確認するために再開ボタンを押していくと,
現在の実行が235行目 → 228行目 → 235行目と移っていくことが確認できます.

いったんデバッグ実行を終了してから, 再びデバッグ実行してください.
そのあと, 再開ボタンを3回押して, 2回目の228行目にまで進んでください.
ここで, ステップインを1回押して, getView() メソッドの中に入ってください.

AbstractTool クラスの getView() メソッドの中に入りました.
ここで, 変数ビュー上の this を開くと, DelegationSelectionTool クラスのインスタンス(id = 599587451) が
フィールド editor として DefaultDrawingEditor クラスのインスタンス (id = 1859859960) を参照していることがわかります.
これを確認したら, ステップインを3回押して getActiveView() メソッドの中に入ってください.

DefaultDrawingView クラスの getActiveView() メソッドの中に入りました.
ここで, 変数ビュー上の this を開くと, DefaultDrawingEditor クラスのインスタンス (id = 1859859960) が
フィールド activeView として DefaultDrawingView クラスのインスタンス (id = 150367587) を参照していることがわかります.
これを確認したら, 呼び出しスタック上で2つ呼び出し元をクリックして戻ってください.

呼び出し元である SelectionTool クラスの mousePressed(MouseEvent) メソッドに戻ってきました.
ここで課題B2は終了です. 時間計測を終了してください.


課題B2の機能理解が終了しましたら, 以下のアンケートにお答えください.

アンケート回答

次へ