JavaScript SE: Chapter 12 Copyright ©1996, Que Corporation. All rights reserved. No part of this book may be used or reproduced in any form or by any means, or stored in a database or retrieval system without prior written permission of the publisher except in the case of brief quotations embodied in critical articles and reviews. Making copies of any part of this book for any purpose other than your own personal use is a violation of United States copyright laws. For information, address Que Corporation, 201 West 103rd Street, Indianapolis, IN 46290 or at support@mcp.com.

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.


Chapter 12 - More About Java

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:

The Java Class Hierarchy

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.

Classes and Subclasses

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. Every Java 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.1 A 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.

Packages in Java

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

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.

Using Java Threads

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 The Structure 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 of Java 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

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.

The Advanced Windowing Toolkit

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 default Java 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

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.

The java.util and java.io Packages

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.

A Pop-Up Document Viewer Applet

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.4 Viewing 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

Initializing the SD Applet

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.

Event Handling in the SD Applet

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.

Opening a New Document with the SD Applet

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:
  1. Read in the document string using tf.getText().
  2. Find the first colon character using the String method, charAt().
  3. Set the local variable, len, to the length of the substring up to that colon.
  4. Continue as written in listing 12.4

An Image Viewer Applet

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.5 Displaying 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

Initializing the Simple Image Viewer

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 and JPEG 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>

Running the Simple Image Applet

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.

Internet & New Technologies Home Page - Que Home Page
For technical support for our books and software contact support@mcp.com
© 1996, Que Corporation