Sorting the rows in a JTable by clicking on a column header

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



SortableTableExample.java:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.text.*;
import javax.swing.*;
import javax.swing.table.*;
  
/**
 * @version 1.0 02/25/99
 */
public class SortableTableExample extends JPanel {
 
  public SortableTableExample(){
    setLayout(new BorderLayout());
    String[] headerStr = {"Name","Date","Size","Dir"};
    int[] columnWidth = {100,150,100,50};
    
    SortableTableModel dm = new SortableTableModel() {
      public Class getColumnClass(int col) {
        switch (col) {
          case  0: return String.class;
          case  1: return Date.class;
          case  2: return Integer.class;
          case  3: return Boolean.class;
          default: return Object.class;
        }
      }
      public boolean isCellEditable(int row, int col) {
        switch (col) {
          case  1: return false;
          default: return true;
        }
      }  
      public void setValueAt(Object obj, int row, int col) {
        switch (col) {
          case  2: super.setValueAt(new Integer(obj.toString()), row, col); return;
          default: super.setValueAt(obj, row, col); return;
        }
      }
    };
    dm.setDataVector(new Object[][]{
      {"b"   ,getDate("98/12/02"),new Integer(14),new Boolean(false)},
      {"a"   ,getDate("99/01/01"),new Integer(67),new Boolean(false)},
      {"d"   ,getDate("99/02/11"),new Integer(2) ,new Boolean(false)},
      {"c"   ,getDate("99/02/27"),new Integer(7) ,new Boolean(false)},
      {"foo" ,new Date()         ,new Integer(5) ,new Boolean(true)},
      {"bar" ,new Date()         ,new Integer(10),new Boolean(true)}},
      headerStr);
     
    JTable table = new JTable(dm);
    //table.setShowGrid(false);
    table.setShowVerticalLines(true);
    table.setShowHorizontalLines(false);
    SortButtonRenderer renderer = new SortButtonRenderer();
    TableColumnModel model = table.getColumnModel();
    int n = headerStr.length;
    for (int i=0;i<n;i++) {
      model.getColumn(i).setHeaderRenderer(renderer);
      model.getColumn(i).setPreferredWidth(columnWidth[i]);
    }
    
    JTableHeader header = table.getTableHeader();
    header.addMouseListener(new HeaderListener(header,renderer));
    JScrollPane pane = new JScrollPane(table);
    add(pane, BorderLayout.CENTER);
  }
 
  public static void main(String[] args) {
    JFrame f= new JFrame("SortableTable Example");
    f.getContentPane().add(new SortableTableExample(), BorderLayout.CENTER);
    f.setSize(400, 160);
    f.setVisible(true);
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {System.exit(0);}
    });
  }
  
  private static DateFormat dateFormat = 
    DateFormat.getDateInstance(DateFormat.SHORT, Locale.JAPAN);
 
  private static Date getDate(String dateString) {
    Date date = null;
    try {
      date = dateFormat.parse(dateString);
    } catch(ParseException ex) {
      date = new Date();
    }
    return date;
  }
 
  class HeaderListener extends MouseAdapter {
    JTableHeader   header;
    SortButtonRenderer renderer;
  
    HeaderListener(JTableHeader header,SortButtonRenderer renderer) {
      this.header   = header;
      this.renderer = renderer;
    }
  
    public void mousePressed(MouseEvent e) {
      int col = header.columnAtPoint(e.getPoint());
      int sortCol = header.getTable().convertColumnIndexToModel(col);
      renderer.setPressedColumn(col);
      renderer.setSelectedColumn(col);
      header.repaint();
      
      if (header.getTable().isEditing()) {
        header.getTable().getCellEditor().stopCellEditing();
      }
      
      boolean isAscent;
      if (SortButtonRenderer.DOWN == renderer.getState(col)) {
        isAscent = true;
      } else {
        isAscent = false;
      }
      ((SortableTableModel)header.getTable().getModel())
        .sortByColumn(sortCol, isAscent);    
    }
  
    public void mouseReleased(MouseEvent e) {
      int col = header.columnAtPoint(e.getPoint());
      renderer.setPressedColumn(-1);                // clear
      header.repaint();
    }
  }
}

SortableTableModel.java:

import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
 
/**
 * @version 1.0 02/25/99
 */
public class SortableTableModel extends DefaultTableModel {
  int[] indexes;
  TableSorter sorter;
 
  public SortableTableModel() {   
  }
    
  public Object getValueAt(int row, int col) {
    int rowIndex = row;
    if (indexes != null) {
      rowIndex = indexes[row];
    }
    return super.getValueAt(rowIndex, col);
  }
    
  public void setValueAt(Object value, int row, int col) {    
    int rowIndex = row;
    if (indexes != null) {
      rowIndex = indexes[row];
    }
    super.setValueAt(value, rowIndex, col);
  }
  
 
  public void sortByColumn(int column, boolean isAscent) {
    if (sorter == null) {
      sorter = new TableSorter(this);
    }   
    sorter.sort(column, isAscent);   
    fireTableDataChanged();
  }
  
  public int[] getIndexes() {
    int n = getRowCount();
    if (indexes != null) {
      if (indexes.length == n) {
        return indexes;
      }
    }
    indexes = new int[n];
    for (int i=0; i<n; i++) {
      indexes[i] = i;
    }
    return indexes;
  }
}

TableSorter.java:

import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
 
/**
 * @version 1.0 02/25/99
 */
public class TableSorter {
  SortableTableModel model;
 
  public TableSorter(SortableTableModel model) {
    this.model = model;
  }
  
  
  //n2 selection
  public void sort(int column, boolean isAscent) {   
    int n = model.getRowCount();
    int[] indexes = model.getIndexes();   
    
    for (int i=0; i<n-1; i++) {
      int k = i;
      for (int j=i+1; j<n; j++) {
        if (isAscent) {
          if (compare(column, j, k) < 0) {
            k = j;
          }
        } else {
          if (compare(column, j, k) > 0) {
            k = j;
          }
        }
      }
      int tmp = indexes[i];
      indexes[i] = indexes[k];
      indexes[k] = tmp;
    }
  }
    
 
  // comparaters
  
  public int compare(int column, int row1, int row2) {
    Object o1 = model.getValueAt(row1, column);
    Object o2 = model.getValueAt(row2, column); 
    if (o1 == null && o2 == null) {
      return  0; 
    } else if (o1 == null) {
      return -1; 
    } else if (o2 == null) { 
      return  1; 
    } else {   
      Class type = model.getColumnClass(column);
      if (type.getSuperclass() == Number.class) {
        return compare((Number)o1, (Number)o2);
      } else if (type == String.class) {
        return ((String)o1).compareTo((String)o2);
      } else if (type == Date.class) {
        return compare((Date)o1, (Date)o2);
      } else if (type == Boolean.class) {
        return compare((Boolean)o1, (Boolean)o2);
      } else {
        return ((String)o1).compareTo((String)o2);
      }      
    }
  }
  
  public int compare(Number o1, Number o2) {
    double n1 = o1.doubleValue();
    double n2 = o2.doubleValue();
    if (n1 < n2) {
      return -1;
    } else if (n1 > n2) {
      return 1;
    } else {
      return 0;
    }
  }
  
  public int compare(Date o1, Date o2) {
    long n1 = o1.getTime();
    long n2 = o2.getTime();
    if (n1 < n2) {
      return -1;
    } else if (n1 > n2) {
      return 1;
    } else {
      return 0;
    }
  }
  
  public int compare(Boolean o1, Boolean o2) {
    boolean b1 = o1.booleanValue();
    boolean b2 = o2.booleanValue();
    if (b1 == b2) {
      return 0;
    } else if (b1) {
      return 1;
    } else {
      return -1;
    }
  }
}

SortButtonRenderer.java:

import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
 
/**
 * @version 1.0 02/25/99
 */
public class SortButtonRenderer extends JButton implements TableCellRenderer {
  public static final int NONE = 0;
  public static final int DOWN = 1;
  public static final int UP   = 2;
  
  int pushedColumn;
  Hashtable state;
  JButton downButton,upButton;
  
  public SortButtonRenderer() {
    pushedColumn   = -1;
    state = new Hashtable();
    
    setMargin(new Insets(0,0,0,0));
    setHorizontalTextPosition(LEFT);
    setIcon(new BlankIcon());
    
    // perplexed  
    // ArrowIcon(SwingConstants.SOUTH, true)   
    // BevelArrowIcon (int direction, boolean isRaisedView, boolean isPressedView)
    
    downButton = new JButton();
    downButton.setMargin(new Insets(0,0,0,0));
    downButton.setHorizontalTextPosition(LEFT);
    downButton.setIcon(new BevelArrowIcon(BevelArrowIcon.DOWN, false, false));
    downButton.setPressedIcon(new BevelArrowIcon(BevelArrowIcon.DOWN, false, true));
    
    upButton = new JButton();
    upButton.setMargin(new Insets(0,0,0,0));
    upButton.setHorizontalTextPosition(LEFT);
    upButton.setIcon(new BevelArrowIcon(BevelArrowIcon.UP, false, false));
    upButton.setPressedIcon(new BevelArrowIcon(BevelArrowIcon.UP, false, true));
    
  }
  
  public Component getTableCellRendererComponent(JTable table, Object value,
                   boolean isSelected, boolean hasFocus, int row, int column) {
    JButton button = this;
    Object obj = state.get(new Integer(column));
    if (obj != null) {
      if (((Integer)obj).intValue() == DOWN) {
        button = downButton;
      } else {
        button = upButton;
      }
    }
    button.setText((value ==null) ? "" : value.toString());
    boolean isPressed = (column == pushedColumn);
    button.getModel().setPressed(isPressed);
    button.getModel().setArmed(isPressed);
    return button;
  }
  
  public void setPressedColumn(int col) {
    pushedColumn = col;
  }
  
  public void setSelectedColumn(int col) {
    if (col < 0) return;
    Integer value = null;
    Object obj = state.get(new Integer(col));
    if (obj == null) {
      value = new Integer(DOWN);
    } else {
      if (((Integer)obj).intValue() == DOWN) {
        value = new Integer(UP);
      } else {
        value = new Integer(DOWN);
      }
    }
    state.clear();
    state.put(new Integer(col), value);
  } 
  
  public int getState(int col) {
    int retValue;
    Object obj = state.get(new Integer(col));
    if (obj == null) {
      retValue = NONE;
    } else {
      if (((Integer)obj).intValue() == DOWN) {
        retValue = DOWN;
      } else {
        retValue = UP;
      }
    }
    return retValue;
  } 
}

BevelArrowIcon.java:

import java.awt.*;
import javax.swing.*;
 
/**
 * @version 1.0 02/26/99
 */
public class BevelArrowIcon implements Icon {
  public static final int UP    = 0;         // direction
  public static final int DOWN  = 1;
   
  private static final int DEFAULT_SIZE = 11;
 
  private Color edge1;
  private Color edge2;
  private Color fill;
  private int size;
  private int direction;
 
  public BevelArrowIcon(int direction, boolean isRaisedView, boolean isPressedView) {
    if (isRaisedView) {
      if (isPressedView) {
        init( UIManager.getColor("controlLtHighlight"),
              UIManager.getColor("controlDkShadow"),
              UIManager.getColor("controlShadow"),
              DEFAULT_SIZE, direction);
      } else {
        init( UIManager.getColor("controlHighlight"),
              UIManager.getColor("controlShadow"),
              UIManager.getColor("control"),
              DEFAULT_SIZE, direction);
      }
    } else {
      if (isPressedView) {
        init( UIManager.getColor("controlDkShadow"),
              UIManager.getColor("controlLtHighlight"),
              UIManager.getColor("controlShadow"),
              DEFAULT_SIZE, direction);
      } else {
        init( UIManager.getColor("controlShadow"),
              UIManager.getColor("controlHighlight"),
              UIManager.getColor("control"),
              DEFAULT_SIZE, direction);
      }
    }
  }
 
  public BevelArrowIcon(Color edge1, Color edge2, Color fill,
                   int size, int direction) {
    init(edge1, edge2, fill, size, direction);
  }
 
  public void paintIcon(Component c, Graphics g, int x, int y) {
    switch (direction) {
      case DOWN: drawDownArrow(g, x, y); break;
      case   UP: drawUpArrow(g, x, y);   break;
    }
  } 
 
  public int getIconWidth() {
    return size;
  }
 
  public int getIconHeight() {
    return size;
  }
 
 
  private void init(Color edge1, Color edge2, Color fill,
                   int size, int direction) {
    this.edge1 = edge1;
    this.edge2 = edge2;
    this.fill = fill;
    this.size = size;
    this.direction = direction;
  }
 
  private void drawDownArrow(Graphics g, int xo, int yo) {
    g.setColor(edge1);
    g.drawLine(xo, yo,   xo+size-1, yo);
    g.drawLine(xo, yo+1, xo+size-3, yo+1);
    g.setColor(edge2);
    g.drawLine(xo+size-2, yo+1, xo+size-1, yo+1);
    int x = xo+1;
    int y = yo+2;
    int dx = size-6;      
    while (y+1 < yo+size) {
      g.setColor(edge1);
      g.drawLine(x, y,   x+1, y);
      g.drawLine(x, y+1, x+1, y+1);
      if (0 < dx) {
        g.setColor(fill);
        g.drawLine(x+2, y,   x+1+dx, y);
        g.drawLine(x+2, y+1, x+1+dx, y+1);
      }
      g.setColor(edge2);
      g.drawLine(x+dx+2, y,   x+dx+3, y);
      g.drawLine(x+dx+2, y+1, x+dx+3, y+1);
      x += 1;
      y += 2;
      dx -= 2;     
    }
    g.setColor(edge1);
    g.drawLine(xo+(size/2), yo+size-1, xo+(size/2), yo+size-1); 
  }
 
  private void drawUpArrow(Graphics g, int xo, int yo) {
    g.setColor(edge1);
    int x = xo+(size/2);
    g.drawLine(x, yo, x, yo); 
    x--;
    int y = yo+1;
    int dx = 0;
    while (y+3 < yo+size) {
      g.setColor(edge1);
      g.drawLine(x, y,   x+1, y);
      g.drawLine(x, y+1, x+1, y+1);
      if (0 < dx) {
        g.setColor(fill);
        g.drawLine(x+2, y,   x+1+dx, y);
        g.drawLine(x+2, y+1, x+1+dx, y+1);
      }
      g.setColor(edge2);
      g.drawLine(x+dx+2, y,   x+dx+3, y);
      g.drawLine(x+dx+2, y+1, x+dx+3, y+1);
      x -= 1;
      y += 2;
      dx += 2;     
    }
    g.setColor(edge1);
    g.drawLine(xo, yo+size-3,   xo+1, yo+size-3);
    g.setColor(edge2);
    g.drawLine(xo+2, yo+size-2, xo+size-1, yo+size-2);
    g.drawLine(xo, yo+size-1, xo+size, yo+size-1);
  }
}

BlankIcon.java:

import java.awt. *;
import javax.swing. *;
 
/**
 * @version 1.0 02/26/99
 */
public class BlankIcon implements Icon {
  private Color fillColor;
  private int size;
 
  public BlankIcon() {
    this(null, 11);
  }
 
  public BlankIcon(Color color, int size) {
    //UIManager.getColor("control")
    //UIManager.getColor("controlShadow")
    fillColor = color;
 
    this.size = size;    
  }
 
  public void paintIcon(Component c, Graphics g, int x, int y) {
    if (fillColor != null) {
      g.setColor(fillColor);
      g.drawRect(x, y, size-1, size-1);
    }
  }
 
  public int getIconWidth() {
    return size;
  }
 
  public int getIconHeight() {
    return size;
  }
}

Creating a JTable with pushable headers

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



PushableHeaderExample.java:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
 
/**
 * @version 1.0 02/25/99
 */
public class PushableHeaderExample extends JPanel {

  public PushableHeaderExample(){
    setLayout(new BorderLayout());
    String[] headerStr = {"Push","me","here"};
    
    DefaultTableModel dm = new DefaultTableModel(headerStr, 4);
    JTable table = new JTable(dm);
    ButtonHeaderRenderer renderer = new ButtonHeaderRenderer();
    TableColumnModel model = table.getColumnModel();
    int n = headerStr.length;
    for (int i=0;i<n;i++) {
      model.getColumn(i).setHeaderRenderer(renderer);
    }
    
    JTableHeader header = table.getTableHeader();
    header.addMouseListener(new HeaderListener(header,renderer));
    JScrollPane pane = new JScrollPane(table);
    add(pane, BorderLayout.CENTER);
  }

  public static void main(String[] args) {
    JFrame f= new JFrame("PushableHeaderTable Example");
    f.getContentPane().add(new PushableHeaderExample(), BorderLayout.CENTER);
    f.setSize(400, 100);
    f.setVisible(true);
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {System.exit(0);}
    });
  }

  class HeaderListener extends MouseAdapter {
    JTableHeader   header;
    ButtonHeaderRenderer renderer;
  
    HeaderListener(JTableHeader header,ButtonHeaderRenderer renderer) {
      this.header   = header;
      this.renderer = renderer;
    }
  
    public void mousePressed(MouseEvent e) {
      int col = header.columnAtPoint(e.getPoint());
      renderer.setPressedColumn(col);
      header.repaint();
      
      System.out.println("Ouch! " + col);
    }
  
    public void mouseReleased(MouseEvent e) {
      int col = header.columnAtPoint(e.getPoint());
      renderer.setPressedColumn(-1);                // clear
      header.repaint();
    }
  }
    
  class ButtonHeaderRenderer extends JButton implements TableCellRenderer {
    int pushedColumn;
  
    public ButtonHeaderRenderer() {
      pushedColumn   = -1;
      setMargin(new Insets(0,0,0,0));
    }
  
    public Component getTableCellRendererComponent(JTable table, Object value,
                   boolean isSelected, boolean hasFocus, int row, int column) {
      setText((value ==null) ? "" : value.toString());
      boolean isPressed = (column == pushedColumn);
      getModel().setPressed(isPressed);
      getModel().setArmed(isPressed);
      return this;
    }
  
    public void setPressedColumn(int col) {
      pushedColumn = col;
    }
  }
}

Programmatically selecting a JTable cell

Use setRowSelectionInterval and setColumnSelectionInterval.

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");
 
      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);
      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);
         }
      });
 
      // programmatically select rows
      table.setRowSelectionInterval(1, 2);
      table.setColumnSelectionInterval(1, 2);
 
      pack();
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.show();
   }
}

Creating a JTree without node icons

Set the OpenIcon, ClosedIcon and LeafIcon to null in a DefaultTreeCellRenderer.

Main.java:

import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import java.awt.*;
import java.net.*;
import java.awt.event.*;
 
public class Main extends JFrame
{
   public Main() {
      DefaultMutableTreeNode root = createNodes();
      JTree tree = new JTree(root);
 
      DefaultTreeCellRenderer cr = new DefaultTreeCellRenderer();
      cr.setOpenIcon(null);
      cr.setClosedIcon(null);
      cr.setLeafIcon(null);
  
      tree.setCellRenderer(cr);
 
      getContentPane().add(new JScrollPane(tree));
 
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(0);
         }
      });
   }
 
   public static DefaultMutableTreeNode createNodes() {
      DefaultMutableTreeNode root = new DefaultMutableTreeNode("Java");
      
      DefaultMutableTreeNode j2se = new DefaultMutableTreeNode("J2SE");
      DefaultMutableTreeNode j2ee = new DefaultMutableTreeNode("J2EE");
      DefaultMutableTreeNode j2me = new DefaultMutableTreeNode("J2ME");
 
      j2se.add(new DefaultMutableTreeNode("http://java.sun.com/j2se/"));
      j2ee.add(new DefaultMutableTreeNode("http://java.sun.com/j2ee/"));
      j2me.add(new DefaultMutableTreeNode("http://java.sun.com/j2me/"));
 
      root.add(j2se);
      root.add(j2ee);
      root.add(j2me);
 
      return root;
   }
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(400, 400);
      main.setVisible(true);
   }
}

Creating an HTML page from an XML template using XMLC

Enhydra’s XMLC allows you to have random access to your XML document by compiling the XML document into a Java class that contains a DOM (document object model) representation. For every XML template that you provide, a Java class equivalent is generated. Using this class, you can not only access your tags with standard DOM methods, but XMLC also provides convenience methods like getters and setters to dynamically alter the elements.

So, with XMLC, you don’t have to include Java code inside your HTML (JSP) nor vice versa (servlets).

For every tag that contains a ID attribute, a getElementXXX() is generated. For example, if you have the following tag defined in your template:

   <font id="topcolor" color="">test color</font>

The method getElementTopcolor() is generated which returns an object of type org.w3c.dom.html.HTMLFontElement.

It also creates setTextXXX methods for your ID elements, so you can easily change the text between tags.

The following is a simple example that starts from a template HTML, dynamically modifies its element and prints out the resulting HTML.

First follow the instructions on this page to download and install XMLC.

I created the file C:testingxmlTestTemplate.html:

<html>
<head>
   <title id="title">test title</title>
</head>
<body>
   <h1 id="topheader">test header</h1>
   <font id="topcolor" color="">test color</font>
   <br>
   <br>
   <span id="maintext">test span</span>
</body>
</html>

To generate the .class file for this HTML:

Enhydra$ //c/javalibs/xmlc2.0.1/bin/xmlc -keep TestTemplate.html

(Note 1: I installed xmlc in c:javalibs; Note 2: -keep specifies that the .java file should not be deleted after generation. This way, you can inspect the .java file and find out what methods are available!)

The “client”: Main.java:

import org.w3c.dom.html.*;
import org.w3c.dom.*;
 
public class Main
{
   public static void main(String []args) {
      TestTemplate tt = new TestTemplate();
 
      // get title and change it 
      HTMLTitleElement title = tt.getElementTitle();
      title.setText("Generated XML");
 
      // set element with id "topheader"
      tt.setTextTopheader("Insightful quote");
  
      // set element with it "topcolor"
      tt.setTextTopcolor("[I cannot be held responsible]");
      HTMLFontElement elem = tt.getElementTopcolor();
      elem.setColor("#ff0000");
 
      // set element with id "maintext"
      tt.setTextMaintext("The shorter you live, the longer you're dead");
 
      // generate new HTML
      System.out.print(tt.toDocument());
   }
}

Compile and run. Result:

<HTML>
<HEAD>
   <TITLE id="title">Generated XML</TITLE>
</HEAD>
<BODY>
   <H1 id="topheader">Insightful quote</H1>
   <FONT color="#ff0000" id="topcolor">[I cannot be held responsible]</FONT> 
   <BR> 
   <BR> 
   <SPAN>The shorter you live, the longer you're dead</SPAN>
</BODY>
</HTML>

For more information, check the tutorial at http://www.pisoftware.com/publications/xmlc-tutorial/intro.html.

Saving binary data in XML

You can encode the binary data in base-64 format. The following example Image2XML creates an XML and stores an image, encoded in base-64 format, in one of the tags. XML2Image does the opposite: given the XML, it will convert the base-64 back to binary and save it in a file.

The XML structure it creates looks like this:

<xmlimage>
   <format></format>
   <width></width>
   <height></height>
   <data></data>
</xmlimage>

Image2XML.java:

import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
  
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
 
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.dom.DOMSource;
 
import javax.swing.*;
import java.awt.*;
import java.io.*;
   
public class Image2XML
{
   public static void main(String []args) {
      if (args.length != 1) {
         System.out.println("Usage: java Image2XML <image>");
         System.exit(1);
      }
 
      // get format, width and height 
      String format = "";
      int findex = args[0].indexOf(".");
      if (findex > -1) {
         format = args[0].substring(findex+1);
      }
   
      ImageIcon imageIcon = new ImageIcon(args[0]);
      Image img = imageIcon.getImage();
 
      double width  = img.getWidth(null);
      double height = img.getHeight(null);       
 
      // get binary data and convert to base64 
      byte[] buffer = null;
      try {
         buffer = readBinaryFile(args[0]);
      }
      catch(Exception e) {
         e.printStackTrace();
         System.exit(1);
      }
 
      String data = new sun.misc.BASE64Encoder().encode(buffer);
  
      // create XML document
      Document doc;
      try {
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
         DocumentBuilder db = dbf.newDocumentBuilder();
         doc = db.newDocument();
  
         Element xmlimageEl = doc.createElement("xmlimage");
         Element formatEl   = doc.createElement("format");
         Element widthEl    = doc.createElement("width");
         Element heightEl   = doc.createElement("height");
         Element dataEl     = doc.createElement("data");
         xmlimageEl.appendChild(formatEl);
         xmlimageEl.appendChild(widthEl);
         xmlimageEl.appendChild(heightEl);
         xmlimageEl.appendChild(dataEl);
 
         formatEl.appendChild(doc.createTextNode(""+format));
         widthEl.appendChild(doc.createTextNode(""+width));
         heightEl.appendChild(doc.createTextNode(""+height));
         dataEl.appendChild(doc.createTextNode(data));
 
         doc.appendChild(xmlimageEl);
  
         TransformerFactory tf = TransformerFactory.newInstance();
         Transformer transformer = tf.newTransformer();
         PrintWriter pw = new PrintWriter(new FileWriter(args[0]+".xml"));
         transformer.transform(new DOMSource(doc), new StreamResult(pw));
      }
      catch(Exception e) {
         e.printStackTrace();
      }
 
      System.exit(0);  // awt thread has been started
   }
 
   public static byte[] readBinaryFile(String filename)
                            throws IOException, FileNotFoundException {
      File f = new File(filename);         
      BufferedInputStream bis = new BufferedInputStream(
                                      new FileInputStream(f));         
      byte[] buffer = new byte[(int) f.length()];         
      bis.read(buffer, 0, buffer.length);         
      bis.close();
 
      return buffer;
   }
}

XML2Image.java:

import org.w3c.dom.*;
import org.w3c.dom.traversal.*;
  
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
  
import javax.swing.*;
import java.awt.*;
import java.io.*;
   
public class XML2Image
{
   public static void main(String []args) {
      if (args.length != 1) {
         System.out.println("Usage: java XML2Image <xml>");
         System.exit(1);
      }
 
      Document doc;
      try {
         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
         DocumentBuilder db = dbf.newDocumentBuilder();
         doc = db.parse(new File(args[0]));
 
         Node root = doc.getFirstChild();  
         NodeList nl = root.getChildNodes();
          
         String format="", width="", height="", data="";
         for (int i=0; i<nl.getLength(); i++) {
            Node item = nl.item(i);
            Node text = item.getFirstChild();
            if (item.getNodeName().equals("format"))      format = text.getNodeValue();
            else if (item.getNodeName().equals("width"))  width  = text.getNodeValue();
            else if (item.getNodeName().equals("height")) height = text.getNodeValue();
            else if (item.getNodeName().equals("data"))   data   = text.getNodeValue();
         }
 
         System.out.println("Image width = " + width + ", height = " + height);
 
         byte[] b = new sun.misc.BASE64Decoder().decodeBuffer(data);
 
         BufferedOutputStream bos = new BufferedOutputStream(
                                           new FileOutputStream(args[0]+"."+format));
         bos.write(b);
         bos.close();
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   } 
}

Using a PreparedStatement

If you need to execute a certain SQL statement a number of times, you can have it optimized by the underlying DBMS by using a PreparedStatement. A PreparedStatement is called a precompiled SQL statement, but can still be parametrized. In other words, it can accept certain input parameters.

So:

   Statement stmt = connection.createStatement();
   stmt.executeUpdate("INSERT INTO products (description, price) " +
                      "VALUES ('Chateau Meyney, St. Estephe', 18.75, 2)");

will have the same result as:

   PreparedStatement insertStmt = connection.prepareStatement("
                                       "INSERT INTO products (description, price) " +
                                       "VALUES (?, ?, ?)";
   insertStmt.setString(1, "Chateau Meyney, St. Estephe");
   insertStmt.setFloat(2, 18.75f);
   insertStmt.setInt(3, 2);
   insertStmt.executeUpdate();

The latter one does take somewhat more time to set up (precompilation), but if you need to insert a lot of rows, it is beneficial cause you can reuse insertStmt and change its parameters.

executeUpdate returns the number of rows that were affected.

You can do the same thing for SELECT statements with executeQuery. It returns a ResultSet containing the rows that match your query.

If you need to clear the parameters that were set, invoke the method clearParameters.

Main.java:

import java.util.*;
import java.sql.*;
  
public class Main {
   public static void main(String []args) {
      try {
         Database db = new Database("org.gjt.mm.mysql.Driver",
                                    "jdbc:mysql://192.168.0.1/esus",
                                    "joris",
                                    "mypass");
         Connection con = db.getConnection();
         ProductDAO prodDAO = new ProductDAO(con);
 
         printCollection(prodDAO.getAllRows()); 
 
         prodDAO.insert(new Product("Chateau Meyney, St. Estephe",       18.75f, 2));
         prodDAO.insert(new Product("Chateau Montrose, St. Estephe",     54.25f, 2));
         prodDAO.insert(new Product("Chateau Gloria, St. Julien",        22.99f, 2));
         prodDAO.insert(new Product("Chateau Beychevelle, St. Julien",   61.63f, 2));
         prodDAO.insert(new Product("Chateau La Tour de Mons, Margeaux", 57.03f, 2));
         prodDAO.insert(new Product("Chateau Brane-Cantenac, Margeaux",  49.92f, 2));
  
         printCollection(prodDAO.getAllRows()); 
         db.close();
      }
      catch(DatabaseException e) {
         e.printStackTrace();
      }
   }
 
   public static void printCollection(Collection c) {
      Iterator iter = c.iterator();
      while (iter.hasNext()) {
         System.out.println(iter.next());
      }
   }
}
 
class Product
{
   private String description;
   private float price;
   private int itemsleft;
 
   public Product() { }
 
   public Product(String description, float price, int itemsleft) {
      setDescription(description);
      setPrice(price);
      setItemsleft(itemsleft);
   }
  
   public void setDescription(String description) {
      this.description = description;
   }
 
   public void setPrice(float price) {
      this.price = price;
   }

   public void setItemsleft(int itemsleft) {
      this.itemsleft = itemsleft;
   }
 
   public String getDescription() {
      return description;
   }
 
   public float getPrice() {
      return price;
   }
  
   public int getItemsleft() {
      return itemsleft;
   }
 
   public String toString() {
      return "Product [description=" + getDescription() + ", price=" + getPrice() + 
             ", itemsleft=" + getItemsleft() + "]";
   }
}
 
class Database
{
   Connection connection = null;
  
   public Database(String driver, String url, String user, String pass) 
                      throws DatabaseException 
   {
      try {
         Class.forName(driver).newInstance();
 
         connection = DriverManager.getConnection(url, user, pass);
      }
      catch(Exception e) {
         throw new DatabaseException(e.getMessage());
      }
   }
 
   public Connection getConnection() {
      return connection;
   }
 
   public void close() throws DatabaseException {
      try {
         connection.close();
      }
      catch(Exception e) {
         throw new DatabaseException(e.getMessage());
      }
   } 
}   
 
class DatabaseException extends Exception {
   public DatabaseException() {
   }
 
   public DatabaseException(String message) {
      super(message);
   }
}
 
class ProductDAO 
{
   Connection connection = null;
   PreparedStatement insertStmt = null;
 
   public ProductDAO(Connection connection) {
      this.connection = connection;
   }
 
   public void insert(Product prod) throws DatabaseException {
      try {
         if (insertStmt == null) {
            insertStmt = connection.prepareStatement(
                            "INSERT INTO products (description, price, itemsleft) " +
                            "VALUES (?, ?, ?)");
         }
 
         insertStmt.setString(1, prod.getDescription());
         insertStmt.setFloat(2, prod.getPrice());
         insertStmt.setInt(3, prod.getItemsleft());
         insertStmt.executeUpdate();  
      }
      catch(SQLException e) {
         throw new DatabaseException(e.getMessage());
      }
   }
 
   public Collection getAllRows() throws DatabaseException {
      try {
         ArrayList al = new ArrayList();
 
         Statement stmt = connection.createStatement();
         ResultSet rs = stmt.executeQuery("SELECT * FROM products");
         while (rs.next()) {
            Product prod = new Product();
            prod.setDescription(""+rs.getString("description"));
            prod.setPrice(rs.getFloat("price"));
            prod.setItemsleft(rs.getInt("itemsleft"));
            al.add(prod);
         }
         
         stmt.close();
 
         return al;
      }
      catch(SQLException e) {
         throw new DatabaseException(e.getMessage());
      }
   }
}

Creating a thread-safe singleton class

The best way is:

public class ExampleSingleton {
 
  private static ExampleSingleton instance;
 
  public static ExampleSingleton getInstance() {
    if( instance == null ) {
      synchronized( ExampleSingleton.class ) {
        if ( instance == null ) {
           instance = new ExampleSingleton();
        }
      }//sync ends
    }
 
    return instance;
  }
}

to make it threadsafe you have to use synchronization. But if you sync the whole method, you will create a tiny unnecessary performance overhead. Watch that the second if-condition is vitally important. Lets say instance is null, at this point two threads are executing the method. one thread goes in the sync block and the other starts waiting. when the second thread gets the lock and enters sync block, the Singleton has already been creates by the first thread. If you ommit the second if-condition, the second thread will create a second instance of the singleton.

Hope it helps.

Creating a formatted textfield that only accepts IP addresses

JDK1.4 introduced the JFormattedTextfield with which you can set a mask on a text field.
This example not only shows you how to set the mask (eg. 255.255.xxx.xxx), but also how to use an InputVerifier to check whether the inputted digits lie in the range [0-255].

Main.java:

import javax.swing.JFormattedTextField.*;
import javax.swing.text.*;
import java.awt.event.*;
import javax.swing.*;
import java.text.*;
import java.util.*;
import java.awt.*;
 
public class Main extends JFrame 
{
   public Main() throws Exception
   {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      MaskFormatter formatter = new MaskFormatter("255.255.###.###");
      formatter.setPlaceholderCharacter('0');
 
      final JFormattedTextField formattedTf = new JFormattedTextField(formatter);
      formattedTf.setInputVerifier(new IPTextFieldVerifier());
 
      final JTextField normalTf = new JTextField(25);
      JButton button = new JButton("Get value");
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent ae) { 
            normalTf.setText(""+formattedTf.getValue());
         }
      });
 
      getContentPane().setLayout(new FlowLayout(FlowLayout.LEFT));
      getContentPane().add(formattedTf);
      getContentPane().add(button);
      getContentPane().add(normalTf);
 
      formattedTf.setPreferredSize(normalTf.getPreferredSize());
   }
  
   public static void main(String args[]) throws Exception 
   {
      Main main = new Main();
      main.setSize(300, 150);
      main.setVisible(true);
   }
}
 
class IPTextFieldVerifier extends InputVerifier {
   public boolean verify(JComponent input) {
      if (input instanceof JFormattedTextField) {
         JFormattedTextField ftf = (JFormattedTextField)input;
         AbstractFormatter formatter = ftf.getFormatter();
         if (formatter != null) {
            String text = ftf.getText();
            StringTokenizer st = new StringTokenizer(text, ".");
            while (st.hasMoreTokens()) {
               int value = Integer.parseInt((String) st.nextToken());
               if (value < 0 || value > 255) {
                  // to prevent recursive calling of the 
                  // InputVerifier, set it to null and
                  // restore it after the JOptionPane has
                  // been clicked.
                  input.setInputVerifier(null);
                  JOptionPane.showMessageDialog(new Frame(), "Malformed IP Address!", "Error", 
                                                JOptionPane.ERROR_MESSAGE);
                  input.setInputVerifier(this);  
                  return false;
               }
            }
            return true;
         }
      }
      return true;
   }
 
   public boolean shouldYieldFocus(JComponent input) {
      return verify(input);
   }
}