java - Subclasificación de DefaultRowSorter para permitir la clasificación de tablas de árbol
swing swingx (1)
Basándome en la biblioteca aephyr que he comprobado, si modifica el clasificador de nodos de esta manera, debería ordenar solo los primeros elementos:
public class NodeSorter extends DefaultRowSorter<M, Integer> {
[.....]
private Comparator noopComparator = new Comparator(){
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
@Override
public Comparator<?> getComparator(int column) {
// if it has no parent then compare --> sort
// if it has a parent then do nothing
if(parent != null)
return noopComparator;
Comparator<?> c = super.getComparator(column);
return c != null ? c : getMaster().getComparator(column);
}
[....]
En esta pregunta , pregunté cómo era posible hacer que una JXTreeTable (SwingX) clasificara su elemento superior.
aephyr un vistazo a la biblioteca ( aephyr ) sugerida por mKorbel e intenté combinarla con JXTreeTable (creé una nueva clase llamada JXSortableTreeTable copiando el código fuente de JXTreeTable).
Hasta ahora, pude implementar un mecanismo para ordenar los nodos de la tabla de árbol, es decir, cuando se convertRowIndexToModel
de mi clasificador personalizado (ver más abajo), los índices que devuelve son correctos.
Así tengo una clase que se parece a eso:
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());
}
}
Si los datos del modelo se organizan así (con el índice de cada nodo global en el modelo que se muestra en la última columna):
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
.
.
.
Si ordeno la segunda columna, me gustaría obtener esta salida:
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
.
.
.
Lo que realmente obtengo es exactamente igual a la versión no ordenada, excepto que el resultado de convertRowIndexToModel es correcto porque si lo llamo para cada fila del modelo, obtengo:
V -> M
0 -> 3
1 -> 5
2 -> 4
3 -> 0
4 -> 1
5 -> 2
Mi pregunta se reduce a:
Al crear una subclase de DefaultRowSorter para implementar la clasificación de una tabla de árbol como JXTreeTable, ¿qué métodos debo anular? Como no puedo anular viewToModel del rowSorter (privado) o cualquier función que lo modifique (también es privado), implementé mi propia creación de mapa V-> M y me aseguré de que se usara al llamar a convertRowIndexTo (Modelo | Vista) de mi clasificador.
Siento que falta algo como una llamada al método de conversión en alguna parte en JXSortableTreeTable o tal vez en el clasificador personalizado.
Muchas gracias por su ayuda y comentarios perspicaces!
Editar> Después de probar el resultado un poco más, parece estar más bien relacionado con la columna jerárquica de la JXTreeTable para la cual los datos no se actualizan, ya que funciona perfectamente al ordenar en las otras columnas (también intenté cambiar la columna jerárquica (establecido en otra columna) y el anterior funciona una vez que ya no es jerárquico)