Saturday, June 30, 2007

Sorting and Filtering Tables

This Tech Tip reprinted with permission by java.sun.com

Java Standard Edition (Java SE) 6.0 (code name Mustang), adds some features that make sorting and filtering the contents of a Swing JTable much easier. (Final inclusion of these features is subject to JCP approval.) Most modern table-driven user interfaces allow users to sort columns by clicking on the table header. This could be done with the Swing JTable support in place prior to Mustang. However the functionality had to be added manually in a custom way for each table that needed this feature. With Mustang, enabling this functionality requires minimal effort. Filtering is another option commonly available with user interfaces. Filtering allows users to display only the rows in a table that match user-supplied criteria. With Mustang, enabling filtering of JTable contents is also much easier.
Sorting Rows

The basis for sorting and filtering rows in Mustang is the abstract RowSorter class. RowSorter maintains two mappings, one of rows in a JTable to the elements of the underlying model, and back again. This allows for one to do sorting and filtering. The class is generic enough to work with both TableModel and ListModel. However only a TableRowSorter is provided with the Mustang libraries to work with JTable.

In the simplest case, you pass the TableModel to the TableRowSorter constructor, and then pass the created RowSorter into the setRowSorter() method of JTable. Here's an example program, SortTable, that demonstrates the approach:
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;

public class SortTable {
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
JFrame frame = new JFrame("Sorting JTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Object rows[][] = {
{"AMZN", "Amazon", 41.28},
{"EBAY", "eBay", 41.57},
{"GOOG", "Google", 388.33},
{"MSFT", "Microsoft", 26.56},
{"NOK", "Nokia Corp", 17.13},
{"ORCL", "Oracle Corp.", 12.52},
{"SUNW", "Sun Microsystems", 3.86},
{"TWX", "Time Warner", 17.66},
{"VOD", "Vodafone Group", 26.02},
{"YHOO", "Yahoo!", 37.69}
};
String columns[] = {"Symbol", "Name", "Price"};
TableModel model =
new DefaultTableModel(rows, columns) {
public Class getColumnClass(int column) {
Class returnValue;
if ((column >= 0) && (column < getColumnCount())) {
returnValue = getValueAt(0, column).getClass();
} else {
returnValue = Object.class;
}
return returnValue;
}
};

JTable table = new JTable(model);
RowSorter sorter =
new TableRowSorter(model);
table.setRowSorter(sorter);
JScrollPane pane = new JScrollPane(table);
frame.add(pane, BorderLayout.CENTER);
frame.setSize(300, 150);
frame.setVisible(true);
}
};
EventQueue.invokeLater(runner);
}
}
Image

Click on one of the columns of the displayed table, and notice that the contents of the column are reordered.
Image

You might ask why not simply use the DefaultTableModel, as opposed to creating a custom subclass of it? The answer is that TableRowSorter has a set of rules to follow for sorting columns. By default, all columns of a table are thought to be of type Object. So, sorting is done by calling toString(). By overriding the default getColumnClass() behavior of DefaultTableModel, RowSorter sorts according to the rules of that class, assuming it implements Comparable. You can also install a custom Comparator for a column by calling setComparator(int column, Comparator comparator).

The key three lines in the SortTable program that are pertinent to sorting are shown here:
JTable table = new JTable(model);
RowSorter sorter =
new TableRowSorter(model);
table.setRowSorter(sorter);

The first line associates the model with the table. The second line creates a RowSorter specific to the model. The third line associates the RowSorter with the JTable. This enables a user to click on the column header to sort that column. Clicking a second time on the same column reverses the sort order.

If you want to add your own action when the sort order changes, you can attach a RowSorterListener to the RowSorter. The interface has one method:

void sorterChanged(RowSorterEvent e)

The method allows you to update the text on the status bar, or perform some additional task. The RowSorterEvent for the action allows you to discover how many rows were present before the sort, in the event that the RowSorter filtered rows in or out of the view.
Filtering Table Rows

You can associate a RowFilter with the TableRowSorter and use it to filter the contents of a table. For instance, you can use a RowFilter such that a table displays only rows where the name starts with the letter A or where the stock price is greater than $50. The abstract RowFilter class has one method that is used for filtering:

boolean include(RowFilter.Entry entry)

For each entry in the model associated with the RowSorter, the method indicates whether the specified entry should be shown in the current view of the model. In many cases, you don't need to create your own RowFilter implementation. Instead, RowFilter offers six static methods for creating filters.

* andFilter(Iterable> filters)
* dateFilter(RowFilter.ComparisonType type, Date date, int... indices)
* notFilter(RowFilter filter)
* numberFilter(RowFilter.ComparisonType type, Number number, int... indices)
* orFilter(Iterable> filters)
* regexFilter(String regex, int... indices)

For the RowFilter factory methods that have an argument of indices (dateFilter, numberFilter, regexFilter), only the set of columns corresponding to the specified indices are checked in the model. If no indices are specified, all columns are checked for a match.

The dateFilter allows you to check for a matching date. The numberFilter checks for a matching number. The notFilter is used for reversing another filter, in other words, it includes entries that the supplied filter does not include. You can use it to do things like find entries where something was not done on 12/25/2005. The andFilter and orFilter are for logically combining other filters. The regexFilter uses a regular expression for filtering. Here's a program, FilterTable, that uses a regexFilter to filter table content:
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.util.regex.*;

public class FilterTable {
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
JFrame frame = new JFrame("Sorting JTable");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Object rows[][] = {
{"AMZN", "Amazon", 41.28},
{"EBAY", "eBay", 41.57},
{"GOOG", "Google", 388.33},
{"MSFT", "Microsoft", 26.56},
{"NOK", "Nokia Corp", 17.13},
{"ORCL", "Oracle Corp.", 12.52},
{"SUNW", "Sun Microsystems", 3.86},
{"TWX", "Time Warner", 17.66},
{"VOD", "Vodafone Group", 26.02},
{"YHOO", "Yahoo!", 37.69}
};
Object columns[] = {"Symbol", "Name", "Price"};
TableModel model =
new DefaultTableModel(rows, columns) {
public Class getColumnClass(int column) {
Class returnValue;
if ((column >= 0) && (column < getColumnCount())) {
returnValue = getValueAt(0, column).getClass();
} else {
returnValue = Object.class;
}
return returnValue;
}
};
JTable table = new JTable(model);
final TableRowSorter sorter =
new TableRowSorter(model);
table.setRowSorter(sorter);
JScrollPane pane = new JScrollPane(table);
frame.add(pane, BorderLayout.CENTER);
JPanel panel = new JPanel(new BorderLayout());
JLabel label = new JLabel("Filter");
panel.add(label, BorderLayout.WEST);
final JTextField filterText =
new JTextField("SUN");
panel.add(filterText, BorderLayout.CENTER);
frame.add(panel, BorderLayout.NORTH);
JButton button = new JButton("Filter");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String text = filterText.getText();
if (text.length() == 0) {
sorter.setRowFilter(null);
} else {
try {
sorter.setRowFilter(
RowFilter.regexFilter(text));
} catch (PatternSyntaxException pse) {
System.err.println("Bad regex pattern");
}
}
}
});
frame.add(button, BorderLayout.SOUTH);
frame.setSize(300, 250);
frame.setVisible(true);
}
};
EventQueue.invokeLater(runner);
}
}

The display sets a filter for all strings with the characters SUN somewhere in them. This is specified by the string "SUN". Use the characters '^' and '$' to test for exact matches at the beginning and end of the string respectively.
Image

The filter internally uses Matcher.find() for inclusion testing when the user presses the "Filter" button at the bottom.
Image

Change the filter text to change the set of rows shown in the table. If you want to see all the rows in the table, remove the filter text.

One last thing worth mentioning -- when sorting or filtering, the selection is in terms of the view. So that if you need to map to the underlying model, you need to call the convertRowIndexToModel() method. Similarly, if you want to convert from the model to the view you need to use convertRowIndexToView().

For more information about RowSorter, TableRowSorter, and RowFilter see the javadoc for the respective classes:

* RowSorter
* TableRowSorter
* RowFilter

Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.

Wednesday, June 27, 2007

A simple Action that copies text from a Frame object

This Java Swing tip illustrates a method of copying text from a PageFrame object. This is a general tip that can be used by developers to include in any of their applications. Here the CopyAction class extends AbstractAction which provides a useful mechanism to implement action listeners that can be shared and coordinated.
import java.awt.event.ActionEvent;
import javax.swing.*;

public class CopyAction extends AbstractAction {

SiteManager manager;

public CopyAction(SiteManager sm) {
super("", new ImageIcon("copy.gif"));
manager = sm;
}

public void actionPerformed(ActionEvent ae) {

JInternalFrame currentFrame = manager.getCurrentFrame();
if (currentFrame == null) { return; }
// can't cut or paste sites
if (currentFrame instanceof SiteFrame) { return; }
((PageFrame)currentFrame).copyText();

}
}
source: http://java-tips.org

Monday, June 25, 2007

A program to print limits of the primitive types

This Java program prints the limits of primitive types (e.g. byte, short, int ...) in Java:

public class PrintLimits
{

public PrintLimits()
{
}

public static void main(String args[]) {

System.out.println("Min byte value = " + Byte.MIN_VALUE);
System.out.println("Max byte value = " + Byte.MAX_VALUE);
System.out.println("Min short value = " + Short.MIN_VALUE);
System.out.println("Max short value = " + Short.MAX_VALUE);
System.out.println("Min int value = " + Integer.MIN_VALUE);
System.out.println("Max int value = " + Integer.MAX_VALUE);
System.out.println("Min float value = " + Float.MIN_VALUE);
System.out.println("Max float value = " + Float.MAX_VALUE);
System.out.println("Min double value = " + Double.MIN_VALUE);
System.out.println("Max double value = " + Double.MAX_VALUE);
}
}

source: http://java-tips.org

Saturday, June 23, 2007

A demonstration of Java2D transformations

Image

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.TexturePaint;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;

/** A demonstration of Java2D transformations */
public class Paints extends JPanel {
static final int WIDTH = 800, HEIGHT = 375; // Size of our example

public String getName() {
return "Paints";
}

public int getWidth() {
return WIDTH;
}

public int getHeight() {
return HEIGHT;
}

/** Draw the example */
public void paint(Graphics g1) {
Graphics2D g = (Graphics2D) g1;
// Paint the entire background using a GradientPaint.
// The background color varies diagonally from deep red to pale blue
g.setPaint(new GradientPaint(0, 0, new Color(150, 0, 0), WIDTH, HEIGHT,
new Color(200, 200, 255)));
g.fillRect(0, 0, WIDTH, HEIGHT); // fill the background

// Use a different GradientPaint to draw a box.
// This one alternates between deep opaque green and transparent green.
// Note: the 4th arg to Color() constructor specifies color opacity
g.setPaint(new GradientPaint(0, 0, new Color(0, 150, 0), 20, 20,
new Color(0, 150, 0, 0), true));
g.setStroke(new BasicStroke(15)); // use wide lines
g.drawRect(25, 25, WIDTH - 50, HEIGHT - 50); // draw the box

// The glyphs of fonts can be used as Shape objects, which enables
// us to use Java2D techniques with letters Just as we would with
// any other shape. Here we get some letter shapes to draw.
Font font = new Font("Serif", Font.BOLD, 10); // a basic font
Font bigfont = // a scaled up version
font.deriveFont(AffineTransform.getScaleInstance(30.0, 30.0));
GlyphVector gv = bigfont.createGlyphVector(g.getFontRenderContext(),
"JAV");
Shape jshape = gv.getGlyphOutline(0); // Shape of letter J
Shape ashape = gv.getGlyphOutline(1); // Shape of letter A
Shape vshape = gv.getGlyphOutline(2); // Shape of letter V

// We're going to outline the letters with a 5-pixel wide line
g.setStroke(new BasicStroke(5.0f));

// We're going to fake shadows for the letters using the
// following Paint and AffineTransform objects
Paint shadowPaint = new Color(0, 0, 0, 100); // Translucent black
AffineTransform shadowTransform = AffineTransform.getShearInstance(
-1.0, 0.0); // Shear to the right
shadowTransform.scale(1.0, 0.5); // Scale height by 1/2

// Move to the baseline of our first letter
g.translate(65, 270);

// Draw the shadow of the J shape
g.setPaint(shadowPaint);
g.translate(15, 20); // Compensate for the descender of the J
// transform the J into the shape of its shadow, and fill it
g.fill(shadowTransform.createTransformedShape(jshape));
g.translate(-15, -20); // Undo the translation above

// Now fill the J shape with a solid (and opaque) color
g.setPaint(Color.blue); // Fill with solid, opaque blue
g.fill(jshape); // Fill the shape
g.setPaint(Color.black); // Switch to solid black
g.draw(jshape); // And draw the outline of the J

// Now draw the A shadow
g.translate(75, 0); // Move to the right
g.setPaint(shadowPaint); // Set shadow color
g.fill(shadowTransform.createTransformedShape(ashape)); // draw shadow

// Draw the A shape using a solid transparent color
g.setPaint(new Color(0, 255, 0, 125)); // Transparent green as paint
g.fill(ashape); // Fill the shape
g.setPaint(Color.black); // Switch to solid back
g.draw(ashape); // Draw the outline

// Move to the right and draw the shadow of the letter V
g.translate(175, 0);
g.setPaint(shadowPaint);
g.fill(shadowTransform.createTransformedShape(vshape));

// We're going to fill the next letter using a TexturePaint, which
// repeatedly tiles an image. The first step is to obtain the image.
// We could load it from an image file, but here we create it
// ourselves by drawing a into an off-screen image. Note that we use
// a GradientPaint to fill the off-screen image, so the fill pattern
// combines features of both Paint classes.
BufferedImage tile = // Create an image
new BufferedImage(50, 50, BufferedImage.TYPE_INT_RGB);
Graphics2D tg = tile.createGraphics(); // Get its Graphics for drawing
tg.setColor(Color.pink);
tg.fillRect(0, 0, 50, 50); // Fill tile background with pink
tg.setPaint(new GradientPaint(40, 0, Color.green, // diagonal gradient
0, 40, Color.gray)); // green to gray
tg.fillOval(5, 5, 40, 40); // Draw a circle with this gradient

// Use this new tile to create a TexturePaint and fill the letter V
g.setPaint(new TexturePaint(tile, new Rectangle(0, 0, 50, 50)));
g.fill(vshape); // Fill letter shape
g.setPaint(Color.black); // Switch to solid black
g.draw(vshape); // Draw outline of letter

// Move to the right and draw the shadow of the final A
g.translate(160, 0);
g.setPaint(shadowPaint);
g.fill(shadowTransform.createTransformedShape(ashape));


g.fill(ashape); // Fill letter A
g.setPaint(Color.black); // Revert to solid black
g.draw(ashape); // Draw the outline of the A
}
public static void main(String[] a) {
JFrame f = new JFrame();
f.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
f.setContentPane(new Paints());
f.setSize(800,375);
f.setVisible(true);
}
}

source: http://java-tips.org

Thursday, June 21, 2007

A custom combobox editor for use with the EditableComboBox class

This Java Swing tip illustrates a method of creating a custom combobox editor for use with the EditableComboBox class. A combo box uses a renderer to display each item in its menu. An editable combo box, on the other hand, uses an editor to display the selected item.

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;

public class ComboBoxEditorExample implements ComboBoxEditor {
Map map;
ImagePanel panel;
ImageIcon questionIcon;

public ComboBoxEditorExample(Map m, BookEntry defaultChoice) {
map = m;
panel = new ImagePanel(defaultChoice);
questionIcon = new ImageIcon("question.gif");
}

public void setItem(Object anObject) {
if (anObject != null) {
panel.setText(anObject.toString());
BookEntry entry = (BookEntry)map.get(anObject.toString());
if (entry != null)
panel.setIcon(entry.getImage());
else
panel.setIcon(questionIcon);
}
}

public Component getEditorComponent() { return panel; }
public Object getItem() { return panel.getText(); }
public void selectAll() { panel.selectAll(); }

public void addActionListener(ActionListener l) {
panel.addActionListener(l);
}

public void removeActionListener(ActionListener l) {
panel.removeActionListener(l);
}

// We create our own inner class to handle setting and
// repainting the image and the text.
class ImagePanel extends JPanel {

JLabel imageIconLabel;
JTextField textField;

public ImagePanel(BookEntry initialEntry) {
setLayout(new BorderLayout());

imageIconLabel = new JLabel(initialEntry.getImage());
imageIconLabel.setBorder(new BevelBorder(BevelBorder.RAISED));

textField = new JTextField(initialEntry.getTitle());
textField.setColumns(45);
textField.setBorder(new BevelBorder(BevelBorder.LOWERED));

add(imageIconLabel, BorderLayout.WEST);
add(textField, BorderLayout.EAST);
}

public void setText(String s) { textField.setText(s); }
public String getText() { return (textField.getText()); }

public void setIcon(Icon i) {
imageIconLabel.setIcon(i);
repaint();
}

public void selectAll() { textField.selectAll(); }

public void addActionListener(ActionListener l) {
textField.addActionListener(l);
}
public void removeActionListener(ActionListener l) {
textField.removeActionListener(l);
}
}
}

source: http://java-tips.org

Wednesday, June 20, 2007

Opening default browser

Opening default browser in Java can be done using the following BrowserControl class. To be able to run it with different browsers in Linux , you just need to change UNIX_PATH and UNIX_FLAG parameters.(Note that you don't need to change anyting for windows)

import java.io.IOException;

public class BrowserControl {

// Used to identify the windows platform.
private static final String WIN_ID = "Windows";
// The default system browser under windows.
private static final String WIN_PATH = "rundll32";
// The flag to display a url.
private static final String WIN_FLAG = "url.dll,FileProtocolHandler";
// The default browser under unix.
private static final String UNIX_PATH = "netscape";
// The flag to display a url.
private static final String UNIX_FLAG = "-remote openURL";

/**
* Display a file in the system browser. If you want to display a
* file, you must include the absolute path name.
*
* @param url the file's url (the url must start with either "http://"
* or
* "file://").
*/
public static void displayURL(String url) {
boolean windows = isWindowsPlatform();
String cmd = null;
try {
if (windows) {
// cmd = 'rundll32 url.dll,FileProtocolHandler http://...'
cmd = WIN_PATH + " " + WIN_FLAG + " " + url;
Process p = Runtime.getRuntime().exec(cmd);
} else {
// Under Unix, Netscape has to be running for the "-remote"
// command to work. So, we try sending the command and
// check for an exit value. If the exit command is 0,
// it worked, otherwise we need to start the browser.
// cmd = 'netscape -remote openURL(http://www.java-tips.org)'
cmd = UNIX_PATH + " " + UNIX_FLAG + "(" + url + ")";
Process p = Runtime.getRuntime().exec(cmd);
try {
// wait for exit code -- if it's 0, command worked,
// otherwise we need to start the browser up.
int exitCode = p.waitFor();
if (exitCode != 0) {
// Command failed, start up the browser
// cmd = 'netscape http://www.java-tips.org'
cmd = UNIX_PATH + " " + url;
p = Runtime.getRuntime().exec(cmd);
}
} catch(InterruptedException x) {
System.err.println("Error bringing up browser, cmd='" +
cmd + "'");
System.err.println("Caught: " + x);
}
}
} catch(IOException x) {
// couldn't exec browser
System.err.println("Could not invoke browser, command=" + cmd);
System.err.println("Caught: " + x);
}
}
/**
* Try to determine whether this application is running under Windows
* or some other platform by examing the "os.name" property.
*
* @return true if this application is running under a Windows OS
*/
public static boolean isWindowsPlatform() {
String os = System.getProperty("os.name");
if ( os != null && os.startsWith(WIN_ID))
return true;
else
return false;

}
}

You can use this class with different kind of documents as follows:

  • BrowserControl.displayURL("http://www.java-tips.org");
  • BrowserControl.displayURL("file://c:\\docs\\index.html");
  • BrowserContorl.displayURL("file:///user/joe/index.html");
source: http://www.java-tips.org