swingx-master/swingx-core/src/test/java/org/jdesktop/swingx/table/TableRowHeightControllerTest.java
/*
 * $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.table;

import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Logger;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

import org.jdesktop.swingx.InteractiveTestCase;
import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.renderer.HighlighterClientVisualCheck.FontHighlighter;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/**
 * Tests for TableRowHeightController.
 * 
 * @author Jeanette Winzenburg
 */
@RunWith(JUnit4.class)
public class TableRowHeightControllerTest extends InteractiveTestCase {
    
    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(TableRowHeightControllerTest.class.getName());

    private static final String BIG = "BIG";
    private static final String SMALL = "small";
    private TableRowHeightController controller;
    private JXTable table;

    public static void main(String[] args) {
        TableRowHeightControllerTest test = new TableRowHeightControllerTest();
        try {
            test.runInteractiveTests();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void interactiveCoreTableRowHeights() {
        final JTable table = createCoreTableWithIndyRowHeights();
        table.setAutoCreateRowSorter(true);
        new TableRowHeightController().install(table);
        JXFrame frame = wrapWithScrollingInFrame(table, "core: row heights");
        Action toggleBig = new AbstractAction("toggleSize") {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                if (table.getSelectionModel().isSelectionEmpty()) return;
                int selected = table.getSelectedRow();
                Object value = BIG.equals(table.getValueAt(selected, 0)) ? SMALL : BIG;
                table.setValueAt(value, selected, 0);
            }
        };
        addAction(frame, toggleBig);
        Action toggleModel = new AbstractAction("toggleModel") {
            boolean evenBig;
            @Override
            public void actionPerformed(ActionEvent e) {
                table.setModel(createTableModel(evenBig));
                evenBig = !evenBig;
            }
        };
        addAction(frame, toggleModel);
        show(frame);
    }
    
    
    public void interactiveRowHeights() {
        final JXTable table = createXTableWithIndyRowHeights();
        new TableRowHeightController().install(table);
        table.packAll();
        JXFrame frame = wrapWithScrollingInFrame(table, "x: row heights");
        Action toggleBig = new AbstractAction("toggleSize") {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                if (table.getSelectionModel().isSelectionEmpty()) return;
                int selected = table.getSelectedRow();
                Object value = BIG.equals(table.getValueAt(selected, 0)) ? SMALL : BIG;
                table.setValueAt(value, selected, 0);
            }
        };
        addAction(frame, toggleBig);
        Action toggleModel = new AbstractAction("toggleModel") {
            boolean evenBig;
            @Override
            public void actionPerformed(ActionEvent e) {
                table.setModel(createTableModel(evenBig));
                evenBig = !evenBig;
            }
        };
        addAction(frame, toggleModel);
        show(frame);
    }
    
    /**
     * @return
     */
    private JTable createCoreTableWithIndyRowHeights() {
        DefaultTableModel model = createTableModel(false);
        JTable table = new JTable(model);
        final Font derivedFont = table.getFont().deriveFont(50f);
        TableCellRenderer renderer = new DefaultTableCellRenderer() {

            @Override
            public Component getTableCellRendererComponent(JTable table,
                    Object value, boolean isSelected, boolean hasFocus,
                    int row, int column) {
                Component comp = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
                        row, column);
                if (BIG.equals(value)) {
                    setFont(derivedFont);
                }
                return comp;
            }
            
        };
        table.setDefaultRenderer(Object.class, renderer);
        return table;
    }
    
    /**
     * Creates and returns a JXTable with model as returned by createTableModel with
     * eventBig == false.
     * Has a FontHighlighter which decorates the renderer with a big font if 
     * the cell value is BIG.
     *  
     * @return a JXTable configured 
     */
    private JXTable createXTableWithIndyRowHeights() {
        DefaultTableModel model = createTableModel(false);
        JXTable table = new JXTable(model);
        HighlightPredicate predicate = new HighlightPredicate() {

            @Override
            public boolean isHighlighted(Component renderer,
                    ComponentAdapter adapter) {
                return BIG.equals(adapter.getValue());
            }
            
        };
        FontHighlighter highlighter = new FontHighlighter(predicate, table.getFont().deriveFont(50f));
        table.addHighlighter(highlighter);
        return table;
    }

    /**
     * Creates and returns a table model with value in first column alternating between
     * SMALL and BIG.
     * 
     * @param evenBig starts with BIG if true, starts with SMALL otherwise
     * @return a DefaultTableModel with alternating SMALL/BIG values in first column.
     */
    private DefaultTableModel createTableModel(boolean evenBig) {
        int compare = evenBig ? 0 : 1;
        DefaultTableModel model = new DefaultTableModel(10, 2) {

            @Override
            public Class<?> getColumnClass(int columnIndex) {
                if (columnIndex == getColumnCount() - 1) {
                    return Boolean.class;
                }
                return super.getColumnClass(columnIndex);
            }
            
        };
        for (int i = 0; i < model.getRowCount(); i++) {
            if (i % 2 == compare) {
                model.setValueAt(BIG, i, 0);
            } else {
                model.setValueAt(SMALL, i, 0);
            }
        }
        return model;
    }
    
    @Test
    public void testSetModelRewiresModelListeners() throws InterruptedException, InvocationTargetException {
        final int smallRowHeight = table.getRowHeight(1);
        final AbstractTableModel oldModel = (AbstractTableModel) table.getModel();
        assertEquals("sanity: model listener installed", true, containsModelListener(oldModel));
        final DefaultTableModel model = createTableModel(true);
        table.setModel(model);
        
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                assertEquals("new model must be installed", true, containsModelListener(model));
                assertEquals("oldModel must be released", false, containsModelListener(oldModel));
                assertEquals(smallRowHeight, table.getRowHeight(0));
            }
        });
        
    }

    /**
     * @param model
     * @return
     */
    private boolean containsModelListener(AbstractTableModel model) {
        TableModelListener[] listeners = model.getListeners(TableModelListener.class);
        for (TableModelListener l : listeners) {
            if (l == controller.getTableModelListener()) return true;
        }
        return false;
    }

    @Test
    public void testRowHeightAdjustedOnInsert() throws InterruptedException, InvocationTargetException {
        final int bigRowHeight = table.getRowHeight(1);
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                ((DefaultTableModel) table.getModel()).insertRow(0, new Object[] {BIG});
            }
        });
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                assertEquals(bigRowHeight, table.getRowHeight(0));
            }
        });
        
    }
    
    @Test
    public void testRowHeightAdjustedOnUpdate() throws InterruptedException, InvocationTargetException {
        final int smallRowHeight = table.getRowHeight(0);
        table.setValueAt(table.getValueAt(0, 0), 1, 0);
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                assertEquals(smallRowHeight, table.getRowHeight(1));
            }
        });
     }
    
    @Test
    public void testRowHeightAdjustedWithPrepare() {
        int rowHeight = table.getRowHeight(0);
        int bigRowHeight = table.getRowHeight(1);
        assertEquals(getRendererRowHeight(0, 0), table.getRowHeight(0));
        assertEquals(getRendererRowHeight(1, 0), table.getRowHeight(1));
        assertTrue(" ", 2 * rowHeight < bigRowHeight);
    }
    
    /**
     * @param i
     * @return
     */
    private int getRendererRowHeight(int row, int column) {
        TableCellRenderer renderer = table.getCellRenderer(row, column);
        Component comp = table.prepareRenderer(renderer, row, column);
        return comp.getPreferredSize().height;
    }

    @Override
    @Before
    public void setUp() throws Exception {
        table = createXTableWithIndyRowHeights();
        controller = new TableRowHeightController(table);
//        controller.install(table);
    }
    
    
    
}
