A Complete Porting Example
Introduction
In this Magercise, we will port a complete OS/2 application from C/PM to
Java. This Magercise will cover the following concepts:
The application that we will be porting is a "Mini Personal
Information Manager" known as MiniPIM. It's a fairly simple application that's
really four applications in one. A set of four buttons at the top of all screens
allow you to switch between the components of MiniPIM:
Notepad -- a very simple text editor/viewer. Text can be
opened or saved using menus.
Phone List -- a simple phone list that will contains business, home and
fax numbers. This information is automatically loaded when you start MiniPIM, and is
saved when you exit MiniPIM.
Calculator -- a simple four-function floating-point calculator.
To-Do List -- a simple to do list. Entries are loaded and saved in
the same manner as the phone list.
Complete source code and the executable for the OS/2 version is here.
The original application looks as follows:

The MiniPIM Notepad

The MiniPIM Phone List

The MiniPIM Calculator

The MiniPIM To-Do List
One thing you may notice is that the interface looks very simple.
Depending upon how you implement it, it might prove to be tricky to get it to look quite
like you'd like.
A big difference in most OS/2 and Java GUIs is resizability of the
windows. In many OS/2 applications, the windows cannot be resized at all. In
some, you can resize the window, but the contents stay fixed in the same position.
And in a few, resizing the window will cause the GUI to be rearranged dynamically like
Java Layout Managers.
This application will allow you to resize windows, but it does not adjust
component positions. Keep this in mind for when we discuss enhancements.
Examine each of the above screen pictures. Think about how each
screen is organized, which parts of the GUI could be split out into sub parts.
The MiniPIM application has the following behavior:
Phone List
Data is saved upon MiniPIM exit, loaded on re-entry. A fixed file
name of minipim.ini is used to hold the data. The data is stored in binary format.
Names can be typed or edited in the combobox and this will change the
current value. Keep this in mind -- Java does not have ComboBoxes and we must come up with
a different way to handle this.
Add Button adds a new entry to the list.
Remove removes the currently-displayed entry from the list.
Calculator
To-Do List
Entries may be typed in the TextEntryField at the top of the screen.
Pressing the Add button adds the entry to the central list.
Pressing the Remove button removes the currently-selected item in the
list.
The list is saved upon MiniPIM exit, loaded on reentry. A fixed
file name of minipim.ini is used to hold the data. The data is stored in binary
format.
Whenever you are porting an application, it usually is going to involve a great deal of
work.
If you have been thinking about possible enhancements to the application being ported,
it would be a very wise idea to consider the enhancements before you port it. This
is important for two reasons:
- Thinking about later enhancements ensures that you have an open mindset. If you
consider possible changes, you are much less likely to implement your port in a manner
that prevents those enhancements from being added later. Better still, many times
this will help influence design decisions that will make it even easier to modify the
application later.
- Thinking about enhancements opens the possibility that some enhancements can be included
during the port itself. Most language-to-language ports cannot possibly implement
the exact same behavior as the original application. This is usually due to
restrictions in the target language, or the lifting of restirctions in the target langauge
that allow for a more natural way to implement the program. Because the program is
likely to be different anyway, it can often make sense to make some enhancements at the
time of the port. This decision should be weighed against the complexity of the
enhacement. You want to make sure the program was ported correctly, and give as low
a chance as possible of enhancements breaking the application.
We will implement the following enhancements/changes during our port:
The first step to building the new GUI in Java is to determine how we want to lay out
the components. Examine the above screen shots of the OS/2 components. Now
pretend that the application had been implemented using a nice tabbed panel look in OS/2.
If you pretend hard enough, it almost appears as though someone has used a paint
program to create some newer screen pictures...




That's the look we want to implement in Java! And when we're finished the
application might look like this:




Looks like our imagination is fertile enough. But how exactly are
those components laid out to make the Java application look like that? That's where
you have to do some thinking!
If you need help, follow this link to see some
diagrams and trees that describe the GUI layouts used for this application.
The tabbed panel component is called com.magelang.TabSplitter.
You can add it to your design by using the Options->Add bean
menu item and specifying its name. You add componets or panels to it by dropping
panels on it. One note -- as it uses a CardLayout for its representation of
components, you'll need to use the Beans List to move between the
components. You can get to the Beans List via the Tools->Beans List
menu item. Get used to the Beans List -- there are many times when it will become
the only way to manipulate components, especially when adding components to a BorderLayout
that has a container in its center.
If you're really having trouble building the GUI, follow this
link to get instructions on how to proceed.
When you're finished, make sure you save and test your application's GUI.
Just for fun, try dragging one of the tabs in the TabSplitter on top of another tab...
In Examining the Old Behavior, we discussed
the behavior of the application as it currently exists. We want to emulate all of
the existing behavior, with the following exceptions:
- Component positions should be dynamic -- this is taken care of through the use of layout
managers in the previous step.
- ComboBox is not supported in Java, so we need to bring up a dialog to add an entry.
- File names for the phone list and to-do list should not be hardcoded in the code - they
should be provided as property values to the beans.
- We used a tabbed panel instead of buttons.
- We want to handle floating point problems in a nicer way.
To implement the functionality, we'll need to define four Kernel classes. The
Kernel is a piece of code that is essentially the "guts" of the application.
We're having four Kernels because the functionality is well separated between the
components. We shall call these Kernels:
- NotepadKernel
- PhoneListKernel
- CalculatorKernel
- ToDoListKenel
The following describes the behavior of these Kernels in pseudocode. See the
solution code in project MageLang Magercise Solutions, package magercises.minipim
for a sample implementation.
Define all the listed properties and public methods using the BeanInfo browser in
VisualAge. Inside BeanInfo, press the "New property feature" button on the
toolbar to add a propety, "New method feature" to add a method.
Note that there are two types of file I/O mentioned in the behavior specification:
binary and ASCII. Take a look at the descriptions of the Java File I/O classes in
the Java API reference (select "Help->Reference" inside VisualAge.)
NotepadKernel
Properties |
String dirName |
name of directory containing file to save |
String fileName |
name of file to save |
TextArea text |
a ref to the text area to load/save |
Methods |
public void load() |
open the file that dirName and fileName refer to for READ
set readText to ""
while not end-of-file
read text and append it to readText
set the contents of text to readText
close the file
|
public void save() |
if no filename is set
throw a runtime exception "no file set"
open the file that dirName and fileName refer to for WRITE
dump the contents of text to the file
close the file
|
PhoneListKernel
Properties |
Choice name |
the Choice list of names in the phone book |
Hashtable fax |
a ref to a hash table of fax numbers |
Hashtable business |
a ref to a hash table of business numbers |
Hashtable home |
a ref to a hash table of home numbers |
String fileame |
the file name to store the phone list |
String dirName |
the dir containing the save file |
Methods |
public void load() |
open the file referenced by dirName/fileName for READ
for each group of stuff in the file
read a String for the name
read a String for the fax number
read a String for the home number
read a String for the business number
add the name to the name Choice
add (name, fax) to the fax hashtable
add (name, home) to the home hashtable
add (name, business) to the business hashtable
close the file
|
public void save() |
open the file referenced by dirName/fileName for WRITE
for each name in the name Choice
write the name to the file
get the fax number for the name & write it to the file
get the home number for the name & write it to the file
get the business number for the name & write it to the file
close the file
|
CalculatorKernel
Properties |
String display |
the value to display on the calculator Note that this should be a bound
property! That is, whenever this field changes, it will fire a propertyChange
event. This is easily done by checking the "bound" box when creating a
property under BeanInfo. |
Other (Recommended) Data |
float memory |
an internal memory to keep track of current computation |
boolean startNewDisplay |
tells the key() method whether to append the key pressed to the display or start a new
display |
String pendingOperation |
the label of the _last_ operation key pressed(remember, when a key is pressed, we are
in the middle of the current operation, so we finalize the last operation and store the
current one for the next operation key pressed to compute |
Methods |
protected void compute() |
convert display to a float
compute the new memory value based on pendingOperation
set the display
|
public void key(String keyName) |
if the key pressed was 0-9
if we should start a new display or
current display is "0"
set display to key pressed
make sure we do not start a new display next time
else
append key pressed to current display
else if the key pressed was clear
clear the display to "0"
make sure we start a new display next time
clear memory
send pendingOperation to "+" (adds display to 0 memory)
else if the key pressed was an operation key or "="
compute()
set pending operation to the key that was pressed
make sure we start a new display the next time
if key pressed was "="
clear memory
|
ToDoListKernel
Properties |
String fileName |
the name of the file to save/load |
String dirName |
the name of the directory containing fileName |
List list |
the list of to-do items |
Methods |
public void load() |
open the file referenced by dirName/fileName for READ
for each String in the file
add the string to list
close the file
|
public void save() |
open the file referenced by dirName/fileName for WRITE
for each String in list
write the string to the file
close the file
|
Finally, you need to connect the GUI components to the kernel components. If you
paid close attention to the kernel descriptions given, yo may have noticed that the
kernels do not peform all of the work. Much of the work to be done in the
connections will be done entirely in the GUI. For example, you will need to set up
the To-Do List buttons to add and remove items from the List component. The kernel
isn't even used until the aplication is exited or restarted.
Note that you will need to add the following pieces to finish the Magercise:
- A dialog that asks for the name of a person to add to the phone list
- An instance of each of the kernels that you defined in the last step
- Three hashtables to be used for storing data in the phone listing component. (Use
Options->Add Bean and add java.util.Hashtable components.)
If you need help determining which connections you need, follow
this link.
|