Notice: This material is excerpted from Special
Edition Using JavaScript, ISBN: 0-7897-0789-6. The electronic version
of this material has not been through the final proof
reading stage that
the book goes through before being published in printed form. Some errors
may exist here that are corrected before the book is published. This material
is provided "as is" without any warranty of any kind.
One of the major advantages of the Java language is its power
and flexibility.
Java is a full-featured
programming language
with all the constructs one needs to develop object-oriented applications.
However, as you have already seen in Chapter 11, "A Java
Tutorial,"
Java is not as directly connected with the environment
of its
Web page as JavaScript.
Java cannot really access
HTML
elements on a
Web page in a direct manner. As compensation for
this deficiency,
Java provides some extremely powerful tools for
manipulating images and URLs. Java also has a set of components,
known as the Advanced Windowing Toolkit (AWT), which enable
Java applets to create pushbuttons, text entry fields, and other
HTML-like entities.
The term Java encompasses many things. In chapter 11, we
focused on gaining some initial understanding of Java as a programming
language. In the process, you encountered some old familiar methods,
such as parseInt() and charAt(), and also some
new ones, such as paint().This points to the fact that
Java is more than a language. Java is also a set of methods, organized
into a collection known as the Java Class Hierarchy, which enables
us to do complex tasks. Much of the expressiveness of
Java only
becomes clear when we learn more about some of the components
of the Java Class Hierarchy and what they can do for us.
This chapter explores the Java Class Hierarchy with particular
emphasis on image and
URL manipulation. It presents the basic
concepts necessary to explore Java further as well as enable you
to write more complex and interesting Java applets.
In this chapter, you will learn how to do the following:
In Chapter 11, "A Java Tutorial," you were first exposed
to the concept of inheritance in Java. In particular, in
the applet described in the section "An Accounting Applet
in Java" you saw three Java classes working together: the
Account class, the Acex class, which drove the
applet itself, and, implicitly, the Java class java.applet.Applet.
We were introduced to the special keyword extends and
we saw that Acex was said to extend the built-in class
java.applet.Applet. This idea of having one class extend
another, also known as subclassing, is critical to understanding
the Java Class Hierarchy.
See "An Accounting Applet in Java," in Chapter 11 for a detailed example of subclassing the Applet class.
In chapter 11, we built our Account class from the ground up and gradually refined the methods to perform a set of simple, but useful, operations. We could have continued this process ad infinitum, adding more and more functions for more and more specialized situations. This would make the Account class cover a larger number of situations, but it would also lead to dramatic overkill in some cases. It would be nice to have the capability to handle escrow accounts, foreign currency transactions, and the like, but in many situations, you would not use these extra capabilities.
This leads to the notion that perhaps we do not want to extend
a class by adding more and more to it, but rather by creating
specialized versions of that class. The specialized versions would
have all the capabilities of the generalized class, but would
also have their own unique features. The specialized classes,
such as EscrowAccount and InternationalAccount,
have all the methods and instance variables of Account,
but also have their own methods, which Account does not
have. The specialized classes inherit the attributes of
their parent. The specialized classes are subclasses of
their parent class, which is known as the superclass.
There is no multiple inheritance in Java. EveryJava class has exactly one parent class.
Naturally, this simple idea of inheritance acquires some twists
and turns when it is actually implemented. The first such variation
is the idea of having a subclass override a method in the
superclass. The
Acex applet discussed at the end of Chapter
11 overrides the
paint method of its java.applet.Applet
superclass. It does not override the inherited method repaint-it
just uses it as is.
You can imagine that the international version of Acex
would keep the withdraw and balance methods
the same and would add convert and transfer
methods (to convert between different currencies and to transfer
money). It might also override the deposit method so
that deposits could be made in foreign as well as local currency.
A subclass not only extends its superclass, it also tends to modify
its behavior for special situations.
Java has a special keyword, super, that is used to refer
to the
superclass of a class.
Superclass instance variables can
be accessed as super.
varname, and
superclass methods
can be invoked as super.methodname(). This keyword is
particularly useful if you want the subclass to use its own method
named NAME and also used its parent's method, also named
NAME.
For example, our internationalized version of the deposit
method might look something like listing 12.1. This version of
deposit simply converts the deposit amount, in any arbitrary currency,
into the local equivalent (line 3) and then calls the deposit
method in the superclass (Acex) to perform the deposit.
This avoids the tedious approach of copying all the deposit
code in any subclass that overrides it.
Listing 12.1A Class Method Calls Its
Superclass Method // Assume that "currency" is a variable specifying the type of currency, // and that convert is a method that converts between currencies // this is the subclass deposit method void deposit(int amount, int which, int currency) { // 1; int localamount; localamount = convert(amount, currency); // 3; convert to local super.deposit(localamount, which); // 4; invoke superclass method }
What happens to instance variables of a class when a subclass
is derived from it? As one might imagine, public instance variables
remain public. Interestingly enough, private instance variables
(and private methods) are completely private-they are unknown
in the subclass just as they are unknown outside the class. This
means that no subclass can reference private instance variables
or make use of private methods of its
superclass.
Java also has
a third category, known as protected variables and methods, which
are known to the class and to all its subclasses, but remain invisible
outside the class. Figure 12.1 illustrates the relationship between
the various types of class members and their subclass counterparts.
Fig. 12.1 Subclassing can be used in Java to create specialized classes.
The Java Class Hierarchy is the collection of all the classes
that are provided as a standard part of
Java. These classes are
organized in a class hierarchy, as described above, with a series
of very general classes-such as the ubiquitous class known as
Object-at the top of this hierarchy. This might lead
you to guess that the class java.applet.Applet, which
is the superclass of all applets, is a subclass of java.applet,
which is in turn a subclass of an all encompassing java
class. This is an excellent guess, but it is incorrect.
Java actually has two kinds of organization for its classes. It
has a strict class hierarchy, which describes all the children
of each class. It also has a more horizontal organizational structure,
known as the
Java package system. Packages are used to
group together similar, but not necessarily directly related,
classes into a set of groups. These groups are the Java packages.
Packages can be distinguished notationally from classes because
they all begin with a lowercase letter, while classes always start
with an uppercase letter. Thus Applet is a class in the
java.applet package, which is a part of the java
package. As a class, Applet is derived as follows:
An applet is therefore actually a specialized form of the graphic
class Panel, which is derived from two other graphics
classes, Container and Component, and ultimately
from Object. This is an excellent example of the matrix
organization of classes and packages. Applet is a member
of the java.applet package; Panel, Container,
and Component are members of the
java.awt (Advanced
Windowing Toolkit) package; and Object is the member
of the
java.lang package.
The top of the Java package hierarchy is the
java package.
There are other top level hierarchies, such as the sun
hierarchy, which are platform and/or operating system dependent.
The
java hierarchy, however, is always guaranteed to
be present. It contains the following packages:
The java.lang package is one of the most important and
fundamental of the java packages. It defines the basic object
types that correspond to elements of the language. It also includes
several very interesting pieces of machinery that are used throughout
Java programming, including the critical concept of a thread,
which will be reviewed shortly.
The data type classes contained within java.lang include
Boolean, Character, and String as well
as the numerical types Integer, Long, Float
and Double. These latter four classes are actually subclasses
of a
generic Number class. As one might expect, each
of the numerical types defines
conversion methods. You have already
see one of these, namely the
parseInt method of the Integer
class, which is used to convert strings to integers.
The Acex applet, which appears in the section describing "An
Accounting Applet in Java" of chapter 11, illustrates the use of this method.
The java.lang package also contains a class known as
Math, which is very similar to the JavaScript object
of the same name. Math provides an expanded set of mathematical
operations. The same can be said for the
String class,
which is a full fledged class (object) in Java-unlike its implicit
counterpart in JavaScript.
Java also provides a second string
class within the
java.lang package known as StringBuffer.
This is used for extensible strings. Whenever you concatenate
strings using the plus sign (+) operator, you are actually using
a StringBuffer. More precisely, whenever the
Java compiler
sees an expression that involves merging two strings, it rewrites
that expression to use a StringBuffer behind the scenes.
Finally, the java.lang package contains two critical
classes with enormous utility: System and Thread.
The System class provides system-level functionality
with a platform-independent interface. The way in which it is
actually implemented, of course, depends heavily on the actual
platform. You have already seen an example of the System
class in the print statement, System.out.println("message"),
which sends the string "message," with a subsequent
carriage return, to the standard output. Where this output
actually goes is, of course, platform dependent. Threads
are the subject of the next section and are used in the "
Image
Viewer Applet" section at the end of this chapter.
In Netscape Navigator, the output generated by System.out.println can be seen by activating the
Java Console under the
Options menu.
It is often very useful to do several things at once. Not only
does this get more done, it brings everything being done to completion
earlier. Of course, in this aspect, most humans are like most
computers. It is not really possible to do more than one meaningful
thing at a time, such as reading two books at once, but it is
often highly desirable (particularly for one's image) to make
it appear that way. This is the advantage of modern multitasking.
Each user process gets its own set of tiny slices of a single
CPU, and the illusion of simultaneous processing is maintained.
Most modern operating systems enable you to seem to perform several
tasks, such as editing while printing.
In this hustle and bustle world of ours, there is never enough
time to do all the things we want to do without it looking like
we are ignoring someone or something. The same can be said for
the programs we write. In the days of plain old DOS, for instance,
people were used to waiting for the program to finish printing
or repaginating before they could do something else. Microsoft
(TM) brought Windows (r) to the
DOS world, and suddenly you could
run more than one program at a time, thus enabling you to do more
than one thing at a time. You were, however, the computer was
not.
A CPU (Central Processing Unit) really only executes one instruction
at a time, and each instruction belongs to a particular program.
But, and here's where it gets interesting, the
CPU does not care
where the instruction comes from; it just executes it. Essentially
what operating systems for UNIX, Windows, and Macintosh computers
do is cleverly pass instructions to the CPU from the loaded programs
such that it looks like they are all running at the same time,
but in fact each of them is getting its 'slice' of the
CPUs time
in a sort of
round-robin fashion. This enables the programs to
print or repaginate while your off playing Solitaire or something.
The Thread concept gives each program the capability to
have its own little programs that it manages, while the operating
system manages it. (T.E. CAM)
There are often cases in which it is highly desirable to be able
to perform many tasks within a single program. This is particularly
true in graphics programs. In attempting to display multiple images,
it is advantageous to be working on image 5, while image 4 is
being displayed, for example. Java provides such a capability
as part of its java.lang package through the medium of
the Thread class.
A Java thread is very similar to an ordinary thread in a garment.
It has a definite starting point, a definite endpoint, and can
weave through the garment in tandem with other threads. A complete
description of Java threads is well beyond the scope of this chapter.
However, we can examine the general structure of a threaded Java
applet. This structure is used in the
Image Display applet to
realize precisely the goal described above: interleaving graphic
operations and other operations. The template for a
multithreaded
Java applet is shown in listing 12.2.
Listing 12.2 TheStructure of a Runnable Java Applet
public class MTApplet extends
java.applet.Applet implements
Runnable { Thread mythread = null; // the thread we will create public void init() { // init method, as before ... //
initialization stuff goes here }
public void start() { // start method, creates thread if ( mythread == null ) { mythread = new Thread(); mythread.start(); } } public void stop() { // stop method, stops thread if ( mythread != null ) { mythread.stop(); mythread = null; } } public void paint( Graphics g ) { // local paint method ... // custom drawing goes here }
public void run() { // the work method of the thread ... // the main body of the thread } }
This template has several familiar features as well as some new
wrinkles. The first thing to notice is that the class declaration
for this MTApplet class not only extends
java.applet.Applet,
as it must, it also "implements Runnable." Runnable
is a new type of Java element: a Java interface. An interface,
like a superclass, expresses a set of methods. A class, such as
MTApplet, which implements this interface, must also
implement these methods. In particular, it must implement a run
method. The purpose of the run method will become clear
in a moment.
The MTApplet class has the very familiar init() method, which is used to do whatever initialization is required. This usually involves parsing user parameters accessed via the getParameter() method. If images are to be manipulated, the init() method is also a good place to begin loading those images. The paint() method is also much as before: it is used to perform our applet specific drawing operations. These operations are now done in parallel, however, using threads.
The start() and stop() methods shown in listing
12.2 are not templates or placeholders: they are shown in their
entirety. The start method examines the instance variable
mythread to see if it is null (its initial value).
If it is, then the start method creates a new
Thread
instance by invoking the new operator and sets
mythread
to be that instance. The effect of creating a new thread is that
there is now one extra task that can be run. This new thread is
not yet running, however. The final statement in the start method
launches this new thread by saying
mythread.start().
This calls the start method of the new thread. The new
thread now runs as an independent entity within the applet.
The stop method is the mirror image of the start
method. It also examines the mythread instance variable.
If it is not null, then that thread is halted by calling
its stop method. Cleanup is then performed by setting
the
mythread variable back to null. The interplay
between start and stop is such that at most,
one new thread will be created. If start finds that mythread
is not null, it will do nothing. Also, stop
insures that the new thread will never be stopped twice. None
of this yet explains how the new thread accomplishes anything,
however.
The answer to this mystery is provided by the new run()
method. When a class implements the Runnable interface
and a new thread is created and set running by that class, then
its run() method will be entered. In fact, every applet
is already a thread, known as the main thread. Unless
a new thread is created by instantiating the
Thread class,
the main thread is the only thread, so there is effectively
no parallelism.
Once the second thread is activated and the run method
entered, the new thread can do one set of operations while the
main thread is doing something else. This is the key
idea behind parallelism in Java. If the run method performs
some graphical operations and ends up triggering paint(),
the actual drawing is performed in the main thread, while
the computations leading up to it are performed in the second
thread.
The actual implementation ofJava threads is platform dependent at this time. This is because threads require some cooperation from the underlying operating system, and different operating systems cooperate in different ways. A thread based applet that works perfectly under Solaris may fail on
Windows NT, and vice versa. Applets using threads should be thoroughly tested on all major platform types (UNIX, Windows, Macintosh).
The java.net package contains the basic classes and methods that are used for network communications. This package contains classes representing network connections (sockets), network addresses, and, most significantly, URLs. This might sound like an extremely rich source for interesting Java programming ideas, but the Java security model limits what you can do with this package quite severely. It is worthwhile to review these limitations because they have a significant effect on what is possible and what is not.
Every Java applet is activated within the context of a
Web page
via that page's APPLET tag. This
Web page in turn was
obtained from some URL and is therefore associated with a particular
Web server. We will refer to the Web page that activated the applet
as that applet's document and the server from which that page
was obtained as the applet's server.
The first restriction on network access within Java is that it
is prohibited from opening a network connection to any host other
than the applet's server. This means that it is not even possible
to make a network connection to the user's own host! The second
restriction is that a Java applet can only access documents within
the
directory hierarchy rooted at the applet's document BASE.
These two restrictions combined might seem quite grim because
the set of documents accessible within
Java is rendered very small.
Fortunately, there are no restrictions on documents that Java
can ask its browser to open. This concept is one of the subtleties
of Java. Java does not actually implement
graphics, network connections,
or anything else that impacts the external environment. It has
a series of methods where it can ask its browser to do these things
for it. When you create a button or open a URL in Java, it is
actually the browser that is doing these things for you.
Having said all this, there is one very important class in the
java.net package that you can (and will) use quite effectively:
the URL class. As the name implies, this class is used
to construct an abstract representation of a URL. This class has
several different constructions, as follows:
The first form takes a String, such as the literal
http://home.netscape.com,
and attempts to construct a URL instance from it. The second
form is used to concatenate a String representing a relative pathname
onto an existing URL. This form can be used to descend from the
applet's document BASE to an HTML file within its tree.
The third and fourth forms are used to build a URL from its component
parts. The third form takes a protocol name, such as http,
a hostname, such as home.netscape.com, and a filename,
such as index.html, and produces a URL from that combination.
The fourth form enables you to also explicitly set the port number
for those rare cases in which the protocol is not being accessed
on its default port. (
http is occasionally received on
port 1080 or 8080 rather than its default 80, for example.)
When we review our two major Java applets later in this chapter,
you will see the first two forms of the URL class constructor,
and also how one politely asks one's browser to open a "foreign"
URL. The discussion just below on the java.applet package
also shows how to obtain the URL that corresponds to the applet's
document BASE.
We have already observed that Java cannot interact directly with
HTML elements, unlike JavaScript. There are no HTML FORM
components within the Java Class Hierarchy. This means that Java
programmers must construct their own buttons, text entry fields,
and the like if they want such items to be part of their applets.
The
Advanced Windowing Toolkit (AWT) is
Java's set of capabilities
for doing this. It is contained within the package.
The classes in the AWT can be subdivided into three categories:
display items (such as Button), layouts (such as FlowLayout),
and overall graphics items (such as Color and Font).
The first category is the largest and includes an extensive set
of elements, including the following:
As you can see from this enumeration, many familiar HTML elements
are also present in the AWT. As in HTML, it is quite simple to
glue together a set of graphical items in a page, but it is somewhat
more difficult to make the presentation attractive and crisp.
HTML has a number of markup styles and directives that can be
used to control the
visual format of various elements, including
tables and forms.
The means to control where elements are placed, how they are aligned
with one another, and how they are sized and spaced is always
an issue in graphics programming. This applies to all windowing
systems. Java is no exception. The
Java AWT has chosen an approach
with several different, quite distinct layout styles. Within each
style, the display elements that you create, such as Buttons
and TextAreas, are placed according to a well defined
system. However, it can still take time to get things looking
just the way you want, and if all else fails, you can still programatically
position objects at specific coordinates.
The defaultJava layout is FlowLayout with
CENTER justification. Use this until you become more comfortable with the AWT.
At present, there are five Java layout styles. Each has its own peculiarities, and you will almost certainly find yourself using a combination of styles once you acquire some skill with the AWT. The Java layout classes are:
The BorderLayout approach is based on the idea of placing
elements at one of the four cardinal points-North, South, East
or West-or in the Center. It is often ideal for arranging items
in case you would like two or three arranged in a vertical (North,
Center, South) or horizontal (West, Center, East) stacking order.
BorderLayout is also used with Panels for hierarchical
organization of items. If you would like a top row of Buttons
and perhaps a Label below, you would create two Panels,
place them at the North and South locations in a BorderLayout,
and then add the Buttons to the northern Panel,
and a Label to the southern Panel. Listing 12.3
shows a code fragment that does just this.
Listing 12.3 An Example of Hierarchical Layout in Java BorderLayout bl; Button but[]; Panel nopa, sopa; Label la; bl = new BorderLayout(); // 5; create a new BorderLayout instance setLayout(bl); // 6; make it the default layout nopa = new Panel(); // 7; create two new panels sopa = new Panel(); add("North", nopa); // 9; put nopa at the North edge add("South", sopa); // 10; add sopa at the South edge but = new Button[4]; // 11; allocate space for 4 buttons but[0] = new Button("Back"); // 12; create the buttons with various labels but[1] = new Button("Forward"); but[2] = new Button("Home"); but[3] = new Button("Done"); for(int i = 0; i < 4; i++) { // 16; add the buttons to the North panel nopa.add(but[i]); // 17; it will default to a FlowLayout la = new Label("Southern Label"); // 18; create new label sopa.add(la); // 19; add to south Panel }
This example begins by allocating a new instance of the BorderLayout
class (line 5) and then calling the
setLayout method
to make this the current layout. Remember that a Java applet is
actually a subclass of a Panel, so that the bare call
to
setLayout on line 6 applies to the Panel
containing the entire applet. The next two statements create
Panel
instances. Note that one can create instances of graphical items
all day long, but they are not displayed until they are added
to the applet.
The North and South Panels are added in lines 9 and 10
using the add method. The add method is overridden
in all the layout classes, which means that it has its own distinct
syntax for each one. In the case of a BorderLayout, the
first argument to add must be one of the five permissible
directions. We use North and South to split the applet vertically.
The next five lines create four Buttons with some text
to name them. Lines 16 and 17 then add those buttons to the North
panel. This is accomplished by explicitly invoking the add
method of nopa, the North panel instance. If we had mistakenly
just used add(but[i]) on line 17, this would have attempted
to add these buttons to the entire applet's panel. Lines 18 and
19 create and add a Label to the south Panel
in a similar way.
At the moment, button labels must be text. It is not currently possible to put an image inside a button using the Button class. Asubclass of the Button class would have to be written to do this.
The FlowLayout class implements an approach in which
elements are added incrementally across one or more rows. Elements
can be justified within a given row using LEFT, CENTER
(the default), or RIGHT justification. If an element
does not fit on a given row, the layout wraps around to the beginning
of the next row. FlowLayout is often used for rows of
buttons or other components of similar size and shape. As mentioned
above, FlowLayout is the default layout for any newly
created
graphical container (such as a Frame or Panel).
The other three layout types are more specialized. CardLayout
is used to create slide show like presentations. Elements of a
CardLayout are presented sequentially rather than displayed
simultaneously on the screen. GridLayout lives up to
its name. It enables you to position elements based on their row
and column location. It is used by first specifying the number
of rows and columns to be allocated and then placing individual
elements in their desired (row,column) location. GridBagLayout
is a much more powerful version of GridLayout. It is
also regrettably complex because it is necessary to first construct
a description of the layout, using the subsidiary class GridBagConstraints,
and then actually build the layout on top of that.
The final set of classes in the immense java.awt package
are the classes that correspond to general
graphical constructs
rather than things that are actually drawn. We have already seen
three examples of these classes in our tiny applets from chapter
11, "A Java Tutorial": the Color, Dimension
and
Graphics classes. The
Color class is usually
used by invoking its
static instance variables that name the primary
colors (such as Color.Red), although it can also be used to construct
arbitrary color values directly from red, green, and blue levels.
The
Dimension class is used to hold information about
the size of a component. The
Graphics class captures
the entire
graphical state of an applet. Recall that the method
signature for the applet paint() method is
public
void paint( Graphics g ).
Within paint(), you can call a set of methods too numerous
to mention to draw strings, rectangles, and other common primitive
graphics operations. Some of the other important classes in this
general
graphics category are the following:
The Event class is extremely important because it enables
us to respond to user events, such as a button being pushed inside
our applet. The Applet class has another method, known
as action(), that is called whenever user interaction
takes place. Its method signature is public Boolean action(
Event ev, Object arg ). It is called whenever the
Object
arg (a Button, for example) is pushed and generates
the
Event ev. If you override the default action
method, you can control what happens when events occur, just as
in JavaScript.
Java Events and
JavaScript events are not directly related. At present, Java can not respond to events outside its applet. It is also not possible to install a JavaScript event handler for Events inside a Java applet.
The Font class is used to manipulate the text appearance
of any item that contains text. It can be used to load a particular
font by name (such as TimesRoman or Helvetica),
to set the
font style (such as PLAIN, BOLD or
ITALICS), and also to set the
font size. The oddly named
MediaTracker class is
Java's answer to the patient
projectionist.
It is almost always used to track the progress of images being
progressively loaded over the network. You will see examples of
all three of these AWT classes below.
The java.applet package is quite small and has just one interesting
class, Applet, with a small number of interesting methods.
You have already seen the getParameter() method, which
accepts a String argument giving the
NAME of
a PARAM, and returns the VALUE of the PARAM
(or null if there is no matching name). The other three
Applet methods that you will use most frequently are
the following:
You can probably guess that the first of these methods returns
a URL instance representing the value of the
BASE attribute
of the applet's document. It is the top of the document directory
tree that the applet can access on the server host. The second
of these methods is similar: it returns the URL representing the
value of the
CODEBASE attribute given in the
APPLET
tag, if any. This is used when all the Java class binaries are
kept in a different server directory than the HTML files. That
directory would be named in the CODEBASE attribute.
The URLs returned by getDocumentBase() and getCodeBase() are always valid for use in Java applets as long as they are not null.
See "The Applet Tag," section of
Chapter 11 for a description of the HTML elements used in declaring an applet.
The getAppletContext method is used to talk directly
to the browser. The applet context really refers to the browser
environment in which the applet is running. Once you have obtained
the applet context, you can then use it to ask the browser to
display a URL, for example. This is not a task that you can perform
directly in Java because of security restrictions. You will see
an example of this the section entitled "
A Pop-up Document
Viewer Applet" later in this chapter.
These packages are the last two on our tour of the Java Class
Hierarchy. The java.
util package provides various
utility
classes, while the
java.
io package handles input and
output to files and streams. The java.
util package contains
the
Date object for manipulating date items, as in JavaScript.
It also contains a series of classes that can be used to manipulate
structured collections of things, including the Vector,
HashTable, Dictionary, and Stack classes.
One the most useful utility classes is StringTokenizer. This class is used to solve the age-old problem of decomposing a string, such as the following:
into its individual components, which will be delimited by a separator:
The traditional way of solving this problem would be to search for the separator character, which is the comma character (,) in this case, and keep track of the individual substrings which occurred between the separators. We would find the first comma and separate the initial string into "this" and "is,a,comma,separated,list" and then repeat the procedure until each of the individual elements was extracted. The StringTokenizer class completely automates this tedious, but extremely common parsing task.
Anyone who has ever written string manipulation code that attempts
to interpret a string a series of separate items (tokens) will
appreciate the StringTokenizer class.
There is not much to be said about the java.io class
for applet developers. One of Java's security restrictions prohibits
local file access of any kind inside an applet. While we can certainly
ask the browser to open a document using the file:
protocol, the applet can not do so itself. This restriction
may be weakened in some future version of Java, but at the moment
Java cannot touch the local file system.
This section analyzes and presents a pop-up document viewer applet
in Java. This applet enables the user to specify the
communication
protocol to be used via a pop-up menu and also permits a full
document name to be entered into a text field. Once the user commits
to a particular document name by pressing a button, the applet
requests that the browser open that document in a new window.
This applet is designed as a simple demonstration of some of the
capabilities of the
java.applet and java.awt
packages. It also illustrates Java's variety of event handling.
The code is shown in listing 12.4.
Listing 12.4Viewing a Document in a
New Browser Window Using Java /** A Java Applet to launch a document in a new
window Comments for "
javadoc" follow. @author Mark C. Reynolds @version 1.0 */ import java.awt.*; // 1; get AWT components import java.net.*; // 2; get URL and friends import java.applet.*; // 3; get Applet class methods public class SD extends java.applet.Applet { String whatproto = "http"; // 5; initial protocol to use String prevproto = whatproto; // 6; previous protocol used
Choice ch; // 7; A pop-up menu choice TextField tf; // 8; User entered document name
AppletContext ac; // 9; Ask the browser... public void init() { // 10; Init method FlowLayout fl; Button bu;
Font fo; // create a new left-justified
flowlayout with 10 pixels of spacing //on each side of each item fl = new FlowLayout(FlowLayout.LEFT, 10, 10); // 14 setLayout(fl); // 15; make it the current layout fo = new Font("TimesRoman",
Font.PLAIN, 18); // 16; a fairly big
font setFont(fo); // 17; make it the current
font ch = new Choice(); // 18; create a Choice instance ch.setFont(fo); // 19; make this the current
font ch.addItem(whatproto); // 20; add "http" as a choice ch.addItem("gopher"); // 21; add literal "gopher" as a choice ch.addItem("ftp"); ch.addItem("file"); add(ch); // 24; add the pop-up menu to our flowlayout bu = new Button("Open"); // 25; create "Open" button add(bu); // 26; add the button to our
flowlayout // create a
textfield of length 70, and put the string "
http://" in it tf = new TextField(whatproto + "://", 70); // 27 tf.setEditable(true); // 28; enable the user to modify the field add(tf); // 29; add the text field to our flowlayout ac = getAppletContext(); // 30; discover our context } // 31; end of init method public void start() { // 32; start method does nothing } public void stop() { // 34; stop method does nothing too } // change the text entry when user changes protocol private void modifytext() { // 36; int len = prevproto.length(); // 37; string len of prev protocol String cur = tf.getText(); // 38; get the current text String left = cur.substring(len); // 39; get the document name part // new name = new proto + old document name
tf.setText(whatproto + left); // 40; } // 41; end of modifytext() private method private void launchdoc() { // 42; ask browser to open a document String doc = tf.getText(); // 43; get document name URL u = null; // 44; document's
URL // test to insure that there is a doc name, more than just proto:// if ( doc.length() <= ( whatproto.length() + 3 ) ) return; // 45 try { // 46; execute something that might abort u = new URL(doc); // 47; convert doc name to URL instance } catch (MalformedURLException ue) { // 48; // if it failed then print a message indicating why System.err.println("Invalid URL: " + ue.getMessage()); // 49; return; // 50; and give up } // 51; end of try clause // ask for the document to be opened in a new window named "
New Window" ac.showDocument(u, "New Window"); // 52 } // 53; end of launchdoc public boolean action(Event ev, Object arg) { // 54; event handler if ( ev.target instanceof Choice ) { // 55; Choice event prevproto = whatproto; // 56; save prev protocol name whatproto = arg.toString(); // 57; get the choice selected modifytext(); // 58; change the text displayed return(true); // 59; indicate event handled } // 60; end of Choice event if ( ev.target instanceof Button ) { // 61; Button event // if the "Open" button was selected then... if ( arg.toString().equals("Open") ) { // 62; launchdoc(); // 63; try to launch the document return(true); // 64; event handled } // 65; end of if statement } // 66; end of Button event return(false); // 67; did not handle event } // 68; end of action method } // 69; end of SD class
The init() method for the SD (Show Document) applet begins
on line 10. Its job is to construct all the graphical elements
that are displayed and, in the process, to initialize various
instance variables that are used in the event handling methods,
modifytext() and launchdoc(). It starts out
by creating a FlowLayout instance on line 14. This instance
is left justified so that new elements are added starting at the
left edge of each row. We also indicate that we would like at
least 10 pixels between each element in a row (the second argument
to the constructor), and between rows (the third argument). Line
15 makes this layout the current layout. Because an applet is
actually a Panel, this now applies to the entire applet.
Line 16 accesses a plain Times Roman font with 18 point type.
If your system does not have this particular
font, you may need
to adjust this statement to choose another font name (such as
Helvetica or Geneva and perhaps another font size (such as 24
point). You can also specify the empty string ""
as the first parameter to the
Font constructor; this
will select a default font. Line 17 makes this
font the current
font for the applet's panel. Now, three items are put into the
flow layout beginning at line 18: a pop-up menu, a button, and
a single line text field.
The pop-up menu is created on line 18. Because pop-ups have their
own fonts, which may be separate from the Panel in which
they reside, you must set the font of the pop-up (line 19). This
pop-up presents the user with a choice of four communication protocols
that will be used. These are added to the pop-up in lines 20 through
23. Note that the default item, which represents the default protocol,
is the one added first. That will be the initial value of the
instance variable
whatproto, which is the
String
"http." Line 24 finally adds this pop-up to the layout.
Line 25 creates a Button whose label is "Open."
This is the button that the user presses to attempt to load a
new document. It is added to the layout in line 26. The third
item in our layout is an editable text field, which is created
in line 27. The initial String that will be displayed
is "http://", obtained by concatenating the default
protocol "http" with the literal delimiter "://."
Line 28 makes this text field read/write, and line 29 adds it
to the layout. Because this text field is quite long, it will
be added in a new row below the pop-up menu and the Open button.
Finally, line 30 initializes the instance variable, ac,
to the applet's context. This is used in the launchdoc()
method. Figure 12.2 shows the initial appearance of the SD applet
after the init() method has been executed.
Fig. 12.2 The ShowDocument Applet uses AWT elements, which are very similar to HTML forms components.
You will notice immediately that the start() and stop() methods of the SD applet do absolutely nothing. All of the activity in this applet is triggered in response to user interaction. As a result, all of our code is within the action method and none in start or stop. There is also no run method in this applet because we are not implementing any threads (the next applet we consider uses threads).
There are many different ways of performing event handling in
Java. For example, Java applets that desire to handle only
mouse
down events can override a specialized method known as
mouseDown.
If you were only interested in button clicks on the Open button,
you could use this approach. Because we are actually interested
in handling events on the pop-up menu and button clicks on Open,
the SD applet uses the more general approach.\
If an applet overrides the action method, this indicates
that it wants to handle more than one event type. The code for
the action method begins on line 54. Note that this method
accepts two arguments: an Event instance indicating the
type of event, and an Object instance indicating where
the event occurred. The target element of an Event indicates
which graphical element was associated with the event
On line 55, the Java keyword, instanceof, is used to
ask if the event was associated with a
Choice item (a
pop-up menu). If the result is true, then the code in
lines 56 through 59 is executed. This code saves the previous
choice value (line 56), stores the new choice value by extracting
the String version of the Object selected (line
57), and then invokes the modifytext() private method
to fix up the document name being displayed. It then returns true
in line 59 to indicate that this event was successfully processed.
All applet event handling methods must return true to indicate that the event has been handled and false to say that it has not. Failure to do so may cause the applet (and the browser) to become horribly confused.
To understand what is going on, consider a concrete example. Suppose
that the user had typed the document name, "http://ftp.javasoft.com",
in the text field and then suddenly realized that this was not
going to work because it would require the FTP protocol rather
than the
http protocol. The user then invokes the pop-up choice
menu and selects
FTP.
This selection triggers the action method of the SD applet.
The test on line 55 will pass; prevproto will become
the string "
http" and
whatproto the string
"
ftp." The modifytext() method on line 36 is
now executed. It gets the length of the prevproto string
(which will be 4), and also fetches the current document string
on line 38. This will be the string "http://ftp.javasoft.com".
It then peels off the substring that contains everything except
the protocol name in line 39.
The local variable left will be the string "://ftp.javasoft.com."
Finally, it glues the new protocol (stored in whatproto)
onto the front of this substring and pushes that string out to
the textfield in line 40. The text now reads, "ftp://ftp.javasoft.com."
The reader is encouraged to perform this experiment and verify
that the protocol part of the text field changes in lockstep with
the value of the choice selected from the pop-up menu.
The action method is also equipped to handle Button
events. If the test on line 61 succeeds, this indicates that some
button has been pressed, and the code on line 62 will be executed.
Line 62 is a bit of defensive programming in which you test to
make sure that it was the Open button that was pressed.
In our example, this test is superfluous because we have only
one button. This line compactly converts the arg argument
to a String and then uses its equals method
to test against the literal "Open". This test must pass
in our case, so line 63 will be executed and the launchdoc()
method invoked. When that method returns, the action
method returns true to indicate that the button press
was handled (line 64). If this event was neither a pop-up selection
nor a button press, then the action method returns false
on line 67.
The launchdoc() method is used to actually ask the browser to open a document URL. It first gets the text of the document name in line 43. It then checks to make sure that that string is long enough on line 45. If the string is just a bare protocol, such as "file://", this test fails and the method returns at that line. The extra 3 in this test accounts for the three characters ://.
We now have a string representing a URL stored in the local variable
doc, say "http://home.netscape.com." We would
like to convert this to a URL instance because that is what we
need for the subsequent request to the browser. This is executed
in the try block beginning on line 46. A try
block is required whenever a method invocation might generate
a Java exception. Without being too specific, we can say
that an exception results when you attempt to do something and
it fails in a potentially unpleasant way. The URL constructor
on line 47 is just such a statement.
How did we know this? Is it necessary to remember all the functions
that can generate exceptions? Fortunately, the answer is no.
If you had tried to write u = new URL(doc); without enclosing
it in a try block, the Java compiler would thoughtfully
tell you that URL constructors can generate exceptions and that
you should try to catch the MalformedURLException. We
have complied with this request and enclosed the ominous statement
in a try block, which always takes the following form:
try { ominous statement(s) } catch (SomeException e) { do something if an exception occurs }
In our case, if doc does not correspond to a valid URL for any reason, the applet receives the MalformedURLException and the code on lines 49 and 50 (within the catch clause) is executed. This code prints out a message indicating the reason for the failure on line 49, and then returns. Note that all exceptions have a getMessage() method that we have used to tell the user why the URL was malformed. A URL might be malformed because it was entered incorrectly, referred to a nonexistent server, or mentioned a document which the server did not wish the user to see, among other reasons.
If the URL was well formed, then the catch clause will
not be executed and the code will arrive at line 52. This is the
critical statement that actually communicates with the browser.
We use the showDocument method of the AppletContext
ac to ask it to open the
URL u in a new window
whose name is "
New Window." This method call can still
fail, of course, even if the
URL u is well constructed.
The reader should experiment with this applet by typing in various
valid and invalid URLs, hitting the
Open button and observing
the results
TROUBLESHOOTING
The modifytext() method is the workhorse that handles the event associated with changing the choice. When you clear the text field, you are wiping out the protocol part ("http" for example) of the document name. The applet does not know this, however, because it is assuming that you will only change the protocol using the Choice item. Said another way, once you have cleared the text field, the protocol part of the document name is null, but the value of the instance variable, whatproto, is still set to the last protocol used. If you are going to enable the user to change the protocol directly, then modifytext() has to become smarter. Use the following algorithm:
- Read in the document string using tf.getText().
- Find the first colon character using the String method, charAt().
- Set the local variable, len, to the length of the substring up to that colon.
- Continue as written in listing 12.4
The real power of Java comes through in its capability to rapidly
display multiple images, giving the appearance of true animation
on a
Web page. You now have enough knowledge about Java threads
and also about the AWT, that you can present a simple image viewer
applet in Java. This applet provides the first concrete example
of something that would be extremely difficult to accomplish in
JavaScript. This applet can also be used as a template for writing
more sophisticated applets that use Java threads. The code for
the image viewer is shown in listing 12.5.
Listing 12.5Displaying Multiple Images Is
Easy Using Java Threads import java.applet.*; import java.awt.*; import java.net.*;
public class Simimg extends Applet implements
Runnable { Image imgs[]; // 6; the images themselves int imgidx = 0; // 7; image currently being displayed int nimg = 0; // 8; total number of images Thread mythread = null; // 9; animation thread public void init() { // 10; get params and images MediaTracker mt; // 11; track image loading using this class String tmp; // 12; tmp string String imgloc; // 13; location of images URL db; // 14; Applet's document BASE imgloc = getParameter("imgloc"); // 15; locate image directory if ( imgloc == null ) return; // 16; no image directory-give up tmp = getParameter("nimg"); // 17; get number of images if ( tmp == null ) return; // 18; no images-give up nimg = Integer.parseInt(tmp); // 19; convert to integer if ( nimg <= 0 ) return; // 20; invalid image count-give up imgs = new Image[nimg]; // 21; allocate array for images // create a mediatracker for the images mt = new MediaTracker(this); // 22; db = getDocumentBase(); // 23; find Applet's doc BASE // this loop starts loading all the images for(int i = 0, j = 1; i < nimg; i++, j++) { // 24; imgs[i] = getImage(db, imgloc + j + ".gif"); // 25; // tell the MediaTracker instance to track this image as ID 0 mt.addImage(imgs[i], 0); // 26; } // 27; end of image loading loop try { mt.waitForID(0); // 28; wait for all images } catch (InterruptedException e) {
nimg = 0; // 30; if it failed set # images to 0 } // 31; end of catch clause of try block } // 32; end of init method public void run() { // 33; thread's run method Thread me; // 34; current thread me = Thread.currentThread(); // 35; get current thread me.setPriority(Thread.NORM_PRIORITY-1); // 36; decrease priority while ( imgidx < nimg ) { // 37; loop over images repaint(); // 38; draw current image try { Thread.sleep(100); // 40; wait a little while } catch (InterruptedException e) {} imgidx++; // 42; update index to next image } // 43; end of while loop } // 44; end of run method public void start() { if ( mythread == null ) { mythread = new Thread(this); mythread.start(); } } public void stop() { if ( mythread != null ) { mythread.stop(); mythread = null; } } public void paint( Graphics g ) { // 57; draw the current image if ( ( imgs != null ) && ( 0 <= imgidx ) && ( imgidx < nimg ) && imgs[imgidx] != null ) { // 59; sanity check all values g.drawImage(imgs[imgidx], 0, 0, this); // 60; draw it! } } for(int i = 0, j = 1; i < nimg; i++, j++) { // 24; loop to load all images imgs[i] = getImage(db, imgloc + j + ".gif"); // 25; begin loading to for(int i = 0; i < nimg; i++) { // 24; loop to load all images imgs[i] = getImage(db, imgloc + (i + 1) + ".gif"); // 25; begin loading
The init method for the
Simimg applet performs two functions:
it gets user parameters and it loads the images. This applet requires
two PARAM tags to be specified, indicating where the
images are to be found and how many there are. On line 15, the
imgloc parameter is accessed; if it is not present, the
init method returns immediately (line 16). Lines 17 through
20 get the
nimg parameter, convert it to an integer,
and make sure that it is a positive number. If this parameter
is not present or is not a valid positive number, the init
method returns.
Line 21 allocates an array just large enough to hold the indicated
number of images. Line 22 initializes a MediaTracker
instance. This instance will be used shortly to insure that all
images are loaded before the
init method completes. Line
23 uses the getDocumentBase() method from the java.applet
package to discover the applet's document BASE, saving that value
in the local URL variable, db.
Statement 24 sets up a for loop to load all the images
into the image array imgs. Note that two iteration variables,
i and j, are used. This is because the imgs
array is indexed from zero, but we are assuming that the names
of the images will be something like IMG1.gif, IMG2.gif, and so
forth. The i iteration variable marches through the array,
while the j variable is used to build the names of the
successive images.
The getImage() method is used on line 25 to launch the image loading process. It takes two arguments: a URL specifying a server directory and a String giving the name of the file within that directory that is to be loaded. We are using the applet's document BASE as the first argument, and we are constructing the successive image names using the value of the imgloc parameter (with a numeric suffix) as the second argument. This particular version assumes that all the images are GIFs.
At present, the getImage() method only understands the GIF andJPEG image formats. Other formats will be added in the future.
The getImage() method is slightly deceptive in that is does not guarantee that the image is actually gotten when the method returns. All it does is begin to load the image. This is the purpose of statement 26. We add the image being loaded to the MediaTracker instance mt, which indicates that we are going to subsequently watch the loading process, presumably to insure that it is done.
The addImage method takes two arguments: an Image
instance, and an integer ID. The ID is used to group images into
pools. We could, for example, track the first half of the images
as
ID 0 and the second half as
ID 1. In this way we could be displaying
the completely loaded
ID 0 images while the
ID 1 images were still
being loaded.
This applet takes a brute force approach. All images are declared
to have ID 0. On line 28, we actually wait for all the ID 0 images,
which are, in fact, all the images, to be fully loaded. Because
this method can generate an InterruptedException, it
must be executed within a try block, as we have seen
in the SD applet. If this exception occurs, then we set the number
of images nimg to 0, insuring that none will be displayed.
The Simimg applet, unlike the SD applet, requires
PARAM
tags to properly function. A sample
HTML file that uses this applet is shown in listing 12.6. Note
that this particular
HTML file indicates that we will load sixteen
images, that they will be located in the subdirectory "images"
of the document's base directory, and that they will have the
prefix "T." This means that the applet will attempt
to load sixteen images named images/T1.gif, images/T2.gif,...images/T16.gif.
It is also worth noting that this
HTML implicitly assumes that
all the images will fit in a drawing area that is 300 x 150.
Listing 12.6 HTML for the Simimg Applet <HTML> <HEAD> <TITLE>A Simple Image Player</TITLE> </HEAD> <BODY> <HR> <APPLET CODE="Simimg.class" WIDTH=300 HEIGHT=150> <PARAM NAME="imgloc" VALUE="images/T"> <PARAM NAME="nimg" VALUE="16"> </APPLET> <HR> The <A HREF="Simimg.java">source</A>. </BODY> </HTML>
The formal structure of this applet is exactly the same as we described above in our discussion of threads. The start() and stop() methods are each responsible for creating the "animation" thread and for stopping it, respectively. The actual work is done by the run() method and indirectly by the paint() method.
The run method first discovers the identity of its own
thread by invoking the static method currentThread()
of the Thread class on line 35. It then lowers its own
priority to be just slightly less than the default priority for
threads (line 36). This makes sense if we think of threads in
terms of a standard multitasking operating system. Higher priority
tasks get more of the real
CPU and generally execute more frequently.
The same model applies to
Java threads. By declaring itself less
important, it is implicitly declaring that the drawing activity
is more important.
Line 37 is the main image loop. As long as the instance variable, imgidx, is less than the total number of images, nimg, the loop will continue. Each pass through the loop issues a call to repaint(), which results in the paint() method being executed (line 38). Each pass through the loop also puts the animation thread to sleep for 100 microseconds (line 40). This is another way to give the drawing activity even more time and to also insure that it is actually executed.
One of the side effects of using the static sleep method
of the Thread class is to insure that other threads that
are waiting to run get a chance to do so. This method can also
generate an exception, which we dutifully ignore. Finally, at
the end of the loop, we update imgidx to process the
next image.
The paint method, which begins on line 57, is a model
of
defensive programming. It checks to make sure that the
imgs
array is not null, that
imgidx is neither too
small nor too large, and that the actual image in the
imgs
array itself is not null. If all these tests pass, then
it uses the
drawImage method of the Graphics
instance, g, to actually draw the image (line 60). Figure
12.3 shows the result after 16 images of
Sun's Tumbling Duke image
have been loaded and successfully displayed. If you download the
Java Development Kit from http://sun.javasoft.com, you
will find these images in the directory,
java/demo/TumblingDuke/images/tumble.
Fig. 12.3 Java simplifies the tasks of image manipulation and animation.
For technical support for our books and software contact support@mcp.com
Copyright ©1996, Que Corporation