/* * $Id$ * * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, * Santa Clara, California 95054, U.S.A. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ package org.jdesktop.swingx.plaf.basic.core; import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.List; import java.util.logging.Logger; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListModel; import javax.swing.DefaultRowSorter; import javax.swing.ListModel; import javax.swing.RowFilter; import javax.swing.SortOrder; import javax.swing.RowFilter.Entry; import javax.swing.event.ListDataEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.jdesktop.swingx.InteractiveTestCase; import org.jdesktop.swingx.JXEditorPaneTest; import org.jdesktop.swingx.JXList; import org.jdesktop.swingx.JXListSortRevamp.DefaultListModelF; import org.jdesktop.swingx.hyperlink.LinkModel; import org.jdesktop.swingx.plaf.basic.core.ListSortUI.ModelChange; import org.jdesktop.swingx.sort.ListSortController; import org.jdesktop.swingx.sort.RowFilters; import org.jdesktop.swingx.sort.TableSortController; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for SortManager (mainly to understand). * * @author Jeanette Winzenburg */ @RunWith(JUnit4.class) @SuppressWarnings({ "rawtypes", "unchecked" }) public class ListSortUITest extends InteractiveTestCase { @SuppressWarnings("unused") private static final Logger LOG = Logger.getLogger(ListSortUITest.class .getName()); protected DefaultListModelF ascendingListModel; private JXList list; private ListSortController<ListModel> controller; // private ListSortUI sortUI; private int testRow; /** * Issue #1536-swingx: AIOOB on restoring selection with filter * Reopened: overfixed - the removeIndexInterval _does_ take * the endIndex instead of length. * */ @Test public void testSelectionWithFilterXListRemove() { JXList table = new JXList(ascendingListModel, true); // set selection somewhere below the filtered (which is 0) int selected = 1; table.setSelectionInterval(selected, selected); // exclude rows based on identifier (here: first item final RowFilter filter = new RowFilters.GeneralFilter() { List excludes = Arrays.asList(0); @Override protected boolean include( Entry<? extends Object, ? extends Object> entry, int index) { return !excludes.contains(entry.getIdentifier()); } }; table.setRowFilter(filter); assertEquals("sanity: filtered selection", selected - 1, table.getSelectedIndex()); // remove last row ascendingListModel.remove(ascendingListModel.getSize() - 1); assertEquals("filtered selection unchanged", selected - 1, table.getSelectedIndex()); assertFalse(table.isSelectionEmpty()); } /** * Issue #1536-swingx: AIOOB on restoring selection with filter * This is a core issue, sneaked into ListSortUI by c&p * */ @Test public void testSelectionWithFilterXListInsert() { DefaultListModel model = new DefaultListModel(); // a model with 3 elements is the minimum size to demonstrate // the bug int last = 2; for (int i = 0; i <= last; i++) { model.addElement(i); } JXList table = new JXList(model, true); // set selection to the end table.setSelectionInterval(last, last); // exclude rows based on identifier final RowFilter filter = new RowFilters.GeneralFilter() { List excludes = Arrays.asList(0); @Override protected boolean include( Entry<? extends Object, ? extends Object> entry, int index) { return !excludes.contains(entry.getIdentifier()); } }; table.setRowFilter(filter); // insertRow _before or at_ selected model index, such that // endIndex (in event) > 1 model.add(2, "x"); } @Test(expected = IllegalStateException.class) public void testConstructorDifferentSorter() { new ListSortUI(new JXList(true), new ListSortController<ListModel>(ascendingListModel)); } @Test(expected = NullPointerException.class) public void testConstructorNullSorter() { new ListSortUI(new JXList(), null); } @Test(expected = NullPointerException.class) public void testConstructorNullList() { new ListSortUI(null, new ListSortController<ListModel>(ascendingListModel)); } @Test public void testSetFilterKeepsSelection() { int selection = 0; list.setSelectedIndex(selection); RowFilter<Object, Integer> filter = RowFilters.regexFilter(".*", 0); controller.setRowFilter(filter); assertEquals("setting filters must keep selection", selection, list.getSelectedIndex()); } /** * Issue #223 * test if selection is updated on add row above selection. * */ @Test public void testAddRowAboveSelectionInvertedOrder() { // select the last row in view coordinates int selectedRow = list.getElementCount() - 2; list.setSelectedIndex(selectedRow); // revert order list.setSortOrder(SortOrder.DESCENDING); assertEquals("second row must be selected", 1, list.getSelectedIndex()); // add row in model coordinates // insert high value Object row = new Integer(100); ascendingListModel.addElement(row); assertEquals(row, list.getElementAt(0)); // selection must be moved one below assertEquals("selection must be incremented by one ", 2, list.getSelectedIndex()); } /** * Issue #855-swingx: throws AIOOB on repeated remove/add. * Reason is that the lead/anchor is not removed in removeIndexInterval */ @Test public void testAddRemoveSelect() { list.setSortOrder(SortOrder.ASCENDING); list.setSelectedIndex(0); ascendingListModel.remove(0); assertTrue("sanity - empty selection after remove", list.isSelectionEmpty()); ascendingListModel.addElement(-1); assertTrue("sanity - empty selection re-adding", list.isSelectionEmpty()); list.setSelectedIndex(0); } /** * * Issue #173-swingx. * * table.setFilters() leads to selectionListener * notification while internal table state not yet stable. * * example (second one, from Nicola): * http://www.javadesktop.org/forums/thread.jspa?messageID=117814 * */ @Test public void testSelectionListenerNotification() { assertEquals(20, list.getElementCount()); final int modelRow = 0; // set a selection list.setSelectedIndex(modelRow); ListSelectionListener l = new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) return; int viewRow = list.getSelectedIndex(); assertEquals("view index visible", 0, viewRow); // JW: the following checks if the reverse conversion succeeds list.convertIndexToModel(viewRow); } }; list.getSelectionModel().addListSelectionListener(l); RowFilter<ListModel, Integer> filter = RowFilters.regexFilter("0", 0); ((ListSortController<? extends ListModel>) list.getRowSorter()).setRowFilter(filter); assertEquals(2, list.getElementCount()); } @Test public void testSelectionAfterSort() { // use the 2 to be sure the comparable is used list.setSelectedIndex(testRow); list.setSortOrder(SortOrder.DESCENDING); int index = list.getSelectedIndex(); assertEquals("last row must be selected after sorting", ascendingListModel.getSize() - (testRow + 1) , index); } /** * Issue #477-swingx: * * Selection must be cleared after setModel. This is from * super's contract. * */ @Test public void testSetModelEmptySelection() { final JXList list = new JXList(new DefaultListModel(), true); int selection = 0; list.setSelectedIndex(selection); list.setModel(ascendingListModel); assertTrue("setting model must clear selectioon", list.isSelectionEmpty()); assertEquals(ascendingListModel.getSize(), list.getElementCount()); } /** * Selection must be cleared after dataChanged. * */ @Test public void testDataChangedEmptySelection() { final JXList list = new JXList(ascendingListModel, true); int selection = 0; list.setSelectedIndex(selection); ascendingListModel.fireContentsChanged(); assertTrue("dataChanged must clear selection", list.isSelectionEmpty()); } /** * Selection must be cleared after dataChanged. * */ @Test public void testDataChangedSortedEmptySelection() { final JXList list = new JXList(ascendingListModel, true); int selection = 0; list.setSelectedIndex(selection); list.setSortOrder(SortOrder.DESCENDING); ascendingListModel.fireContentsChanged(); assertTrue("dataChanged must clear selection", list.isSelectionEmpty()); } /** * test if selection is kept after deleting a row above the * selected. * * This fails because the ui-delegate has its hands in removing * selection after removed, that is they are doubly removed. * */ @Test public void testSelectionAfterAddAbove() { // selecte second row list.setSelectedIndex(1); // remove first ascendingListModel.insertElementAt(5, 0); assertEquals("selected must have moved after adding at start", 2, list.getSelectedIndex()); } /** * test if selection is kept after deleting a row above the * selected. * * This fails because the ui-delegate has its hands in removing * selection after removed, that is they are doubly removed. * */ @Test public void testSelectionAfterDeleteAbove() { // selecte second row list.setSelectedIndex(1); // remove first ascendingListModel.remove(0); assertEquals("first row must be selected removing old first", 0, list.getSelectedIndex()); } //------------------- ModelChange - temporary ... @Test public void testModelAdded() { int first = 3; int last = 5; ListDataEvent e = new ListDataEvent(new DefaultListModel(), ListDataEvent.INTERVAL_ADDED, last, first); ModelChange change = new ModelChange(e); assertEquals(ListDataEvent.INTERVAL_ADDED, change.type); assertFalse(change.allRowsChanged); assertEquals(last-first +1, change.length); assertEquals(first, change.startModelIndex); assertEquals(last, change.endModelIndex); } @Test public void testModelRemoved() { int first = 3; int last = 5; ListDataEvent e = new ListDataEvent(new DefaultListModel(), ListDataEvent.INTERVAL_REMOVED, last, first); ModelChange change = new ModelChange(e); assertEquals(ListDataEvent.INTERVAL_REMOVED, change.type); assertFalse(change.allRowsChanged); assertEquals(last-first +1, change.length); assertEquals(first, change.startModelIndex); assertEquals(last, change.endModelIndex); } @Test public void testModelChanged() { int first = 3; int last = 5; ListDataEvent e = new ListDataEvent(new DefaultListModel(), ListDataEvent.CONTENTS_CHANGED, last, first); ModelChange change = new ModelChange(e); assertEquals(ListDataEvent.CONTENTS_CHANGED, change.type); assertFalse(change.allRowsChanged); assertEquals(last-first +1, change.length); assertEquals(first, change.startModelIndex); assertEquals(last, change.endModelIndex); } @Test public void testModelAllChanged() { int first = -1; int last = -1; ListDataEvent e = new ListDataEvent(new DefaultListModel(), ListDataEvent.CONTENTS_CHANGED, last, first); ModelChange change = new ModelChange(e); assertEquals(ListDataEvent.CONTENTS_CHANGED, change.type); assertTrue(change.allRowsChanged); assertEquals(-1, change.startModelIndex); assertEquals(-1, change.endModelIndex); assertEquals(-1, change.length); } //-------------------- factory methods, setup protected ListModel createListModel() { JXList list = new JXList(); return new DefaultComboBoxModel(list.getActionMap().allKeys()); } protected DefaultListModelF createAscendingListModel(int startRow, int count) { DefaultListModelF l = new DefaultListModelF(); for (int row = startRow; row < startRow + count; row++) { l.addElement(new Integer(row)); } return l; } protected DefaultListModel createListModelWithLinks() { DefaultListModel model = new DefaultListModel(); for (int i = 0; i < 20; i++) { try { LinkModel link = new LinkModel("a link text " + i, null, new URL("http://some.dummy.url" + i)); if (i == 1) { URL url = JXEditorPaneTest.class.getResource("resources/test.html"); link = new LinkModel("a resource", null, url); } model.addElement(link); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return model; } @Override protected void setUp() throws Exception { super.setUp(); ascendingListModel = createAscendingListModel(0, 20); list = new JXList(ascendingListModel, true); controller = new ListSortController<ListModel>(list.getModel()); list.setComparator(TableSortController.COMPARABLE_COMPARATOR); list.setRowSorter(controller); testRow = 2; } @Override protected void tearDown() throws Exception { super.tearDown(); } @Before public void setUpJ4() throws Exception { setUp(); } @After public void tearDownJ4() throws Exception { tearDown(); } }