Java FAQ - Part 2

Question:

How do you hide columns and rows in a JTable component?

Is there a way for the user to hide specific columns or rows in a JTable and make them visible again?

Answer:

The preferred solution is to use addColumn and removeColumn and re-arrange the columns using table.getColumnModel().moveColumn(...)

This solution resolves issues with the earlier answer posted below. One important point is that although setting the size to zero will hide the column it will still need to redraw that column each time. Also the cursor will disappear when moving across this table if the column size is zero.

Question:

Problems setting a security manager when using JDBCTM in JDKTM 1.2 Beta 4.

When I specify a SecurityManager and grant AllPermissions for a simple JDBC application, the DriverManager skips all registered Drivers. The AccessControlContexts associated with the current SecurityContext and the Driver, appear to be the same, but I get an SQLException with the message, "No suitable driver."

For example, this doesn't work:

java -Djava.security.manager -Djava.security.policy=myPolicy MyJDBCApp 

But this does: 

java MyJDBCApp 

This is especially annoying if, for example, you must have a security manager with an RMI application.

Answer:

This is a known bug--number 4153145. This problem has been fixed in the JDK 1.2 final release. Here is a brief summary.

Synopsis: java.sql.DriverManager methods do not work correctly with a non-null security manager.

This happens because the following check for a non-null securityContext fails.

    if (di.securityContext != null && 
        di.securityContext != currentSecurityContext) {

You are referring to the JDK 1.2 final release in the answer. When will it be?

Now we use J-BUILDER 1.01,and development client/server system.At NOS WindowsNT server ver4.0(WORKGROUP) normaly operate,but used DNS not operate.Error reason is refuse connect.J-builder use no.1099 socket at normal,client required 1182...etc.Using command is Naming.lookup.May be wrong setup for DNS.

Question:

When I click a JComboBox in my applet and select an element, JComboBox never changes.

I am using Netscape Communicator with the JavaTM Plugin 1.1.1 When I try exactly the same applet in Internet Explorer it works. How can I work around this problem?

Answer:

The problem is a bug in Netscape Communicator, which only occurs when you have a JComboBox in a Netscape Frame. When the Jlist of the JComboBox is brought down, the frame loses focus. The next time you click on the frame, and try to select something in the list, the frame regains focus and the JComboBox is redrawn. Fortunately, although there is no bug fix in sight, there is the following workaround.

Extend the JLabel class and implement the ListCellRenderer interface. Then use a focus listener in the class to be notified when the JComboBox loses focus. When the focus listener is called, set the currently selected item in the JComboBox to the last selected item before focus was lost.

Here is an inner class that implements the workaround.

// This inner class works around a netscape 
// focusing bug.
// Basically, it listens for a focus lost and then 
// sets the list to that item. 
class MyCellRenderer extends JLabel implements 
			ListCellRenderer, FocusListener {
    JComboBox mycombo;  
    String last = null;

    public MyCellRenderer( JComboBox combo ) {
        setOpaque(true);
        mycombo = combo; 
        mycombo.addFocusListener( this ); 
    } 
    public Component getListCellRendererComponent(
        JList list,  
        Object value, 
        int index, 
        boolean isSelected,  
        boolean cellHasFocus) { 

     // Make sure that if value == null, that there 
     // is no null pointer exception
        if ( value != null ) {     
            setText(value.toString()); 
            if ( isSelected ) { 
                last = value.toString();
            } 
               
            setBackground(isSelected ? 
		list.getSelectionBackground() : 
			mycombo.getBackground() );
            setForeground(isSelected ? 
		list.getSelectionForeground() : 
			mycombo.getForeground() );
            } else { 
                setText("");
            }   
            return this;   
        } 
         
        public void focusLost( FocusEvent e ) {
            if ( last != null ) {
                mycombo.setSelectedItem( last );
            }
        }           
                    
        public void focusGained( FocusEvent e ) {
        }
}

To integrate this into your code create your JComboBox and add the following line:

combobox.setRenderer( new MyCellRenderer( combobox ) ); 

Question:

Where can I get technical information about Enterprise JavaBeans?

Does anyone have a tutorial, or know where to get an implementation, or other technical information on Enterprise JavaBeans?

Answer:

Check out EJBHome at http://www.ejbhome.com; there is even a free implementation at this site!

A beginner's guide to Enterprise JavaBeans at http://www.javaworld.com/javaworld/jw-10-1998/jw-10-beans.html.

How to write a session EJB (Enterprise JavaBean) http://www.javaworld.com/jw-07-1998/jw-07-step.html.

There is a free developer's guide available at http://www.Nova-Labs.com.

Weblogic has information about the Tengah implementation at http://www.weblogic.com/docs/classdocs/.

The JDC is planning an Enterprise JavaBeans tutorial for the near future too.

Question:

What's this I hear about having to convert my Swing code to use new package names?

Answer:

That's correct. Swing was initially developed as an 'extension' (for JDK 1.1TM), but has since migrated to the 'core' runtime for the JDK 1.2TM; thus the name changes.

If you have code that uses earlier package names, such as com.sun.java.swing, then you will need to convert it. The new and final package names will start with javax.swing.

See the JFC homepage for more details, as well as information on a PackageRenamer utility to help you convert to the new package names.

Question:

How can I launch a web browser from my Java application?

I'm trying to figure out the best way to enable my Java application to launch the default browser to a specific web page on Windows 95/NT

I have been able to do this using showDocument method from the java.applet.AppletContext class, but I haven't found anything as convenient from an application.

Answer:

The most versatile way to do this is by using the Windows95 start command as follows:

Runtime.getRuntime().exec("start iexplore [webaddress or file]");

or

Runtime.getRuntime().exec("start netscape [file or address]"); 

By using the start command your Java program loads faster and guarantees a new browser window.

This can be further refined by taking advantage of the fact that html files are bound to the default browser, so you don't even have to specify the browser!

Runtime.getRuntime().exec("start http://www.microsoft.com");

and

Runtime.getRuntime().exec("start c:\\java\\product.html"); 

are both valid.

[note from calvin: You will need to implement your own "start" script on other platforms]

Many thanks to JDC members, paternostro and penses for contributing to this answer

Question:

How do you remove the 'X' closing button from a dialog box?

I have a dialog box that contains a list of items from which the user must choose, otherwise they can cancel the operation by clicking a Cancel button. However, if they close the Dialog window by clicking the 'X' button the Dialog component doesn't return a value to the calling method, despite setting the default action to cancel.

Is there a way to remove the 'X' button or ensure that the Dialog returns a value when the window closes?

Answer:

You can do one of two things: either don't code a windowListener for the windowClosing event, or write a windowListener that will dispatch an actionPerformed event to the Close button. This will work for the Close window menu on X window clients too.

The following samples demonstrate both methods.

// PromptClose.java
//
import java.awt.event.*;
import java.awt.*;

public class PromptClose {

 public static void main(String args[]) {
  Frame f;
  new PromptCloseDialog(f = 
      new Frame(),"Test Dialog",true);
  f.dispose();
  System.exit(0);
 }
}

class PromptCloseDialog extends Dialog 
    implements ActionListener {
 Button ok = new Button("OK");

 PromptCloseDialog(Frame parent, 
     String title, boolean modal) {
  super(parent,title,modal);

  ok.addActionListener(this);

  add(BorderLayout.NORTH,new Label(
      "Do you want to close the dialog box?"));
  add(BorderLayout.SOUTH,ok);

  /* Size the frame */
  pack();

  /* Center the dialog */
  Dimension screenDim = 
      Toolkit.getDefaultToolkit().getScreenSize();
  Rectangle frameDim = getBounds();
  setLocation((screenDim.width - frameDim.width) 
	/ 2,(screenDim.height - frameDim.height) / 2);

  /* Show the dialog */
  setVisible(true);
 }

 public void actionPerformed(ActionEvent evt)
 {
  if (evt.getSource() == ok)
   dispose();
 }
}


// PromptClose2.java
//
import java.awt.event.*;
import java.awt.*;

public class PromptClose2 {

 public static void main(String args[]) {
  Frame f;
  new PromptCloseDialog(f = new 
      Frame(),"Test Dialog",true);
  f.dispose();
  System.exit(0);
 }
}

class PromptCloseDialog extends 
    Dialog implements ActionListener {
 Button ok = new Button("OK");

 PromptCloseDialog(Frame parent, 
     String title, boolean modal) {
  super(parent,title,modal);

  addWindowListener(new WindowAdapter() {
   public void windowClosing(WindowEvent evt) {
    ok.dispatchEvent(new ActionEvent(
        ok,ActionEvent.ACTION_PERFORMED,"OK"));
    }
   });

  ok.addActionListener(this);

  add(BorderLayout.NORTH,new Label(
      "Do you want to close the dialog box?"));
  add(BorderLayout.SOUTH,ok);

  /* Size the frame */
  pack();

  /* Center the dialog */
  Dimension screenDim = 
      Toolkit.getDefaultToolkit().getScreenSize();
  Rectangle frameDim = getBounds();
  setLocation((screenDim.width - frameDim.width) 
	/ 2,(screenDim.height - frameDim.height) / 2);

  /* Show the dialog */
  setVisible(true);
 }

 public void actionPerformed(ActionEvent evt)
 {
  if (evt.getSource() == ok)
   dispose();
 }
}

Question:

How do I change a Swing font across my whole application?

I would like to change the font for all panels, labels, menus, and so on, in my Swing application. I have tried to change the font using setFont as follows:

frame.getContentPane().setFont( sysfont ); 

However, the original default font continues to be used. Is there a way to do this in Swing or AWT?

Answer:

Unless you want to define your own 'look and feel,' you should update the user default font properties via the keys you see from: UIManager.getDefaults.

There is no magic way to alter panels, labels, menus, tooltips, and so on, with one statement, because they must all be set separately. For instance, the keyname for Panel is Panel.font, and the keyname for ToolTip is ToolTip.font, but that pattern breaks down for some Components, such as MenuItem.acceleratorFont.

Once you have the keyname, you should be able to make your 'global' change by using the put method of the UIManager class.

For example:

UIManager.put("ToolTip.font",new Font("SansSerif",Font.ITALIC,10)); 

changes the font for all tooltips. And 

UIManager.put("Label.font",new Font("SansSerif",Font.ITALIC,10)); 

will change the font for all labels. 

Question:

How do you configure callbacks in Java IDL?

Can you use CORBA callbacks with a Java Applet? I tried to create a simple callback in which I created a CORBA object on the client side, and passed it to the server side so that later the server could call the client back.

This seems to work fine when the client is an application. However, when the client is an applet, a COMM_FAILURE exception is thrown. I've tried it with appletviewer, and Netscape with the Java Plugin without success.

Answer:

There is an example of how to do this as an application in the JDKTM 1.2 IDL Guide.

Without any modification this example works when the client and server are on the same machine. To get an application to connect to a server on another machine you can pass the argument -ORBInitialHost servermachine, servermachine is where the CORBA server resides.

To get this working in an applet you need to sign the applet . Instructions for signing an applet are covered in the JDC signing article. The IDL code uses the codebase host to connect the client from the server machine.

Question:

How can I debug Swing events?

I want to monitor my Swing application in order to check that events are being sent when I test my application. Do I need to get an instance of the system event queue, which only allows a blocking getNextEvent method, or nonblocking peekEvent method? Is there another way that doesn't involve me taking control of the event queue?

Answer:

The simplest method to analyze any Swing, or JDKTM 1.1 events, is to use the AWTEventListener. This listener takes an event mask that is built from an OR operation of the AWTEvent you wish to monitor. You can obtain a simple list from the output of the command javap -public java.awt.AWTEvent.

The following is a simple example.

//EventTest.java
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;

public class EventTest extends JFrame {
    public EventTest() {
        JButton jb1=new JButton("hello");
        getContentPane().add(jb1);

//      AWTEventListener 

        getToolkit().addAWTEventListener(
	    new AWTEventListener() {
	        public void eventDispatched(AWTEvent e) {
		    System.out.println(e+"\n");
	        }
	    },
	    AWTEvent.MOUSE_EVENT_MASK | 
			AWTEvent.FOCUS_EVENT_MASK
	);
    }
	
    public static void main(String args[]){
		
	EventTest et=new EventTest();
	et.setSize(300,300);
	et.pack();
	et.show();
    }
}

Question:

How can I speed up printing in my JDK 1.2 Swing application?

I wrote a small program to test the new features of the new printing API and tried the examples given on the JDC. They both seemed to work OK, but even for these small examples there seems to be large amount of printer output, especially on UNIX where the files reach hundreds of Megabytes. Is there some way I can improve my application's printing performance?

Answer:

There are two things you can try to help increase the performance of your print jobs.

1. When printing any Swing component, turn off double buffering. This feature is enabled by default, and although it is very useful for increasing graphics performance, it will incur extra unnecessary work for the print routines.

You can disable double buffering in your print code by using the RepaintManager method as follows:

RepaintManager.currentManager(this).setDoubleBufferingEnabled(false); 

2. With UNIX there is a problem in trying to read pixels from PostScript printers. The temporary workaround is to set the java2d.font.usePlatformFont runtime flag, for example:

java -Djava2d.font.usePlatformFont=true PrintBook 

Question:

How can I launch a Java 2 application from a Windows NT icon?

This is probably a simple thing, but I cannot figure out how to launch an application from an icon in Windows NT. I have tried using a batch file and putting a shortcut on the desktop, using javaw.exe, but this brings up a command prompt, which immediately disappears.

I have tried various batch files, all of which work fine from the command line, but I can't figure out how to have an icon that launches the application.

Examples of batch files:

    C:\jdk1.2\jre\bin\javaw -cp C:"mypath" myclass
    C:\jdk1.2\bin\javaw -cp C:"mypath" myclass
    javaw -cp C:"mypath" myclass

Note: "mypath" in this example is your user classpath, for example c:\java\demo.

Answer:

One solution is to create a shortcut on your desktop. In the command line of your shortcut enter the target javaw -cp C:"mypath" myclass. Then set the working directory to the directory your class file is in.

If your Java environment is configured correctly your shortcut will automatically run the application.

The good news is that the Java 2 Platform now includes an executable JAR file. When you archive your classes, you just need to include the Main-Class parameter in the manifest to tell the JVM where your main method is.

For example, if the main method was in the file, myclass, create a manifest file, for example, mymanifest, with the following lines

Manifest-Version: 1.0
Main-Class: myclass

The JAR file is created as follows:

jar cmf mymanifest myjar myclass.class 

You can include other classes in the JAR file by adding them to the end of the command line. To execute this JAR file, called myjar, you can either double click on the myjar icon or use the command java -jar myjar.

For more information on executable JAR files see the extensions guide

Question:

How can I use the NewAudioClip method to read a local audio file?

I'm wondering how to use the NewAudioClip(URL) method to read a sound file into an application without having to use import java.net.

I'm also wondering if there is a way to track the sound loading so I can be sure it's available to play.

Answer:

There is now a convenience method to convert File objects to 'file:' URL. However, you still have to catch and handle any MalformedURLExceptions, for example:

File f = new File("mysound.au");
AudioClip theSound;
try {
  theSound = Applet.newAudioClip(f.toURL());
} catch (java.net.MalformedURLException e) {
theSound = null;
}
if (theSound != null) {
theSound.play();
}

If you have a small application and the play method is your last line you might need to put in a sleep statement after loading the audio file. This allows the sound to be loaded before the application exits.

Thread.currentThread().sleep(100000); 

Question:

How can I clear data entered into a JTable?

I have a JTable inside a JTabbedPane running in an applet. The AbstractTableModel has multiple text and Boolean columns that are displayed by the model as checkboxes.

If I enter data in a text column but don't click the mouse afterwards in another column, text or button, the data is visible, but not recognized by the table. Clearing the table does not clear the entered data. If I then populate the table, the data that I entered is still visible and overrides what should be visible. I have tried calling repaint and validate but they have no effect.

Answer:

If the CellEditor doesn't get a "stopCellEditing" call, the underlying DataModel doesn't get updated via setValueAt(row,col,t). Moving the focus elsewhere, for example a mouse click, stops the CellEditor and posts the changes.

If you want to stop the CellEditor programmatically, try this:

table.getCellEditor().stopCellEditing(); 

However, keep in mind that this triggers a ChangeEvent for the CellEditor. If you want the text in the DataModel to be updated, you need to do something similiar to the following.

If a button press is the trigger to leave a JTable cell you can do this in the Button event handler.

1. Check if an edit is in progress

if (table.isEditing()) 

continue

(if not, then exit). 

2. Get the editor component and retrieve the text

t=((JTextComponent)table.getEditorComponent()).getText();

3. Set the value in the table

table.setValueAt(t,row,column) 

4. Cancel editing

table.getCellEditor().cancelCellEditing(); 

Question:

How can I implement a splash screen for my application?

Is it possible to get a Java application to provide a splash screen while it is loading? By splash screen I mean a gif image placed in the center of the screen while all the classes load.

Answer:

To create a splash screen use the AWT Window class from which Frame is derived. It creates a window without a border and a title. The following is a working sample:

import java.awt.event.*;
import java.awt.*;

public class SplashTest {

    public static void main(String args[]) {
       new SplashWindowFrame();
    }
}

class SplashWindowFrame extends Frame {
    SplashWindow sw;
    Image splashIm;

    SplashWindowFrame() {
       super();

       /* Add the window listener */
       addWindowListener(new WindowAdapter() {
          public void windowClosing(WindowEvent evt) {
              dispose(); 
	      System.exit(0);
	  }});

       /* Size the frame */
       setSize(200,200);

       /* Center the frame */
       Dimension screenDim = 
            Toolkit.getDefaultToolkit().getScreenSize();
       Rectangle frameDim = getBounds();
       setLocation((screenDim.width - frameDim.width) / 2,
		(screenDim.height - frameDim.height) / 2);

       MediaTracker mt = new MediaTracker(this);
       splashIm = Toolkit.getDefaultToolkit(
           ).getImage("test.gif");
       mt.addImage(splashIm,0);
       try {
          mt.waitForID(0);
       } catch(InterruptedException ie){}

       sw = new SplashWindow(this,splashIm);

       try {
	  Thread.sleep(3000);
       } catch(InterruptedException ie){}

       sw.dispose();

       /* Show the frame */
       setVisible(true);
       }
}

class SplashWindow extends Window {
    Image splashIm;

    SplashWindow(Frame parent, Image splashIm) {
        super(parent);
        this.splashIm = splashIm;
        setSize(200,200);

        /* Center the window */
        Dimension screenDim = 
             Toolkit.getDefaultToolkit().getScreenSize();
        Rectangle winDim = getBounds();
        setLocation((screenDim.width - winDim.width) / 2,
		(screenDim.height - winDim.height) / 2);
        setVisible(true);
    }

    public void paint(Graphics g) {
       if (splashIm != null) {
           g.drawImage(splashIm,0,0,this);
       }
    }
}

Question:

How can I implement a splash screen for my application?

Is it possible to get a Java application to provide a splash screen while it is loading? By splash screen I mean a gif image placed in the center of the screen while all the classes load.

Answer:

To create a splash screen use the AWT Window class from which Frame is derived. It creates a window without a border and a title. The following is a working sample:

import java.awt.event.*;
import java.awt.*;

public class SplashTest {

    public static void main(String args[]) {
       new SplashWindowFrame();
    }
}

class SplashWindowFrame extends Frame {
    SplashWindow sw;
    Image splashIm;

    SplashWindowFrame() {
       super();

       /* Add the window listener */
       addWindowListener(new WindowAdapter() {
          public void windowClosing(WindowEvent evt) {
              dispose(); 
	      System.exit(0);
	  }});

       /* Size the frame */
       setSize(200,200);

       /* Center the frame */
       Dimension screenDim = 
            Toolkit.getDefaultToolkit().getScreenSize();
       Rectangle frameDim = getBounds();
       setLocation((screenDim.width - frameDim.width) / 2,
		(screenDim.height - frameDim.height) / 2);

       MediaTracker mt = new MediaTracker(this);
       splashIm = Toolkit.getDefaultToolkit(
           ).getImage("test.gif");
       mt.addImage(splashIm,0);
       try {
          mt.waitForID(0);
       } catch(InterruptedException ie){}

       sw = new SplashWindow(this,splashIm);

       try {
	  Thread.sleep(3000);
       } catch(InterruptedException ie){}

       sw.dispose();

       /* Show the frame */
       setVisible(true);
       }
}

class SplashWindow extends Window {
    Image splashIm;

    SplashWindow(Frame parent, Image splashIm) {
        super(parent);
        this.splashIm = splashIm;
        setSize(200,200);

        /* Center the window */
        Dimension screenDim = 
             Toolkit.getDefaultToolkit().getScreenSize();
        Rectangle winDim = getBounds();
        setLocation((screenDim.width - winDim.width) / 2,
		(screenDim.height - winDim.height) / 2);
        setVisible(true);
    }

    public void paint(Graphics g) {
       if (splashIm != null) {
           g.drawImage(splashIm,0,0,this);
       }
    }
}

Question:

How can I restore an iconfied JFrame to its normal state?

I want to return my iconified JFrame to its normal state under the control of my program. If this is impossible, then is there a way to create a JFrame object without the minimize/maximize icon at the upper-right corner of the frame? I know you can do this with JInternalFrame, but what about JFrame?

Answer:

The JFrame class in the JavaTM 2 platform inherits a setState method from the Frame class, which can be used to iconify your JFrame and return it to its normal size.

If you have multiple frames you can iconify them all using the following code:

int iterator;
Frame[] array = getFrames();
for(iterator = 0; iterator < array.length; iterator++) {
     array[iterator].setState(ICONIFIED);
}
To return them to normal size, do the following: 


for(iterator = 0; iterator < array.length; iterator++) {
    array[iterator].setState(NORMAL);
}

To remove the minimize and maximize buttons, and disable the menu items you can use the setResizable(false) method. However, this will only work on JDKTM 1.1.7B and later releases.

Alternatively if you are not using JDK 1.1.7B, use a JWindow instead of a JFrame. This will not only eliminate the minimize/maximize buttons, but also the entire title bar.

Question:

How can I calculate the minimum amount of memory needed to run my application?

I have written an application using the JavaTM 2 platform and Swing, which is soon to be released. However I would like to specify the minimum system requirements needed to run my application. I tried unsuccessfully to find the minimum memory required to run the Java Runtime, and I couldn't find any mention of this in the documentation either.

I need this information for both Windows 95/NT and SolarisTM 2.5.

Answer:

One technique you can use to find a Java applications memory usage is to start an additional watcher thread that periodically checks the values from Runtime.totalMemory and Runtime.freeMemory. The numbers returned by these methods are approximate, but are perfectly acceptable for use in future -mx mem and -ms mem parameters when starting the Java1 Virtual Machine.

For example if your application runs at 32 megabytes under normal testing, you will see better startup performance by using the parameter -ms32mb when starting the Java Virtual Machine:

java -ms32mb MyApplication

You can also use the tools available on the operating system to find out how much memory your application is using. On Windows there are memory management programs such as mem watch or the taskmanager program that can provide this information. On Solaris the same information can be obtained from the ps command.

ps -o osz -p

To calculate the amount of megabytes this program has used, multiply the number displayed by this command by the page size, 8192. Alternatively you can use the command ls -l /proc/.

$ ls -l /proc/217
-rw-------   1 root other 45473792 Feb  5 12:30 /proc/217

Both should give a total, in this example of approximately 45 megabytes. This value is the maximum amount of memory this process used, the garbage collector may have freed some of this memory but the size of the process remains the same.

On Linux, check the ps command output or check the contents of the file /proc//stat for similar information.

Question:

How can I store an encrypted password?

I am looking for a way to encrypt passwords so that I can store them in a database, something like the UNIX password encryption scheme. I understand that there are one-way hash algorithms to encrypt passwords, but I don't have the foggiest idea of where to look for more information, or how to even start!

Answer:

The JDKTM 1.1 and later releases include a secure hash method based on the National Institute of Standards and Technology's (NIST) Secure Hash Standard. A secure hash is a function that works in only one direction. It is like a trap door--you can easily fall in, but you can't climb back out.

The documentation for SHA is available in the java.security.MessageDigest class.

Here is an example that compares two strings that are supplied on the command line:

import java.io.*;
import java.security.*;
        
public class PasswordTest {

   public static void main(String args[]) {
        
    if (args.length != 2) {
        System.err.println("usage: 
             PasswordTest password testvalue");
        System.exit(1);
    }

    byte[] buf= new byte[args[0].length()];
    args[0].getBytes(0, args[0].length(), buf, 0);

    MessageDigest algorithm=null;
    try {
        algorithm = MessageDigest.getInstance("SHA-1");
    }catch (NoSuchAlgorithmException e) {
        System.out.println(e);
    }
    algorithm.reset();
    algorithm.update(buf);
    byte[] digest1 = algorithm.digest();

    algorithm.reset();
    buf= new byte[args[1].length()];
    args[1].getBytes(0, args[1].length(), buf, 0);
    algorithm.update(buf);
    byte[] digest2 = algorithm.digest();

    if(digest1.length != digest2.length) {
	System.out.println("They do not match!");
	System.exit(0);
    }

    for(int i=0; i

Question:

How can I print the contents of a large JTable?

I am trying to print out a JTable containing data that runs over more than one page. I can get the header to print, but not the header and the data together. How can I get this to work?

Answer:

There is a trick that works for both printing components that exceed a single page and printing more than one component on a page. To do either, you'll need to use the translate(double, double) and setClip methods from the Graphics2D class. Note: There are different translate methods in Graphics2D to ensure that you supply double arguments.

An example that sets the clipping and translates follows:

import javax.swing.*;
import javax.swing.table.*;

import java.awt.print.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.Dimension;

public class SalesReport implements Printable{

    JFrame frame;
    JTable tableView;

    public SalesReport() {
        frame = new JFrame("Sales Report");
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) 
                {System.exit(0);}});

        final String[] headers = {"Description", 
            "open price", 
            "latest price", "End Date", "Quantity"};
        final Object[][] data = {
	    {"Box of Biros", "1.00", "4.99", 
	        new Date(), new Integer(2)},
	    {"Blue Biro", "0.10", "0.14", 
	        new Date(), new Integer(1)},
	    {"legal pad", "1.00", "2.49", 
	        new Date(), new Integer(1)},
	    {"tape", "1.00", "1.49", 
	        new Date(), new Integer(1)},
	    {"stapler", "4.00", "4.49", 
	        new Date(), new Integer(1)},
	    {"legal pad", "1.00", "2.29", 
	        new Date(), new Integer(5)}
        };

        TableModel dataModel = new AbstractTableModel() {
            public int getColumnCount() 
                { return headers.length; }
            public int getRowCount() { return data.length;}
            public Object getValueAt(int row, int col) {
		return data[row][col];}

            public String getColumnName(int column) {
			return headers[column];}
            public Class getColumnClass(int col) {
			return getValueAt(0,col).getClass();}
            public boolean isCellEditable(int row, int col) {
			return (col==1);}
            public void setValueAt(Object aValue, 
                int row, int column) {
                data[row][column] = aValue;
            }
         };

	tableView = new JTable(dataModel);
        JScrollPane scrollpane = new JScrollPane(tableView);

        scrollpane.setPreferredSize(new Dimension(500, 80));
	frame.getContentPane().setLayout(new BorderLayout());
        frame.getContentPane().add(BorderLayout.CENTER,scrollpane);
        frame.pack();
	JButton printButton= new JButton();

	printButton.setText("print me!");

	frame.getContentPane(
	    ).add(BorderLayout.SOUTH,printButton);

	// for faster printing turn double buffering off

	RepaintManager.currentManager(
	    frame).setDoubleBufferingEnabled(false);

	printButton.addActionListener( new ActionListener(){
	    public void actionPerformed(ActionEvent evt) {
	        PrinterJob pj=PrinterJob.getPrinterJob();

		pj.setPrintable(SalesReport.this);
		pj.printDialog();

		try{ 
		    pj.print();
		}catch (Exception PrintException) {}
	    }
	});

        frame.setVisible(true);
    }

    public int print(Graphics g, PageFormat pageFormat, 
		int pageIndex) throws PrinterException {

        Graphics2D  g2 = (Graphics2D) g;
	g2.setColor(Color.black);
	int fontHeight=g2.getFontMetrics().getHeight();
	int fontDesent=g2.getFontMetrics().getDescent();

	//leave room for page number
	double pageHeight = 
	    pageFormat.getImageableHeight()-fontHeight;

	double pageWidth = pageFormat.getImageableWidth();
	double tableWidth =
	    (double) tableView.getColumnModel(
	    ).getTotalColumnWidth();
	double scale = 1; 
	if (tableWidth >= pageWidth) {
		scale =  pageWidth / tableWidth;
	}

	double headerHeightOnPage=
	    tableView.getTableHeader().getHeight()*scale;
	double tableWidthOnPage=tableWidth*scale;

	double oneRowHeight=(tableView.getRowHeight()+
			tableView.getRowMargin())*scale;
	int numRowsOnAPage=
	    (int)((pageHeight-headerHeightOnPage)/oneRowHeight);
	double pageHeightForTable=oneRowHeight*numRowsOnAPage;
	int totalNumPages= (int)Math.ceil((
		(double)tableView.getRowCount())/numRowsOnAPage);
	if(pageIndex>=totalNumPages) {
		return NO_SUCH_PAGE;
	}

	g2.translate(pageFormat.getImageableX(), 
	    pageFormat.getImageableY());
	g2.drawString("Page: "+(pageIndex+1),(int)pageWidth/2-35,
	    (int)(pageHeight+fontHeight-fontDesent));//bottom center

	g2.translate(0f,headerHeightOnPage);
	g2.translate(0f,-pageIndex*pageHeightForTable);
	//TODO this next line treats the last page as a full page
	g2.setClip(0, (int)(pageHeightForTable*pageIndex),(int) 
	    Math.ceil(tableWidthOnPage),
	    (int) Math.ceil(pageHeightForTable));

	g2.scale(scale,scale);
	tableView.paint(g2);
	g2.scale(1/scale,1/scale);
	g2.translate(0f,pageIndex*pageHeightForTable);
	g2.translate(0f, -headerHeightOnPage);
	g2.setClip(0, 0,(int) Math.ceil(tableWidthOnPage), 
		(int)Math.ceil(headerHeightOnPage));
	g2.scale(scale,scale);
	tableView.getTableHeader().paint(g2);//paint header at top

	return Printable.PAGE_EXISTS;
	}


    public static void main(String[] args) {
        new SalesReport();
    }
}