RowFilter.NumberFilter: не может обрабатывать «смешанные» конкретные типы чисел - PullRequest
0 голосов
/ 03 ноября 2011

Происходит, если хотя бы одно из значений (значения == значение в RowFilter, значение в записи) является десятичным.Вот неудачный тест:

@Test
public void testRowFilterNumberMixCore() {
    TestEntry entry = new TestEntry(1.2f);
    RowFilter filter = RowFilter.numberFilter(ComparisonType.AFTER, 1, 0);
    assertTrue(entry + "must be included " + filter, filter.include(entry));
}

Вывод:

junit.framework.AssertionFailedError: 
[entry: 1.2] must be included [RowFilter: ComparisonType = AFTER, comparableValue: 1, comparableClass: class java.lang.Integer]

Причина в том, что NumberFilter возвращается к сравнению чисел по их number.longValue (), если они не являютсяодин и тот же класс (и тем самым сравнимый друг с другом)

Зная эту деталь, провал теста не удивителен (задним числом, никогда бы не подумал, что это проблема ;-) Один уровень защиты -чтобы убедиться - в коде клиента - что числа для сравнения являются одного и того же класса.Это не всегда возможно (подумайте, например: tableColumn с номером columnClass). Поэтому мне интересно, можно ли / как улучшить запасной вариант.Что-то вроде:

if (one instanceof Comparable && one.getClass() == other.getClass()) {
    // same class, use comparator
    return ((Comparable) one).compareTo(other);
}
if (areIntegers(one, other)) {
    // all integers, use longValue
    return longCompare(one, other);
}
if (areDecimals(one, other)) {
    // anything to do here?
}
// at last resort convert to BigDecimal and compare those: 
BigDecimal bigOne = new BigDecimal(one.toString());
BigDecimal bigOther = new BigDecimal(other.toString());
return bigOne.compareTo(bigOther);

Делая так, вы проходите тест - я немного опасаюсь скрытых (читай: неизвестных мне :) подводных камней.Любые предупреждения / альтернативы приветствуются!

К вашему сведению: кросс-пост на Форум Swing OTN

Последующие действия

реализованы, как описано выше, теперь ожидаютклиенты будут жаловаться - в этом случае будут показывать пальцем все, кто меня здесь не предупреждал :-)

1 Ответ

4 голосов
/ 04 ноября 2011

У меня нет лучшего ответа, но пример ниже иллюстрирует эффект. В частности, RowFilter на основе примитива double равен в штучной упаковке как Double, что дает ожидаемую таблицу с values > 1. Напротив, тот, который основан на float, помечен как Float. Поскольку литералы класса не совпадают, include() сравнивает значения long, неожиданно фильтруя все дробные values < 2.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.RowFilter;
import javax.swing.RowFilter.ComparisonType;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;

/** @see http://stackoverflow.com/questions/7993546 */
public class FilterTest {

    private static TableRowSorter<TableModel> sorter;
    private static RowFilter<TableModel, Integer> dFilter;
    private static RowFilter<TableModel, Integer> fFilter;
    private static boolean b;

    public static void main(String[] args) {
        TableModel model = new TableModel();
        JTable table = new JTable(model);
        sorter = new TableRowSorter<TableModel>(model);
        dFilter = RowFilter.numberFilter(ComparisonType.AFTER, 1d, 0);
        fFilter = RowFilter.numberFilter(ComparisonType.AFTER, 1f, 0);
        sorter.setRowFilter(dFilter);
        table.setRowSorter(sorter);
        JScrollPane scrollPane = new JScrollPane(table);
        table.setPreferredScrollableViewportSize(new Dimension(320, 240));

        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(scrollPane, BorderLayout.CENTER);
        f.add(new JToggleButton(new AbstractAction("Toggle") {

            @Override
            public void actionPerformed(ActionEvent e) {
                b = !b;
                if (b) {
                    sorter.setRowFilter(fFilter);
                } else {
                    sorter.setRowFilter(dFilter);
                }
            }
        }), BorderLayout.SOUTH);

        f.pack();
        f.setVisible(true);
    }

    private static class TableModel extends AbstractTableModel {

        private static final int ROWS = 16;
        private static final int COLS = 4;
        private Double[][] matrix = new Double[ROWS][COLS];

        public TableModel() {
            double v = 0;
            for (Object[] row : matrix) {
                Arrays.fill(row, Double.valueOf(v += 0.25));
            }
        }

        @Override
        public int getRowCount() {
            return ROWS;
        }

        @Override
        public int getColumnCount() {
            return COLS;
        }

        @Override
        public Object getValueAt(int row, int col) {
            return matrix[row][col];
        }

        @Override
        public Class<?> getColumnClass(int col) {
            return Number.class;
        }
    }
}
...