« TableViewer向けLabelProviderの作成 | トップページ | TableViewerのホントの利用方法 »

2004.04.21

TableViewer向けLabelProviderの真実

前回「TableViewer向けLabelProviderの作成」において,TableViewerが各列に何を表示すればよいかを判断するためのLabelProviderの作り方を解説した。その中で通常はITableLabelProviderインタフェースを実装すれば良い」という何とも意味深な表現をした。今回は,なぜ「通常は」なのか?そしてその問題の奥に潜むTableViewerとLabelProviderの真実について述べてみたい。

TableViewer向けContentProviderの作成」において,LabelProviderをセットしていないにも関わらずオブジェクトのハッシュ値(toStringメソッドの結果)がTableViewerに表示されていた。ContentProviderをセットしないでTableViewerを使った場合,setInputメソッドの呼び出しの時点で「ContentProviderがないぞ」とAssertionFailedException例外が発生する。しかし,LabelProviderはセットしなくても一応TableViewerは動作する。

なぜLabelProviderをセットしなくても動作するかというと,実はTableViewerはデフォルトでLabelProviderを内部で生成して持っているからなのである。これは,

  Table table = ...;
  TableViewer viewer = new TableViewer(table);
  System.out.println(viewer.getLabelProvider().getClass().getName());

というコードを実行すると,getLabelProviderメソッドの戻り値がnullではないことからわかる事実である。つまり「TableViewer向けContentProviderの作成」では,TableViewerがデフォルトで持っているLabelProviderが使われることによってエラーにならずに表示ができていた,ということなのである。

さて,上記のコードを実行すると,標準出力に以下のように出力される。

  org.eclipse.jface.viewers.LabelProvider

このLabelProviderクラスは「TableViewer向けLabelProviderの作成」でITableLabelProviderインタフェースの実装クラスを自作した際に継承したクラスである。そのクラスがTableViewerのデフォルトLabelProviderとして使われているというのはちょっとした驚きがある。今までの知識では,いくつかの謎がでてくる。まず,LabelProviderクラスはITableLabelProviderインタフェースを実装していない。にも関わらず,TableViewerのLabelProviderとして使用できている。「話が違うじゃないか!」と思うかもしれないが,「TableViewer向けLabelProviderの作成」で使った「通常は」という表現がここで活きてくる。

実は,TableViewerで使用できるLabelProviderは,ITableLabelProviderインタフェースの実装クラスのほかに,ILabelProviderインタフェースの実装クラスも使用することができるLabelProviderクラスは,ILabelProviderインタフェースを実装しているので,TableViewerのLabelProviderとして使用することができたというわけだ。ただし,ITableLabelProviderインタフェースとILabelProviderインタフェースでは,規定されているメソッドも全く違っていて,当然TableViewerの動きが大きく違ってくる。

ITableLabelProviderインタフェースの実装クラスでは,getColumnTextメソッドやgetColumnImageメソッドを実装して,引数で渡ってくる列のインデックスに対応した表示文字列や表示イメージを返却する処理を記述した。それに対して,ILabelProviderインタフェースの場合は,

  String getText(Object element) - 指定された行オブジェクトを元に表示文字列を返す
  Image getImage(Object element) - 指定された行オブジェクトを元に表示イメージを返す

という2つのメソッドを実装する必要がある。引数に列のインデックスがないことからもわかることだが,ILabelProviderインタフェースの実装オブジェクトがsetLabelProviderメソッドによりセットされた場合,TableViewerはgetTextメソッドおよびgetImageメソッドの戻り値の文字列およびイメージを0列目(一番左の列)に表示する,という動きになる。

そして,LabelProviderクラスではgetTextメソッドは引数のelementオブジェクトのtoStringメソッドの戻り値を取得して返却する,getImageメソッドは必ずnullを返却する,という実装になっているので,TableViewerにLabelProviderを何もセットしなかったときは,「TableViewer向けContentProviderの作成」のときのように,ContentProviderが生成した行オブジェクトのtoStringメソッド呼び出しの結果が0列目に表示される,という動きになるのである。

上記の説明だと,もう1つ疑問なことが出てくる。「TableViewer向けLabelProviderの作成」で作ったEmployeeLabelProviderクラスは,LabelProviderクラスを継承し,ITableLabelProviderインタフェースを実装していた。つまり,ILabelProviderインタフェースとITableLabelProviderインタフェースを両方とも実装している,ということになる。これでは「どっちが使われるんじゃ!?」ということになってしまう。

実は,TableViewerではITableLabelProviderインタフェースが実装されているかどうかを先にチェックしている。つまり,両インタフェースを実装していた場合は,ITableLabelProviderインタフェースが優先される。すなわち,getColumnTextメソッドおよびgetColumnImageメソッドが呼び出され,getTextメソッドおよびgetImageメソッドは呼び出されない。つまりEmployeeLabelProviderクラスの例では,あくまでgetColumnTextメソッドおよびgetColumnImageメソッドしかTableViewerから呼び出されないのである。ではなぜLabelProviderクラスを継承したのか?LabelProviderクラスの継承の目的は,単にILabelProviderListener関連のメソッドの実装をパクりたかっただけ,なのである。

ただし,ほとんどの場合はTableViewerではILabelProviderインタフェースの実装では役に立たないと思われるので「通常はITableLabelProviderインタフェースを実装する」と表現したのである。TableViewerを使うということはマルチカラムにしたいのだから,ITableLabelProviderインタフェースを使うのが普通でしょ?って話である。

|

« TableViewer向けLabelProviderの作成 | トップページ | TableViewerのホントの利用方法 »

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/12631/472305

この記事へのトラックバック一覧です: TableViewer向けLabelProviderの真実:

« TableViewer向けLabelProviderの作成 | トップページ | TableViewerのホントの利用方法 »