Monday, June 24, 2013

An Interesting post about if-else vs Switch

When I was working on if-else statements on netbeans IDE, it gave me an advice to change if-else to switch.

So I googled it why I had to use switch instead of if-else, and heres the answer

http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&frm=1&source=web&cd=5&ved=0CFAQFjAE&url=http%3A%2F%2Fwww.ashishpaliwal.com%2Fblog%2F2009%2F08%2Fif-else-vs-switch-%25E2%2580%2593-which-is-better%2F&ei=XAbIUfm8E8ejkAWJ64CYBw&usg=AFQjCNHMSGjKS0cjY_t_rOmKqBuz7VkVEQ&sig2=hVPuZARGnOVwsh0zvc10sw&bvm=bv.48293060,d.dGI

so in short, switch has more performance and readability advantage.



Programming is fun

Found really cool array copy method

I was working on array copy menually, then my IDE netbeans gave me a warning sign.

"You are Copying Arrays Manually."

And it showed me how to do it using System.arraycopy!

I think this IDE is so awesome, and people who made it are geniuses.

I hope I could be like them someday

http://docs.oracle.com/javase/6/docs/api/java/lang/System.html

Thursday, June 20, 2013

How to change background color of a row relate to a column data

In this post, I explain how to change background color according to data of a column.

First, this is my goal.

The first row, second column is "male", and the background color of the row is light gray.

 
As I change the male to female, the background color is changed to Orange. 

 
 
I will explain this in source code.


package jtbl_color;

import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

/**
 *
 * @author Lee
 */
public class ColoredTable extends JPanel{
    
    private JTable table;
    private JScrollPane scrollPane;
    private MyTableModel model;
    private int sexColumn = 1;
    
    // data for the table
    private Object[][] data = {
        {"Singed", "male", false}, 
        {"Akali", "female", false},    
        {"Tristina", "female", true},    
        {"Udyr", "male", false},     
        {"Janna", "female", true}
    };
    
    // column names for the table
    private final String[] columnNames = {
        "Name", "Sex", "Vegetarian"
    };
    
    public ColoredTable() {
        super(new BorderLayout());
        initComponents();
    }
    
    public void initComponents() {
        // Initiate the Abstract Table Model
        model = new MyTableModel();
        
        // Add TableModelListener to the model
        model.addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                // get the event, and check if the changed event is data update
                if(e.getType() == TableModelEvent.UPDATE) {
                    // repaint the row
                    rowRepaint(table, table.convertRowIndexToView(e.getFirstRow()));
                }
            }
        });
        // Add the model to the JTable
        table = makeTable(model);
        
        // set the Combo box to sex column
        setSexColumn(table.getColumnModel().getColumn(sexColumn));
        
        // Add the table to the JScrollPane
        scrollPane = new JScrollPane(table);
        
        // Add the scrollPane to the JPanel
        this.add(scrollPane);
    }
    
    // set combo box for sex column
    public static void setSexColumn(TableColumn sexColumn){
        // new combo box
        JComboBox comboBox = new JComboBox();
        // add male and female items to the combo box
        comboBox.addItem("male");
        comboBox.addItem("female");
        // set combo box to the sex column
        sexColumn.setCellEditor(new DefaultCellEditor(comboBox));
    }
    
    // repaint the selected row 
    private static void rowRepaint(JTable table, int row) {
        Rectangle r = table.getCellRect(row, 0, true);
        r.width = table.getWidth();
        table.repaint(r);
    }
    
    // make table for changing colors
    private JTable makeTable(final MyTableModel model) {
        return new JTable(model) {
            @Override
            public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
                Component c = super.prepareRenderer(renderer, row, column);
                String sex = (String)model.getValueAt(
                        convertRowIndexToModel(row), sexColumn);
                
                if(sex.equals("female")) {
                        c.setBackground(Color.ORANGE);
                    } else if (sex.equals("male")) {
                        c.setBackground(Color.LIGHT_GRAY);
                    }
                return c;
            }
        };
    }
    
    // Define the model for the table using AbstractTableModel
    class MyTableModel extends AbstractTableModel {
        // Decide the row count according to the data length   
        @Override       
        public int getRowCount() {        
            return data.length;     
        }     
        
        // decide the column count according to the columnNames length   
        @Override     
        public int getColumnCount() {      
            return columnNames.length;     
        }        
        
        // put the data values into the table.     
        @Override       
        public Object getValueAt(int rowIndex, int columnIndex) {     
            return data[rowIndex][columnIndex];    
        }               
        
        // put the columnNames values into the table column names    
        @Override      
        public String getColumnName(int col){      
            return columnNames[col];     
        }              
        
        // set if the cells in the table are editable.     
        // In this example, all the cells are editable  
        @Override      
        public boolean isCellEditable(int row, int col) {     
            return true;       
        }                
        
        // this is for data change.      
        @Override      
        public void setValueAt(Object value, int row, int col) {      
            data[row][col] = value;      
            fireTableCellUpdated(row, col);   
        }    
        /*     
         * JTable uses this method to determine the default renderer/    
         * editor for each cell.  If we didn't implement this method,       
         * then the last column would contain text ("true"/"false"),     
         * rather than a check box.  (From oracle JTable tutorial)   
         */        
        @Override
        public Class getColumnClass(int c) {        
            return getValueAt(0, c).getClass();     
        }
    }
    
    /**       
     * Create the GUI and show it.   
     * For thread safety,      
     * this method should be invoked from the    
     * event-dispatching thread.       
     * (from Oracle table tutorial)    
     */  
    private static void createAndShowGUI() {     
        //Create and set up the window.       
        JFrame frame = new JFrame("TableRenderDemo");      
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     
        //Create and set up the content pane.      
        ColoredTable newContentPane = new ColoredTable();        
        newContentPane.setOpaque(true); //content panes must be opaque     
        frame.setContentPane(newContentPane);                     
        //Display the window.      
        frame.pack();      
        frame.setVisible(true);       
    }        
    public static void main(String[] args) {     
        //Schedule a job for the event-dispatching thread:   
        //creating and showing this application's GUI.      
        SwingUtilities.invokeLater(new Runnable() {        
            @Override        
            public void run() {   
                createAndShowGUI();   
            }    
        });   
    }
}

I don't remember where I saw this method, so I cannot give you the reference page.
Also, I'm not quite sure what the prepareRenderer does..
More study is required here

How to share a data among two tables

In this post, I'll explain how to share one data among two table.

First, I will define a DataContainer class that holds all the data I want.

package jtbl_2tbl_abst;


/**
 *
 * @author Lee
 */
public class DataContainer {
    
    private Object[][] data = {
        {"Singed", "male", false},
        {"Akali", "female", false},
        {"Tristina", "female", true},
        {"Udyr", "male", false},
        {"Janna", "female", true}
    };
    
    private final String[] columnNames = {
        "Name", "Sex", "Vegetarin"
    };

    /**
     * @return the data
     */
    public Object[][] getData() {
        return data;
    }

    /**
     * @param data the data to set
     */
    public void setData(Object[][] data) {
        this.data = data;
    }
    
    public void setData(Object data, int row, int col) {
        this.data[row][col] = data; 
    }

    /**
     * @return the columnNames
     */
    public String[] getColumnNames() {
        return columnNames;
    }
}

Now I want MyTableModel that extends AbstractTableModel.
(otherwise I should redefine MyTableModel for each table.)

package jtbl_2tbl_abst;

import javax.swing.table.AbstractTableModel;

/**
 *
 * @author Lee
 */
public class MyTableModel extends AbstractTableModel {
    
    // holds column names and data from outer source
    private String[] columnNames;
    private Object[][] data;
    
    public MyTableModel(Object[][] data, String[] columnNames) {
        this.data = data;
        this.columnNames = columnNames;
    }
    

    // the explanation is ommitted since I've covered it in the last post
    // "Using AbstractTableModel"
    @Override
        public int getRowCount() {
            return data.length;
        }

        @Override
        public int getColumnCount() {
            return columnNames.length;
        }

        @Override
        public Object getValueAt(int row, int col) {
            return data[row][col];
        }
        
        @Override
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }
        
        @Override
        public boolean isCellEditable(int row, int col) {
            return true;
        }
        
        @Override
        public void setValueAt(Object value, int row, int col) {
            data[row][col] = value;
            fireTableCellUpdated(row, col);
        }
    
}

now, let's define Table1 class

package jtbl_2tbl_abst;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import net.miginfocom.swing.MigLayout;

/**
 *
 * @author Lee
 */
public class Table1 extends JPanel implements TableModelListener{
    
    // JScrollPane that takes JTable
    private JScrollPane scrollPane;
    // This is table
    private JTable table1;
    // this holds data from DataContainer
    DataContainer container = new DataContainer();
    // this holds AbstractTableModel from MyTableModel
    private AbstractTableModel model;
    
    public Table1(DataContainer container) {
        super(new MigLayout());
        this.container = container;
        initComponents();
    }
    
    private void initComponents() {
        // initiate the table model
        model = new MyTableModel(container.getData(), container.getColumnNames());
        // initiate the table
        table1 = new JTable(model);
        // add TableModelListener so that we can change the data, and the 
        // changed data can be detected
        table1.getModel().addTableModelListener(this);
        scrollPane = new JScrollPane(table1);
        
        this.add(scrollPane);
    }

    @Override
    public void tableChanged(TableModelEvent e) {
        // get the first row that has been changed
        int row = e.getFirstRow();
        // get the column that has been changed
        int column = e.getColumn();
        // get the changed model of the table
        TableModel model = (TableModel)e.getSource();
        
        // tableChanged method throws -1 when theres no change of row and columns
        // but wants to update the whole table.
        // And that causes unexpected errors, so I put if(column >=0) to prevent that
        if(column >= 0 ) {
            String columnName = model.getColumnName(column);
            // get the changed data from the table model
            Object data = model.getValueAt(row, column);
            // apply the change to the container data
            container.setData(data, row, column);
        }
    }

    /**
     * @return the table1
     */
    public JTable getTable1() {
        return table1;
    }

    /**
     * @param table1 the table1 to set
     */
    public void setTable1(JTable table1) {
        this.table1 = table1;
    }

    /**
     * @return the model
     */
    public AbstractTableModel getModel() {
        return model;
    }

    /**
     * @param model the model to set
     */
    public void setModel(AbstractTableModel model) {
        this.model = model;
    }
}

Similarly, we need to define Table2. The explanation is omitted since Table2 is identical to Table1

package jtbl_2tbl_abst;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import net.miginfocom.swing.MigLayout;

/**
 *
 * @author Lee
 */
public class Table2 extends JPanel implements TableModelListener{

    private JScrollPane scrollPane;
    private JTable table2;
    private AbstractTableModel model;
    DataContainer container = new DataContainer();
    
    public Table2(DataContainer container) {
        super(new MigLayout());
        this.container = container;
        initComponents();
    }
    
    public void initComponents() {
        model = new MyTableModel(container.getData(), container.getColumnNames());
        table2 = new JTable(model);
        table2.getModel().addTableModelListener(this);
        
        scrollPane = new JScrollPane(table2);
        this.add(scrollPane);
    }
    
    @Override
    public void tableChanged(TableModelEvent e) {
        int row = e.getFirstRow();
        int column = e.getColumn();
        TableModel model = (TableModel)e.getSource();

        if(column >= 0 ) {
            String columnName = model.getColumnName(column);
            Object data = model.getValueAt(row, column);
            System.out.println("changed data: " + data);
            container.setData(data, row, column);
        }
    }

    /**
     * @return the table2
     */
    public JTable getTable2() {
        return table2;
    }

    /**
     * @param table2 the table2 to set
     */
    public void setTable2(JTable table2) {
        this.table2 = table2;
    }

    /**
     * @return the model
     */
    public AbstractTableModel getModel() {
        return model;
    }

    /**
     * @param model the model to set
     */
    public void setModel(AbstractTableModel model) {
        this.model = model;
    }
}

Now we need JFrame that holds the two tables.

package jtbl_2tbl_abst;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;

/**
 *
 * @author Lee
 */
public class Frame {
    
    // JFrame that holds Table1
    JFrame frame1  = new JFrame();
    // JFrame that holds Table2
    JFrame frame2 = new JFrame();
    // This button will open frame2 with Table2
    JButton opnBttn = new JButton("Open Table2");
    // This button will close frame2 updating the changed data
    JButton mdfBttn = new JButton("OK");
    // This button will Update frame2(table2) when table1 has been changed
    JButton udtBttn = new JButton("Update");
    DataContainer container = new DataContainer();
    Table1 table1Panel;
    Table2 table2Panel;
    
    public Frame() {
        initTable1();
        initTable2();
    }
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Frame();
            }
        });
    }
    
    // initiate Table1
    public void initTable1() {
        // add ActionListener to opnBttn
        opnBttn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                opnBttnClicked(evt);
            }
        });
        
        // add ActionListener to udtBttn
        udtBttn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                udtBttnClicked(evt);
            }
        });
        
        // initiate Table1 with the sharing data
        table1Panel = new Table1(container);
        
        // setup frame1
        frame1.setLayout(new MigLayout());
        frame1.add(table1Panel, "span, growx, wrap");
        frame1.add(opnBttn);
        frame1.add(udtBttn);
        frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame1.pack();
        frame1.setVisible(true);
    }
    
    // initiate Table2
    public void initTable2() {
        // add ActionListener to mdfBttn
        mdfBttn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                mdfBttnClicked(evt);
            }
        });
        
        // initiate Table2 with the sharing data
        table2Panel = new Table2(container);
        
        // set up frame2
        frame2.setLayout(new MigLayout());
        frame2.add(table2Panel, "wrap");
        frame2.add(mdfBttn, "center");
        frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame2.pack();
    }

    // when opnBttn is clicked, frame2(table2) become visible
    public void opnBttnClicked(ActionEvent evt) {
        // clikcing the opnBttn when one is editing the table cell will cause 
        // data lose since the changed value is still in Editor, not in Renderer.
        // To send the changed value from Editor to Render, I need the following
        if (table1Panel.getTable1().isEditing()) {
            table1Panel.getTable1().getCellEditor().stopCellEditing();
        }
        
        // open frame2(table2)
        frame2.setVisible(true);
    }
    
    // when mdfBttn is clicked, frame2 is closed 
    public void mdfBttnClicked(ActionEvent evt) {
        // same as above
        if (table2Panel.getTable2().isEditing()) {
            table2Panel.getTable2().getCellEditor().stopCellEditing();
        }
        frame2.setVisible(false);
        // notify table1 that the data may have been changed
        table1Panel.getModel().fireTableDataChanged();
    }
    
    // when udtBttn is clicked, table2 refreshes 
    public void udtBttnClicked(ActionEvent evt) {
        if (table1Panel.getTable1().isEditing()) {
            table1Panel.getTable1().getCellEditor().stopCellEditing();
        }
        table2Panel.getModel().fireTableDataChanged();
    }
}

How to debug

my boss taught me how to debug today, and here's the code.

Put this where error occurs.



try {
    throw new Exception();
} catch (Exception e) {
    e.printStackTrace();
}

Using AbstractTableModel

In this post, I explain how to make a JTable using AbstractTableModel

Using AbstractTableModel is very straight foward, so I will just post
a source code that one can use as a basic form like DefaultTableModel

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jtbl_ex;

import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;

/**
 *
 * @author Lee
 */
public class TableOne extends JPanel{
    
    private JTable table;
    private JScrollPane scrollPane;
    private AbstractTableModel model;
    
    // data for the table
    private Object[][] data = {
        {null, null, null, null},
        {null, null, null, null},
        {null, null, null, null},
        {null, null, null, null}
    };

    // column names for the table
    private String[] columnNames = {
        "Name1", "Name2", "Name3", "Name4"
    };
    
    
    
    public TableOne() {
        // set the Layout for the JPanel
        super(new BorderLayout());
       
        initComponents();
    }
    
    public void initComponents() {
        // Initialize the Abstract Table Model
        model = new MyTableModel();
        
        // Add the model to the JTable
        table = new JTable(model);
        
        // Add the table to the JScrollPane
        scrollPane = new JScrollPane(table);
        
        // Add the scrollPane to the JPanel
        this.add(scrollPane);
    }
    
    // Define the model for the table using AbstractTableModel
    class MyTableModel extends AbstractTableModel{
        
        // Decide the row count according to the data length
        @Override
        public int getRowCount() {
            return data.length;
        }

        // decide the column count according to the columnNames length
        @Override
        public int getColumnCount() {
            return columnNames.length;
        }

        // put the data values into the table. 
        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return data[rowIndex][columnIndex];
        }
        
        // put the columnNames values into the table column names
        @Override
        public String getColumnName(int col){
            return columnNames[col];
        }
        
        // set if the cells in the table are editable. 
        // In this example, all the cells are editable
        @Override
        public boolean isCellEditable(int row, int col) {
            return true;
        }
         
        // this is for data change. 
        @Override
        public void setValueAt(Object value, int row, int col) {
            data[row][col] = value;
            fireTableCellUpdated(row, col);
        }

        /* 
         * JTable uses this method to determine the default renderer/  
         * editor for each cell.  If we didn't implement this method,  
         * then the last column would contain text ("true"/"false"),     
         * rather than a check box.  (From oracle JTable tutorial)
        */
        @Override
        public Class getColumnClass(int c) {
            return getValueAt(0, c).getClass();
        }

    }
    
    /**      
     * Create the GUI and show it.  
     * For thread safety,     
     * this method should be invoked from the     
     * event-dispatching thread.    
     * (from Oracle table tutorial)
     */
    private static void createAndShowGUI() {     
        //Create and set up the window.      
        JFrame frame = new JFrame("TableRenderDemo");    
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
        
        //Create and set up the content pane.        
        TableOne newContentPane = new TableOne();      
        newContentPane.setOpaque(true); //content panes must be opaque    
        frame.setContentPane(newContentPane);         
        
        //Display the window.    
        frame.pack();      
        frame.setVisible(true);    
    } 
    
    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:       
        //creating and showing this application's GUI.
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }
}


for more information about AbstractTableModel,  check
http://docs.oracle.com/javase/tutorial/uiswing/components/table.html

Thursday, June 13, 2013

Short commnet about MigLayout

Wow 

The guy who made MigLayout is a genius.
It is so easy to use, extremely flexible, and many more!
I was using BorderLayout and Grid Layout to build my interface, and was having hard time. But once I've changed the layout to MigLayout,

BOOM!

No more headaches!

I wish there were someone around who could tell me these kind of information..