Creating a JTable with images in the headers

Courtesy of Nobuo Tamemasa (http://www2.gol.com/users/tame/swing/examples/JTableExamples8.html)



AnimatedIconHeaderExample.java:

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

/**
 * @version 1.0 06/19/99
 */
public class AnimatedIconHeaderExample extends JFrame {

  public AnimatedIconHeaderExample(){
    super( "AnimatedIconHeader Example" );
    
    final Object[][] data =  new Object[][]{
      {"Leopard","Lycaon"},
      {"Jagur"  ,"Jackal"},
      {"Cheetah","Coyote"},
      {"Puma"   ,"Dingo" },
      {"Lynx"   ,"Fox"   },
      {"Tom"    ,"Hot"  }};
    final String[] column = new String[]{"Cat","Dog"};
    
    ImageIcon[] icons = {new ImageIcon("images/3-119.gif"),
                         new ImageIcon("images/3-6.gif")};
        
    AbstractTableModel model = new AbstractTableModel() {
      public int getColumnCount() { return column.length; }
      public int getRowCount()    { return data.length; }
      public String getColumnName(int col) {
       return column[col]; 
      }
      public Object getValueAt(int row, int col) { 
        return data[row][col]; 
      }
    };
        
    JTable table = new JTable( model );
    JTableHeader header = table.getTableHeader();
    JLabel renderer;
    for (int i=0;i<model.getColumnCount();i++) {
      renderer = (JLabel)table.getColumn(column[i]).getHeaderRenderer();
      renderer.setIcon(icons[i]);
      
      // If you have only one column.
      // icons[i].setImageObserver(header);
      
      icons[i].setImageObserver(new HeaderImageObserver(header, i));
    }
    JScrollPane pane = new JScrollPane(table);
    getContentPane().add(pane);
  }
    
  class HeaderImageObserver implements ImageObserver {
    JTableHeader header;
    int col;
      
    HeaderImageObserver(JTableHeader header, int col) {
      this.header = header;
      this.col    = col;
    }
    
    public boolean imageUpdate(Image img, int flags,
                               int x, int y, int w, int h) {
      if ((flags & (FRAMEBITS|ALLBITS)) != 0) {
        Rectangle rect = header.getHeaderRect(col);
        header.repaint(rect);
      }
      return (flags & (ALLBITS|ABORT)) == 0;
    }
  }    
  
  public static void main(String[] args) {
    AnimatedIconHeaderExample frame = new AnimatedIconHeaderExample();
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    frame.setSize( 300, 140 );
    frame.setVisible(true);
  }
}

Creating borders for JTable columns

Courtesy of Nobuo Tamemasa (http://www2.gol.com/users/tame/swing/examples/JTableExamples7.html)



ColumnBorderTableExample.java:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
 
/**
 * @version 1.0  3/06/99
 */
public class ColumnBorderTableExample extends JFrame {

  public ColumnBorderTableExample() {
    super( "Column Border Example" );
        
    DefaultTableModel dm = new DefaultTableModel(4,10);
    JTable table = new JTable( dm );
    table.setIntercellSpacing(new Dimension(0,0));
    
    Color color = table.getGridColor();
    BorderCellRenderer[] renderers = new BorderCellRenderer[6];
    renderers[0] = createRenderer(color, new Insets(0,0,0,1));
    renderers[1] = createRenderer(color, new Insets(0,1,0,1));
    renderers[2] = createRenderer(color, new Insets(0,1,0,2));
    renderers[3] = createRenderer(color, new Insets(0,2,0,2));
    renderers[4] = createRenderer(color, new Insets(0,2,0,0));
    renderers[5] = createRenderer(Color.red, new Insets(0,1,1,1));
    
    TableColumnModel model = table.getColumnModel();
    model.getColumn(1).setCellRenderer(renderers[0]);
    model.getColumn(2).setCellRenderer(renderers[0]);
    model.getColumn(3).setCellRenderer(renderers[0]);
    model.getColumn(4).setCellRenderer(renderers[1]);
    model.getColumn(5).setCellRenderer(renderers[2]);
    model.getColumn(6).setCellRenderer(renderers[3]);
    model.getColumn(7).setCellRenderer(renderers[4]);
    model.getColumn(8).setCellRenderer(renderers[5]);
    
    JScrollPane scroll = new JScrollPane( table );
    getContentPane().add(scroll, BorderLayout.CENTER);
  }

  public static void main(String[] args) {
    ColumnBorderTableExample frame = new ColumnBorderTableExample();
    frame.addWindowListener( new WindowAdapter() {
      public void windowClosing( WindowEvent e ) {
        System.exit(0);
      }
    });
    frame.setSize( 300, 120 );
    frame.setVisible(true);
  }
  
  private static BorderCellRenderer createRenderer(Color color, Insets insets) {
    BorderCellRenderer renderer = new BorderCellRenderer();
    renderer.setColumnBorder(new LinesBorder(color, insets));    
    return renderer;
  }  
}

BorderCellRenderer.java:

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.border.*;
 
/**
 * @version 1.0 03/06/99
 */
public class BorderCellRenderer extends JLabel
    implements TableCellRenderer {
  protected Border noFocusBorder; 
  protected Border columnBorder; 
 
  public BorderCellRenderer() {
    noFocusBorder = new EmptyBorder(1, 2, 1, 2);
    setOpaque(true);
  }

  public Component getTableCellRendererComponent(JTable table, Object value,
                 boolean isSelected, boolean hasFocus, int row, int column) {
    if (isSelected) {
      setForeground(table.getSelectionForeground());
      setBackground(table.getSelectionBackground());
    } else {
      setForeground(table.getForeground());
      setBackground(table.getBackground());
    }
    setFont(table.getFont());
    
    if (hasFocus) {
      setBorder( UIManager.getBorder("Table.focusCellHighlightBorder") );
      if (table.isCellEditable(row, column)) {
        setForeground( UIManager.getColor("Table.focusCellForeground") );
        setBackground( UIManager.getColor("Table.focusCellBackground") );
      }
    } else {
      if (value instanceof CellBorder) {
        Border border = ((CellBorder)value).getBorder();
        setBorder(border);
      } else {
        if (columnBorder != null) {
          setBorder(columnBorder);
        } else {
          setBorder(noFocusBorder);
        }
      }
    }
    setText((value == null) ? "" : value.toString());        
    return this;
  }
  
  public void setColumnBorder(Border border) {
    columnBorder = border;
  }
  
  public Border getColumnBorder() {
    return columnBorder;
  }
}

CellBorder.java:

import java.awt.*;
import javax.swing.border.*;
 
/**
 * @version 1.0 03/06/99
 */
public interface CellBorder {
   
  public Border getBorder();
  public Border getBorder(int row, int column);
  
  public void setBorder(Border border);
  public void setBorder(Border border, int row, int column);
}

LinesBorder.java:

import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
 
/**
 * @version 1.0 03/09/99
 */
public class LinesBorder extends AbstractBorder implements SwingConstants { 
  protected int northThickness;
  protected int southThickness;
  protected int eastThickness;
  protected int westThickness;  
  protected Color northColor;
  protected Color southColor;
  protected Color eastColor;
  protected Color westColor;
  
  public LinesBorder(Color color) {
    this(color, 1);
  }

  public LinesBorder(Color color, int thickness)  {
    setColor(color);
    setThickness(thickness);
  }

  public LinesBorder(Color color, Insets insets)  {
    setColor(color);
    setThickness(insets);
  }

  public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
    Color oldColor = g.getColor();
    
    g.setColor(northColor);
    for (int i = 0; i < northThickness; i++)  {
      g.drawLine(x, y+i, x+width-1, y+i);
    }
    g.setColor(southColor);
    for (int i = 0; i < southThickness; i++)  {
      g.drawLine(x, y+height-i-1, x+width-1, y+height-i-1);
    }
    g.setColor(eastColor);
    for (int i = 0; i < westThickness; i++)  {
      g.drawLine(x+i, y, x+i, y+height-1);
    }
    g.setColor(westColor);
    for (int i = 0; i < eastThickness; i++)  {
      g.drawLine(x+width-i-1, y, x+width-i-1, y+height-1);
    }

    g.setColor(oldColor);
  }

  public Insets getBorderInsets(Component c)       {
    return new Insets(northThickness, westThickness, southThickness, eastThickness);
  }

  public Insets getBorderInsets(Component c, Insets insets) {
    return new Insets(northThickness, westThickness, southThickness, eastThickness);    
  }


  public boolean isBorderOpaque() { return true; }
    
  public void setColor(Color c) {
    northColor = c;
    southColor = c;
    eastColor  = c;
    westColor  = c;
  }
  
  public void setColor(Color c, int direction) {
    switch (direction) {
      case NORTH: northColor = c; break;
      case SOUTH: southColor = c; break;
      case EAST:  eastColor  = c; break;
      case WEST:  westColor  = c; break;
      default: 
    }
  }
    
  public void setThickness(int n) {
    northThickness = n;
    southThickness = n;
    eastThickness  = n;
    westThickness  = n;
  }
    
  public void setThickness(Insets insets) {
    northThickness = insets.top;
    southThickness = insets.bottom;
    eastThickness  = insets.right;
    westThickness  = insets.left;
  }
  
  public void setThickness(int n, int direction) {
    switch (direction) {
      case NORTH: northThickness = n; break;
      case SOUTH: southThickness = n; break;
      case EAST:  eastThickness  = n; break;
      case WEST:  westThickness  = n; break;
      default: 
    }
  }

  public void append(LinesBorder b, boolean isReplace) {
    if (isReplace) {
      northThickness = b.northThickness;
      southThickness = b.southThickness;
      eastThickness  = b.eastThickness;
      westThickness  = b.westThickness;
    } else {
      northThickness = Math.max(northThickness ,b.northThickness);
      southThickness = Math.max(southThickness ,b.southThickness);
      eastThickness  = Math.max(eastThickness  ,b.eastThickness);
      westThickness  = Math.max(westThickness  ,b.westThickness);
    }
  }

  public void append(Insets insets, boolean isReplace) {
    if (isReplace) {
      northThickness = insets.top;
      southThickness = insets.bottom;
      eastThickness  = insets.right;
      westThickness  = insets.left;
    } else {
      northThickness = Math.max(northThickness ,insets.top);
      southThickness = Math.max(southThickness ,insets.bottom);
      eastThickness  = Math.max(eastThickness  ,insets.right);
      westThickness  = Math.max(westThickness  ,insets.left);
    }
  }
}

Opening a new window when double clicking on a JTable row

Add a MouseListener to your JTable.

Main.java:

import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame 
{
   public Main() {
      super("Table example, Wines from Bordeaux");
 
      final Object[][] tabledata = {
            { "Chateau Meyney, St. Estephe", 	   new Integer(1994), "$18.75"},
            { "Chateau Montrose, St. Estephe", 	   new Integer(1975), "$54.25" },
            { "Chateau Gloria, St. Julien", 	   new Integer(1993), "$22.99" },
            { "Chateau Beychevelle, St. Julien",   new Integer(1970), "$61.63" },
            { "Chateau La Tour de Mons, Margeaux", new Integer(1975), "$57.03" },
            { "Chateau Brane-Cantenac, Margeaux",  new Integer(1978), "$49.92" },
      };
 
      String columnheaders[] = { "Wine", "Vintage", "Price" };
 
      JTable table = new JTable(tabledata, columnheaders) {
         public boolean editCellAt(int row, int column, java.util.EventObject e) {
            return false;
         }
      };
 
      table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      table.setPreferredScrollableViewportSize(new Dimension(500, 70));
      JScrollPane scrollPane = new JScrollPane(table);
      getContentPane().add(scrollPane);
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
 
      pack();
 
      table.addMouseListener(new MouseAdapter() {
         public void mouseClicked(MouseEvent e) {
            if (e.getClickCount() == 2) {
               JTable target = (JTable) e.getSource();
               int row = target.getSelectedRow();
               StringBuffer sb = new StringBuffer();
               String lineSeparator = System.getProperty("line.separator");
               sb.append(tabledata[row][0] + lineSeparator);
               sb.append(tabledata[row][1] + lineSeparator);
               sb.append(tabledata[row][2] + lineSeparator);
               TextFrame textFrame = new TextFrame(sb.toString());
               textFrame.setVisible(true);
            }
         }
      });
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.show();
   }
}
 
class TextFrame extends JFrame
{
   public TextFrame(String content) {
      super("TextFrame");
 
      JTextArea ta = new JTextArea();
      ta.setText(content);
      getContentPane().add(ta);
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            dispose();
         }
      });
      
      setSize(200, 100);
   }
}

Have dotted lines in a JTree

That appends on your Look & Feel – you can use three lineStyle’s:

// show hierarchy-lines
JTree#putClientProperty(“JTree.lineStyle”, “Angled”);

// show horizontal lines
JTree#putClientProperty( “JTree.lineStyle”, “Horitontal” );

// do not show any lines
JTree#putClientProperty(“JTree.lineStyle”, “None”);

Serializing a Java object into XML

Representing a complex object graph into an XML is not easy and binary serialization happens much faster. If you still need to represent your Java objects in XML, you can use the package JSX, downloadable from http://www.freshmeat.net/projects/jsx.

Put JSX0.9.5.0.jar in your classpath and try out the following example.

Customer.java:

import java.util.*;
 
class Customer {
   private String name;
   private int age;
   private Vector addresses;
   private Hashtable phones;
 
   public Customer(String name, int age, Vector addresses, Hashtable phones) {
      this.name = name;
      this.age = age;
      this.addresses = (Vector) addresses.clone();   
      this.phones = (Hashtable) phones.clone();
   }
 
   public String toString() {
      StringBuffer s = new StringBuffer();
      s.append("Name = " + name + "n");
      s.append("Age  = " + age  + "n");
      s.append("addresses = " + addresses + "n");
      s.append("phones    = " + phones);
      return s.toString();
   }
}

MainOut.java:

import java.util.*;
import java.io.*;
import JSX.*;
 
public class MainOut
{
   public static void main(String []args) {
      String name = "Jefke McCann";
      int age = 24;
      Vector addresses = new Vector();
      addresses.add("43, 5th Ave. 59715, Bozeman, MT");
      addresses.add("Handelskaai 3, 1000 Brussels, Belgium");
      Hashtable phones = new Hashtable();
      phones.put("001 (406) 585-2345", "ISDN");
      phones.put("0032 (2) 219.28.39", "FAX");
 
      Customer cust = new Customer(name, age, addresses, phones);
 
      try {
         ObjOut out = new ObjOut(true, new FileWriter("test.xml"));
         out.writeObject(cust);
      }
      catch(IOException e) {
         e.printStackTrace();
      }
   }
}

MainIn.java:

import java.util.*;
import java.io.*;
import JSX.*;
 
public class MainIn
{
   public static void main(String []args) {
      try {
         ObjIn in = new ObjIn(new FileReader("test.xml"));
         Customer cust = (Customer) in.readObject();
         System.out.println(cust);
      }
      catch(ClassNotFoundException e) {
         e.printStackTrace();
      }
      catch(IOException e) {
         e.printStackTrace();
      }
   }
}

If you execute java MainOut, the following XML will be written to disk.
test.xml:

<? jsx version="1"?>
<Customer
 name="Jefke McCann"
 age="24">
  <java.util.Vector obj-name="addresses">
    <java.lang.String valueOf="43, 5th Ave. 59715, Bozeman, MT"/>
    <java.lang.String valueOf="Handelskaai 3, 1000 Brussels, Belgium"/>
  </java.util.Vector>
  <java.util.Hashtable obj-name="phones">
    <java.lang.String valueOf="001 (406) 585-2345"/>
    <java.lang.String valueOf="ISDN"/>
 
    <java.lang.String valueOf="0032 (2) 219.28.39"/>
    <java.lang.String valueOf="FAX"/>
  </java.util.Hashtable>
</Customer>

MainIn will read in test.xml and create a Customer object from it.

Using comments in a JSP page

There are several ways to use comments in JSP:

1) JSP comments (not compiled nor executed)
   <%-- Counter is <%=counter%> --%>
 
or
2) Java comments (not compiled nor executed)
   <%
      // Calculate the counter
      /*
         Counter is <%=counter%>
      */
 
or
3)  HTML comments (compiled and executed, but not displayed
    by browser!)
 
   <!--
      Counter is <%=counter%> 
   -->

Using transactions with JDBC

SQL statements can be grouped together in a single statement to ensure the ACID (Atomicity, Consistency, Isolation, Durability) characteristics. By default, each SQL statement is auto-commited. In other words, it is treated as a transaction by itself and will be committed right away. This may not be desirable in cases where all the SQL statements need to be executed or none of them, if one fails. The way this can be done with JDBC is by turning off auto-commit mode before the group of statements and turn auto-commit back on after.

   conn.setAutoCommit(false);
   Statement stmt = conn.createStatement();
   stmt.executeUpdate("UPDATE accounts SET balance = 100 where accountid = 1");
   stmt.executeUpdate("UPDATE products SET itemsleft = 0 where prodid = 4");
   stmt.rollback();
   stmt.executeUpdate("UPDATE products SET itemsleft = 0 where prodid = 3");
   stmt.commit();
   conn.setAutoCommit(true);  

In this example, the first two updates will not affect the database table because of rollback. The last update is explicitely committed.

Typically, you would use commit and rollback in conjunction with Exception handling. eg.

   try {
      conn.setAutoCommit(false);
 
      updateInventory();
      updateBalance();
 
      conn.commit();
      conn.setAutoCommit(true);
   }
   catch(ShoppingException e) {
      e.printStackTrace();
      conn.rollback();
      conn.setAutoCommit(true);
   }

Using the Observable/Observer pattern

An Observable object is an object that contains data in which one or more Observer objects are interested in.
The Observers want to know when the state of the Observable object changes.

The implementation is simple. In the package java.util, you find the class Observable. The object that you want to be able to monitor should extend from it. For example, suppose we have a Mailbox object (that can only contain one email at a time) and we know that other objects may be interested in state changes:

import java.util.*;
 
class Mailbox extends Observable
{
   private String message;
 
   public void newMail(String message) {
      this.message = message;
      setChanged();
      notifyObservers();
   }   
 
   public String getMessage() {
      return message;
   }
}

Observable is a class that encapsulates the common details about the pattern, like being able to “register” an object that is interested. Whenever the method newMail is invoked, the state changes and the object is marked as being changed. A call to notifyObservers means to notify the Observer objects that have previously registered themselves through addObserver, a method inherited from Observable.

An Observer is an interface that all your observers need to implement to ensure the availability and accessibility of the method update(Observable obs, Object o).

In our example, suppose we have two observers to our Mailbox: a MailGui and MailLog.

class MailGui implements Observer
{
   public void update(Observable obs, Object o) {
      Mailbox mb = (Mailbox) obs;
      System.out.println("MailGui has received notification of new " +
                         "message: " + mb.getMessage());
   }
}
 
class MailLog implements Observer
{
   public void update(Observable obs, Object o) {
      Mailbox mb = (Mailbox) obs;
      System.out.println("MailLog has received notification of new " +
                         "message: " + mb.getMessage());
   }
}

The notifyObservers method in Observable will loop through a list of objects of type Observer and calls the method update sequentially on each one of them. It provides itself as the first parameter (so the Observer knows which Observable we’re talking about – it may have registered itself with different ones) and an optional object as a second parameter. This optional object comes from when you call the notifyObservers(Object o) variant.

Here’s the Main class:

import java.util.*;
 
public class Main {   
   public static void main(String[] args) {
      Mailbox mb = new Mailbox();
 
      MailGui gui = new MailGui();
      MailLog log = new MailLog();
 
      mb.addObserver(gui);
      mb.addObserver(log);
 
      mb.newMail("... email#1 ...");
      mb.newMail("... email#2 ...");
   }
}

and the output:

MailLog has received notification of new message: ... email#1 ...
MailGui has received notification of new message: ... email#1 ...
MailLog has received notification of new message: ... email#2 ...
MailGui has received notification of new message: ... email#2 ...

Decoupling

It is important to realize that when an Observable wants to notify Observers of a change through calling update on them, it is tighly coupled to it. If update decides to go do some calculations, surf the net or play SkateOrDie, all actions that take forever to complete, the Observable cannot go on notifying other interested Observers cause it happens all in the same thread.

To perform some decoupling, you should place some threaded FIFO datastructure in between the Observable and Observer. The FIFO object would then both be an Observable and Observer.
Before:

                 (update)
  [ Observer1 ] <--------- [            ]
  [ Observer2 ] <--------- [ Observable ]
  [ Observer3 ] <--------- [            ] 

After:

                 (update)
  [ Observer1 ] <--------- [                 ]  (update)
  [ Observer2 ] <--------- [ FIFO Observable ] <--------- [ Observable ]
  [ Observer3 ] <--------- [                 ]   

Turn off copy/paste in a JTextArea

Main.java:

import javax.swing.event.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame
{
   public Main() {
      JTextArea ta = new JTextArea();
      getContentPane().add(new JScrollPane(ta));
 
      ta.getInputMap().put(KeyStroke.getKeyStroke("control C"), "none");
      ta.getInputMap().put(KeyStroke.getKeyStroke("control V"), "none");
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(400, 400);
      main.setVisible(true);
   }
}