NatTable入門（Layerの基礎）

# はじめに
直近でEclipse RCPを使ったGUI開発に携わりました。
画面を開発していく中でデータをテーブル形式で表示するためにNatTableというものを使ったのですが、日本語の参考サイトをなかなか見つけられず習得に苦労しました...
そこで、NatTableの特徴的かつベースの考え方であるレイヤについて備忘を兼ねて簡単にまとめました。


# Eclipse RCP とは
そもそも Eclipse RCP とは Eclipse Rich Client Platform の略で、統合開発環境であるEclipseをベースにしたリッチクライアントアプリケーションのプラットフォームです。
SWT[^1]やJFace[^2]を使用しており、Javaで画面を実装することができます。


# NatTable とは
[こちらのサイト](https://projects.eclipse.org/projects/technology.nebula.nattable)では、NatTableについて以下のように説明されています。
```
NatTable is a powerful and flexible SWT table/grid widget that is built to handle very large data sets, real-time updates, dynamic styling, and more.
```
つまり、**NatTableは大量のデータをテーブル形式でリアルタイムに更新しつつ、動的にスタイル設定したい場合に有用なSWTのテーブル/グリッドウィジェット**ということです。
たとえば、大量の取引データを表示しつつ特定の条件でデータをハイライトさせたいような要件がある場合には、NatTableのカスタマイズ性の高さは有用ではないかと考えます。

# NatTableの構成
この図では、テーブルの各構成要素（ヘッダ、ボディなど）に対して様々な"Layer"を重ねていくことでテーブルの見た目や動きをカスタマイズできることを表しています。
今回はこの"Layer"がどのような役割なのかをご紹介します。
![NatTable_Architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799996/4acbc96a-5c45-427f-85da-5c8666e1c2d7.png)
[https://www.vogella.com/tutorials/NatTable/article.html#architecture_layers](https://www.vogella.com/tutorials/NatTable/img/NatTable_Architecture.png)より引用

## Layer
Layerはある単機能についての動きを定義するものです。
Layerは重ねることができ、複数のLayerを重ねていくことで、テーブルに対して様々な動きを設定することができます。
たとえば、以下のサンプルコードではDataLayerという最下層のレイヤの上に2つのレイヤを重ねている状態です。
- DataLayer：表示データを提供するレイヤ
- SelectionLayer：データ選択に関する動きを定義するレイヤ
- ViewportLayer：画面上の表示領域を管理するためのレイヤ

``` Java
// 複数のレイヤを重ねて1つのNatTableのふるまいを定義している
DataLayer dataLayer = new DataLayer(dataProvider); // dataProviderはデータの提供方法を定義したオブジェクト
SelectionLayer selectionLayer = new SelectionLayer(dataLayer);
ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
```
レイヤを積み重ねると、レイヤは1つ下のレイヤで定義された状態に対して自身で定義された状態を適用していきます。
レイヤは他にもフィルタリング、ソート、行および列の並べ替えなど様々な機能についても定義が可能です。

サンプルコードのイメージ図

![NatTableレイヤイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799996/7be51b6b-5058-4738-bd4a-413d1b4e0e89.png)

ここで、レイヤのベースは常にDataLayerである必要があります。
DataLayerはテーブルに表示するデータを提供する役割を持っているため、DataLayerが未定義の場合はテーブルに何も表示することができません。
上述のサンプルコードにもあるとおり、DataLayerはインスタンス生成時にdataProviderを受け取るのですが、このdataProviderはIDataProviderを実装したクラスであり、カラムインデックス・列インデックスをもとに表示データを取得したり設定したりする際の動きを定義しています。
DataLayerはIDataProviderの定義を使ってNatTableに表示データを提供します。
``` Java
// IDataProviderの定義
public interface IDataProvider {

    // セルに表示するデータを取得する際に呼び出される
    Object getDataValue(int columnIndex, int rowIndex);
    
    // セルに表示するデータを書き換える際に呼び出される
    void setDataValue(int columnIndex, int rowIndex, Object newValue);
    
    int getColumnCount();
    
    int getRowCount();
}
```

### GridLayer
GridLayerを使うことで、NatTableを4つの領域（＝Region）に分けて管理することができるようになります。（最初に示した図では各レイヤの最上位に記載されています）
各領域をRegionとして分けることによって、それぞれに対してスタイルや動きを定義することができ、より自由にカスタマイズすることができます。

GridLayerによって分けられた各領域とその役割
Region | 役割
--- | ----
Corner Region | テーブル左上の角の部分
Column Header Region | カラムヘッダを表示する領域
Row Header Region | 行ヘッダを表示する領域 
Body Region | データを表示する領域

![NatTable_Regionイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799996/ab6c95e3-46e5-47eb-98ec-490e124c890d.png)

``` Java
// Regionごとに定義したレイヤを受け取ったGridLayerをNatTableに渡している
GridLayer gridLayer = new GridLayer(viewportLayer, columnHeaderLayer, rowHeaderLayer, cornerLayer);
NatTable natTable = new NatTable(parent, gridLayer); // parentはNatTableをのせる親コンポジット
```

### CompositeLayer
CompositeLayerはテーブルを任意の数の領域に分けて管理するためのレイヤです。
インスタンス生成時にX方向（横）の領域の数とY方向（縦）の領域の数を指定することでテーブルを任意の数の領域に分割することができます。

たとえば、`new CompositeLayer(2, 2)` とすると2×2の領域に分割することができます。
第一引数がX方向の領域の数を、第二引数がY方向の領域の数に対応しており、分割された4領域は(X,Y)という形で表すと、図中に記載のとおりにラベリングされます。


![NatTable_CompositeLayer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799996/e9473c58-7750-485b-9ead-bf0b52c9905b.png)


こちらは1×2の領域に分割する場合のサンプルコードです。
``` Java
// 横1×縦2の領域に分割
CompositeLayer compositeLayer = new CompositeLayer(1, 2);
// 上側の領域をカラムヘッダとして設定
compositeLayer.setChildLayer(GridRegion.COLUMN_HEADER, columnHeaderLayer, 0, 0);
// 下側の領域をテーブルボディとして設定
compositeLayer.setChildLayer(GridRegion.BODY, bodyLayer, 0, 1);
```
CompositeLayerのsetChildLayerメソッドが以下のように定義されているため、どこに何を配置するかを自由に設定することができます。
```
public void setChildLayer(String regionName, ILayer childLayer, int layoutX, int layoutY)
```

そのため、上のサンプルコードを以下のように書き換えるとテーブルボディの下にカラムヘッダを表示することもできます。
```
// 4番目の引数を 0 → 1 に変更
compositeLayer.setChildLayer(GridRegion.COLUMN_HEADER, columnHeaderLayer, 0, 1);
// 4番目の引数を 1 → 0 に変更
compositeLayer.setChildLayer(GridRegion.BODY, bodyLayer, 0, 0);
```


![NatTable_Composition_Reverse.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799996/f9f8c35b-13aa-4c0e-b1c0-8477b2dde3ed.png)


さて、既にお気づきの方もいらっしゃると思いますが、先に紹介したGridLayerはCompositeLayerの一種です。
GridLayerは、テーブルを4領域に分割しテーブルの各構成要素を既定の位置に表示することに特化したクラスとなっています。
``` java
// GridLayerと同様の構成をCompositeLayerで実現
CompositeLayer compositeLayer = new CompositeLayer(2, 2);
compositeLayer.setChildLayer(GridRegion.BODY, bodyLayer, 1, 1);
compositeLayer.setChildLayer(GridRegion.COLUMN_HEADER, columnHeaderLayer, 1, 0);
compositeLayer.setChildLayer(GridRegion.ROW_HEADER, rowHeaderLayer, 0, 1);
compositeLayer.setChildLayer(GridRegion.CORNER, cornerLayer, 0, 0);
```


# 最後に
以上、NatTableにおけるレイヤの考え方についてご紹介しました。
私がはじめにNatTableを見たときは、いろいろ定義され過ぎていてよくわからない...というものが率直な感想でした。
ただ、個別に定義できるからこその自由度がNatTableにはあります。
（今回は紹介していませんがNatTableは通常の表形式に加えてツリー形式でデータを表示することも可能です）
Eclipse RCPおよびNatTableは最近では主流とは言えない技術かもしれませんが、何かのきっかけでNatTableを初めて扱うことになった方に対してこの記事が少しでも役に立ったら幸いです。


# 参考
- [Nebula NatTable - high performance SWT data grid](https://eclipse.dev/nattable/)
- [Welcome to the Nebula NatTable Wiki!](https://github.com/eclipse-nattable/nattable/wiki)
- [Nebula NatTable - Getting Started Tutorial](https://www.vogella.com/tutorials/NatTable/article.html#layer_composite_grid_body)

- [NatTableのGitHub](https://github.com/eclipse-nattable/nattable/tree/master/org.eclipse.nebula.widgets.nattable.examples/src/org/eclipse/nebula/widgets/nattable/examples)
    - こちらに様々なサンプルコードが置かれているため、実際の実装を確認したい方はこちらをご覧ください
    - なお、SWTは別途ダウンロードが必要なため、[The Eclipse Project Downloads](https://download.eclipse.org/eclipse/downloads/index.html#Latest_Release)より任意のバージョンを選択→「SWT Binary and Source」から任意のものをダウンロードしてください
![SWT_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799996/e8d1e9c9-ae60-4bdb-921b-6fa9ea26179b.png)


[^1]： SWT：Standard Widget Toolkitの略で、EclipseのGUIを実現するために開発されたJava用のGUIツールキット 
[^2]： JFace：SWTを利用して実装されたUIツールキットで、SWTの各コンポーネントをより使いやすくすることを目的に開発された