Creating a ProgressBar in SWT

Main.java:

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
 
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
 
public class Main {
   private DownloadThread downloadThread = null;
   private boolean disposed = false;
    
   public Main() {
      final Display display = new Display();
      Shell shell = new Shell(display);
      shell.setText("SWT ProgressBar Demonstration");

      shell.setLayout(new FormLayout());
      shell.setBounds(200, 200, 700, 200);
       
      Label downloadLabel = new Label(shell, SWT.NONE);
      FormData data0 = new FormData();
      data0.left = new FormAttachment(0, 0);
      downloadLabel.setLayoutData(data0);
      downloadLabel.setText("FTP file: ");
 
      final Text downloadField = new Text(shell, SWT.SINGLE | SWT.BORDER);
      FormData data1 = new FormData();
      data1.left = new FormAttachment(downloadLabel, 15);
      data1.right = new FormAttachment(100, -100);
      downloadField.setLayoutData(data1);
      downloadField.setText("ftp://ftp.mozilla.org/pub/mozilla/moz-sol/1.2a-02-09-25/mozilla-sparc-sun-solaris2.7.tar.gz");
            
      Label localLabel = new Label(shell, SWT.NONE);
      FormData data2 = new FormData();
      data2.left = new FormAttachment(0, 0);
      data2.top = new FormAttachment(localLabel, 15);
      localLabel.setLayoutData(data2);
      localLabel.setText("Save As: ");
      
      final Text localField = new Text(shell, SWT.SINGLE | SWT.BORDER);
      FormData data3 = new FormData();
      data3.left = new FormAttachment(localLabel, 15);
      data3.top = new FormAttachment(downloadField, 5);
      data3.right = new FormAttachment(100, -400);
      localField.setLayoutData(data3);
      localField.setText("c:\temp\tmp.bin");
      
      final Button downloadButton = new Button(shell, SWT.NONE);
      FormData data4 = new FormData();
      data4.left = new FormAttachment(downloadField, 5);
      data4.right = new FormAttachment(100, -5);
      downloadButton.setLayoutData(data4);
      downloadButton.setText("Download");
      
      Label progressLabel = new Label(shell, SWT.NONE);
      progressLabel.setText("Download progress:");
      FormData data5 = new FormData();
      data5.top = new FormAttachment(localField, 15);
      data5.left = new FormAttachment(10, 15);      
      data5.right = new FormAttachment(90, -15);
      progressLabel.setLayoutData(data5);      
             
      final ProgressBar progressBar = new ProgressBar(shell, SWT.NONE);
      FormData data6 = new FormData();
      data6.top = new FormAttachment(localField, 35);
      data6.left = new FormAttachment(10, 15);      
      data6.right = new FormAttachment(90, -15);
      progressBar.setLayoutData(data6);
 
      shell.open();
 
      final DownloadListener downloadListener = new DownloadListener() {
         public void setMinimum(final int min) {
            display.asyncExec(new Runnable() {
               public void run() {
                  if (!disposed) {
                     progressBar.setMinimum(min);
                  }
               }
            });
         }
         
         public void setMaximum(final int max) {
            display.asyncExec(new Runnable() {
               public void run() {
                  if (!disposed) {
                     progressBar.setMaximum(max);
                  }
               }
            });
         }
         
         public void setSelection(final int selection) {
            display.asyncExec(new Runnable() {
               public void run() {
                  if (!disposed) {
                     progressBar.setSelection(selection);
                  }
               }
            });
         }
         
         public void downloadEnded() {
            if (!disposed) {
               display.asyncExec(new Runnable() {
                  public void run() {
                     System.out.println("disposed = " + disposed);
                     if (!disposed) {                  
                        downloadButton.setText("Download");  
                     }
                  }
               });
            }
         }
      };
      
      downloadButton.addSelectionListener(new SelectionAdapter() {
         public void widgetSelected(SelectionEvent event) {
            try {
               Button button = (Button) event.getSource();
               if (button.getText().equalsIgnoreCase("download")) {
                  if (downloadField.getText().trim().equals("")) return;
                  
                  File localFile = new File(localField.getText());
                  downloadThread = new DownloadThread(downloadListener, 
                                                      localFile,
                                                      new URL(downloadField.getText()));
                  downloadThread.start();
                  downloadButton.setText("Stop");
               }
               else {
                  downloadThread.terminate();
               }
            }
            catch(MalformedURLException e) {
               e.printStackTrace();
            }
         };
      });
 
      shell.addShellListener(new ShellAdapter() {
         public void shellClosed(ShellEvent se) {
            disposed = true;     
            if (downloadThread != null) {         
               downloadThread.terminate();
            }
         }
      });        
      
      while (!shell.isDisposed()) {
         if (!display.readAndDispatch()) {
            display.sleep();
         }
      }
      
      display.dispose();
   }
   
   public static void main(String[] args) {
      new Main();
   }
}
 
interface DownloadListener {
   public void setMinimum(int min);
   public void setMaximum(int max);
   public void setSelection(int selection);
   public void downloadEnded();
}
 
class DownloadThread extends Thread
{
   private int length = 0;
   private URL url = null;
   private File localFile = null;
   private DownloadListener listener = null;
   private boolean terminated = false;
   
   public DownloadThread(DownloadListener listener, File localFile, URL url) {
      this.url = url;
      this.localFile = localFile;
      this.listener = listener;
   }
   
   public void terminate() {
      terminated = true;
   }
   
   public void run() {
      try {
         System.out.println(url);
         URLConnection con = url.openConnection();
         System.out.println("Connecting...");
         con.connect();
         System.out.println("Connected!");
         int length = con.getContentLength();
 
         listener.setMinimum(0);
         listener.setMaximum(length);
          
         int block = 4096;
         int count = 0;
         FileOutputStream fos = new FileOutputStream(localFile);
         InputStream is = con.getInputStream();
         byte[] buff = new byte[block];
         int read = 0;
 
         listener.setSelection(0);
         while((read = is.read(buff, 0, block)) != -1) {
            byte[] bytes;
            if(read != buff.length) {
               bytes = new byte[read];
               System.arraycopy(buff, 0, bytes, 0, read);
            } 
            else {
               bytes = buff;
            }
            fos.write(bytes);
            count += read;
           
            if (terminated) {
               break;
            }
 
            listener.setSelection(count);
         }
         fos.flush();
         fos.close();
         
         listener.downloadEnded();
      } catch(Exception e) {
         System.out.println("Error downloading file " + url);
         e.printStackTrace();
      }
   }
}

Using a DigestOutputStream

With a DigestOutputStream, you can calculate a digest (SHA, MD5) while you are passing data through the stream. The process of calculating the digest can be turned off and on with the method DigestOutputStream.on(boolean).

In the following example, Main.java is read in and written to Main.java.digest. In the process, every other byte is taken into account to update the digest. In the end, the digest is appended to Main.java.digest.

Main.java:

import java.security.*;
import java.io.*;
 
public class Main
{
   public static void main(String []args) {
      try {
         FileInputStream fis = new FileInputStream("Main.java");
         FileOutputStream fos = new FileOutputStream("Main.java.digest");
 
         MessageDigest md = MessageDigest.getInstance("SHA");
         DigestOutputStream dos = new DigestOutputStream(fos, md);
 
         int b;
         boolean state = true;
         while ((b = fis.read()) != -1) {
            dos.write(b);
 
            state = !state;
            dos.on(state);
         }
 
         // turn of digest calculation
         // and write digest to end of file
         dos.on(false);
         byte[] buffer = md.digest();
         dos.write(buffer, 0, buffer.length);
         dos.close();
      }
      catch(Exception e) {
         e.printStackTrace();
      }
   }
} 

Changing the traversal keys that control focus

In 1.4, you can use the following method.

Main.java:

import javax.swing.*;
import java.util.*;
import java.awt.*;
 
public class Main extends JFrame
{
   JLabel label = new JLabel("Forward: TAB or alt-f, Backward: SHIFT-TAB or alt-b");
   JButton button = new JButton("Button");
   JTextField textfield1 = new JTextField();
   JTextField textfield2 = new JTextField();
 
   public Main() {
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
      Container contentPane = getContentPane();
      contentPane.setLayout(new BorderLayout());
      JPanel panel = new JPanel(new GridLayout(4, 1, 10, 10));
      panel.add(label);
      panel.add(textfield1);   
      panel.add(textfield2);   
      panel.add(button);
      contentPane.add(BorderLayout.NORTH, panel);
 
      // add "alt f" as an alternative to TAB 
      Set set = contentPane.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
      KeyStroke altf = KeyStroke.getKeyStroke("alt F");
      set = new HashSet(set);
      set.add(altf);
      contentPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, set);
 
      // add "alt b" as an alternative to SHIFT-TAB 
      set = contentPane.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
      KeyStroke altb = KeyStroke.getKeyStroke("alt B");
      set = new HashSet(set);
      set.add(altb);
      contentPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, set);
   } 
 
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(350, 200);
      main.setVisible(true);
   }
}

Typesafe enum in JDK 1.5

Earlier version of the JDK did not have true enumerated types. Many developers have implemented workarounds for this, a popular one described in Effective Java, item 21. Starting from 1.5, enumerated types have been integrated with the language, allowing developers to write more robust programs without having to write dull boilerplate code, which is automatically generated.

A simple example showing some advantages of the new enum feature:

enum DestinationType
{
   queue,
   topic
}

public class Main
{
   public static void main(String []args) {
      DestinationType dt = DestinationType.queue;

      // Outputting an enumerated type is user-friendly
      System.out.println(dt);   // prints out "queue"
      
      // Switch can be used on an enumerated type, no need to qualify the name of the constant
      switch (dt) {
         case queue:  System.out.println("Using a queue");
                      break;
         case topic:  System.out.println("Using a topic");
                      break;
         default:
      }
   }
}

It is possible to iterate over all declared enum constants:

import java.util.*;

enum DestinationType
{
   queue,
   topic
}

public class Main
{
   public static void main(String []args) {
      printDestinations(DestinationType.values());
      
      // or
      
      for (DestinationType dt : DestinationType.values()) {
         System.out.println(dt);
      }
   }
   
   public static void printDestinations(DestinationType[] list) {
      for (int i=0; i<list.length; i++) {
         System.out.println(list[i]);
      }
   }
}

It is even possible to declare methods on enum constants and attach behavior to the constants.

DestinationType.java:

public abstract enum DestinationType
{
   queue {
      public void print(String queueName) {
         System.out.println("This is queue " + queueName);
      }
   },
   topic {
      public void print(String topicName) {
         System.out.println("This is topic " + topicName);
      }
   };
   
   abstract void print(String name);
}

Main.java:

public class Main
{
   public static void main(String []args) {
      DestinationType dt = DestinationType.queue;

      dt.print("queue1");
   }
}

Decompiling JDK 1.5 classes

jad expects class file versions (major.minor) to be 45.3 or 46.0. Sources compiled with 1.5 have the file version 48.0. The following error message is displayed when trying to decompile a 1.5 .class file:

Parsing RequestForEnhancement.class...The class file version is 48.0, expected 45.3 or 46.0
JavaClassFileParseException: Class file version mismatch

To be able to use JAD, we can simply change the class file version of a 1.5 compiled class into 46. The following small application sets the magic number of a .class file or all classes in a jar file to whatever you desire.

ChangeMagicNumber.java:

import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
 
public class ChangeMagicNumber
{
   public static void main(String []args) throws Exception {
      if (args.length != 2) {
         System.out.println("Usage: java ChangeMagicNumber [file.class | file.jar] magicNumber (eg. 46)");
         System.exit(1);
      }
       
      String filename = args[0];
      byte desiredMagicNumber = (byte) Integer.parseInt(args[1]);
      if (filename.toLowerCase().endsWith(".jar")) {
         processJar(filename, desiredMagicNumber);
      }
      else {
         processFile(filename, desiredMagicNumber);
      }
   }
    
   public static void processFile(String filename, byte desiredMagicNumber) throws Exception {
      RandomAccessFile raf = new RandomAccessFile(filename, "rw");
      raf.seek(7);
      byte b = raf.readByte();
      if (b != desiredMagicNumber) {
         raf.seek(7);
         raf.writeByte((byte) desiredMagicNumber);
      }
      raf.close();
   }
    
   public static void processJar(String filename, byte desiredMagicNumber) throws Exception {
      JarFile jf = new JarFile(filename);
      JarOutputStream jos = new JarOutputStream(new FileOutputStream(filename + "." + desiredMagicNumber));
      CRC32 crc = new CRC32();
      Enumeration enum = jf.entries();
      while (enum.hasMoreElements()) {
         ZipEntry entry = (ZipEntry) enum.nextElement();
         BufferedInputStream bis = new BufferedInputStream(jf.getInputStream(entry));
         int length = bis.available();
         byte[] buffer = null;
         if (length > 0) {
            buffer = new byte[length];
            bis.read(buffer, 0, length);
            
            buffer[7] = desiredMagicNumber;
            crc.reset();
            crc.update(buffer, 0, length);
            entry.setCrc(crc.getValue());
         }
         
         String e = entry.toString();
         jos.putNextEntry(entry);
         if (length > 0) {
            jos.write(buffer, 0, length);
         }
      }
      jf.close();
      jos.close();
   }
}

For example:

java ChangeMagicNumber rt.jar 46  

produces rt.jar.46, all classes in JAR file can be decompiled with JAD

Now the decompilation of the following 1.5 enum-example source works fine, after changing its file class version to 46.

Main.java:

enum DestinationType
{
   queue,
   topic
}
 
public class Main
{
   public static void main(String []args) {
      DestinationType dt = DestinationType.queue;

      // Outputting an enumerated type is user-friendly
      System.out.println(dt);   // prints out "queue"
      
      // Switch can be used on an enumerated type, no need to qualify the name of the constant
      switch (dt) {
         case queue:  System.out.println("Using a queue");
                      break;
         case topic:  System.out.println("Using a topic");
                      break;
         default:
      }
   }
}

Execute:

   java ChangeMagicNumber DestinationType.class 46
   jad DestinationType.class

results in:

DestinationType.jad:

// Decompiled by Jad v1.5.6d. Copyright 1997-99 Pavel Kouznetsov.
// Jad home page: http://www.geocities.com/SiliconValley/Bridge/8617/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   Main.java
 
native class DestinationType extends Enum
{
    public static final DestinationType[] values()
    {
        return (DestinationType[])_2B_VALUES.clone();
    }
 
    public static DestinationType valueOf(String s)
    {
        DestinationType adestinationtype[] = _2B_VALUES;
        int i = adestinationtype.length;
        for(int j = 0; j < i; j++)
        {
            DestinationType destinationtype = adestinationtype[j];
            if(destinationtype.name().equals(s))
                return destinationtype;
        }
 
        throw new IllegalArgumentException(s);
    }
 
    DestinationType(String s, int i)
    {
        super(s, i);
    }
 
    public volatile int compareTo(Enum enum)
    {
        return super.compareTo((DestinationType)enum);
    }
 
    public static final native DestinationType queue;
    public static final native DestinationType topic;
    private static final DestinationType _2B_VALUES[] = {
        queue, topic
    };
 
    static 
    {
        queue = new DestinationType("queue", 0);
        topic = new DestinationType("topic", 1);
    }
}

Read a JPG image, rescale it, and write it back to disk

This example shows how to read, scale and rewrite all jpeg images from the current directory.

Main.java:

import com.sun.image.codec.jpeg.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.*;
import java.io.*;
 
public class Main
{
   private final static float JPG_QUALITY = 1f;   // keep original JPEG quality
  
   public static void main(String []args) throws Exception {
      if (args.length != 2) {
         System.err.println("Usage: java Main scalex scaley");
         System.err.println("Scales all .jpg and .gif images to scalex, scaley");
         System.exit(1);
      } 
 
      int scalex = Integer.parseInt(args[0]);
      int scaley = Integer.parseInt(args[1]);
 
      File f = new File(".");
      String[] fileList = f.list();
 
      for (int i=0; i<fileList.length; i++) {
         if (fileList[i].endsWith(".jpg")) {
            System.out.print("Rescaling " + fileList[i] + "...");  
            readScaleWrite(fileList[i], scalex, scaley);
            System.out.println(" done.");
         }
      }
 
      System.exit(1);
   }
 
   public static void readScaleWrite(String filename, int scalex, int scaley) 
                                                           throws Exception {
      Image img = new javax.swing.ImageIcon(filename).getImage(); 
      BufferedImage bi = toBufferedImage(img);
      BufferedImage scaledBi = new BufferedImage(scalex, scaley, bi.getType());
 
      AffineTransform at = new AffineTransform();
      at.scale((double) scalex / bi.getWidth(), (double) scaley / bi.getHeight());
 
      Graphics2D g2d = scaledBi.createGraphics();
      g2d.drawImage(bi, at, null);
      g2d.dispose();
 
      FileOutputStream fos = new FileOutputStream(filename);
      if (filename.endsWith(".jpg")) {
         JPGWrite(scaledBi, fos);
      }
      fos.close();
   }
 
   public static void JPGWrite(BufferedImage bi, OutputStream out) throws IOException {
      JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
      JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
      param.setQuality(JPG_QUALITY, true);
      encoder.encode(bi, param);
   }
 
   public static BufferedImage toBufferedImage(Image image) {
      BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), 
                                           BufferedImage.TYPE_INT_RGB); 
 
      // copy the original image
      Graphics g = bi.createGraphics();
    
      g.drawImage(image, 0, 0, null);
      g.dispose();
 
      return bi;
   }
}

Converting an InputStream to a Reader

An InputStream is byte oriented. A Reader is Character oriented. To hook up an InputStream to a Reader and have the conversion done automatically for you, use the class InputStreamReader.

Main.java:

import java.io.*;
 
public class Main {   
   public static void main(String[] args) throws Exception {
      byte[] b = new byte[] { 65, 66, 67 };
 
      ByteArrayInputStream bais = new ByteArrayInputStream(b);
      InputStreamReader isr = new InputStreamReader(bais);
      int k;
      while ((k = isr.read()) > -1) {
         System.out.println((char) k);
      }
   }
}

outputs:

A
B
C

Using a PipedReader and PipedWriter

A pipe is a communication channel between two threads. It is typically used in producer-consumer systems where one thread produces data (producer) necessary for the other thread (consumer) to operate. In Java, a pipe is implemented by connecting a PipedReader instance to a PipedWriter instance:

   PipedWriter producer = new PipedWriter();
   PipedReader consumer = new PipedReader(producer);

All data written to producer will be stored in a pipe (buffer) until the consumer reads it.

In this example, a thread mimics the behavior of scores for 10 contestants coming in by generating random numbers between [0, 10[. Another thread keeps track of the total votes per contestant.

Main.java:

import java.util.*;
import java.io.*;
 
public class Main {   
   public static void main(String[] args) throws Exception {
      PipedWriter producer = new PipedWriter();
      PipedReader consumer = new PipedReader(producer);
 
      NumberProducer np = new NumberProducer(producer);
      NumberConsumer nc = new NumberConsumer(consumer);
 
      np.start();
      nc.start();
   }
}
 
class NumberProducer extends Thread
{
   BufferedWriter bw;
 
   public NumberProducer(Writer w) {
      this.bw = new BufferedWriter(w);
   }
 
   // thread continually produces random votes
   public void run() {
      try {
         Random r = new Random();
         while (true) {
            String vote = "" + Math.abs((r.nextInt() % 10));
 
            bw.write(vote);
            bw.newLine();
            bw.flush();
 
            sleep(10);
         }  
      }   
      catch(IOException e) {
         System.err.println(e);
      }
      catch(InterruptedException e) {
         System.err.println(e);
      }      
   }
}
 
class NumberConsumer extends Thread
{
   BufferedReader br;
   int[] votes = new int[10];
  
   public NumberConsumer(Reader r) {
      br = new BufferedReader(r);
   }
 
   public void run() {
      try {
         String line;
         int count = 0;
         while ((line = br.readLine()) != null) {
            int contestant = Integer.parseInt(line);
            votes[contestant]++;
 
            count++;
            if (count % 100 == 0) 
               printVotes();
         }      
      }   
      catch(IOException e) {
         System.err.println(e);
      }
   }
 
   public void printVotes() {
      for (int i=0; i<votes.length; i++) {
         System.out.println("Contestant #" + i + ": " + votes[i]);
      }
      System.out.println("——————");
   }
}

outputs:

Contestant #0: 7
Contestant #1: 8
Contestant #2: 10
Contestant #3: 15
Contestant #4: 5
Contestant #5: 11
Contestant #6: 4
Contestant #7: 15
Contestant #8: 16
Contestant #9: 9
------------------
Contestant #0: 22
Contestant #1: 22
Contestant #2: 26
Contestant #3: 21
Contestant #4: 11
Contestant #5: 18
Contestant #6: 13
Contestant #7: 26
Contestant #8: 26
Contestant #9: 15
------------------
Contestant #0: 32
Contestant #1: 29
Contestant #2: 37
Contestant #3: 30
Contestant #4: 29
Contestant #5: 31
Contestant #6: 22
Contestant #7: 35
Contestant #8: 34
Contestant #9: 21
------------------
. . .

Generating a mouse event programatically in Swing

In JDK 1.3, you can use the Robot class, very useful for testing purposes. The following example creates a JFrame with a button and left clicks in the center.

Main.java:

import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import java.awt.*;
  
public class Main extends JFrame
{
   public Main() throws Exception {
      JButton button = new JButton("This button will be clicked");
      getContentPane().add(button);
      pack();
 
      button.addActionListener(new ActionListener() {
         public void actionPerformed(ActionEvent evt) {
            System.out.println("Button was clicked!");
         }
      });
  
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }
      });
   }
 
   public void generateClickEvent() throws Exception {
      Robot robot = new Robot();
      robot.mouseMove((int) (getSize().getWidth() / 2), (int) (getSize().getHeight() / 2));
      robot.mousePress(InputEvent.BUTTON1_MASK);
      robot.mouseRelease(InputEvent.BUTTON1_MASK);
   }
  
   public static void main(String args[]) throws Exception {
      Main main = new Main();
      main.show();
      try { Thread.sleep(1000); } catch(InterruptedException e) { } 
      main.generateClickEvent();
   }
}