Создание подкласса 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
.
.
.

Если я отсортирую второй столбец, я хотел бы получить этот вывод:

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 (частного) или любой функции, которая его модифицирует (также закрытой), я реализовал собственное создание карты V->M и убедился, что она используется при вызове convertRowIndexTo(Model|View) моего сортировщика.

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

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

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


person ixM    schedule 04.04.2012    source источник
comment
Вам удалось заставить его работать? Есть ли причины, по которым вы сохранили код JXTreeTable вместо использования класса aephyr.TreeTable?   -  person assylias    schedule 26.04.2012
comment
Привет, я работал всего несколько часов, и у меня возникла проблема, для которой я не вижу решения. По сути, я могу правильно отсортировать все строки и даже заставить маркеры отображать правильную строку, но это не работает для иерархического столбца. Даже если значение отображается правильно, сама древовидная структура не меняется. Скажем, дерево имеет двух детей с 2 ​​и 3 детьми соответственно. Если сортировка таблицы дерева приводит к тому, что второй дочерний элемент корня становится первым, значения упорядочены правильно, но у первого дочернего элемента дерева все еще есть 2 дочерних элемента, хотя мы ожидали, что их будет 3.   -  person ixM    schedule 27.04.2012
comment
Что касается библиотеки aephyr, так как я использую некоторые другие вещи из Swingx и особенно очень удобные хайлайтеры, я не хотел переключаться на другую библиотеку. Но это очень вдохновило меня, когда я пытался заставить JXTreeTable сортировать свои элементы, и это кажется очень хорошим :)   -  person ixM    schedule 27.04.2012


Ответы (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);
    }
   [....]
person TecHunter    schedule 25.07.2012