Создание подклассов DefaultRowSorter для разрешения сортировки по древовидным таблицам

В этом вопросе я спросил, как можно сделать сортировку JXTreeTable (SwingX) своим верхним элементом.

Я взглянул на библиотеку ( aephyr), предложенную mKorbel, и попытался объединить ее с JXTreeTable (я создал новый класс с именем JXSortableTreeTable, скопировав исходный код JXTreeTable).

До сих пор я смог реализовать механизм сортировки узлов древовидной таблицы, т.е. когда 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

Мой вопрос, таким образом, сводится к:

При создании подкласса DefaultRowSorter для реализации сортировки древовидной таблицы, такой как JXTreeTable, какие методы мне следует переопределить? Так как я не могу переопределить viewToModel для rowSorter (private) или любой функции, которая его изменяет (также private), я реализовал свое собственное создание карты V->M и убедился, что оно используется при вызове convertRowIndexTo(Model|View) моего сортировщика.

Я чувствую, что чего-то не хватает, например, вызова метода преобразования где-то в JXSortableTreeTable или, возможно, в пользовательском сортировщике.

Большое спасибо за вашу помощь и проницательные комментарии!

Правка> После тестирования результата немного больше, кажется, что он скорее связан с иерархическим столбцом JXTreeTable, для которого данные не обновляются, так как он отлично работает при сортировке по другим столбцам (я также пытался изменить иерархический столбец (установить в другой столбец), и первый работает, когда он больше не является иерархическим)

1 ответ

Основываясь на библиотеке aephyr, которую я проверил, если вы изменяете сортировщик узлов таким образом, он должен сортировать только первые элементы:

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);
    }
   [....]
Другие вопросы по тегам