この質問では、JXTreeTable (SwingX) で最上位の要素をソートする方法を尋ねました。
mKorbel が提案したライブラリ ( aephyr )を見て、 JXTreeTableと組み合わせてみました (JXTreeTable のソース コードをコピーして、JXSortableTreeTable という名前の新しいクラスを作成しました)。
これまでのところ、ツリー テーブルのノードを並べ替えるメカニズムを実装できました。つまりconvertRowIndexToModel
、カスタム ソーター (以下を参照) が呼び出されたときに、返されるインデックスは正しいものでした。
したがって、次のようなクラスがあります。
public class TreeTableRowSorter <M extends TreeTableModelAdapter> extends DefaultRowSorter {
private M treeTableModel; // model
private List<Integer> indices; // list where each entry is the model index
private IdentityHashMap<Object, NodeSorter> sorters;
private class IndicesMapFiller { // class that fills the "map" this.indices
public void fillIndicesMap(Object node) { // recursive
// Fills the indices "map"
}
}
@Override
public int convertRowIndexToModel(int index) {
return indices.get(index);
}
@Override
public int convertRowIndexToView(int index) {
return indices.indexOf(index);
}
@Override
public void toggleSortOrder(int columnIndex) {
sorters.get(treeTableModel.getRoot()).toggleSortOrder(columnIndex);
super.toggleSortOrder(columnIndex);
}
@Override
public void sort() {
getRowSorter(treeTableModel.getRoot()).sort(true);
indices = new ArrayList<Integer>();
for (int i = 0; i < getViewRowCount(); i++)
indices.add(-1);
IndicesMapFiller filler = new IndicesMapFiller(indices);
filler.fillIndicesMap(treeTableModel.getRoot());
System.out.println("INDICES: " + indices);
}
private class TreeTableRowSorterModelWrapper extends ModelWrapper<M, Integer> {
private final Object node;
private final TreeTableRowSorterModelWrapper parent;
public TreeTableRowSorterModelWrapper(Object node, TreeTableRowSorterModelWrapper parent) {
this.node = node;
this.parent = parent;
}
@Override
public M getModel() {
return treeTableModel;
}
@Override
public int getColumnCount() {
return (treeTableModel == null) ? 0 : treeTableModel.getColumnCount();
}
@Override
public int getRowCount() {
// Returns only the number of direct visible children in order for
// each NodeSorter to only sort its children (and not its children's
// children)
int rowCount = treeTableModel.getDirectVisibleChildrenCount(node, getParentPath());
return rowCount;
}
public TreePath getParentPath() {
Object root = treeTableModel.getRoot();
if (root == null || node == root)
return null;
if (parent.getNode() == root)
return new TreePath(root);
return parent.getParentPath().pathByAddingChild(parent);
}
@Override
public Object getValueAt(int row, int column) {
Object node = treeTableModel.getChild(getNode(), row);
return treeTableModel.getValue(node, column);
}
public Object getNode() {
return node;
}
}
public class NodeSorter extends DefaultRowSorter<M, Integer> {
private NodeSorter parent;
private Map<Object, NodeSorter> children;
public NodeSorter(Object root) {
this(null, root);
}
public NodeSorter(NodeSorter par, Object node) {
parent = par;
TreeTableRowSorterModelWrapper parentWrapper =
(parent == null) ? null : (TreeTableRowSorterModelWrapper) parent.getModelWrapper();
TreeTableRowSorterModelWrapper wrapper =
new TreeTableRowSorterModelWrapper(node, parentWrapper);
setModelWrapper(wrapper);
children = createChildren();
if (parent != null)
setMaxSortKeys(Integer.MAX_VALUE);
if (node == null)
return;
// Create the sorters for all children (even those not yet shown)
int childCount = getModel().getChildCount(node);
for (int i = 0; i < childCount; i++) {
Object childNode = getModel().getChild(node, i);
getChildSorter(childNode, sorters);
}
}
protected Map<Object, NodeSorter> createChildren() {
int childCount = getModel().getChildCount(getNode());
IdentityHashMap<Object, NodeSorter> map = new IdentityHashMap<Object, NodeSorter>(childCount);
return map;
}
public NodeSorter getParent() {
return parent;
}
TreeTableSortController<M> getMaster() {
return TreeTableSortController.this;
}
NodeSorter getChildSorter(Object node, Map<Object, NodeSorter> map) {
NodeSorter s = children.get(node);
if (s == null && map != null) {
s = new NodeSorter(this, node);
children.put(node, s);
map.put(node, s);
}
return s;
}
protected TreeTableRowSorterModelWrapper getTreeTableModelWrapper() {
return (TreeTableRowSorterModelWrapper) getModelWrapper();
}
public Object getNode() {
return ((TreeTableRowSorterModelWrapper) getModelWrapper()).getNode();
}
@Override
public void toggleSortOrder(int columnIndex) {
for (NodeSorter childSorter : children.values()) {
childSorter.toggleSortOrder(columnIndex);
}
super.toggleSortOrder(columnIndex);
}
private boolean firePathEvent = true;
void sort(boolean sortChildren) {
if (!isVisible())
return;
firePathEvent = false;
try {
super.sort();
} finally {
firePathEvent = true;
}
if (!sortChildren)
return;
for (NodeSorter sorter : children.values())
sorter.sort(sortChildren);
}
private TreePath getPathToRoot() {
if (parent == null)
return new TreePath(getNode());
return parent.getPathToRoot()
.pathByAddingChild(getNode());
}
}
モデル データが次のように構成されている場合 (モデル内の各グローバル ノードのインデックスは最後の列に示されています):
Name / Date / File UID | Glob. View Idx | Glob. Model Idx
(Root)
|- Mr. X / 1996/10/22 / AE123F6D | 0 | 0
|--- File1 / 2012/01/10 / DFC2345Q | 1 | 1
|--- File2 / 2012/01/11 / D321FEC6 | 2 | 2
|- Mrs. Y / 1975/03/03 / G2GF35EZ | 3 | 3
|--- File3 / 2012/02/29 / 35GERR32 | 4 | 4
|--- File4 / 2012/01/22 / LKJY2342 | 5 | 5
.
.
.
2 番目の列を並べ替えると、次の出力が得られます。
Name / Date / File UID | Glob. View Idx | Glob. Model Idx
(Root)
|- Mrs. Y / 1975/03/03 / G2GF35EZ | 0 | 3
|--- File4 / 2012/01/22 / LKJY2342 | 1 | 5
|--- File3 / 2012/02/29 / 35GERR32 | 2 | 4
|- Mr. X / 1996/10/22 / AE123F6D | 3 | 0
|--- File1 / 2012/01/10 / DFC2345Q | 4 | 1
|--- File2 / 2012/01/11 / D321FEC6 | 5 | 2
.
.
.
実際に得られるものは、モデルの各行に対して呼び出すと次のようになるため、convertRowIndexToModel の結果が正しいことを除いて、ソートされていないバージョンとまったく同じです。
V -> M
0 -> 3
1 -> 5
2 -> 4
3 -> 0
4 -> 1
5 -> 2
したがって、私の質問は次のように要約されます。
JXTreeTable のようなツリー テーブルの並べ替えを実装するために DefaultRowSorter をサブクラス化する場合、どのメソッドをオーバーライドする必要がありますか? rowSorter (プライベート) またはそれを変更する関数 (プライベート) の viewToModel をオーバーライドできないため、独自の V->M マップの作成を実装し、ソーターの convertRowIndexTo(Model|View) を呼び出すときに使用されるようにしました。
JXSortableTreeTable またはカスタム ソーターのどこかに変換メソッドの呼び出しのような何かが欠けているように感じます。
あなたの助けと洞察力のあるコメントをありがとうございました!
編集>結果をもう少しテストした後、他の列でソートするときに完全に機能するため、データが更新されないJXTreeTableの階層列に関連しているようです(階層列も変更しようとしました(別の列に設定)、階層的でなくなると前者が機能します)