You are on page 1of 13

Core Java Technologies Tech Tips

Tips, Techniques, and Sample Code


Welcome to the Core Java Technologies Tech Tips for July 27, 2004.
Here you'll get tips on using core Java technologies and APIs,
such as those in Java 2 Platform, Standard Edition (J2SE).
This issue covers:
* Using GridBagLayout
* Updating JAR Files
These tips were developed using Java 2 SDK, Standard Edition,
v 1.4.2.
This issue of the Core Java Technologies Tech Tips is written by
John Zukowski, president of JZ Ventures, Inc.
(http://www.jzventures.com).
You can view this issue of the Tech Tips on the Web at
http://java.sun.com/developer/JDCTechTips/2004/tt0727.html
See the Subscribe/Unsubscribe note at the end of this newsletter
to subscribe to Tech Tips that focus on technologies and products
in other Java platforms.
For more Java technology content, visit these sites:
java.sun.com - The latest Java platform releases, tutorials, and
newsletters.
java.net - A web forum for collaborating and building solutions
together.
java.com - The marketplace for Java technology, applications and
services.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - USING GRIDBAGLAYOUT
Layout managers abstract away the rules for the layout of child
components in a container. They help in the creation of
cross-platform graphical user interfaces. Instead of relying on
exact positioning, such as saying "place this button at position
(100, 250)," you provide a set of rules to follow in the form of
a layout manager. Then, through the layout manager, you position
graphical components, such as buttons and labels, by following
the rules. For instance, with the FlowLayout manager, components
are placed next to each other. The components wrap when the
screen width is too small to fit all of them on one line. With
BorderLayout, you place components on the edges of the screen,
using hints such as North, South, East, or West. Using the
simpler layout managers such as FlowLayout and BorderLayout, you
can place containers inside containers and in this way create
many desired screen layouts. For more complicated screens, and
as an alternative to placing containers inside containers, you
can use the more complex GridBagLayout manager and its

associated GridBagConstraints support class. The GridBagLayout


manager and the GridBagConstraints class allow you to build more
complicated screens inside a single container.
The basis of the GridBagLayout is positioning components inside
a grid. Another layout manager, GridLayout, also works with a
grid. GridLayout-managed components are typically positioned
based on the order in which components are added to the
container. The GridLayout doesn't offer the flexibility provided
by GridBagLayout. You get more explicitly positioned components
when using GridBagLayout, and you have additional options
available regarding the number of row and columns to span, how
to allocate additional screen area, and how much space to use
for padding around components.
To demonstrate, the following program, GridBagSample, creates
a screen with seven buttons in a three-by-three grid.
import java.awt.*;
import javax.swing.*;
public class GridBagSample {
// Shared empty insets
private static final Insets insets = new Insets(0,0,0,0);
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
JFrame frame = new JFrame("GridBagLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = frame.getContentPane();
c.setLayout(new GridBagLayout());
JButton one = new JButton("One");
GridBagConstraints constraints =
new GridBagConstraints(
0, 0, // gridx, gridy
1, 1, // gridwidth, gridheight
0.0, 0.0, // weightx, weighty
GridBagConstraints.CENTER, // anchor
GridBagConstraints.BOTH, // fill
// GridBagConstraints.NONE, // fill
// GridBagConstraints.VERTICAL, // fill
// GridBagConstraints.HORIZONTAL, // fill
insets, // insets
0, // ipadx
0); // ipady
c.add(one, constraints);
JButton two = new JButton("Two");
constraints =
new GridBagConstraints(
1, 0, // gridx, gridy
1, 1, // gridwidth, gridheight
1.0, 0.0, // weightx, weighty
GridBagConstraints.CENTER, // anchor
GridBagConstraints.BOTH, // fill
// GridBagConstraints.NONE, // fill
// GridBagConstraints.VERTICAL, // fill

// GridBagConstraints.HORIZONTAL, // fill
insets, // insets
0, // ipadx
0); // ipady
c.add(two, constraints);
JButton three = new JButton("Three");
constraints =
new GridBagConstraints(
2, 0, // gridx, gridy
1, 1, // gridwidth, gridheight
0.0, 0.0, // weightx, weighty
GridBagConstraints.CENTER, // anchor
GridBagConstraints.BOTH, // fill
// GridBagConstraints.NONE, // fill
// GridBagConstraints.VERTICAL, // fill
// GridBagConstraints.HORIZONTAL, // fill
insets, // insets
0, // ipadx
0); // ipady
c.add(three, constraints);
JButton four = new JButton("Four");
constraints =
new GridBagConstraints(
0, 1, // gridx, gridy
2, 1, // gridwidth, gridheight
1.0, 1.0, // weightx, weighty
GridBagConstraints.CENTER, // anchor
GridBagConstraints.BOTH, // fill
// GridBagConstraints.NONE, // fill
// GridBagConstraints.VERTICAL, // fill
// GridBagConstraints.HORIZONTAL, // fill
insets, // insets
0, // ipadx
0); // ipady
c.add(four, constraints);
JButton five = new JButton("Five");
constraints =
new GridBagConstraints(
2, 1, // gridx, gridy
1, 2, // gridwidth, gridheight
0.0, 1.0, // weightx, weighty
GridBagConstraints.CENTER, // anchor
GridBagConstraints.BOTH, // fill
// GridBagConstraints.NONE, // fill
// GridBagConstraints.VERTICAL, // fill
// GridBagConstraints.HORIZONTAL, // fill
insets, // insets
0, // ipadx
0); // ipady
c.add(five, constraints);
JButton six = new JButton("Six");
constraints =
new GridBagConstraints(
0, 2, // gridx, gridy
1, 1, // gridwidth, gridheight
0.0, 0.0, // weightx, weighty

GridBagConstraints.CENTER, // anchor
GridBagConstraints.BOTH, // fill
// GridBagConstraints.NONE, // fill
// GridBagConstraints.VERTICAL, // fill
// GridBagConstraints.HORIZONTAL, // fill
insets, // insets
0, // ipadx
0); // ipady
c.add(six, constraints);
JButton seven = new JButton("Seven");
constraints =
new GridBagConstraints(
1, 2, // gridx, gridy
1, 1, // gridwidth, gridheight
1.0, 0.0, // weightx, weighty
GridBagConstraints.CENTER, // anchor
GridBagConstraints.BOTH, // fill
// GridBagConstraints.NONE, // fill
// GridBagConstraints.VERTICAL, // fill
// GridBagConstraints.HORIZONTAL, // fill
insets, // insets
0, // ipadx
0); // ipady
c.add(seven, constraints);
frame.pack();
frame.show();
}
};
EventQueue.invokeLater(runner);
}
}
Looking at the screen output, notice how some buttons cross over
multiple rows and columns. Resizing the screen also demonstrates
another feature of GridBagLayout. You have total control over
which rows and columns get additional space as a screen grows.
Each component added to a container that is being laid out by
GridBagLayout should be associated with a constraints object,
GridBagConstraints. This object communicates the specific
constraints for that component to the layout manager.
There are many different types of constraints. Each controls the
layout of the associated component. Each constraint is a public
field of the class. Constraints can either be set all at once
through the constructor, or individually through direct access
to its field. There are no setter or getter methods for these
fields.
Let's examine the constraints:
anchor
The anchor specifies the direction in which the component will
drift when it is smaller than available space. CENTER is the
default. The eight compass points represent the other settings.
Each setting is a constant that can be assigned to the field.
The possible settings are:

CENTER
EAST
NORTH
NORTHEAST
NORTHWEST
SOUTH
SOUTHEAST
SOUTHWEST
WEST
Additional constraints are available that position components
based on the current ComponentOrientation. Their settings
for the typical English horizontal, left-to-right orientations
are shown below following the constants:
FIRST_LINE_END (NORTHEAST)
FIRST_LINE_START (NORTHWEST)
LAST_LINE_END (SOUTHEAST)
LAST_LINE_START (SOUTHWEST)
LINE_END (EAST)
LINE_START (WEST)
PAGE_START (NORTH)
PAGE_END (SOUTH)
fill
The value of fill controls the component's resize policy when
more space is available.. If fill is set to NONE (the default),
the GridBagLayout manager tries to give the component its
preferred size. If fill is set to VERTICAL, the layout manager
resizes the component's height (that is, if additional space is
available). For HORIZONTAL, the layout manager resizes width. If
fill is set to BOTH, the layout manager takes advantage of all
the space available in both directions.
The previous example program, GridBagSample, had a fill of BOTH.
Try running the program with another fill value: VERTICAL,
HORIZONTAL, or NONE. Uncomment out the appropriate fill setting
line to get the desired results. For example, here is the result
produced if fill is set for all seven buttons to NONE.
gridx and gridy
The gridx and gridy variables specify the component's position
in the grid. The gridx variable specifies the cell that contains
the component's leftmost position. The gridy variable specifies
the cell that contains the component's top position. Cell (0, 0)
is the cell at the origin of the screen (top left). Because
components can span multiple cells, the gridx, gridy location
is typically the starting cell of the component. Although you
can explicitly specify the cells directly, you can also use the
RELATIVE constant for either setting. A gridx setting of
RELATIVE results in the new component to the right of the prior
one. For gridy, that component would start a new row and be
below the prior one.
gridheight and gridwidth
The gridheight and gridwidth fields specify the number of cells
along the horizontal (gridwidth) and vertical (gridheight) axis

that a component should occupy. In addition to specifying the


number of cells in a column and row, you can also use the class
constants RELATIVE and REMAINDER. Specifying a RELATIVE setting
for either field results in that component being the
second-to-last component in the column (gridheight) or row
(gridwidth). The REMAINDER setting is reserved for specifying
the last element of the respective column or row. Until you are
comfortable with using GridBagLayout, it tends to be easier to
directly specify the gridx, gridy, gridheight, and gridwidth
settings, instead of relying on RELATIVE and REMAINDER. Even
after developing a level of comfort, you might find that you
still prefer to avoid using RELATIVE and REMAINDER.
insets
The insets field specifies the external padding, in pixels,
around the component. The padding represents empty space. Think
of the padding as a transparent border around the component. In
setting the insets field, you specify the values individually
for the top, bottom, left, and right padding. Notice that in the
GridBagSample program the insets setting is (0,0,0,0) and it's
set for a common Insets object.
ipadx and ipady
The ipadx and ipady fields specify the internal padding for
a component. The ipdax field adds padding to the width of a
component. The ipady field adds padding to the height of a
component. For instance, the ipadx setting reserves extra space
around both the right and left sides of the component.
weightx and weighty
The weightx and weighty settings allow you to control which
components grow or shrink when space is available in the
container, for example, when the user resizes the screen. The
weight settings are doubles (all the other settings besides
insets are int values).
Typically, the weightx and weighty settings are in the range of
0.0 to 1.0. A setting of 0.0 means the component gets no extra
space. A setting of 1.0 means the component gets all the extra
space (or at least, the component shares the extra space with
other components that have a setting of 1.0). Although you can
limit the settings to be within 0.0 to 1.0, any settings are
accepted. The system will allocate space proportionally to the
sum of settings for a specific row or column. For instance, four
components with a setting of 1.0 will share extra space equally.
Three settings of 0.0 and one of 1.0 gives all the extra space
to one component. One component at 0.0, two at 0.5, and one with
a weight of 1.0 gives the 1.0 component half the extra space,
and splits the remaining extra space between the two 0.5
components.
If your screen sizes are resizable, don't forget to think about
what the various weight settings need to be. Smart use of the
combined settings for anchor and weightx/weighty allow your
components to "hug" screen edges on resize, and to grow
proportionally, keeping screens in balance.

Try changing some of the GridBagConstraints settings in the


GridBagSample program and see what happens. Here's a table that
lists the initial GridBagConstraints settings in the program.
Component
One
Two
Three
Four
Five
Six
Seven

gridx
0
1
2
0
2
0
1

gridy
0
0
0
1
1
2
2

gridwidth
1
1
1
2
1
1
1

gridheight
1
1
1
1
2
1
1

For more information on using GridBagLayout and


GridBagConstraints, see the "How to Use GridBagLayout" trail
(http://java.sun.com/tutorial/uiswing/layout/gridbag.html) in the
Java Tutorial.
Also, before you take advantage of
GridBagConstraints, you might want
As you design screens that require
or columns, it might be helpful to
grid positions beforehand.

GridBagLayout and its related


to pick up some graph paper.
you to bridge multiple rows
draw the components in their

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - UPDATING JAR FILES


As is the case for ZIP files, JAR files are used to package
multiple files together, such as classes, images, and sounds.
You have a choice of tools to create JAR files. You can use the
jar command-line tool that comes with the J2SE Software
Development Kit (SDK) or you can use the classes in the
java.util.jar package. Although the command-line tool allows you
to add files to an existing JAR file, there is no way to
programmatically add a file. Also, there is no way using the SDK
or the command-line tool to delete a single file from a JAR or
delete multiple files. Instead, to update an existing JAR file,
you must create a new JAR file and copy any desired content from
the old JAR file.
Before showing how to update an existing JAR, let's examine the
java.util.jar package and see how to list the contents of a JAR
file. The key classes of the API are JarFile and JarEntry. The
JarFile class provides a reference to the whole JAR file, the
JarEntry class points to each of the files contained within the
JAR.
There are five ways to create a JAR file using JarFile:
o
o
o
o
o

JarFile(File file)
JarFile(File file, boolean verify)
JarFile(File file, boolean verify, int mode)
JarFile(String name)
JarFile(String name, boolean verify)

Essentially, you can specify the location as either a File


object or a text string. The verify flag indicates whether or
not the file should be verified if it is signed. The default (if
the flag is not specified) is to verify the file. If access is

denied, the security manager throws a security exception. The


mode setting is created with the help of two class constants
of ZipFile: OPEN_READ or OPEN_READ | OPEN_DELETE. If OPEN_DELETE
is set, the file is flagged for deletion by the system.
Here's an example of using JarFile:
JarFile jar = new JarFile("myjar.jar");
After you have a reference to a JAR file, you get its list of
entries with the entries method. This method returns an
Enumeration of JarEntry objects. For each JarEntry object, you
can get specific information about that entry. For example, you
can get its name or size, with getName and getSize respectively.
Most of these informational methods are inherited from the
parent ZipEntry class.
Here's a simple program, ListJar, that lists the contents of JAR
files specified from the command line. Because standard ZIP
files are JAR files without the optional manifest, the program
works with ZIP files, too.
import java.io.*;
import java.util.*;
import java.util.jar.*;
public class ListJar {
public static void main(String args[]) {
for (int i=0, n=args.length; i<n; i++) {
try {
listjar(args[i]);
} catch (IOException e) {
System.err.println("Problems reading: " + args[i]);
}
}
}
private static void listjar(String name)
throws IOException {
System.out.println("Jar: " + name);
JarFile jar = new JarFile(name);
Enumeration e = jar.entries();
while (e.hasMoreElements()) {
listInfo((JarEntry)e.nextElement());
}
System.out.println();
}
private static void listInfo(JarEntry entry) {
System.out.println("\t" + entry.getName());
}
}
For example, if you have a jar file named myjar.jar that
contains three class files: AmbientEx, CutAndShuffle, and
GridBagSample, running the ListJar program against the jar file:
java ListJar myjar.jar
displays the following:

META-INF/
META-INF/MANIFEST.MF
AmbientEx.class
CutAndShuffle.class
GridBagSample.class
SpotEx.class
Now let's explore updating a JAR, by adding a new file. To
update a JAR file, you must create a copy of the original,
update the copy, remove the original JAR file, and then
rename the updated JAR to the original name. Deletion
generally works the same way, but instead of adding a new file
to the JAR, you don't copy one or more of the entries.
The general process of updating a JAR works similarly to listing
the contents. However instead of getting the name of each
JarEntry, you get the InputStream for the entry and write the
contents to the new destination. In addition to writing the
bytes, you have to tell the JAR file about each entry through
the putNextEntry method.
while (entries.hasMoreElements()) {
JarEntry entry = (JarEntry) entries.nextElement();
InputStream is = jar.getInputStream(entry);
newJar.putNextEntry(entry);
while ((bytesRead = is.read(buffer)) != -1) {
newJar.write(buffer, 0, bytesRead);
}
}
The output here is written to an instance of type
JarOutputStream. It deals with the compression.
File tempJar = File.createTempFile(nameOfJar, null);
JarOutputStream newJar =
new JarOutputStream(
new FileOutputStream(tempJar));
Provided that there are no problems with the update, don't
forget to delete the original JAR file and rename the new file
to the original. You don't want to use the OPEN_DELETE flag
here, in case an error occurs during processing.
if (success) {
File origFile = new File(nameOfJar);
origFile.delete();
tempJar.renameTo(origFile);
}
Here's a program that puts all these steps together for you.
import
import
import
import

java.io.*;
java.util.*;
java.util.zip.*;
java.util.jar.*;

public class AddFileToJar {


public static void main(String args[]) {

if (args.length != 2) {
System.err.println(
"command: java AddFileToJar example.jar filetoadd.txt");
System.exit(-1);
}
String nameOfJar = args[0];
String nameToAdd = args[1];
File tempJar = null;
try {
tempJar = File.createTempFile(nameOfJar, null);
} catch (IOException e) {
System.err.println("Unable to create intermediate file.");
System.exit(-2);
}
JarFile jar = null;
try {
jar = new JarFile(nameOfJar);
} catch (IOException e) {
System.err.println("Unable to access original file.");
System.exit(-3);
}
// Only rename file at end on success
boolean success = false;
try {
JarOutputStream newJar =
new JarOutputStream(
new FileOutputStream(tempJar));
byte buffer[] = new byte[1024];
int bytesRead;
try {
FileInputStream fis = new FileInputStream(nameToAdd);
// Add back the original files
Enumeration entries = jar.entries();
while (entries.hasMoreElements()) {
// Prompt for copy
JarEntry entry = (JarEntry) entries.nextElement();
String name = entry.getName();
System.out.println
("Copy " + name + " into new jar? (y or n)");
BufferedReader reader =
new BufferedReader
(new InputStreamReader(System.in));
String line = reader.readLine();
if ("n".equals(line)) {
// if user enters n, don't copy file,
// move to next name
System.out.println("Skipping: " + name);
continue;
}

InputStream is = jar.getInputStream(entry);
newJar.putNextEntry(entry);
while ((bytesRead = is.read(buffer)) != -1) {
newJar.write(buffer, 0, bytesRead);
}
}
// Add new file last
try {
JarEntry entry = new JarEntry(nameToAdd);
newJar.putNextEntry(entry);
while ((bytesRead = fis.read(buffer)) != -1) {
newJar.write(buffer, 0, bytesRead);
}
} finally {
fis.close();
}
success = true;
} catch (IOException ex) {
System.err.println
("Operation aborted due to : " + ex);
} finally {
try {
newJar.close();
} catch (IOException ignored) {
}
}
} catch (IOException ex) {
System.err.println(
"Can't access new file");
} finally {
try {
jar.close();
} catch (IOException ignored) {
}
if (!success) {
tempJar.delete();
}
}
if (success) {
File origFile = new File(nameOfJar);
origFile.delete();
tempJar.renameTo(origFile);
}
}
}
The AddFileToJar program displays various prompts. If you run
the program without specifying the required parameters (a target
JAR file and a file to add), it displays:
command: java AddFileToJar example.jar filetoadd.txt
The program also prompts you to copy each file to the target JAR
file:
Copy AmbientEx.class into new jar? (y or n)

To delete a file from the JAR, respond "n" to the prompt for
that file.
Suppose you used AddFileToJar to add a file to myjar.jar:
java AddFileToJar myjar.jar DirectionalEx.class
After responding "y" to all the prompts, you should see the
added file in the JAR file contents:
java ListJar myjar.jar
META-INF/
META-INF/MANIFEST.MF
AmbientEx.class
CutAndShuffle.class
GridBagSample.class
SpotEx.class
DirectionalEx.class
If you copy all the original files to the new file, the program
has roughly the same effect as using the -u option of the jar
command line tool. Earlier versions of the tool did not have -u.
Not copying an original file means you've deleted that file from
the archive.
For more information on JAR files, see the JAR File trail
(http://java.sun.com/tutorial/jar/) in The Java Tutorial.
. . . . . . . . . . . . . . . . . . . . . . .
IMPORTANT: Please read our Terms of Use, Privacy, and Licensing
policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developers.sun.com/dispatcher.jsp?uid=6910008
* FEEDBACK
Comments? Please enter your feedback on the Tech Tips at:
http://developers.sun.com/contact/feedback.jsp?category=sdn
* SUBSCRIBE/UNSUBSCRIBE
Subscribe to other Java developer Tech Tips:
- Enterprise Java Technologies Tech Tips. Get tips on using
enterprise Java technologies and APIs, such as those in the
Java 2 Platform, Enterprise Edition (J2EE).
- Wireless Developer Tech Tips. Get tips on using wireless
Java technologies and APIs, such as those in the Java 2
Platform, Micro Edition (J2ME).
To subscribe to these and other JDC publications:
- Go to the JDC Newsletters and Publications page,
(http://developer.java.sun.com/subscription/),
choose the newsletters you want to subscribe to and click
"Update".
- To unsubscribe, go to the subscriptions page,

(http://developer.java.sun.com/subscription/),
uncheck the appropriate checkbox, and click "Update".
- To use our one-click unsubscribe facility, see the link at
the end of this email:
- ARCHIVES
You'll find the Core Java Technologies Tech Tips archives at:
http://java.sun.com/developer/TechTips/index.html
- COPYRIGHT
Copyright 2004 Sun Microsystems, Inc. All rights reserved.
4150 Network Circle, Santa Clara, California 95054 USA.
This document is protected by copyright. For more information, see:
http://java.sun.com/jdc/copyright.html
Core Java Technologies Tech Tips
July 27, 2004
Trademark Information: http://www.sun.com/suntrademarks/
Java, J2SE, J2EE, J2ME, and all Java-based marks are trademarks
or registered trademarks of Sun Microsystems, Inc. in the
United States and other countries.

You might also like