Articles
Java Platform, Standard Edition
|
| By Robert Eckstein, January 2006 |
|
| |
Version 6 of the Java Platform, Standard Edition contains a number of features that make programming with Java technology easier. In this article, we discuss four new features that allow you to do the following:
Component objects to the tabs of a
JTabbedPane
Use the popular
SwingWorker class in conjunction with your Java Foundation Classes/Swing (JFC/Swing) applications
If the JSR 270 Expert Group approves these features through the Java Community Process program, you can expect to find them in the final release of Java SE 6.
Note: To run the code in this article, you must download and install the latest build.
| |
Starting with build 31, you can now set the readable, writable, and executable flag of a file in the local file system. This functionality has been added to the
java.io.File class with the following methods:
public boolean setReadable(boolean readable, boolean ownerOnly)
public boolean setReadable(boolean readable)
public boolean setWritable(boolean writable, boolean ownerOnly)
public boolean setWritable(boolean writable)
public boolean setExecutable(boolean executable, boolean ownerOnly)
public boolean setExecutable(boolean executable)
|
If you're working with a UNIX system, these methods should be very familiar -- they mirror much of the functionality of the
chmod command. These methods attempt to set the appropriate permission flag for the file or directory represented by the current
File object. If the optional second parameter is set to
true, the permission applies only to the owner flag. Otherwise, these methods are applied to all users. Note that if the underlying file system is unable to distinguish the owner's permission from that of others, as is the case with some versions of Microsoft Windows, then the permission will apply to everybody, regardless of the value passed in.
If you're a Windows user with an NT file system (NTFS), you should read this document, which explains how to reach options that control various user permissions for files.
As you might expect, each of these methods will fail (that is, return
false) if the user does not have permission to change the access permissions of this abstract pathname. These methods can also throw a
java.lang.SecurityException if a Java security manager exists and its
checkRead()/checkWrite()/checkExecute() method denies access to the file.
Table 1 shows the typical result of running these commands on various file systems, along with the applicability of the command on the target operating system.
|
Table 1. The
java.io.File Permission Operations on Common OS File Systems
|
|
|
|
Windows XP
|
Linux
|
Solaris
|
|
|---|---|---|---|---|
| |
||||
setReadable(true) returns
|
true
|
true Equivalent to
chmod +r
|
true Equivalent to
chmod +r
|
|
setReadable(false) returns
|
false File readability cannot be set to
false in Windows
|
true Equivalent to
chmod -r
|
true Equivalent to
chmod -r
|
|
setWritable(true) returns
|
true Toggles the Windows read-only file property
|
true Equivalent to
chmod +w
|
true Equivalent to
chmod +w
|
|
setWritable(false) returns
|
true Toggles the Windows read-only file property
|
true Equivalent to
chmod -w
|
true Equivalent to
chmod -w
|
|
setExecutable(true) returns
|
true
|
true Equivalent to
chmod +x
|
true Equivalent to
chmod +x
|
|
setExecutable(false) returns
|
false File executability cannot be set to
false in Windows
|
true Equivalent to
chmod -x
|
true Equivalent to
chmod -x
|
|
| |
||||
The methods of determining whether a file is readable, writable, or executable remain from the previous version of this platform, Java 2 Platform, Standard Edition (J2SE) 5.0.
public boolean canRead();
public boolean canWrite();
public boolean canExecute();
|
| |
In addition to allowing you to set file and directory permissions, Java SE 6 gives you three new methods to determine the amount of space available on the partition represented by a
java.io.File object:
public long getTotalSpace();
public long getFreeSpace();
public long getUsableSpace();
|
Each of these methods returns the requested size, in bytes, of the partition represented by the
java.io.File or
0L if a partition cannot be obtained from the
File object.
With
getFreeSpace() and
getUsableSpace(), the returned number of unallocated bytes is, according to the
documentation, "a hint, but not a guarantee, that it is possible to use most or all of these bytes. The number of unallocated bytes is most likely to be accurate immediately after this call. It is likely to be made inaccurate by any external I/O operations including those made on the system outside of this virtual machine."
What's the difference between these two methods? The
getFreeSpace() method returns an instantaneous count of the amount of free space on the partition. The
getUsableSpace() method, on the other hand, contains extra functionality to check for write permissions and other operating system restrictions, which returns a better estimate of how much space will be available. If you want to determine whether you have enough disk space before writing to a file,
getUsableSpace() will typically give you a more accurate estimate. Note that both of these methods can throw a
SecurityException if a security manager has been installed and it denies a call to
RuntimePermission("getFileSystemAttributes")
JTabbedPane With Components
| |
This is a subtle but valuable change in Swing's
JTabbedPane. Previously with
JTabbedPane, you were limited to representing a tab with a
String, an
Icon, or a combination of both. In addition, you could add a tooltip to the tab if you wanted. Starting with build 39, it is now possible to use a
Component to represent the tab in a
JTabbedPane. Although this may bring to mind a number of possibilities, the most common use of this feature will be to add a Close button that will remove the tab from the
JTabbedPane.
A recent
blog entry by Sun programmer and Swing engineer Alexander Potochkin on this subject specifies the three new methods that have been added to
JTabbedPane.
You can set a
Component as a tab using the following method:
public void setTabComponentAt(int index, Component component) |
You can get this component with the following method:
public Component getTabComponentAt(int index) |
You can test whether a component is used in this
JTabbedPane with this method:
public int indexOfTabComponent(Component tabComponent) |
Here is sample source code for a tabbed pane that allows you to add and remove tabs dynamically from a
JTabbedPane. Note that in this example we have created a
JPanel that consists of two components: a
JLabel on the left side of the panel (
BorderLayout.WEST) and a button with an
ImageIcon on the right side of the panel (
BorderLayout.EAST). The graphic used is a 10x10 pixel
gif that consists of a small X. In order to keep the size of the button small, we reset its preferred size to the icon's width and height plus two.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TabbedPaneExample implements ActionListener {
private JFrame frame;
private JTabbedPane tabbedPane;
private JButton addTabButton;
private ImageIcon closeXIcon;
private Dimension closeButtonSize;
private int tabCounter = 0;
public TabbedPaneExample() {
// Create the tabbed pane.
tabbedPane = new JTabbedPane();
// Create a button that users can press to add a tab
// to the tabbed pane.
addTabButton = new JButton("Add Tab");
addTabButton.addActionListener(this);
// Create a frame to hold the tabbed pane.
frame = new JFrame();
// Create an image icon of the small 'X' for use with a close
// button on teach tab. The gif loaded is a 10x10 graphic
// with transparency on areas that are not black.
closeXIcon = new ImageIcon("C:/CloseX.gif");
// Create a Dimension that can be used to size the close
// buttons.
closeButtonSize = new Dimension(
closeXIcon.getIconWidth()+2,
closeXIcon.getIconHeight()+2);
// Add the tabbed pane to the center of the graphic and the
// "Add Tab" button to the south. Then pack it, size it,
// and show it.
frame.add(tabbedPane, BorderLayout.CENTER);
frame.add(addTabButton, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setMinimumSize(new Dimension(300, 300));
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
final JPanel content = new JPanel();
// Create a panel that represents the tab and ensure that it
// is transparent.
JPanel tab = new JPanel();
tab.setOpaque(false);
// Create a label and a Close button for the tab. Be sure to
// set its preferred size to nearly the size of the icon, and
// create an action listener that will locate the tab and
// remote it from the tabbed pane.
JLabel tabLabel = new JLabel("Tab " + (++tabCounter));
JButton tabCloseButton = new JButton(closeXIcon);
tabCloseButton.setPreferredSize(closeButtonSize);
tabCloseButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int closeTabNumber =
tabbedPane.indexOfComponent(content);
tabbedPane.removeTabAt(closeTabNumber);
}
});
tab.add(tabLabel, BorderLayout.WEST);
tab.add(tabCloseButton, BorderLayout.EAST);
// Add the tab to the tabbed pane. Note that the first
// parameter, which would ordinarily be a String that
// represents the tab title, is null.
tabbedPane.addTab(null, content);
// Instead of using a String/Icon combination for the tab,
// use our panel instead.
tabbedPane.setTabComponentAt(tabbedPane.getTabCount()-1, tab);
}
public static void main(String[] args) {
TabbedPaneExample main = new TabbedPaneExample();
}
}
|
The result is shown in Figure 1.
|
|
Figure 1. A
JTabbedPane With Several
JComponents as Tabs
|
Note that Alexander Potochkin's
blog entry takes a different approach and subclasses
JButton with one that overrides
paintComponent() and draws its own ("X"). This more complex approach is very useful if you don't want to distribute a
gif with your code.
| |
Most Swing programmers know that whenever they write event-handling code, such as an
ActionListener that gets called when a button is pressed, events need to be processed quickly. You never want to spend any more time than you have to processing on the event-handling thread, or your Swing graphical user interface (GUI) will become nonresponsive and be unable to repaint itself efficiently. Taking on a larger task from the event dispatch thread often means kicking off a separate "worker" thread from the event dispatch thread and letting that run in the background. Thus, when writing a multithreaded application using Swing, a programmer needs to keep these two rules in mind:
Because this means that there are at least two threads at play, it helps to have a class that can handle interthread communication. The good news is that the
SwingWorker class, which has been a popular solution for some time with Swing programmers, is included in the latest release.
Here is the official declaration of
SwingWorker from the Javadocs.
public abstract class
SwingWorker<T,V> extends
Object
implements
RunnableFuture<T>
|
Note that the declaration contains two generic type variables:
T and
V. If you're not familiar with type variables, be sure to read about
Generics, a feature that was introduced in J2SE 5.0. Here are the definitions:
T: the result type returned by this
SwingWorker's
doInBackground() and
get() methods
V: the type used for carrying out intermediate results by this
SwingWorker's
publish() and
process() methods
We'll talk about these methods shortly. First, however, let's introduce the thread architecture that is used with a
SwingWorker. To quote from the Javadocs: "There are three threads involved in the life cycle of a
SwingWorker:
execute()
method is called on this thread. It schedules
SwingWorker for the execution on a
worker thread and returns immediately. One can wait for the
SwingWorker to complete using one of two
get() methods.
doInBackground()
method is called on this thread. This is where all background activities should happen. To notify
PropertyChangeListeners about bound properties changes, use the
firePropertyChange
and
getPropertyChangeSupport()
methods. By default, two bound properties are available:
state and
progress.
Event dispatch thread: All Swing-related activities occur on this thread.
SwingWorker invokes the
process() and
done() methods and notifies any
PropertyChangeListeners on this thread."
Typically, the current thread on which you instantiate the
SwingWorker subclass is the event dispatch thread. The
SwingWorker then usually follows this series of events:
execute() method is called or run on the event dispatch thread.
SwingWorker notifies any
PropertyChangeListeners that its state has shifted to
SwingWorker.StateValue.STARTED.
doInBackground() method is executed on the worker thread.
doInBackground() method completes, the
done() method is executed on the current thread.
SwingWorker notifies any
PropertyChangeListeners that its state has shifted to
SwingWorker.StateValue.DONE.
While in the
doInBackground() method, you can set an integer progress property that indicates the current progress of the worker thread using the following method:
protected void setProgress(int progress);
|
Once this method is called, a property change event is fired by
SwingWorker to all registered listeners to notify them of the updated progress value.
You can set or add to the final results of the worker thread using one of two methods:
protected void process(V... chunks);
protected void publish(V... chunks);
|
The latter of these methods,
publish(), will take a variable number of objects of some intermediate type
V and send them to the
process() method for processing. You will typically call the
process() method from the
doInBackground() thread. The
process() method should always be overridden to accept the incoming
V parameters and concatenate these intermediate objects somehow into a type
T. How the
process() method accomplishes this task, of course, depends on the type parameters that are specified in your subclass of
SwingWorker.
Meanwhile, on the current thread, you can call one of two
get() methods to retrieve the results of the worker thread. The first method,
get(), blocks indefinitely until the worker thread has completed. The second method will block for the specified amount of time before retrieving whatever results have been processed.
public T get();
public T get(long timeout, TimeUnit unit);
|
If you wish to cancel the worker thread before it has finished executing, you can call the
cancel() method from the current thread.
public final boolean cancel(boolean mayInterruptIfRunning)
|
Here, the
mayInterruptIfRunning parameter specifies whether the thread executing this task should be interrupted in an attempt to stop the task. Note that calling the
cancel() method will fail if the task has already completed, if it has already been cancelled, or if it could not be cancelled for some other reason. If, however, calling the method returns
true and this task has not already started when the
cancel() method is called, the
SwingWorker should never execute.
| |
Although these features are largely independent of each other, they represent the desire of the development team to address some of the smaller requests that the Java development community has made. In particular, learning to use the
SwingWorker class is a must when creating Swing applications, and it relieves the programmer from troubleshooting complex GUI threading issues. As always, remember that these features are subject to the approval of the JSR 270 Expert Group before they can become part of the final version of Java SE 6.
| |
Download Java SE 6
Documentation for
java.io.File
Documentation for
javax.swing.JTabbedPane
Documentation for
javax.swing.SwingWorker
Using the
SwingWorker Class
(in The Java Tutorial)
Download the
TabbedPaneExample source as a NetBeans IDE project
. If you are interested in only the source code, see the
/src directory inside the distribution.
More information on desktop features in Java SE 6