javaFX、TableViewのソート後の情報取得不具合対応

# この記事について

将来的に書く予定の「JavaFX で DynamoDB Viewer作ってみた」記事の１ステップ。
結構大きな話になると思うので、少しずつ技術ポイント毎に記事を書いて、ある一定程度の要件を満たせた段階で前述まとめ記事書く予定。

第１回記事：[DynamoDBの情報を読み込んでJavaFXで表示してみる](https://qiita.com/silverbox/items/eba824a9f003df67181e)
第２回記事：[JavaFXで動的にテーブル列を設定する](https://qiita.com/silverbox/items/904a01d561014d800696)
第３回記事：[AWS java SDKでDynamoDBテーブル情報を取得してみる](https://qiita.com/silverbox/items/3caefb782f1bb269490f)
第４回記事：[JavaFX の TableView の選択範囲をクリップボードにコピーする。](https://qiita.com/silverbox/items/19e131bd3da02940a8c1)
第５回記事：[JavaFX でコンポーネント作って動的生成してみる](https://qiita.com/silverbox/items/3128fd2128374e11c1f7)
第６回記事：[DynamoDBのデータ型をjava SDKから把握してみる。](https://qiita.com/silverbox/items/3a434ec4a85262f973c9)
第７回記事：[JavaFXでジェネリック使って入力ダイアログ作成してみる](https://qiita.com/silverbox/items/bb3cee5a328ccc63246c)
第８回記事：[JavaFXで各種サイズ制御](https://qiita.com/silverbox/items/17b7982683be06c32240)
第９回記事：[JavaFXのDialogで入力値チェック制御](https://qiita.com/silverbox/items/ad7542db5d34a442dce5)
第１０回記事：[DynamoDBでPartiQLをjavaSDKで実行してみる。](https://qiita.com/silverbox/items/4c80f34285b8691af93a)
第１１回記事：[JavaFXでダイアログの入力チェック制御をちゃんと考えてみる](https://qiita.com/silverbox/items/5fe9a9215f2cf9a2461e)

※これまでの記事が基本になってます。メソッドなど細かい部分で再説明していない部分があります。不明点などありましたらコメントなど頂けたら対応しようと思います。


# 今回の対応問題

- テーブルの内容をソートした後、行の詳細を開くと、ソート前位置のデータが表示される
- テーブルの列を並べ替えた後、コピペすると、並び替え前の列の情報が取得される

# 現在の進捗

特に画面上の変更は無いので画像は省略。
[今回の修正(github)](https://github.com/silverbox/dynamodbtool/pull/18/files)

# 問題の原因

## テーブルの列を並べ替えても中身の情報リストは並び変わらない

TableViewでは、ドラッグで列の移動が可能。

```java:セル文字列取得部分
// TableView<ObservableList<String>> tableResultList;
tableResultList.getItems().get(wkRow).get(wkCol);
```
get(wkRow) 部分では、ちゃんとソート後の表示行の情報が取得される。
しかし、取得される```ObservableList<String>```の中はテーブルの列並び替えではソートされない。

## 詳細表示時、データ保持クラスからレコードを取得している

TableViewでは行ソートが可能。詳細情報は自前で作成したデータ保持クラスから取得するが、データ保持クラスのデータはソートしていないので、表示位置のインデックスでデータ取得するとずれてしまう。

# 修正する

## カラム移動に起因する問題に対しての方針

TableColumnに設定できるId情報を活用する

### 初期処理でカラムに識別情報セットしておく

TableColumn#setIdで、カラムの識別情報を取得できる。TableView初期化時にデータのキー文字列をセットしておく。

```java:カラム初期化部分
TableColumn<DynamoDbViewRecord, String> dataCol = new TableColumn<>(columnName);
dataCol.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().get(finalColIdx)));
// ここで設定
dataCol.setId(columnName);
```

### クリップボードへコピー処理時

#### 表示上のカラム位置の識別情報を取得

```java:wkColは表示上のカラム位置
String colName = tableResultList.getColumns().get(wkCol).getId();
```

#### カラム名から初期カラム位置を取得

現時点で、データセットはTableViewとは別に保持している。そちらではカラム名のリストを所持しているので、そのキー名からデータ位置を取得。キー値にインデックス取得した方が手っ取り早いが、今後の為に識別子としてカラム名を利用する。TableViewのヘッダ名でも代用できると言えば代用できるが、今後、表示している部分に何か機能追加するかもしれないので今のうちから分離しておく。

```java:データ保持クラスに関数追加
public Integer getColumnIndexByName(String colName) {
	if (colNameIndex.containsKey(colName)) {
		return colNameIndex.get(colName);
	}
	return -1;
}
```

セル文字列取得部

```java:最終形
String selColName = tableResultList.getColumns().get(wkCol).getId();
int wkColIdx = dynamoDbResult.getColumnIndexByName(selColName);
String wkCellStr = tableResultList.getItems().get(wkRow).get(wkColIdx);
```

## 行ソート問題に対する方針

TableViewは文字列のリストだけではなく、任意のクラスを行データとして使用できる。
このツールは動的にカラムが変わるので、本体は```ObservableList<String>```としていた。今回は、それに加えてデータのインデックスも保持するクラスを作成して、それをテーブルのレコードとする。
その上で、データ保持クラスからのデータ取得にはこのインデックスを使用する。

```java:TableView用クラス
@Data
@Builder
public class DynamoDbViewRecord {
	int index;
	ObservableList<String> data = FXCollections.observableArrayList();
}
```

```java:選択行のデータインデックス取得関数
private int getCurrentDataIndex() {
	TableViewSelectionModel<DynamoDbViewRecord> selectedModel = tableResultList.getSelectionModel();
	DynamoDbViewRecord selRec = selectedModel.getSelectedItem();
	if (selRec == null) {
		return -1;
	}
	return selRec.getIndex();
}
```

※この変更により、列移動時のソースも変更。そこらへんの最終ソースは [こちら(github)](https://github.com/silverbox/dynamodbtool/tree/15th-fix-sortproblem) から。

# 次回予定

export,importつけたいと思っています。