Chapter 17

Creating a Stand-alone Application


CONTENTS


So far, all the chapters in this book have discussed Java development with applets that run in external viewers. These applets can be transferred across the Web or reside locally on the user's pc. Either way, they cannot be run without a Java-enabled browser or viewer.

This chapter introduces stand-alone applications. These are Java programs that can be run independent of a browser or external viewer. Unlike code that is compiled for a specific operating system, Java applications require the Java interpreter to be loaded each time they are run. They are run by calling the Java interpreter and passing it the name of the class file containing the application.

Until recently, few developers understood the benefits of Java beyond its usefulness to Internet development; therefore, few stand-alone applications were written in Java. This is rather unfortunate, because the true power of Java is in its usefulness to developing software for multiple platforms regardless of whether those platforms are connected to the Internet.

How Stand-alone Applications Differ from Applets

Although applications have many similarities to applets, they have some important differences as well. The similarities arise because both applets and applications are constructed using the Java API. The differences stem from the distinct environments in which the applets and applications run.

Differences in the Code

The most obvious way that stand-alone applications differ from applets is the static main() method. This method is ignored when a class is run as an applet, but is required to start the program if the class is run as an application. The difference is based on the environment that starts the class. This enables the same class to function as either an applet or an application.

Unlike C++, the main() method in Java must be part of the public class that defines an application. The method cannot be created separately from a class because it is used to create the initial classes used in the application.

The main() method is required for a class to be run as an application because this is the method the Java interpreter looks for when it starts. The entire application can be stored in this method; however, that is not a recommended practice. Generally, the main() method should create instances of objects and let the objects control the execution of the program.

Neither the init() method nor the start() method called by the browser for applets is automatically called by the Java interpreter when an application runs, so if an applet is converted to a stand-alone application, these methods must be explicitly called in main().

There are also differences in the expectations users have for applets and applications. Applets are associated with a browser, and the browser handles starting and exiting, so applets usually are not expected to have menus or dialog boxes. Applications are generally larger, more robust, and specialized to serve a particular need. They usually contain multiple windows and a sophisticated user interface.

Differences in Performance

Applets are meant to be run in an external viewer, such as a Web browser, and are expected to operate across a network. Therefore, applets inherit all the overhead of the viewer in which they are running. So, even if your applet needs only 100KB of memory, you are still constrained by the memory needs of the Web browser, which could be an additional 4, 8, or 16MB of memory. On a system that's already running multiple applications, such as a word processor and a spreadsheet program, the additional memory needs of the browser can seriously affect performance.

Applications, on the other hand, are meant to be run in the Java interpreter on a local machine. Without the overhead associated with Java applets, Java applications can provide users with better performance. Better performance translates to improved response times and, possibly, increased productivity.

Differences in Security

There are benefits to using applets that are not directly available in applications. Browsers and other external viewers load local and remote applets using two different mechanisms. If an applet exists on the local file system and is in a directory defined in the CLASSPATH environment variable, the applet is loaded by the file system loader. These local applets are allowed to read and write files and can access native code.

Non-local applets are loaded by the applet class loader and are subject to the restrictions of the applet security manager. To ensure that the integrity of the client machine isn't compromised when accessing remote information, Java adds a layer of security controls that places restrictions on what applets loaded over the network can and cannot do.

To prevent non-local applets from gaining access to or destroying information on the client machine, applets are generally restricted from gaining access to the client's file system. This means applets cannot manipulate files or directories on the client in any way. They cannot read or write files. They cannot make directories. They cannot check file type, size, or modification date. They also cannot start another program on the client.

Although code written in another programming language, such as C or C++, can be called from within Java applications, this cannot be done from within applets loaded over the network. Again, this is because of security concerns: Any program that can invoke native code on a remote machine could also gain direct access to protected areas of the system.

Note
Most of the security controls for applets are provided by the applet security manager. Obviously, you could create similar security controls for applications, but why create what already exists?

Conceptualization and Design of the Application

Conceptualizing and designing an application is similar to what you would do with an applet. However, there are more options available when working with an application, and often applications require an extensive user interface. This implies there is more to consider during the design of an application.

Applets may include pop-up windows, but they are not trusted windows and appear with large warnings to the user. These warnings are produced by the Java-enabled browser and are not present with applications that often use dialog boxes to collect information from the user. When you design an application, you must build all the windows used in the application and define the manner in which the windows are opened and closed.

Note
Because windows are generally associated with applications that are not restricted by the security manager, Java labels windows used by applets as untrusted. This means that the windows are subject to the rules of the security manager.

Applications also can include menus for controlling windows and features. These menus must be included as part of the design. Applets may include menus, but this is uncommon because the applets are being run in the browser.

Recall from Chapter 14, "Creating a Java Applet," that the first step in designing an applet is to have a good overall understanding of the tasks it will perform. This applies to applications as well. This chapter demonstrates how to develop an application that sums an arbitrarily long list of numbers. The development is done in stages to illustrate different concepts that are important to applications.

Here's the initial specification for the application:

Initially, the application will read a list of numbers from the command line and display the sum. You will then allow the user to enter additional values while the application is running. Finally, you will add a title and improve the display of the application.

To meet these requirements, you must create several objects in the application:

Before you define the structure of the application, remember that applications do not always require a sophisticated user interface. Indeed, some applications may be created to run on a server communicating across a network with another application. This type of application could write to a log file and not have any user interface at all.

The application that follows reads in arguments from the command line and returns the sum of the arguments. The purpose of this example is to demonstrate a simple application that does not require a graphical interface.

This application is short, so it is contained in the main() method. No functionality from any class already defined in the API is needed, so it inherits directly from the Object class. This is the default class; it is not necessary to explicitly state the inheritance.

Both the Integer and String classes are used for conversion, so import both into the application. The command-line arguments are read as a string. Each argument must be converted to an integer before it can be added to the sum. The sum is stored internally as an integer and converted to a string for display.

The conversions, shown in the following code fragment, cause the code to appear more complex than it actually is:

import java.lang.Integer;
import java.lang.String;

// Application to add numbers entered on the command line
class SumIt {
     public static void main(String args[]) {
          int theSum=0;          // the sum of the inputs
          int loopCounter;       // the current itteration

          for (loopCounter=0;loopCounter<args.length;loopCounter++)
               theSum = theSum + Integer.parseInt(args[loopCounter]);
          System.out.println("The sum is "+String.valueOf(theSum));
     }
}

This is a complete application in and of itself. Store it in the file SumIt.java. It can be compiled by typing

javac SumIt.java

When the application is compiled, you can run it from the command line by typing

java SumIt 20 35 5

where 20, 35, and 5 are the arguments passed to the application. The output appears as

The sum is 60

This style of application may be sufficient because some tasks do not require user interaction. However, even then it would make sense to divide the application into objects and methods that are then called by the main() method.

The next section discusses structuring the application into objects and methods. Some of the code from the SumIt example is used as you continue to develop the application.

Defining the Application Structure

As applications grow larger, it is cumbersome to work with them entirely in main(). Applications can use multiple objects just as applets can, so applications must be structured around objects just as applets are. To describe the application structure fully, define each of the objects, when the objects are created and destroyed, and how the objects communicate. Recall that these same characteristics are defined for each object in the applets.

Objects do not necessarily have to have separate displays. As the previous example shows, an application does not have to have a display at all. So if the display is not used to divide applications into objects, how does one divide them? There is no simple answer to this question.

Generally, grouping similar functionalities together will help define useful objects. Creating objects that mirror real-world objects is often useful. Objects also can be created to encapsulate particularly complex algorithms or to hide portions of code that should not be modified. Other objects are created strictly to assist with display to the user or output to a file.

The ability to define generic, reusable objects improves with practice. There is no particularly correct manner for determining the objects that compose an application. There can be many correct sets of objects, just as there can be many correct C programs with the same functionality.

In general, the main() method is used to create objects, which are responsible for doing the actual work of running the application. Defining separate objects for each task that the application performs and enabling the objects to perform these tasks provides more flexibility for reusing classes and modifying code.

A Nongraphical Example

A more flexible way to structure the application developed in the first section is to create a separate class to hold the sum. The remainder of the application must be able to interact with this class in specific, well-defined ways. It must be able to create a new sum, include a new number in the sum, and retrieve the sum. It does not have to subtract from the sum or, for our purposes here, clear the sum back to zero.

The following class defines methods appropriate for the object that computes the sums:

// Class to store a sum that is independent of the display mechanism
class ASum {
     int theSum;

     public ASum() {theSum = 0;}     // Constructor sets instance var to 0
     public void incSum(int toAdd) {
          theSum = theSum + toAdd;   // Add to instance variable
     }
     public int getSum(){
          return theSum;             // Return the value of the instance variable
     }
}

Just as you defined new classes in applets to isolate and encapsulate functionality, you do the same in applications. A main() method that utilizes the class defined here would appear as follows:

// Application to sum numbers entered on the command line
// this application uses a separate class to store the sum
class SumObj {
     public static void main(String args[]) {
          ASum theSum = new ASum();    // the sum of the inputs
          int loopCounter;             // the current iteration

          for (loopCounter=0;loopCounter<args.length;loopCounter++)
               theSum.incSum(Integer.parseInt(args[loopCounter]));
          System.out.println("The sum is "+String.valueOf(theSum.getSum()));
     }
}

Now there is no way that the value of the sum could be accidentally reset in the main() method. This type of structuring also means that if you want a graphical front end, you can add it without changing the class that stores the sum.

The capability to create an object in another class that displays it may not seem important for this example, but suppose the needed calculations were more complex. For example, instead of computing a sum, what if the application required a method that took several inputs and then did some abstract modeling or other complex calculations? In this circumstance, the capability to use the same method and add several different user interfaces is a large advantage. It reduces the risk that new interfaces would introduce errors in the model or calculations. This is the advantage of separating the user input from the back-end processing.

A Graphical Example

Adding a graphical front end to an application means adding some objects to the application. The application does not run in the browser, so it must have its own frame in which to run. This frame is created in the main() method.

In addition to the frame, the application needs an object to display the results. Because the results are not directly modifiable by the user, a label is used to display them. Therefore, both the Label and Frame classes must be imported for the application to use. The ASum class can be used exactly as it is in the previous example. The new SumGraph class follows:

import java.lang.Integer;
import java.lang.String;
import java.awt.Label;
import java.awt.Frame;

// Application that sums inputs from the command line and displays
// the result in a graphical interface. This application makes use
// of the ASum object for storing the sum.
class SumGraph {
     public static void main(String args[]) {
          Frame f;
          Label sl;     // Creates a new object to display the result
          ASum theSum = new ASum();     // the sum of the inputs
          int loopCounter;              // the current iteration

          for (loopCounter=0;loopCounter<args.length;loopCounter++)
               theSum.incSum(Integer.parseInt(args[loopCounter]));
          sl = new Label("The sum is "+String.valueOf(theSum.getSum()));

          f = new Frame("Sum");         // Creates frame for applicaton
          f.add("Center",sl);           // Puts the Sum object in frame
          f.resize(300, 300);           // Resizes the frame
          f.show();                     // Displays the frame
     }
}

Although the application now has a graphical user interface, all the control still resides in the main() method. The key to improving the design is to make the main() method responsible for creating objects that can then hold and display the sum. Furthermore, it is currently possible to modify the main() method so that it updates the sum but does not update the label. This results in a display that does not reflect the true value of the sum.

To avoid the problem of the display and the storage object becoming out of sync, make the storage object, theSum, accessible only by the Label class. Then all other objects that attempt to update the sum must do so via that class, so the Label class can adjust itself in response to each update.

To accomplish this, extend the Label class so that it can handle creating the sum as part of its constructor. Then all the main() method has to do is create a frame and display the new class in the frame.

The new class is called SumLabel and is defined as follows:

// Class used to display a Sum in as a Label
// This class makes use of ASum as an object to hold the sum
// The sum is created from an array of strings passed to the constructor
class SumLabel extends Label{
     public SumLabel(String args[]) {
          super();
          ASum theSum = new ASum();     // the sum of the inputs
          int loopCounter;              // the current itteration

          for (loopCounter=0;loopCounter<args.length;loopCounter++)
               theSum.incSum(Integer.parseInt(args[loopCounter]));
          super.setText("The sum is "+String.valueOf(theSum.getSum()));
     }
}

Notice that the constructor for the new class must be passed the argument list from the main() method. The new main() method is shown in the following code fragment:

// Application that sums inputs from the command line and displays
// the result in a graphical interface. This application makes use
// of the SumLabel object for displaying and storing the sum.
class SumGraph {
     public static void main(String args[]) {
          Frame f;
          // Create a new object to display the result
          SumLabel sl = new SumLabel(args);

          f = new Frame("Sum");         // Creates a new frame
          f.add("Center",sl);           // Puts the Sum object in the frame
          f.resize(300, 300);           // Resizes the frame
          f.show();                     // Displays the frame

     }
}

This method is shorter and easier to follow than the previous main(). A considerable amount of the functionality has been moved to the new class. It is also easier to see how another class that displays the average of the values entered, such as AvgLabel, could be added to this method.

Save all three of these classes-ASum, SumLabel, and SumGraph-in the file SumGraph.java and compile it using the command

javac SumGraph.java

When you run the application, you can pass the parameters on the command line as follows:

java SumGraph 23 54 1

The application displays the result in the frame titled Sum. The minimize and maximize buttons can be used to resize the window. However, there is one small problem with the user interface: The Close button on the window does not work. The only way to close the application is to press Ctrl+C with the cursor in the window where it was started.

What's going on here? The window resizes and iconifies correctly, but it does not close when the user presses its Close button. If this were an applet, the browser would take care of closing the applet when the user exits the window, but because this is an application, it is essential to test for the user closing the window and exiting the application. You could also add a Close button to the application. However, even if a separate Close button exists, the application must handle exiting when the window is closed.

The Frame class has an event handler that reacts when the user changes the size of the window, but it does not handle closing the window. There are two methods of solving this problem. First, you could extend the Frame class to a class myFrame, in which case the event handler in the new class would check for the window closing. The other option is to make the SumGraph class itself an extension of Frame. Either way, you should then add an event-handling method to respond to the window closing: a WINDOW_DESTROY event. If you use this event, the application must import the java.awt.Event class.

The following code shows the SumGraph class as extension of the Frame class. The event handler is extended to provide for the window being closed by the user:

import java.lang.Integer;
import java.lang.String;
import java.awt.Label;
import java.awt.Frame;
import java.awt.Event;

// Application that sums inputs from the command line and displays
// the result in a graphical interface. This application makes use
// of the SumLave object for displaying and storing the sum.
class SumGraph extends Frame {
     SumGraph() {
          super("Sum");
     }
     public static void main(String args[]) {
          SumGraph f = new SumGraph();          // Instantiate self
          // Creates a new object to display the result
          SumLabel sl = new SumLabel(args);

          f.add("Center",sl);           // Puts Sum object in the frame
          f.resize(300, 300);           // Resizes the frame
          f.show();                     // Displays the frame

     }
     // Event handler to allow for the closing of the window
     public boolean handleEvent(Event evt)
      {
           if (evt.id == Event.WINDOW_DESTROY)
            {
                 System.exit(0);
               return true;
            }
          return super.handleEvent(evt);
      }
}

Okay, you need just two more objects for the application: an object to allow data entry, for which you can use a standard TextField object, and a heading or title across the top of the display. Although you could do this with an ordinary label with a large font, it is more interesting to use an object, such as the ScrollText applet from the previous chapter. You'll add these objects in the next section.

Building the Application

Although it seems the application is nearly complete, it does not yet interact with the user while it is running, nor does it write the result to a file. This section demonstrates how to add a text box to allow the user to add numbers while the application is running. This section then discusses how to write the result to a file and how to add enhancements such as menus and dialog boxes.

Allowing Interactive Updates to the Application

To allow interactive updates, there must be a way for the user to update the display. Currently, the text of the label used for display is set when the label is created with no provision to update it. In fact, there is no method available to the SumGraph class that will update the sum.

The label and the sum must be updated in the same method, which must be available to the SumGraph class. To accomplish this, create a method in the SumLabel class that updates both the sum and the label. Update the sum using the incSum() method from the ASum object created earlier. The new method follows:

public void updateSum(String newVal) {
     theSum.incSum(Integer.parseInt(newVal));
     super.setText("The sum is "+String.valueOf(theSum.getSum()));
}

For this method to work correctly, it must be able to access the same ASum object used in the constructor of the SumLabel. However, the object was created locally to the constructor. Therefore, the class SumLabel is modified to make the ASum object available to the entire class.

The declaration of theSum is taken out of the constructor and made into an instance variable. It is declared as protected so that no other objects can modify it. This prevents other objects from modifying the sum directly because other objects may modify theSum variable and not change the label appropriately.

You may want other objects to be able to retrieve the stored sum. In this case, create a method in the SumLabel class that will return the current sum. Use this method, getSum(), when creating a method that writes the sum to a file. The final version of the SumLabel class follows:

// Class used to display a Sum in as a Label
// This class makes use of ASum as an object to hold the sum
// The sum is created from an array of strings passed to the constructor
class SumLabel extends Label {
     protected ASum theSum;

     //Create the initial sum from an array of strings
     public SumLabel(String args[]) {
          super();                      // must go first in constructor
          int loopCounter;              // the current iteration

          theSum = new ASum();          // the sum of the inputs
          for (loopCounter=0;loopCounter<args.length;loopCounter++)
               theSum.incSum(Integer.parseInt(args[loopCounter]));
          super.setText("The sum is "+String.valueOf(theSum.getSum()));
          super.setAlignment(Label.CENTER);
     }
     // Allow other classes to view the sum, but not modify it
     public int getSum(){
          // Return the value of the protected object
          return theSum.getSum();
     }
     // Allow other classes to update the sum while also updating label
     public void updateSum(String newVal) {
          theSum.incSum(Integer.parseInt(newVal));
          super.setText("The sum is "+String.valueOf(theSum.getSum()));
     }
}

Now the application must include a call to the updateSum method. The user is expected to enter data into a TextField object created by the main() method. To create this object, the application needs to import the java.awt.TextField class.

When the user is finished adding a new number to the field, he or she is expected to press Enter. Pressing Enter triggers what is called an ACTION EVENT that can be handled either by the action() method or by the handleEvent() method. An action method is shown in the following example:

public boolean action(Event evt, Object arg) {
     if (evt.target instanceof TextField) {
          sl.updateSum(tf);
          return true;
     }
     else return false;
}

Instead of using the action() method, modify the existing handleEvent() method in the SumGraph class so that all the event handling in the SumGraph class occurs in one place and is easy to locate. Event handlers are more generic than action handlers, so it is not a problem for the handleEvent() method to contain all the responses. Actions occur when users press buttons or the Enter key when the cursor is in a text field. Events occur when the program senses a change in the environment. Events include moving the mouse, pressing a key, or leaving a text field in any manner.

Therefore, in the event handler, the programmer must be careful to react only when the user presses Enter in the entry field. The event handler must test that the event occurs in the TextField object and that the event is triggered by the user pressing Enter.

Pressing Enter is an action event. Therefore, an event id being equal to ACTION_EVENT indicates that Enter was pressed if the user is in a TextField. ACTION_EVENT is a static variable defined in the event class. The event id is automatically set to this value when an action event is triggered.

The event handler tests whether the user is in the entry field by testing for the event target being an instance of the TextField object. The complete handleEvent() method follows:

public boolean handleEvent(Event evt)
{
     // Allow for the closing of the window
     if (evt.id == Event.WINDOW_DESTROY)
      {
           System.exit(0);
         return true;
     //Allow for new values being entered in input field
      } else if (evt.target instanceof TextField &&
 evt.id == Event.ACTION_EVENT) {
         sl.updateSum(((TextField)evt.target).getText());
         return true;
    } else
     return super.handleEvent(evt);
}

Notice that it is not necessary to make the name of the TextField object available throughout the class. The event handler can act by using the object of the event. In fact, it is not essential to give a name to the TextField, although a name is created in main() for clarity.

The event handler calls the updateSum() method using sl, an instance of SumLabel that is available to the entire SumGraph class. From the object of the event, the event handler gets the text string to pass to updateSum. The event handler knows that the object of the event is a TextField object, and it can use the getText() method to retrieve the text from that field. The handler must be careful to cast the object to the correct object type so that all the methods associated with that type are available before it calls getText(). Pay careful attention to the parentheses on this line of code.

Writing to the Local Hard Drive

The security concerns that necessitate restricting an applet's capability to write to files are not present when working with applications. Although only the final sum is written to a file for the example, similar code could be used to write repeatedly to a file or multiple files.

A new method, writeSum(), handles writing to the file and is created as part of the SumGraph class. The writeSum() method uses a RandomAccessFile object to create a random-access file and write to it. For this method to compile correctly, the application should import the class java.io.RandomAccessFile.

The methods that write to the file all could throw an exception called IOException when they encounter an error writing to the file. The writeSum() method may either catch the exception or have the method throw the exception back to the caller. To keep the caller isolated from the details and problems of writing to the file, the method is defined to catch the exception. If an exception occurs, the method will print an error message to the screen, indicating that there was a problem writing to the file.

If everything proceeds normally, the method will open the file, get the sum from the SumLabel object, write the sum and appropriate text to the file, and close the file. The method will not return a status. The code for this method follows:

//Write a sum contained in the object sl to a file
public void writeSum(){
     RandomAccessFile     SumFile;

     try {
          SumFile = new RandomAccessFile("SumResult.txt","rw");
          SumFile.writeBytes("The sum is "+sl.getSum()+".\n");
          SumFile.close();
     } catch (java.io.IOException e) {
          System.out.println("Could not write to SumResult file.");
          System.out.println(e);
     }
}

The final step is to call the writeSum()method from an appropriate point in the application. The method should execute at the end of the program to write the final sum. How do you determine that the program is exiting? Well, the application currently exits when the user closes the window. The call to the writeSum() method is added just prior to the System.exit() call to ensure that the sum is written when the application closes.

The application now has most of the required functionality, but it still needs some type of title. The next section describes how to add the ScrollText applet to provide a scrolling title for the application.

Including an Applet in an Application

Applets are objects. Therefore, it seems reasonable that they could be included in applications. They can be, but certain modifications are necessary because the application does not include all the environment variables that the applet often expects. However, if the application happens to define all the objects included in a Java-enabled browser, the applet should run without modification.

The basic problem with including ScrollText in its existing form is that you can't define the parameters used in the init() method. The init() method uses getParameter to determine the height and width of its display and to get the text it will scroll. The application being built does not have a means of setting these parameters.

This can be solved by overloading the init() method. In the new init() method the values for width, height, and text are passed as parameters to the method. The internal values of the ScrollText applet are then set in the same manner as in the original init() method using the values passed to the method. With this one change, the SumGraph application can then make use of the ScrollText applet. The new init() method appears as

//Init() method to allow parameter passing from an application
public void init(String inWidth, String inHeight, String inText) {
     ws = inWidth;
     hs = inHeight;

     if (ws == null){        // Read width as input, if not found use default
          w = 150;
     } else {
            w = Integer.parseInt(ws); // Convert input string to integer
     }
     if (hs == null){        // Read height as input, if not found use default
          h = 50;
     } else {
          h = Integer.parseInt (hs); // Convert input string to integer
     }
     resize(w,h);               // Set font based on height
     setFont(new Font("TimesRoman",Font.BOLD,h - 2));
     s = inText;                    // Read input text, if null use default
     if (s == null) {
         s = " The Java ScrollText Applet at work.";
     }

     separated =  new char [s.length()];
     s.getChars(0,s.length(),separated,0);
}

There are certainly many times when it is appropriate to use classes defined as applets within an application. It is important to be aware of all resources available in the browser that the application has to mimic or avoid.

Tip
Many existing applets can also be turned into applications. To do this, create a main() method for the applet. Within the main() method, create a frame for your application and then instantiate the applet. Because the applet is not running in the normal applet environment, you must make explicit calls to init() and start() within main(). Finally, place the applet in a frame and display the frame. Because you are using the Frame class, be sure to import java.awt.Frame.

Additional Needs

Up to this point the focus of the chapter has been on the organization and functionality of the application, but not its general appearance. Currently, the application appears as shown in Figure 17.1. This section reviews ways of improving the visual appeal of the application without changing the functionality.

Figure 17.1 : The SumGraph application before formatting.

Visual Enhancements

Improving an application's appearance generally does not involve a lot of code. Small coding changes can have large effects. However, changing the appearance can involve a lot of time. You may want to try several different changes to note their effects and experiment with different layouts and fonts until you find a combination that is visually pleasing.

To begin, change the layout of the frame to GridLayout instead of the default border layout. The grid is created with one column and three rows. There is only one column, so don't worry about the spacing between the columns. However, it is reasonable to set some space between the rows. The command is

f.setLayout(new GridLayout(3,1,0,10));

This results in a screen with three sections of the same size. The layout must be set before you start adding objects to the display.

The ScrollText applet is placed in the top section, the data entry field in the middle section, and the results in the bottom section. You can do this by explicitly stating where each will go, but it is easier to simply add them in this order. To use the GridLayout() method, import the class java.awt.GridLayout.

Next, modify the font for the frame to make the SumLabel and the TextField display larger. Set the font for the frame to a larger font size, say 16 points. The command is

f.setFont(new Font("TimesRoman",Font.BOLD,16));

Put this line before the line that creates the objects so that the new font will be applied to the objects. To use the setFont method, import the java.awt.Font class.

Tip
You may have realized that a large portion of formatting the screen lies in importing the correct classes to affect the display the way you want. It should be noted here that TimesRoman is one of several fonts known to Java. The supported fonts vary from platform to platform. You can get a String array of the fonts supported on your platform using the following command:
String[] fonts= Toolkit.getDefaultToolkit().getFontList()

Now that the fonts are easier to read, the next change is to modify the TextField so that it appears as a single-line entry. Currently, the TextField takes up the entire space allocated to it in the grid. To change this, create a panel in the middle section of the layout. The TextField is placed on the new panel, which in turn is placed in the grid. This requires importing the class java.awt.Panel and making minor adjustments to main() as follows:

Panel pl = new Panel();          // Create panel for text field
f.add(pl);                    // Puts the panel for entry in the frame
pl.add(tf);                    // Puts the entry field in the panel

These changes merely affect the display, not the ability to access the TextField object.

The results would look better if they were centered in the layout. Do this by centering the Label in its grid component. Modify the SumLabel class to have the constructor set the alignment as follows:

super.setAlignment(Label.CENTER);

Another feature to include in the application is a set of insets. Insets are border areas of blank space that surround the grid layout. To create an inset, override the insets() method for the SumGraph class with the following method:

public Insets insets() {
     return new Insets(40,10,10,10);
}

This provides an inset of 40 pixels at the top and 10 pixels on the bottom and on each side. The application as it now appears is shown in Figure 17.2.

Figure 17.2 : The SumGraph application after formatting.

Formatting and layout are important parts of application development that you can do as part of the design before any of the application has been implemented. With an object-oriented language such as Java, the exact location of the display can be modified easily. Therefore, it may be easier to delay some aspects of the layout until the application has been created. This enables you to gain a better understanding of the objects needed for the application and the functionality desired.

Menus

The application as it currently exists fulfills the functionality explicitly stated in the specification. However, there are some general expectations about applications that you should consider.

Most users expect graphical applications to have a menu and an explicit Exit option. They usually expect a Help option to give explicit help or state some additional information about the program. They also expect a pop-up window to prompt them before they write output to disk. This section and the following describe how to add these features to the example application.

To add a menu to the application, create an instance of the MenuBar object from the Java API and use the setMenuBar() method in the Frame class to attach the menu bar to the frame. This is done in the constructor for the SumGraph class. It is also necessary to add menu items to the menu. You can create a separate method to add the items so that the constructor does not get too complex. Our constructor now appears as

SumGraph() {
     super("Sum");
     InitializeMenu();
     setMenuBar(mbar);
}

Tip
It is good practice to use a separate method to create your menu and call the method from the constructor. This keeps your constructor short and easy to understand.

In the InitializeMenu() method, two items need to be added to the menu bar. Each is an instance of the class Menu: the File menu, with the Save and Quit items; and the Help menu, with the About item.

To create the menus, the application should import three new classes, java.awt.MenuBar, java.awt.Menu, and java.awt.MenuItem. The InitializeMenu() method is created as private because no other class needs to initiate the menu for the application. The code for the method follows:

// Creates the menu and attaches it to the MenuBar
private void InitializeMenu()
{
     mbar = new MenuBar();
     Menu m = new Menu("File");
     m.add(new MenuItem("Save"));
     m.addSeparator();
     m.add(new MenuItem("Quit"));
     mbar.add(m);

     m = new Menu("Help");
     m.add(new MenuItem("About..."));
     mbar.add(m);
}

So, if you add these methods and recompile the application, the menu will appear on your application with all the items needed. However, the menu is not functional yet; you must associate a response with each of the items using the event handler. Check for an event target that is an instance of MenuItem; then determine which menu item triggered the event and respond accordingly. Here is the expanded handleEvent() method:

public boolean handleEvent(Event evt)
      {
           // Allow for the closing of the window
           if (evt.id == Event.WINDOW_DESTROY)
            {
               writeSum();
                 System.exit(0);
               return true;
           //Allow for new values being entered in input field
            } else if (evt.target instanceof TextField &&
evt.id == Event.ACTION_EVENT) {
               sl.updateSum(((TextField)evt.target).getText());
               return true;
          //Allow for reaction to menu items
          } else if(evt.target instanceof MenuItem) {
               if (evt.arg.equals("Save")) {
                    writeSum();
                    return true;
               } else if (evt.arg.equals("Quit")) {
                    writeSum();
                    System.exit(0);
                    return true;
               } else if (evt.arg.equals("About...")) {
                    AboutBox ab = new AboutBox(this);
                    ab.show();
                    return true;
               }
          }
           return super.handleEvent(evt);
      }

Now the menu is just about done. You still need to define the AboutBox class that is used to create the About box displayed by the About menu item. This class is a dialog box that the user must close after viewing. The next section defines this class.

Dialog Boxes

Dialog boxes are simple windows that provide information to the user or ask easy questions. Dialog boxes are also appropriate for accessing files in the file system. Java provides a class, FileDialog, for that purpose. In this section, a read-only dialog box is added to provide information about the application.

Dialog boxes can be modal. A modal dialog box does not allow input to any other windows in the application while that dialog box is present. This type of dialog box is useful when you want the user to verify a particular operation before continuing with that operation.

In this section you will create a class to provide a basic dialog box. This dialog box displays a short description of the application and remains on the screen until the user presses the OK button on the dialog box. It will be modal, so the user will not be able to enter any additional numbers until the dialog box is closed.

The Java API provides a basic dialog box class that you will extend to create the dialog box. The new class is named AboutBox because it will be displayed from the About menu item.

The current frame is passed to the constructor of the new class. The constructor in turn passes the frame to the constructor of the Dialog class. The frame is used as the parent for the dialog box. In addition, the constructor for the Dialog class takes a string that is used as the title of the dialog box. The final parameter is a boolean value set to true. This indicates that the dialog box is modal and should prevent input to other windows. So far, the code for the dialog box appears as

class AboutBox extends Dialog
{
     public AboutBox(Frame parent)
     {
          super(parent, "About Dialog", true);

Next, display fields are added to the dialog box. BorderLayout is used for the dialog box, so the application must import java.awt.BorderLayout. After setting the layout, the About box adds a Label containing text that describes the program. To create a more generic About box, you can pass the text to the constructor as a parameter.

The About box also must have an OK button so the user can close the window. If the OK button is added directly to the window in the South position, it will spread across the entire bottom of the window. To prevent this, create a new panel to hold the button. This is similar to what you did for the TextField object in the main display window. The About box adds the OK button to the new panel and then adds the panel to the South position in the dialog box.

The last line in the constructor resizes the dialog box so that it is somewhat smaller than the main window. The constructor does not actually display the box. The caller is expected to use the show() method to display the dialog box on the screen. The remaining code for the constructor is

setLayout(new BorderLayout());
add("Center",new Label("Interactive Summation Program",Label.CENTER));
Panel pp = new Panel();
pp.add(new Button("OK"));
add("South",pp);
resize(300,200);
}

One more method is needed for the dialog box to work: The class must handle the action that occurs when the user presses the OK button. To account for this, add an action method to the AboutBox class. This method simply checks that the argument for the event is the OK button and then closes the dialog box. The code appears as

public boolean action(Event evt, Object arg)
     {
     if("OK".equals(arg))
     {
          dispose();
          return true;
     }
     return false;
     }
}

The box is closed using the dispose() method available to all dialog boxes and windows. This method closes the dialog box and releases its resource, which can then be collected by Java's garbage-collection mechanism.

The Finished Stand-alone Application

Sections of the SumGraph application have been mentioned throughout the discussion of different topics in this chapter. Listing 17.1 is the code for the complete application. The long list of imports could be shortened by importing java.awt.*. The individual list is shown to demonstrate the many different classes that are required to create even a simple application.


Listing 17.1. The enhanced SumGraph application.
/**
 * Peter Norton's Guide to Java Programming
 * The SumGraph Application
 * This application sums inputs and displays the result
 * in a graphical interface. You can pass inputs from the
 * command line as arguments or enter them individually in
 * a popup window. The application makes use of the SumLave
 * object for displaying and storing the sum.
 */

import java.lang.Integer;
import java.lang.String;
import java.awt.Label;
import java.awt.Frame;
import java.awt.Event;
import java.awt.TextField;
import java.io.RandomAccessFile;
import java.awt.GridLayout;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Panel;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Dialog;
import java.awt.Button;
import java.awt.BorderLayout;

// Class to store a sum that is independent of the display mechanism
class ASum {
     int theSum;

     public ASum() {theSum = 0;}     // Constructor sets instance var to 0
     public void incSum(int toAdd) {
          theSum = theSum + toAdd;   // Add to instance variable
     }
     public int getSum(){
          return theSum;             // Return the value of the instance variable
     }
}
// Class used to display a Sum in as a Label
// This class makes use of ASum as an object to hold the sum
// The sum is created from an array of strings passed to the constructor
class SumLabel extends Label {
     protected ASum theSum;

     //Create the initial sum from an array of strings
     public SumLabel(String args[]) {
          super();                    // must go first in constructor
          int loopCounter;               // the current iteration

          theSum = new ASum();                    // the sum of the inputs
          for (loopCounter=0;loopCounter<args.length;loopCounter++)
               theSum.incSum(Integer.parseInt(args[loopCounter]));
          super.setText("The sum is "+String.valueOf(theSum.getSum()));
          super.setAlignment(Label.CENTER);
     }
     // Allow other classes to view the sum, but not modify it
     public int getSum(){
          // Return the value of the protected object
          return theSum.getSum();
     }
     // Allow other classes to update the sum while also updating label
     public void updateSum(String newVal) {
          theSum.incSum(Integer.parseInt(newVal));
          super.setText("The sum is "+String.valueOf(theSum.getSum()));
     }
}

// The main class for the application.
// This class extends the Frame class and sets up the menu bar
// and events.
class SumGraph extends Frame {
     static SumLabel sl;
     private MenuBar mbar;

     // Constructor that creates frame and menu
     SumGraph() {
          super("Sum");
          InitializeMenu();
          setMenuBar(mbar);
     }

     // Creates the menu and attaches it to the MenuBar
     private void InitializeMenu()
     {
          mbar = new MenuBar();
          Menu m = new Menu("File");
          m.add(new MenuItem("Save"));
          m.addSeparator();
          m.add(new MenuItem("Quit"));
          mbar.add(m);

          m = new Menu("Help");
          m.add(new MenuItem("About..."));
          mbar.add(m);
     }

     //Main method used to start application
     public static void main(String args[]) {
          SumGraph f = new SumGraph();            // Instantiate self
          f.setLayout(new GridLayout(3,1,0,10));
          f.setFont(new Font("TimesRoman",Font.BOLD,16));

          // Create new instances of needed objects
          sl = new SumLabel(args);
          Panel pl = new Panel();       // Create panel for text field
          TextField tf = new TextField("0",8);
          ScrollText st = new ScrollText();

          st.init("280","85","Sums are fun !");
          st.start();

          f.add(st);          // Puts the scrolling text in the frame
          f.add(pl);          // Puts the panel for entry in the frame
          f.add(sl);          // Puts the panel for sum in the frame
          pl.add(tf);         // Puts the entry field in the panel

          f.resize(300, 300);           // Resizes the frame
          f.show();                     // Displays the frame

     }
     public Insets insets() {
          return new Insets(40,10,10,10);
     }

     //Write a sum contained in the object sl to a file
     public void writeSum(){
          RandomAccessFile     SumFile;

          try {
               SumFile = new RandomAccessFile("SumResult.txt","rw");
               SumFile.writeBytes("The sum is "+sl.getSum()+".\n");
               SumFile.close();
          } catch (java.io.IOException e) {
               System.out.println("Could not write to SumResult file.");
               System.out.println(e);
          }
     }
     public boolean handleEvent(Event evt)
      {
           //Allow for new values being entered in input field
           if (evt.id == Event.WINDOW_DESTROY)
            {
               writeSum();
                 System.exit(0);
               return true;
            //Allow for new values being entered in input field
            } else if (evt.target instanceof TextField &&
                       evt.id == Event.ACTION_EVENT) {
               sl.updateSum(((TextField)evt.target).getText());
               return true;
          //Allow for reaction to menu items
          } else if(evt.target instanceof MenuItem) {
               if (evt.arg.equals("Save")) {
                    writeSum();
                    return true;
               } else if (evt.arg.equals("Quit")) {
                    writeSum();
                    System.exit(0);
                    return true;
               } else if (evt.arg.equals("About...")) {
                    AboutBox ab = new AboutBox(this);
                    ab.show();
                    return true;
               }
          }
           return super.handleEvent(evt);
      }

}
// Class used to create an about box for the SumGraph application
class AboutBox extends Dialog
{
     // Constructor used to create window including information
     public AboutBox(Frame parent)
     {
          super(parent, "About Dialog", true);
          setLayout(new BorderLayout());
          add("Center",new Label("Interactive Summation Program",
                                 Label.CENTER));
          Panel pp = new Panel();
          pp.add(new Button("OK"));
          add("South",pp);
          resize(300,200);
     }
     //Action handler used to close window
     public boolean action(Event evt, Object arg)
     {
          if("OK".equals(arg))
          {
               dispose();
               return true;
          }
          return false;
     }
}

Before you can compile the SumGraph application, you need to make the changes recommended for the ScrollText applet. The revised ScrollText applet with the overloaded init() method can be used in applets and applications. The complete code for the revised ScrollText applet is shown in Listing 17.2.


Listing 17.2. The revised ScrollText applet.
import java.awt.Graphics;
import java.awt.Font;
import java.awt.Frame;

/**
 * Peter Norton's Guide to Programming Java * The ScrollText applet
 * has been reworked so it can be used with applications
 * This object is used to scroll a text banner across the screen
 * and takes TEXT, WIDTH, and HEIGHT as parameters.
 */

public class ScrollText extends java.applet.Applet implements Runnable {

     int h;                    // Height of applet in pixels
     int w;                    // Width of applet in Pixels
     char separated[];         // Output string in array form
     String s = null;          // Input string containing display text
     String hs = null;         // Input string containing height
     String ws = null;         // Input string containing width
     Thread ScrollThread = null;     // Thread to control processing
     int speed=35;             // Length of delay in milliseconds
     boolean threadSuspended = false;
     int dist;

//Init() method to allow parameter passing from an application
public void init(String inWidth, String inHeight, String inText) {
     ws = inWidth;
     hs = inHeight;
     if (ws == null){         // Read width as input, if not found use default
          w = 150;
     } else {
            w = Integer.parseInt(ws); // Convert input string to integer
     }
     if (hs == null){         // Read height as input, if not found use default
          h = 50;
     } else {
          h = Integer.parseInt (hs); // Convert input string to integer
     }
     resize(w,h);               // Set font based on height
     setFont(new Font("TimesRoman",Font.BOLD,h-2));
     s = inText;                    // Read input text, if null use default
     if (s == null) {
         s = " The Java ScrollText Applet at work.";
     }

     separated =  new char [s.length()];
     s.getChars(0,s.length(),separated,0);
 }

/* Start new thread to run applet */
public void start() {
     if(ScrollThread == null)
     {
        ScrollThread = new Thread(this);
        ScrollThread.start();
     }
 }

/* End thread containing applet */
public void stop() {
     ScrollThread = null;
 }

// While applet is running pause then scroll text
public void run() {
     while (ScrollThread != null) {
     try {Thread.sleep(speed);} catch (InterruptedException e){}
     scroll();
     }
     ScrollThread = null;
 }

// Scroll text by determining new location to draw text and redrawing
synchronized void scroll () {
     dist-;               // Move string to left
     // If string has disappeared to the left, move back to right edge
     if (dist + ((s.length()+1)*(h *5 / 11)) == 0){
     dist=w;
}
     repaint();
}

// Redraw string at given location
public void paint(Graphics g) {
     g.drawChars(separated, 0, s.length(), dist,4 *h / 5);
 }

// Suspend thread when mouse is pushed, resume when pused again
public boolean mouseDown(java.awt.Event evt, int x, int y) {
        if (threadSuspended) {
            ScrollThread.resume();
        }
        else {
            ScrollThread.suspend();
        }
        threadSuspended = !threadSuspended;
    return true;
    }
}

Save the updated ScrollText applet to a file named ScrollText.java and run the Java compiler using the command

javac ScrollText.java

After compiling the ScrollText applet, you can compile the SumGraph application. If you haven't already saved the SumGraph application to a file named SumGraph.java, do so now. Then you can run the Java compiler using the command

javac SumGraph.java

Now you can run the SumGraph program by typing

java SumGraph

You can also pass the SumGraph program a list of numbers to sum from the command line, such as

java SumGraph 25 30 45

The program will save the final sum to a file named SumResult.txt in the directory where it runs.

Tip
Testing a stand-alone application is very important. Follow the suggestions described in Chapter 14 for testing applets to create a test plan for your stand-alone application.

Summary

Applications have many similarities to applets and some important differences. Applications require a main() method. Applets do not. Applications have full access to the local file system and can access native code. Applets loaded over the network cannot access the client's file system in any way.

Although applets provide better security, applications are more flexible and generally have better performance. This increased flexibility and performance provides you with more options when creating applications. Most applications access the local file system and include graphical interfaces such as pop-up windows and pull-down menus.