Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures

Home

Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips

Books

Submit News
Search the site here...
Search...
 

Swing Chapter 14. (The basics) Dialogs. Easy for reading, Click here!

Custom Search
Swing Chapter 14. (The basics) Dialogs. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 5/5 



Previous Page Previous Page (4/5)
Subpages: 1. Dialogs and choosers overview 
2.
Adding an "About" dialog 
3.
JOptionPane message dialogs 
4. Customizing JColorChooser 
5. Customizing JFileChooser 

14.5  Customizing JFileChooser

Examples using JFileChooser to load and save files are scattered throughout the whole book. In this section we'll take a closer look at the more advanced features of this component through building a powerful JAR and ZIP archive creation, viewing, and extraction tool. We will see how to implement a custom FileView and FileFilter, and how to access and manipulate the internals of JFileChooser to allow multiple file selection and add our own components. Since this example deals with Java 2 archive functionality, we will first briefly summarize the classes from the java.util.zip and java.util.jar packages we will be using.

Note: The interface presented in this section is extremely basic and professional implementations would surely construct a more ellaborate GUI. We have purposely avoided this here due to the complex nature of the example, and to avoid straying from the JFileChooser topics central to its construction.

14.5.1  ZipInputStream

class java.util.zip.ZipInputStream

This class represents a filtered input stream which uncompresses ZIP archive data. The constructor takes an instance of InputStream as parameter. Before reading data from this stream we need to find a ZIP file entry using the getNextEntry() method. Each entry corresponds to an archived file. We can read() an an array of bytes from an entry, and then close it using the closeEntry() method when reading is complete.

14.5.2  ZipOutputStream

class java.util.zip.ZipOutputStream

This class represents a filtered output stream which takes binary data and writes them into an archive in the compressed (default) or uncompressed (optional) form. The constructor of this class takes an instance of OutputStream as parameter. Before writing data to this stream we need to create a new ZipEntry using the putNextEntry() method. Each ZipEntry corresponds to an archived file. We can write() an array of bytes to a ZipEntry, and close it using the closeEntry() method when writing is complete. We can also specify the compression method for storing ZipEntrys using ZipOutputStream's setMethod() method.

14.5.3  ZipFile

class java.util.zip.ZipFile

This class encapsulates a collection of ZipEntrys and represents a read-only ZIP archive. We can fetch an Enumeration of the contained ZipEntrys using the entries() method. The size() method tells us how many files are contained and getName() returns the archive's full path name. We can retrieve an InputStream for reading the contents of a contained ZipEntry using its getInputStream() method. When we are done reading we are expected to call the close() method to close the archive.

14.5.4  ZipEntry

class java.util.zip.ZipEntry

This class represents a single archived file or directory within a ZIP archive. It allows retrieval of its name and can be cloned using the clone() method. Using typical set/get accessors, we can access a ZipEntry's compression method, CRC-32 checksum, size, modification time, compression method, and a comment attachment. We can also query whether or not a ZipEntry is a directory using its isDirectory() method.

14.5.5  The java.util.jar package

This package contains a set of classes for managing Java archives (JAR files). The relevant classes that we will be dealing with (JarEntry, JarFile, JarInputStream, and JarOutputStream) are direct subclasses of the zip package counterparts, and thus inherit the functionality described above.

14.5.6 Manifest

class java.util.jar.Manifest

This class represents a JAR Manifest file. A Manifest contains a collection of names and their associated attributes specific both for the archive in whole and for a particular JarEntry (i.e. a file or directory in the archive). We are not concerned with the details of JAR manifest files in this chapter, and it suffices to say that the JarOutputStream constructor takes a Manifest instance as parameter, along with an OutputStream.

In this example we create a simple, two-button GUI with a status bar (a label). One button corresponds to creating a ZIP or JAR archive, and another corresponds to decompressing an archive. In each case two JFileChoosers are used to perform the operation. The first chooser is used to allow entry of an archive name to use, or selection of an archive to decompress. The second chooser is used to allow selection of files to compress or decompress. (As noted above, more professional implementations would most likely include a more elaborate GUI.) A custom FileView class is used to represent ZIP and JAR archives using a custom icon, and a FileFilter class is constructed to only allow viewing of ZIP (".zip") and JAR (".jar") files. We also work with JFileChooser as a container by adding our own custom component, taking advantage of the fact that it uses a y-oriented BoxLayout to organize its children. Using similar tactics we show how to gain access to its JList (used for the display and selection of files and directories) to allow multiple selection (an unimplemented feature in JFileChooser as of Java 2 FCS).

Figure 14.12 ZIP/JAR Manager JFileChooser example at startup.

<<file figure14-12.gif>>

Figure 14.13 First step in creating an archive; using JFileChooser to select an archive location and name .

<<file figure14-13.gif>>

Figure 14.14 Second step in creating an archive; using JFileChooser to select archive content.

<<file figure14-14.gif>>

Figure 14.15 First step in uncompressing an archive; using a custom component in JFileChooser.

<<file figure14-15.gif>>

Figure 14.16 Second step in uncompressing an archive; using JFileChooser to select a destination directory.

<<file figure14-16.gif>>

The Code: ZipJarManager.java

see \Chapter14\4

import java.awt.*;

import java.awt.event.*;

import java.io.*;

import java.util.*;

import java.util.zip.*;

import java.util.jar.*;

import java.beans.*;

import java.text.SimpleDateFormat;

import javax.swing.*;

import javax.swing.event.*;

import javax.swing.border.*;

public class ZipJarManager extends JFrame

{

  public static int BUFFER_SIZE = 10240;

  protected JFileChooser chooser1;

  protected JFileChooser chooser2;

  protected File  m_currentDir = new File(".");

  protected SimpleFilter m_zipFilter;

  protected SimpleFilter m_jarFilter;

  protected ZipFileView  m_view;

  protected JButton m_btCreate;

  protected JButton m_btExtract;

  protected JLabel  m_status;

  protected JList m_zipEntries;

  protected File  m_selectedFile;

  public ZipJarManager()

  {

    super("ZIP/JAR Manager");

    JWindow jwin = new JWindow();

    jwin.getContentPane().add(new JLabel(

      "Loading ZIP/JAR Manager...", SwingConstants.CENTER));

    jwin.setBounds(200,200,200,100);

    jwin.setVisible(true);

    chooser1 = new JFileChooser();

    chooser2 = new JFileChooser();

    JPanel p = new JPanel(new GridLayout(3, 1, 10, 10));

    p.setBorder(new EmptyBorder(10, 10, 10, 10));

    m_btCreate = new JButton("Create new Archive");

    ActionListener lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        m_btCreate.setEnabled(false);

        m_btExtract.setEnabled(false);

        createArchive();

        m_btCreate.setEnabled(true);

        m_btExtract.setEnabled(true);

      }

    };

    m_btCreate.addActionListener(lst);

    m_btCreate.setMnemonic('c');

    p.add(m_btCreate);

    m_btExtract = new JButton("Extract from Archive");

    lst = new ActionListener() {

      public void actionPerformed(ActionEvent e) {

        m_btCreate.setEnabled(false);

        m_btExtract.setEnabled(false);

        extractArchive();

        m_btCreate.setEnabled(true);

        m_btExtract.setEnabled(true);

      }

    };

    m_btExtract.addActionListener(lst);

    m_btExtract.setMnemonic('e');

    p.add(m_btExtract);

    m_status = new JLabel();

    m_status.setBorder(new BevelBorder(BevelBorder.LOWERED,

      Color.white, Color.gray));

    p.add(m_status);

    m_zipFilter = new SimpleFilter("zip", "ZIP Files");

    m_jarFilter = new SimpleFilter("jar", "JAR Files");

    m_view = new ZipFileView();

    chooser1.addChoosableFileFilter(m_zipFilter);

    chooser1.addChoosableFileFilter(m_jarFilter);

    chooser1.setFileView(m_view);

    chooser1.setMultiSelectionEnabled(false);

    chooser1.setFileFilter(m_jarFilter);

    javax.swing.filechooser.FileFilter ft =

      chooser1.getAcceptAllFileFilter();

    chooser1.removeChoosableFileFilter(ft);

    getContentPane().add(p, BorderLayout.CENTER);

    setBounds(0,0,300,150);

    WindowListener wndCloser = new WindowAdapter() {

      public void windowClosing(WindowEvent e) {

        System.exit(0);

      }

    };

    addWindowListener(wndCloser);

    jwin.setVisible(false);

    jwin.dispose();

    setVisible(true);

  }

  public void setStatus(String str) {

    m_status.setText(str);

    m_status.repaint();

  }

  protected void createArchive() {

    chooser1.setCurrentDirectory(m_currentDir);

    chooser1.setDialogType(JFileChooser.SAVE_DIALOG);

    chooser1.setDialogTitle("New Archive");

    chooser1.setPreferredSize(new Dimension(450,300));

    if (chooser1.showDialog(this, "OK") !=

      JFileChooser.APPROVE_OPTION)

        return;

    m_currentDir = chooser1.getCurrentDirectory();

    final File archiveFile = chooser1.getSelectedFile();

    if (!isArchiveFile(archiveFile))

      return;

    // Show chooser to select entries.

    chooser2.setCurrentDirectory(m_currentDir);

    chooser2.setDialogType(JFileChooser.OPEN_DIALOG);

    chooser2.setDialogTitle("Select content for "

      + archiveFile.getName());

    chooser2.setMultiSelectionEnabled(true);

    chooser2.setFileSelectionMode(JFileChooser.FILES_ONLY);

    if (chooser2.showDialog(this, "Add") !=

      JFileChooser.APPROVE_OPTION)

        return;

    m_currentDir = chooser2.getCurrentDirectory();

    final File[] selected = getSelectedFiles(chooser2);

    String name = archiveFile.getName().toLowerCase();

    if (name.endsWith(".zip")) {

      Thread runner = new Thread() {

        public void run() {

          createZipArchive(archiveFile, selected);

        }

      };

      runner.start();

    }

    else if (name.endsWith(".jar")) {

      Thread runner = new Thread() {

        public void run() {

          createJarArchive(archiveFile, selected);

        }

      };

      runner.start();

    }

  }

  protected void createZipArchive(File archiveFile,

   File[] selected) {

    try {

      byte buffer[] = new byte[BUFFER_SIZE];

      // Open archive file.

      FileOutputStream stream =

        new FileOutputStream(archiveFile);

      ZipOutputStream out = new ZipOutputStream(stream);

      for (int k=0; k<selected.length; k++) {

        if (selected[k]==null || !selected[k].exists() ||

          selected[k].isDirectory())

            continue;  // Just in case...

        setStatus("Adding "+selected[k].getName());

        // Add archive entry.

        ZipEntry zipAdd = new ZipEntry(selected[k].getName());

        zipAdd.setTime(selected[k].lastModified());

        out.putNextEntry(zipAdd);

        // Read input & write to output.

        FileInputStream in = new FileInputStream(selected[k]);

        while (true) {

          int nRead = in.read(buffer, 0, buffer.length);

          if (nRead <= 0)

            break;

          out.write(buffer, 0, nRead);

        }

        in.close();

      }

      out.close();

      stream.close();

      setStatus("Adding completed OK");

    }

    catch (Exception e) {

      e.printStackTrace();

      setStatus("Error: "+e.getMessage());

      return;

    }

  }

  protected void createJarArchive(

   File archiveFile, File[] selected) {

    try {

      byte buffer[] = new byte[BUFFER_SIZE];

      // Open archive file.

      FileOutputStream stream =

        new FileOutputStream(archiveFile);

      JarOutputStream out = new JarOutputStream(stream,

        new Manifest());

      for (int k=0; k<selected.length; k++) {

        if (selected[k]==null || !selected[k].exists() ||

          selected[k].isDirectory())

            continue;  // Just in case...

        setStatus("Adding "+selected[k].getName());

        // Add archive entry.

        JarEntry jarAdd = new JarEntry(selected[k].getName());

        jarAdd.setTime(selected[k].lastModified());

        out.putNextEntry(jarAdd);

        // Write file to archive

        FileInputStream in = new FileInputStream(selected[k]);

        while (true) {

          int nRead = in.read(buffer, 0, buffer.length);

          if (nRead <= 0)

            break;

          out.write(buffer, 0, nRead);

        }

        in.close();

      }

      out.close();

      stream.close();

      setStatus("Adding completed OK");

    }

    catch (Exception ex) {

      ex.printStackTrace();

      setStatus("Error: "+ex.getMessage());

    }

  }

  protected void extractArchive() {

    chooser1.setCurrentDirectory(m_currentDir);

    chooser1.setDialogType(JFileChooser.OPEN_DIALOG);

    chooser1.setDialogTitle("Open Archive");

    chooser1.setPreferredSize(new Dimension(470,450));

    // Construct a JList to show archive entries.

    m_zipEntries = new JList();

    m_zipEntries.setSelectionMode(

      ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

    TabListCellRenderer renderer = new TabListCellRenderer();

    renderer.setTabs(new int[] {240, 300, 360});

    m_zipEntries.setCellRenderer(renderer);

    // Place entries list in a scroll pane and add it to chooser.

    JPanel p = new JPanel(new BorderLayout());

    p.setBorder(new CompoundBorder(

      new CompoundBorder(

        new EmptyBorder(5, 5, 5, 5),

        new TitledBorder("Files to extract:")),

      new EmptyBorder(5,5,5,5)));

    JScrollPane ps = new JScrollPane(m_zipEntries);

    p.add(ps, BorderLayout.CENTER);

    chooser1.add(p);

    PropertyChangeListener lst = new PropertyChangeListener() {

      SimpleDateFormat m_sdf = new SimpleDateFormat(

        "MM/dd/yyyy hh:mm a");

      DefaultListModel m_emptyModel = new DefaultListModel();

      public void propertyChange(PropertyChangeEvent e) {

        if (e.getPropertyName() ==

         JFileChooser.FILE_FILTER_CHANGED_PROPERTY) {

          m_zipEntries.setModel(m_emptyModel);

          return;

        }

        else if (e.getPropertyName() ==

         JFileChooser.SELECTED_FILE_CHANGED_PROPERTY) {

          File f = chooser1.getSelectedFile();

          if (f != null) { 

            if (m_selectedFile!=null && m_selectedFile.equals(f)

             && m_zipEntries.getModel().getSize() > 0) {

              return;

            }

            String name = f.getName().toLowerCase();

            if (!name.endsWith(".zip") && !name.endsWith(".jar")) {

              m_zipEntries.setModel(m_emptyModel);

              return;

            }

            try {

              ZipFile zipFile = new ZipFile(f.getPath());

              DefaultListModel model = new DefaultListModel();

              Enumeration en = zipFile.entries();

              while (en.hasMoreElements()) {

                ZipEntry zipEntr = (ZipEntry)en.nextElement();

                Date d = new Date(zipEntr.getTime());

                String str = zipEntr.getName()+'\t'+

                  zipEntr.getSize()+'\t'+m_sdf.format(d);

                model.addElement(str);

              }

              zipFile.close();

              m_zipEntries.setModel(model);

              m_zipEntries.setSelectionInterval(0,

                model.getSize()-1);

            }

            catch(Exception ex) {

              ex.printStackTrace();

              setStatus("Error: "+ex.getMessage());

            }

          }

        }

        else {

          m_zipEntries.setModel(m_emptyModel);

          return;

        }

      }

    };

    chooser1.addPropertyChangeListener(lst);

    chooser1.cancelSelection();

    if (chooser1.showDialog(this, "Extract") !=

     JFileChooser.APPROVE_OPTION) {

      chooser1.remove(p);

      chooser1.removePropertyChangeListener(lst);

      return;

    }

    m_currentDir = chooser1.getCurrentDirectory();

    final File archiveFile = chooser1.getSelectedFile();

    if (!archiveFile.exists() || !isArchiveFile(archiveFile)) {

      chooser1.remove(p);

      chooser1.removePropertyChangeListener(lst);

      return;

    }

    Object[] selObj = m_zipEntries.getSelectedValues();

    if (selObj.length == 0) {

      setStatus("No entries have been selected for extraction");

      chooser1.removePropertyChangeListener(lst);

      chooser1.remove(p);

      return;

    }

    final String[] entries = new String[selObj.length];

    for (int k=0; k<selObj.length; k++) {

      String str = selObj[k].toString();

      int index = str.indexOf('\t');

      entries[k] = str.substring(0, index);

    }

    // Show dialog to select output directory.

    chooser2.setCurrentDirectory(m_currentDir);

    chooser2.setDialogType(JFileChooser.OPEN_DIALOG);

    chooser2.setDialogTitle("Select destination directory for "

      + archiveFile.getName());

    chooser2.setMultiSelectionEnabled(false);

    chooser2.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);

    if (chooser2.showDialog(this, "Select") !=

     JFileChooser.APPROVE_OPTION) {

      chooser1.remove(p);

      chooser1.removePropertyChangeListener(lst);

      return;

    }

    m_currentDir = chooser2.getCurrentDirectory();

    final File outputDir = chooser2.getSelectedFile();

    Thread runner = new Thread() {

      public void run() {

        try {

          byte buffer[] = new byte[BUFFER_SIZE];

          // Open the archive file.

          FileInputStream stream =

            new FileInputStream(archiveFile);

          ZipInputStream in = new ZipInputStream(stream);

          // Find archive entry.

          while (true) {

            ZipEntry zipExtract = in.getNextEntry();

            if (zipExtract == null)

              break;

            boolean bFound = false;

            for (int k=0; k<entries.length; k++) {

              if (zipExtract.getName().equals(entries[k])) {

                bFound = true;

                break;

              }

            }

            if (!bFound) {

              in.closeEntry();

              continue;

            }

            setStatus("Extracting " + zipExtract.getName());

            // Create output file and check required directory.

            File outFile = new File(outputDir,

              zipExtract.getName());

            File parent = outFile.getParentFile();

            if (parent != null && !parent.exists())

              parent.mkdirs();

            // Extract unzipped file.

            FileOutputStream out = new FileOutputStream(outFile);

            while (true) {

              int nRead = in.read(buffer, 0, buffer.length);

              if (nRead <= 0)

                break;

              out.write(buffer, 0, nRead);

            }

            out.close();

            in.closeEntry();

          }

          in.close();

          stream.close();

          setStatus("Extracting completed OK");

        }

        catch (Exception ex) {

          ex.printStackTrace();

          setStatus("Error: "+ex.getMessage());

        }

      }

    };

    runner.start();

    chooser1.removePropertyChangeListener(lst);

    chooser1.remove(p);

  }

  public static File[] getSelectedFiles(JFileChooser chooser) {

    // Although JFileChooser won't give us this information,

    // we need it...

    Container c1 = (Container)chooser.getComponent(3);

    JList list = null;

    while (c1 != null) {

      Container c = (Container)c1.getComponent(0);

      if (c instanceof JList) {

        list = (JList)c;

        break;

      }

      c1 = c;

    }

    Object[] entries = list.getSelectedValues();

    File[] files = new File[entries.length];

    for (int k=0; k<entries.length; k++) {

      if (entries[k] instanceof File)

        files[k] = (File)entries[k];

    }

    return files;

  }

  public static boolean isArchiveFile(File f) {

    String name = f.getName().toLowerCase();

    return (name.endsWith(".zip") || name.endsWith(".jar"));

  }

  public static void main(String argv[]) {

    new ZipJarManager();

  }

}

class SimpleFilter extends javax.swing.filechooser.FileFilter

{

  private String m_description = null;

  private String m_extension = null;

  public SimpleFilter(String extension, String description) {

    m_description = description;

    m_extension = "."+extension.toLowerCase();

  }

  public String getDescription() {

    return m_description;

  }

  public boolean accept(File f) {

    if (f == null)

      return false;

    if (f.isDirectory())

      return true;

    return f.getName().toLowerCase().endsWith(m_extension);

  }

}

class ZipFileView extends javax.swing.filechooser.FileView

{

  protected static ImageIcon ZIP_ICON = new ImageIcon("zip.gif");

  protected static ImageIcon JAR_ICON = new ImageIcon("jar.gif");

  public String getName(File f) {

    String name = f.getName();

    return name.equals("") ? f.getPath() : name;

  }

  public String getDescription(File f) {

    return getTypeDescription(f);

  }

  public String getTypeDescription(File f) {

    String name = f.getName().toLowerCase();

    if (name.endsWith(".zip"))

      return "ZIP Archive File";

    else if (name.endsWith(".jar"))

      return "Java Archive File";

    else

      return "File";

  }

  public Icon getIcon(File f) {

    String name = f.getName().toLowerCase();

    if (name.endsWith(".zip"))

      return ZIP_ICON;

    else if (name.endsWith(".jar"))

      return JAR_ICON;

    else

      return null;

  }

  public Boolean isTraversable(File f) {

    return (f.isDirectory() ? Boolean.TRUE : Boolean.FALSE);

  }

}

// Class TabListCellRenderer is taken from Chapter 10,

// section 10.3 without modification.

Understanding the Code

Class ZipJarManager

This class extends JFrame to provide a very simple GUI for our ZIP/JAR archive manager application. One class variable is defined:

int BUFFER_SIZE: used to define the size of an array of bytes for reading and writing files.

Instance variables:

JFileChooser chooser1: file chooser used to select an archive for creation or extraction.

JFileChooser chooser2: file chooser used to select files to include or in an archive or files to extract from an archive.

File m_currentDir: the currently selected directory.

SimpleFilter m_zipFilter: filter for files with a ".zip" extension.

SimpleFilter m_jarFilter: filter for files with a ".jar"extension.

ZipFileView m_view: a custom FileView implementation for JAR and ZIP files.

JButton m_btCreate: initiates the creation of an archive.

JButton m_btExtract: initiates extraction from an archive.

JLabel m_status: label do display status messages.

JList m_zipEntries: list component to display an array of entries in an archive.

File m_selectedFile: the currently selected archive file.

The ZipJarManager constructor first creates and displays a simple JWindow splash screen to let the user know that the application is loading (a progress bar might be more effective here, but we want to stick to the point). Our two JFileChoosers are then instatiated, which takes a significant amount of time, and then the buttons and label are created, encapsulated in a JPanel using a GridLayout, and added to the content pane. The first button titled "Create new Archive" is assigned an ActionListener which invokes createArchive(). The second button titled "Extract from Archive" is assigned an ActionListener which invokes extractArchive(). Our custom SimpleFilters and FileView are then instantiated, and assigned to chooser1. We then set chooser1's multiSelectionEnabled property to false, tell it to use the JAR filter initially, and remove the "All Files" filter. Finally, the splash window is disposed before FileChooser1 is made visible.

The setStatus() method simply assigns a given String to the m_status label.

The createArchive() method is used to create a new archive file using both JFileChoosers. First we set chooser1's title to "New Archive", its type to SAVE_DIALOG, and assign it a new preferred size (the reason we change its size will become clear below). Then we show chooser1 in a dialog to prompt the user for a new archive name. If the dialog is dismissed by pressing "Cancel" or the close button we do nothing and return. Otherwise we store the current directory in our m_currentDir instance variable and create a File instance corresponding to the file specified in the chooser.

Interestingly, JFileChooser does not check whether the filename entered in its text field is valid with respect to its filters when the approve button pressed. So we are forced to check if our File's name has a ".zip" or ".jar" extension manually using our custom isArchiveFile() method. If this method returns false we do nothing and return. Otherwise we set up chooser2 to allow multiple selections to make up the content of the archive, and only allow file selections (by setting the fileSelectionMode property to FILES_ONLY) to avoid overcomplicating our archive processing scheme. Also note that we set the dialog title to specify the name of the archive we are creating.

We use JFileChooser's showDialog() method to display chooser2 in a JDialog and assign "Add" as its approve button text. If the approve button is not pressed we do nothing and return. Otherwise we create an array of Files to be placed in the specified archive using our custom getSelectedFiles() method (see below). Finally, we invoke our createZipArchive() method if the selected archive file has a ".zip" extension, or createJarArchive() if it has a ".jar" extension. These method calls are wrapped in separate threads to avoid clogging up the event dispatching thread.

The createZipArchive() method takes two parameters: a ZIP archive file and an array of the files to be added to the archive. It creates a ZipOutputStream to write the selected archive file. Then for each of files in the given array it creates a ZipEntry instance, places it in the ZipOutputStream, and performs standard read/write operations until all data has been written into the archive. The status label is updated, using our setStatus() method, each time a file is written and when the operation completes, to provide feedback duing long operations.

The createJarArchive() method works almost identically to createZipArchive(), using the corresponding java.util.jar classes. Note that a default Manifest instance is supplied to the JarOutputStream constructor.

The extractArchive() method extracts data from an archive file using both JFileChoosers. First we assign chooser1 a preferred size of 470x450 because we will be adding a custom component which requires a bit more space than JFileChooser normally offers (this is also why we set the preferred size of chooser1 in our createArchive() method). Since JFileChooser is derived from JComponent, we can add our own components to it just like any other container. A quick look at the source code shows that JFileChooser uses a y-oriented BoxLayout. This implies that new components added to a JFileChooser will be placed below all other existing components (see chapter 4 for more about BoxLayout).

We take advantage of this knowledge and add a JList component, m_zipEntries, to allow selection of compressed entries to be extracted from the selected archive. This JList component receives an instance of our custom TabListCellRenderer as its cell renderer to process Strings with tabs (see chapter 10, section 10.3). The location of String segments between tabs are assigned using its setTabs() method. Finally, this list is placed in a JScrollPane to provide scrolling capabilities, and added to the bottom of the JFileChooser component.

A PropertyChangeListener is added to chooser1 to process the user's selection. This anonymous class maintains two instance variables:

SimpleDateFormat m_sdf: used to format file time stamps.

DefaultListModel m_emptyModel: assigned to m_zipEntries when non-archive files are selected, or when the file filter is changed.

This listener's propertyChange() method will receive a PropertyChangeEvent when, among other things, chooser1's selection changes. The selected file can then be retrieved using PropertyChangeEvent's getNewValue() method. If this file represents a ZIP or JAR archive, our implementation creates a ZipFile instance to read it's content, and retrieves an Enumeration of ZipEntries in this archive (recall that JarFile and JarEntry are subclasses of ZipFile and ZipEntry, allowing us to display the contents of a JAR or a ZIP archive identically). For each entry we form a String containing that entry's name, size, and time stamp. This String is added to a DefaultListModel instance. After each entry has been processed, this model is assign to our JList, and all items are initially selected. The user can then modify the selection to specify entries to be extracted from the archive.

Once the PropertyChangeListener is added, chooser1 is displayed with "Extract" as its approve button text. If it is dismissed we remove both the panel containing our list component and the PropertyChangeListener, as they are only temporary additions (remember that we use this same chooser to initiate creation of an archive in the createArchive() method). Otherwise, if the approve button is pressed we check whether the selected file exists and represents an archive. We then create an array of Objects corresponding to each selected item in our list (which are Strings). If no items are selected we report an error in our status label, remove our temporary component and listener, and return. Then we form an array of entry names corresponding to each selected item (which is the portion of each String before the appearance of the first tab character).

Now we need to select a directory to be used to extract the selected archive entries. We use chooser2 for this purpose. We set its fileSelectionMode property to DIRECTORIES_ONLY, and allow only single selection by setting its multiSelectionEnabled property to false. We then show chooser2 using "Select" for its approve button text. If it is dismissed we remove our our temporary component and listener, and return. Otherwise we start the extraction process in a separate thread.

To begin the extraction process we create a ZipInputStream to read from the selected archive file. Then we process each entry in the archive by retrieving a corresponding ZipEntry and verifying whether each ZipEntry's name matches a String in our previously obtained array of selected file names (from our list component that was added to chooser1). If a match is found we create a File instance to write that entry to. If a ZipEntry includes sub-directories, we create these sub-directories using File's mkdirs() method. Finally we perform standard read/write operations until all files have been extracted from the archive. Note that we update the status label each time a file is extracted and when the opertion completes.

The getSelectedFiles() method takes a JFileChooser instance as parameter and returns an array of Files selected in the given chooser's list. Interestingly JFileChooser provides the getSelectedFiles() method which does not work properly as of Java 2 FCS (it always returns null). In order to work around this problem, we use java.awt.Container functionality (which, as we know, all Swing components inherit) to gain access to JFileChooser's components. With a little detective work we found that the component with index 3 represents the central part of the chooser. This component contains several children nested inside one another. One of these child components is the JList component we need access to in order to determine the current selection state. So we can simply loop through these nested containers until we find the JList. As soon as we have gained access to this component we can retrieve an array of the selected objects using JList's getSelectedValues() method. As expected, these objects are instances of the File class, so we need only upcast each and return them in a File[] array.

Note: This solution should be considered a temporary bug workaround. We expect the getSelectedFiles() method to be implemented correctly in future releases, and suggest that you try substituting this method here to determine whether it has been fixed in the release you are working with.

Class SimpleFilter

This class represents a basic FileFilter that accepts files with a given String extension, and displays a given String description in JFileChooser's "Files of Type" combo box. We have already seen and discussed this filter in section 14.1.9. It is used here to create our JAR and ZIP filters in the FileChooser1 constructor.

Class ZipFileView

This class extends FileView to provide a more user-friendly graphical representation of ZIP and JAR files in JFileChooser. Two instance variables, ImageIcon ZIP_ICON and ImageIcon JAR_ICON, represent small images corresponding to each archive type: , jar.gif & , zip.gif. This class is a straight-forward adaptation of the sample FileView class presented in section 14.1.11.

Running the Code

Note that a JWindow is displayed as a simple splash screen because instantiation JFileChooser takes a significant amount of time. Press the "Create new Archive" button and select a name and location for the new archive file in the first file chooser that appears. Press its "OK" button and then select files to be added to that archive in the second chooser. Figure 14.12 shows FileChooser1 in action, and figures 14.13 and 14.14 show the first and second choosers that appear during the archive creation process.

Try uncompressing an existing archive. Press the "Extract from Archive" button and select an existing archive file in the first chooser that appears. Note the custom list component displayed in the bottom of this chooser, figure 14. 15 illustrates. Each time an archive is selected its contents are displayed in this list. Select entries to extract and press the "Extract" button. A second chooser will appear, shown in figure 14.16, allowing selection of a destination directory.

Bug Alert!: As of Java 2 FCS, PropertyChangeEvents are not always fired as expected when JFileChooser's selection changes. This causes the updating of our custom list component to fail occasionaly. We expect this to be fixed in a future release.



[ Return to Swing (Book) ]


Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color


[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3


[ More in News Section ]


Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy