6.170 Laboratory in Software Engineering
Spring 2000
Problem Set 5b: PathFinder Applet
Due: March 16, 2000
Handout 13
Guidelines
To do this assignment as efficiently as possible, and to derive the most
benefit, we recommend that you:
-
Start as early as possible.
-
Read the entire text of the assignment before starting any part of
it.
-
Think before you code. You'll find that sketching out a plan on paper
before
you start will make your coding faster and less error-prone.
-
Follow the Java style guide.
-
Make sure that your answers to questions are succinct and to the
point.
To hand your solution in, follow the standard instructions, which state
in part:
Each assignment should be handed in as hardcopy to your TA
or to the course secretary (NE43-529) by 4:30 PM on its due date. It is
usually most convenient to give the assignment to your TA at the end of
class on that day. Additionally, the source and compiled code for each
problem set should be put online so your TA can test your program.
See the general information sheet (handout 1) for more details.
The directory for online submission of this problem set is
~/6.170/ps5b.
Purpose
In this part of problem set 5 you will build a graphical interface to
the PathFinder program that you wrote in part a. The document that you
hand in will encompass both parts a and b.
Background
Your graphical interface to the PathFinder will use heavily JFC-Swing
API. There are a number of Swing tutorials online, which can be found at
http://developer.java.sun.com/developer/onlineTraining.
You might want to take a look at the "Swing Tutorial" or "Fundamentals
of JFC/Swing", Part I and II.
The basic elements that you will use in this problem are part of the
java.awt and javax.swing packages (you will primarily use javax.swing,
which is based on java.awt, but provides more functionality and
flexibility). Note that many Swing component classes are prefixed with a
letter "J" (such as JApplet) to distinguish them from the old
version of the Java GUI package, java.awt. In general, the Swing "J"
classes are derived from the corresponding AWT classes, and are more
powerful than their AWT superclasses.
In this problem set you will be asked to write an applet. Most applets
are quite simple. To make writing applets easier, the Java library
provides a very powerful abstraction called JApplet, contained in
the javax.swing.JApplet class. The JApplet class provides
all of the functionality necessary for building applets of your own.
Because the JApplet class is so powerful, custom applets can
provide only the code they need. The following code segments completely
describes a working applet that prints the message "Hello" in its
window.
public class Hello extends JApplet {
public void paint(Graphics g) {
g.drawString("Hello", 10, 20);
}
}
You will notice that there is no code for creating or displaying
windows. All of this is managed by the JApplet class. The only
functionality this code provides is that specifically needed for this
applet, i.e. displaying its greeting message. Though the code for this
applet is very simple, this applet doesn't do that much either. We will
see below that we can add buttons, and other user interface elements to
the applet's window to provide greater functionality.
1. Components and Interface Elements
To create more interesting applets than the example above, we must add
some elements to the interface for interacting with the user. Java
provides a number of different interface elements for us to use. Before
we take a look at them, it is necessary for us to understand a little of
the structure of Java's component hierarchy.
1.1 Component Hierarchy
A Component is an object having a graphical representation that
can be displayed on the screen and that the user can interact with. The
Component hierarchy includes a number of interface elements. In
particular, there is a Container class that holds other
Components. You should use the Swing version of the Component
class - JComponent, which extends the Container class (see
figure below for the general relationship between different groups of
classes). JComponent has a number of subclasses, which you will
use in this problem set. They include, but not limited to, an
AbstractButton class that provides button controls that can be clicked
on to tell the program to perform some action (this class is abstract,
thus you will probably want to use JButton, which is derived from
the AbstractButton); a JLabel class
that provides a textual label next to other components to inform the
user of their function; and a JTextComponent class that allows
text to be entered. JTextComponents can be used both for input
and for output and come in single line versions (JTextField) and
multiple versions (JTextArea). For more information on these and
other components, see the
description of the javax.swing package at http://www.javasoft.com/products/jdk/1.2/docs/api/index.html.
A user interface is always made up of a number of interface elements that
are laid out within a container. The layout is performed by the
container itself. JWindow and JApplet are both containers.
A container can hold other components, and therefore, can hold other
containers. For more complex layouts it may be necessary to nest
containers. You can create JPanels to hold your interface elements
and then arrange the JPanels within your JApplet. Containers
can be nested to any level.
The figure below illustrates the basic structure of the
Component hierarchy. In reality, JComponent has a lot more
subclasses than the ones illustrated here, and you are not limited to the
number and type of components discussed in this problem set. For a
complete description of the Swing component hierarchy, see Part
I of the Short Course in Swing.
| Component |
| | |
| Container |
| | |
JComponent |
| | |
|
|
| | |
| |
| |
| |
| |
| JTextComponent |
JList |
JLabel |
AbstractButton |
JScrollPane |
| | |
|
| |
|
|
|
|
|
|     | |
|    |
|
    | |
|    |
|
| JTextArea |
JTextField |
|
JButton |
JMenuItem |
|
1.2 Component Layout
The organization of interface elements within a container is controlled
by a set of helper classes called Layout Managers. Java provides a
number of different layout managers that provide various forms of
automatic organization of the interface elements within them. The most
popular layout managers are BorderLayout, GridLayout, and BoxLayout
but you may also find GridBagLayout, FlowLayout, or
CardLayout useful. Each container comes with a default layout
manager. You can change the default manager by calling the
setLayout method. For example, a call to:
MyContainer.setLayout(new BorderLayout());
sets the current layout manager for the container MyContainer to a new
BorderLayout. Once the layout manager has been set, components
can be added to the container with the add command. There are several
forms of the add command, but the following two are the most useful:
MyContainer.add(Component c);
MyContainer.add(Component c, Object constraint);
The first adds the component c to MyContainer. The second adds a
component along with a constraint. The constraint is used by some layout managers,
such as BorderLayout, to determine where to place the
component. Because many of the layout managers have rather simplistic
formatting algorithms, you will frequently have to take advantage of
container nesting to get a complex arrangement of interface
elements. Remember that a Container is itself a subclass of
Component. Therefore, a whole container with its associated
elements can be moved around just like any other Component. The
only exceptions to this rule are JFrame and JApplet
classes. Although these classes are derived from the Container
class, you can't add components directly to them. In order to place
components inside an applet, you need to get a content pane that
is managed by the JApplet object (a content pane is a container,
which represents an area of an applet). Here is a sample code that will
add a label and a button to an applet that has a border layout:
public class OneButtonApplet extends JApplet {
public void init() {
Container content = getContentPane();
content.setLayout(new BorderLayout());
content.add(new JLabel("Sample Label:"), BorderLayout.WEST);
content.add(new JButton("Sample Button"), BorderLayout.EAST);
}
}
1.3 Useful Components
You will find the following components useful for this assignment:
JTextArea (http://java.sun.com/products/jdk/1.2/docs/api/javax/swing/JTextArea.html) is used to input or output text information. Useful methods are
setEditable to control whether the text is editable,
setText and append which allow you to change the text
displayed in the JTextArea, and getText which returns the
text in the component.
JTextField
(http://java.sun.com/products/jdk/1.2/docs/api/javax/swing/JTextField.html)
is a single line version of JTextArea with similar methods.
JButton (http://java.sun.com/products/jdk/1.2/docs/api/javax/swing/JButton.html)
is used for implementing buttons. Useful methods are setEnabled
for specifying whether the button is usable, and
setActionCommand and addActionListener for event handling.
JList (http://java.sun.com/products/jdk/1.2/docs/api/javax/swing/JList.html)
is used if you need to choose from a list of pre-defined items. Methods
of interest are add to add items to the list, setSelectedValue to
initially select an item, and getSelectedValue to find the
currently selected item.
JLabel (http://java.sun.com/products/jdk/1.2/docs/api/javax/swing/JLabel.html)
is used if you need simple labels. Useful methods are
setAlignmentX and setAlignmentY that control the alignment
of the label.
JScrollPane
(http://java.sun.com/products/jdk/1.2/docs/api/javax/swing/JScrollPane.html)
is used if you want automatic handling of horizontal and vertical
scrolling of content (you might want to do this for JTextArea
and JList). When you want an object to be scrolled, you add it
to the JScrollPane in the following manner:
JScrollPane pane = new JScrollPane(someComponent);
someScrollPane.getViewport().add(someComponent);
2. Events
In Java, interactions with the user, such as clicking on a
JButton or typing in a JTextArea, are communicated to a
program through events. An event is simply an object that contains
information about the event and its source, and it is passed to a set of
classes called event handlers. The call to the event handler is actually
performed by the Java runtime environment automatically; however, a
program is responsible for telling the runtime system which handlers to
call. Because there are many things that can happen in a program, there
are many different classes of events. There are two major categories of
events in Java: low-level events (e.g., MouseEvents and
WindowEvents) and semantic events (e.g., ActionEvents and
AdjustmentEvents). In this problem set, you will deal mostly with
ActionEvents (but you might want to consider other events as well
to add more functionality to your applet). ActionEvents
represent the actions that happen in a program. In particular, when a
button is clicked, that button passes an ActionEvent object to
the appropriate handler. ActionEvents have a number of methods,
but usually, only the getActionCommand and getSource
methods are of any interest. getActionCommand method returns the
name of the action. For JButtons, this is simply the name of the
JButton. getSource method returns the object on which the event
initially occurred.
In order for the runtime system to correctly tell a program that an
event has occurred, it is necessary for that program to register its
event handlers. There are separate event handler methods for each type
of event. For ActionEvents, the handlers are defined by a special
interface class called ActionListener.
The ActionListener class has a single method,
actionPerformed. The object that implements this method is said
to be a handler object. If that object is registered as a handler for a
component, the above method will be called by the runtime system when
action events are generated by that component. Handler objects must be
registered separately for each component, but many components can share
the same handler object by passing the same object during registration.
Registration of handler objects is performed through one of the handler
registration methods. For more information on different kinds of events
and listener interfaces, see Part
II of the Short Course in Swing.
Typically a component will only have handler registration methods for
the events it generates. For the case of ActionEvents, the
registration method is called addActionListener. The handler
object passed to this method is any object that implements the
ActionListener interface. Typically you will design a small class
in conjunction with your Applet that will implement a Listener interface
for every type of action. Then a single instance of this helper class
can be passed to the handler registration methods (such as
addActionListener) for each component that appears in the
Applet.
In the following code sample, a simple applet is defined that contains
two buttons:
public class TwoButtons extends JApplet {
JButton button1;
JButton button2;
ButtonHandler handler;
public void init() {
// create the event handler
handler = new ButtonHandler();
// create the buttons
button1 = new JButton("Button1");
button2 = new JButton("Button2");
// add the listener and set the action
button1.addActionListener(handler);
button2.addActionListener(handler);
// add component to Applet
add(button1);
add(button2);
}
class ButtonHandler implements ActionListener {
// Overview: a button listener object for the TwoButtons Applet
// handler for assigning actions
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == button1)
// code for the case when "Button1" is pressed
else
// code for the case when "Button2" is pressed
}
} // end ButtonHandler
}
In this code, a handler object is defined for ActionEvents and is
registered as the handler for the JButtons. Note that the handler
object handles the event directly based on which button is pressed.
If you consult the samples on the web or in books, you will see other
styles for achieving the goals outlined here. For instance, on occasion
the applet class itself is also the handler and extends the Listener
interface. In addition, sometimes the handler object is implemented with
an outer class, in which case a pointer to the applet has to be passed
to the listener object. For more information on the alternative
approaches of handling events in Swing, see examples in Part
II of the Short Course in Swing.
3. File Access and URL Class
Because of the distributed nature of applets when used on the World Wide
Web, applets can be somewhat dangerous. An applet that is loaded off the
web represents code written by someone else that is running on your
machine. If that code were harmful it could cause damage to your system
before you were aware that it was running. To protect web users from
this hazard, Java applets cannot perform some operations that Java
applications can. Loading files or otherwise accessing the disk is one
such operation. Therefore, when you design an applet you must have
another way to access file data that your applet will need. A common
way of providing this access is through the "java.net.URL" class. This
class is a very powerful class that provides functionality for
downloading files and other resources directly from a web server across
the network.
Because you will need to load a database for your database engine to
perform queries on, it will be necessary for you to read a file from a
URL object. Typically this is done using the following two lines
of code:
URL url = new URL("http://web.mit.edu/6.170/src/ps5a/test.database");
BufferedReader b =
new BufferedReader(new InputStreamReader(url.openStream()));
The first line opens a web connection for the file and the second line
connects a stream to that connection for reading the file. Once the
BufferedReader is obtained it can be used in just the same way
that you would use a BufferedReader obtained from a
FileReader. There is one catch to all of this. An applet can
only request files from the server from which the applet was retrieved.
Fortunately, this restriction is relaxed when you run the applet in
appletviewer. However, keep this in mind if you use Netscape or
Internet Explorer to test your applet.
If you want to serve your own test files from your Athena locker while
working on your problem set, you should place your test files in a
directory with the correct permissions. Files accessible through
web.mit.edu can be placed anywhere in your locker but the
directory must provide read permissions to the system group
system:anyuser, meaning that it is world-readable. You can make
a directory world-readable with the following command:
fs sa <dirpath> -acl system:anyuser read
where <dirpath> is the path to the directory whose permissions
you wish to change. Be careful about which directories you give
system:anyuser read access to, as the whole world will be able to
read these files through the web.
4. Viewing applets
To display an applet, you need an HTML file that references it. The
applet tag is used for this purpose, as in the following HTML
fragment:
<applet code="ps5b/PathFinder.class" width=600 height=500
align="middle">
</applet>
For this problem set, you should create a "PathFinder.html" file for
your applet. You should use "appletviewer" to view your applet:
appletviewer PathFinder.html
Assignment
Create an applet that displays an interface that allows the user to
query a database. The interface should provide some field(s) for
setting up the database file. Once the database is loaded, additional
input devices should support querying the database and displaying the
results. The user must be allowed to load another database at any
time. In the Appendix we provided you with a sample of what
your interface might look like, but you don't have to follow the same
format.
Your interface should contain features such as disabling the "run
query" function (e.g., graying out the button or whatever controls that
function with the "setEnabled" method) and displaying some sort of
message prompting the user to enter a database filename when no database
is loaded, and displaying some kind of "please wait..." message
indicating that the program is busy while loading large
databases.
You should not modify any files in your ~/6.170/ps5a/ 
directory after you have turned in part a. You should copy your
entire ps5a directory to a ps5b directory for use with part b (you
can do so with the command "cp -R ~/6.170/ps5a ~/6.170/ps5b"  ).   We
should be able to run your applet from your ~/6.170/ps5b  
directory with the command:
appletviewer PathFinder.html
Extensions
If you wish you can provide a program that does more than what is
discussed above. However, before doing this, you should be certain that
your program handles all the required commands and formats. Also you
should discuss proposed extensions with your TA before doing them.
Extensions might involve allowing editing of the database file or, for
the truly ambitious, graphically displaying the database and
highlighting the resulting routes.
What to turn in
As mentioned above, we should be able to view your applet using
appletviewer. In addition, you must turn in documentation
covering both parts a and b of problem set 5. Your applet and this
document are due on March 16. On this date you must deliver
hardcopies of both your documentation and your executable code (for both
parts a and b; for part b only include new and modified code) as an
appendix to your document to your TA or to the course secretary by
4:30PM. Remember to indicate the name of your TA on the first page.
Your document should follow all of the guidelines explained in Handout
12.
Be sure that your documentation contains the following:
a.   Data Model. Your data model should
include both a textual part and a graph. The graph can be drawn by
hand. Make sure you have read Chapter 12 of Class Notes, which
describes how to develop a good data model.
b.   Module Dependency Diagram.
c.   Design rationale.
d.   Implementation (in the appendix). You code should
contain specifications for all of your data abstractions. Each
implementation should include a rep invariant, abstraction function,
and implementations of repOK and toString.
e.   Testing strategy , both black box and glass box, for each individual data
abstraction and integration testing for the program as a whole.
f.    A discussion about what you had to modify, if anything,
in your original design and code for part a in building your applet for
part b.
Be clear in your discussions when you are talking about part a and when
you are talking about part b.
Be concise -- do not confuse length with content! Good luck!
Appendix
Click here to look at the sample of the applet interface.