-
Bug
-
Resolution: Fixed
-
P3
-
7, 11
-
b01
-
generic
-
generic
the issue was reported on the OTN swing forum
http://forums.oracle.com/forums/thread.jspa?threadID=2146814&tstart=0
Some details of my track down is there as well, below a failing unit test. The basic problem is the comparator flag caching in the boolean array field useToString, it's not updated in the "optimized" rowsInserted0/rowsDeleted0
- on block delete, the "normal" sort triggered in shouldUseOptimized updates the per-column comparator flags are updated. At that point the empty model returns Object.class which results in a true for useToString
- subsequent single line inserts pass through the rowsInserted0 which doesn't update the flags (though the content now is an integer and the comparator available) thus still using the string comparison
My suggestion is to remove the useToString array completely - aliasing foreign state is always error prone and here doesn't buy much. Instead, replace by a method useToString(column) which is called whenever needed (which is solely in compare(...))
This is a major glitch, IMO, as it results in incorrect sorting which is highly visible to end users plus there is no way to work around in custom rowSorters because everything is private.
/*
* Created on 19.12.2010
*
*/
package org.jdesktop.swingx.sort;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* DefaultRowSorter with incorrect sorting with models that return columnClass
* based on actual content.
*
* The test case simulates the steps to reproduce
* - initialize such a model with ascending Integers.
* - initialize a TableRowSorter with the model and toggle sort to ascending
* - sanity assert original (natural integer) ascending order of values
* - block delete all data in the model and notify the sorter
* - re-fill with model with initial data and notify sorter (line by line)
* - assert native integer ascending order fails (because the data is sorted by string)
*
* @author Jeanette Winzenburg, Berlin
*/
@RunWith(JUnit4.class)
public class DefaultRowSorterCachUseStringComparatorBug extends TestCase {
@Test
public void testSortOptimized() {
Object[] values = new Object[] { 1, 2, 10} ;
Object[][] data = new Object[][] {
{ values[0]},
{ values[1]},
{ values[2]}
};
DefaultTableModel model = new DefaultTableModel(data, new Object[] {"A"}) {
/**
* @inherited <p>
*/
@Override
public Class<?> getColumnClass(int columnIndex) {
if (getRowCount() >0) {
return getValueAt(0, columnIndex).getClass();
}
return super.getColumnClass(columnIndex);
}
};
TableRowSorter<DefaultTableModel> rowSorter = new TableRowSorter<DefaultTableModel>(model);
rowSorter.toggleSortOrder(0);
for (int row = 0; row < model.getRowCount(); row++) {
// values are naturally sorted ascending
// - so with ascending sorting the coordinate transformation is identidy
assertEquals("sanity: identity initially", row, rowSorter.convertRowIndexToView(row));
}
// clear model and notify sorter
model.setRowCount(0);
rowSorter.rowsDeleted(0, values.length - 1);
// refill the model and notify sorter
for (int i = 0; i < values.length; i++) {
model.addRow(new Object[] { values[i]});
rowSorter.rowsInserted(i, i);
}
for (int row = 0; row < model.getRowCount(); row++) {
// values are naturally sorted ascending
// - so with ascending sorting the coordinate transformation is identidy
assertEquals("identity after refill", row, rowSorter.convertRowIndexToView(row));
}
}
}
http://forums.oracle.com/forums/thread.jspa?threadID=2146814&tstart=0
Some details of my track down is there as well, below a failing unit test. The basic problem is the comparator flag caching in the boolean array field useToString, it's not updated in the "optimized" rowsInserted0/rowsDeleted0
- on block delete, the "normal" sort triggered in shouldUseOptimized updates the per-column comparator flags are updated. At that point the empty model returns Object.class which results in a true for useToString
- subsequent single line inserts pass through the rowsInserted0 which doesn't update the flags (though the content now is an integer and the comparator available) thus still using the string comparison
My suggestion is to remove the useToString array completely - aliasing foreign state is always error prone and here doesn't buy much. Instead, replace by a method useToString(column) which is called whenever needed (which is solely in compare(...))
This is a major glitch, IMO, as it results in incorrect sorting which is highly visible to end users plus there is no way to work around in custom rowSorters because everything is private.
/*
* Created on 19.12.2010
*
*/
package org.jdesktop.swingx.sort;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* DefaultRowSorter with incorrect sorting with models that return columnClass
* based on actual content.
*
* The test case simulates the steps to reproduce
* - initialize such a model with ascending Integers.
* - initialize a TableRowSorter with the model and toggle sort to ascending
* - sanity assert original (natural integer) ascending order of values
* - block delete all data in the model and notify the sorter
* - re-fill with model with initial data and notify sorter (line by line)
* - assert native integer ascending order fails (because the data is sorted by string)
*
* @author Jeanette Winzenburg, Berlin
*/
@RunWith(JUnit4.class)
public class DefaultRowSorterCachUseStringComparatorBug extends TestCase {
@Test
public void testSortOptimized() {
Object[] values = new Object[] { 1, 2, 10} ;
Object[][] data = new Object[][] {
{ values[0]},
{ values[1]},
{ values[2]}
};
DefaultTableModel model = new DefaultTableModel(data, new Object[] {"A"}) {
/**
* @inherited <p>
*/
@Override
public Class<?> getColumnClass(int columnIndex) {
if (getRowCount() >0) {
return getValueAt(0, columnIndex).getClass();
}
return super.getColumnClass(columnIndex);
}
};
TableRowSorter<DefaultTableModel> rowSorter = new TableRowSorter<DefaultTableModel>(model);
rowSorter.toggleSortOrder(0);
for (int row = 0; row < model.getRowCount(); row++) {
// values are naturally sorted ascending
// - so with ascending sorting the coordinate transformation is identidy
assertEquals("sanity: identity initially", row, rowSorter.convertRowIndexToView(row));
}
// clear model and notify sorter
model.setRowCount(0);
rowSorter.rowsDeleted(0, values.length - 1);
// refill the model and notify sorter
for (int i = 0; i < values.length; i++) {
model.addRow(new Object[] { values[i]});
rowSorter.rowsInserted(i, i);
}
for (int row = 0; row < model.getRowCount(); row++) {
// values are naturally sorted ascending
// - so with ascending sorting the coordinate transformation is identidy
assertEquals("identity after refill", row, rowSorter.convertRowIndexToView(row));
}
}
}