#

## Chapter 13

## Inside WordSmith

At this point in the book, you've explored various concepts for building channels and examined the source code to various small examples that demonstrate those concepts. But sometimes it's difficult to figure out how to combine those smaller concepts into a larger one. For just that reason, in this chapter you'll finish up the meat of this book by exploring a complete, running example of a complex channel. The channel in question is a game called WordSmith. WordSmith combines Bongo presentations and very sophisticated custom widgets with Java channel code to handle automatic updates, save local data, and uses a transmitter plug-in as well. We won't cover all aspects of this channel here—just those that relate specifically to channel development—but you can explore the source code on your own to get a feel for how the channel was put together.

WordSmith was written by Carl W. Haynes III, a Marimba employee who has written a number of excellent channels (including the extremely popular SameGame channel). He has graciously released the source code for other channel programmers to see. You can get this source code on the CD-ROM for this book, or from the Marimba Web site at `http://www.marimba.com/developer/samplecode.html`. The version I discuss here—the one that comes with this book—is the version current as of this writing; by the time you read this, WordSmith may have new features or updates added to it, and to see and explore those, you'd need to get the source code from the Web site.

> ####

> Note

> There are two versions of the Wordsmith software: one with a dictionary file, and one without. You'll need the dictionary to play the game; but if all you're interested in is the source code, you can download the smaller of the two versions. The version that comes on the CD-ROM with this book has the dictionary.

### The Game

The first step towards understanding how any channel or program works is to play with the final version (or, if the program isn't written yet, to have a general plan for how the program will operate).

When you subscribe to and start the WordSmith channel, the main WordSmith window appears and a new game is set up. Figure 13.1 shows that initial window.

Figure 13.1. The WordSmith channel.

Game play works like this: You can drag and drop tiles from the tops of the columns in the main part of the screen to the top of the screen to form words (you can only use the letters at the top of each column; the letters underneath are unavailable until uncovered). Once you've spelled a word of at least three letters, click the Check button. WordSmith compares your word against a dictionary, and if you've created a real word, those tiles are cleared and you're given a score for that word. (Scoring works exponentially based on the number of letters in the word; you don't get extra points for the Zs and Qs like you do in scrabble.) You can also choose the Clear button to put the letter back in the columns where they were before.

Continue building words until you've used all the letters on the screen (you can also create nonsense words to get unusable letters out of the way, but you won't get any points for those letters).

The part of WordSmith that makes it more than just a simple game is the management of high scores. WordSmith keeps track of two lists of high scores: a local list for the version of the channel running on your system and a global list for everyone who is running that same channel. The WordSmith channel keeps track of the global high scores through the use of channel feedback and updates so that the global high scores will always reflect the top WordSmith players in the world (if you feel particularly competitive about it).

> ####

> Note

> The WordSmith channel has a lot in common—both in game play and in underlying infrastructure—with the SameGame channel, which I mentioned earlier on in this book as an example of a channel to play with. (It's available from `trans.marimba.com`.) If you've played SameGame, you'll most likely recognize many of the same features.

### Channel Organization

Let's start exploring the WordSmith channel by looking at the channel directory and its organization. Figure 13.2 shows the contents of the WordSmith channel directory.

Figure 13.2. The complete WordSmith directory.

The channel directory contains the following files and directories:

- A set of presentation (`.gui`) files for the various windows and dialogs in the game.
- `wordsmith`, a directory containing all the Java class files for the game.
- `plugin`, containing the files for the channel's transmitter plug-in.
- `dict`, a directory that contains the dictionary file for the game.
- `images`, a directory of images relating to the game and its icons.
- `jon`, a directory of images for the famous (infamous?) Jonathan Payne spinning head animation (used as part of the dialogs for the channel).
- Other channel files (`properties.txt`, `parameters.txt`, and a Makefile for building the channel).

An important aspect of this channel organization: the Java class files for this channel are not arbitrarily placed in the `wordsmith` directory. Instead, that directory is serving as a package for all the WordSmith code; each of the Java files inside the `wordsmith` directory are defined to be part of the `wordsmith` package. For the most part, this doesn't affect how you develop your channel; it's simply a good habit to get into as your create groups of interrelated classes.

The Presentations

WordSmith uses a number of Bongo presentations for the various windows and dialogs in the game. Each of these presentations is stored in the top-level channel directory as a `.gui` file. Those presentations are

- `wordsmith.gui`: The main presentation for the WordSmith channel.
- `highscorelist.gui`: A window to display the current local and global high scores.
- `help.gui`: A window to display online help for the channel.
- `about.gui`: A window to display information about the channel.
- `highscore.gui`: A dialog to prompt the user for their name and e-mail address if they get a high score.
- `youwon.gui`: A dialog to congratulate the user on winning.

I'm not going to go over the contents of each of these presentations here; you can use Bongo to view them and their scripts. The main presentation—`wordsmith.gui`—however, is worth at least a glance, as it uses a custom widget called `WordSmithWidget`.

In fact, if you open the `wordsmith.gui` presentation and browse it, you'll notice that most of the basic functionality of the game is contained in this presentation through the `WordSmithWidget` class. The tiles are created and laid out in columns, and you can move them to the rack at the top of the screen or choose the Check or Clear buttons to register the word or to reset the tiles back to normal.

The `wordsmith.gui` presentation is, for the most part, just a display surface for `WordSmithWidget`. `WordSmithWidget` is the heart of the WordSmith application and controls the vast majority of the game play, including choosing and laying out the tiles, animating the tiles when you move them, handling checking your word against a dictionary file, and keeping track of the score as you play.

The code for this widget is contained in the Java file `WordSmithWidget.java`, stored in the `wordsmith` directory. This large file is worth examining, particularly from the standpoint of creating sophisticated custom widgets (a topic that is outside the scope of this book; see the Official Marimba Guide to Bongo for more information). I won't go into detail on this file, but there are a few parts I do want to mention.

The `WordSmithWidget` code uses two utility classes, `Rack` and `Dict`, the source of which is also contained in the `wordsmith` directory. `Rack` keeps track of which tiles can be moved and how many tiles are left in the game; `Dict` manages the dictionary and does the testing to see if the word you've spelled is actually in the dictionary. It also has a handle to an instance of the class `HighScoreManager` for dealing with high scores—we'll go into this class later on in this chapter.

One of the more important parts of the WordSmith widget, which links it to the rest of the application is the `checkForWin()` method, which is called periodically during the game to check to see if the game is over yet. Listing 13.1 shows the code for this method

Listing 13.1 The `checkforWin()` method in WordSmith widget.

     1:public void checkForWin() {

     2:   if (tilesLeft > 2)

     3:      return;

     4:   try {

     5:      int place = -1;

     6:      if ((place = highScores.place(score, HighScoreManager.LOCAL)) != -1) {

     7:          new HighScoreFrame(getFrame(), new URL(getPresentation().getBase(),"highscore.gui"),

     8:              highScores, score, getPresentation()).show();

     9:       }

    10:      else {

    11:         new PlayerDialog(getFrame(), new URL(getPresentation().getBase(),

    12:             "youwon.gui")).show();

    13:      }

    14:   }

    15:   catch (MalformedURLException e) {

    16:      e.printStackTrace();

    17:   }

    18:}

Here's a general overview of what's going on here:

- Line 2 checks to make sure it is indeed the end of the game. (There should be fewer than three tiles left on the board for the game to end.)
- Line 6 checks to see if the score of the current game places it in the current list of local high scores (using methods defined in the `HighScoreManager` class—we'll look at this class in the next section). If so, it launches a dialog to prompt you for your name and e-mail address and to save your high score. This set of lines is the connection between this widget and the parts of the channel that manage high scores.
- Even if the score isn't a high score, you still get a nice congratulatory window (an instance of the Bongo class `PlayerDialog`), in lines 10 through 13.

You see how the `checkForWin()` method connects the WordSmith custom widget to the rest of the channel as this chapter progresses.

Managing High Scores

A significant part of the WordSmith application is dedicated to the management of high scores: reading them, storing them, displaying them in their own window, testing to see if a current score qualifies as a high score, and logging them back to the transmitter for the plug-in to process.

The WordSmith application keeps track of two sets of high scores: a local set, for the local version of the channel; and a global set, which contains scores for everyone who is subscribed to the WordSmith channel. When you win at WordSmith, the application tests to see if your score counts as a high score in either the local or global lists and stores your score, accordingly. If you've been good enough to merit a spot in the global list, the application logs your high score information to be sent to the transmitter at the next update. WordSmith also provides a way for you to display the current lists of global and local high scores.

There are four class files for managing and displaying high scores:

- `HighScore.java`: A utility class to describe an individual high score (with your name, e-mail address, and score).
- `HighScoreManager.java`: The central class for reading, writing and logging local and global high scores, testing to see if a current score merits placement in the list, and inserting the high score in the right slot.
- `HighScoreFrame.java`: A class to display the high score dialog and process the name and e-mail the user enters from there.
- `HighScoreListFrame.java`: A class to display the list of the current local and global high scores.

Let's start with the `HighScoreManager` class because that's the central class to manage high scores.

The `HighScoreManager` Class

The core of high-score management is the `HighScoreManager` class, which is responsible for

- Reading the stored high scores from a file into memory.
- Testing to see if a current score needs to be listed.
- Logging global high scores.
- Writing the score files back to disk when the program exits. A new instance of `HighScoreManager` is created when the application is started, and this object is used both by the main application and the WordSmith widget throughout the execution of the game.

Let's work through the basic life cycle of the `HighScoreManager`. When the class is first instantiated as the application starts up, the `HighScoreManager` creates `Vector` objects to store the local and global high scores and initializes them with empty `HighScore` objects. Then, two methods are called: `readLocalHighScores()` and `readGlobalHighScores()`.

The local high scores file is a file called `highscores.txt` and is contained in the local data directory for the channel, so the `readLocalHighScores()` method uses get `getDataDirectory()` method to locate it. The remainder of the `readLocalHighScores()` method simply opens that file (if it exists), and parses the scores into a `HighScore` object and then inserts that object into the vector in the right spot. (All high scores are sorted by score, highest to lowest.)

The `readGlobalHighScores()` method performs essentially the same operations as `readLocalHighScores`, except that the global high scores file is part of the channel's data; you'll use `context.getBase()` to locate that file and read its values into the vector for the global scores.

With the initialization of the existing scores files done, the `HighScoreManager` class doesn't do anything else until the game has been played and a new score is available. `WordSmithWidget`, given a new score, uses the `HighScoreManager`'s `place()` method to determine whether a score counts as a high score or not. All `place()` does is determine whether a score can be placed in the high score list (if the score is higher than the lowest score in the list). If it can, the widget uses `HighScoreFrame` to get the user's name and address, and `HighScoreFrame` in turn calls the `insert()` method to actually put the new score into the high score list.

The `insert()` method in the `HighScoreManager` is shown in Listing 13.2:

Listing 13.2. The `insert()` method.

     1:public void insert(HighScore hs) {

     2:   int i = place(hs.getScore(), LOCAL);

     3:   if (i != NOT_IN_TOPTEN)

     4:       insertAt(hs, i, LOCAL);

     5:

     6:   i = place(hs.getScore(), GLOBAL);

     7:   if (i != NOT_IN_TOPTEN) {

     8:       insertAt(hs, i, GLOBAL);

     9:       logGlobal(hs);

    10:       context.update();

    11:   }

    12:}

    13:

There are two steps in the `insert()` method: first, to insert the score in the local high score list, `insert()` calls `place()` to find out where to insert the score (line 2), and then calls the `insertAt()` utility method to actually put it in that spot (line 4). The second step is to do basically the same thing for the global high score list: find out where to place it, and if it actually merits entry in the global high scores (which are generally higher than the local high scores), then it does three things:

1. Uses `insertAt()` to insert the score in the global high score list (line 8).
2. Calls a method called `logGlobal()` (line 9), to log the score as feedback data to the transmitter (the `logGlobal()` method simply calls `appendLog()` with a string representation of the score).
3. Calls the `context.update()` method (line 10), which forces an update of the channel to send the new global high score to the transmitter for processing. (More about the update later on.)

The only other main operation the `HighScoreManager` class does is to save the local high scores as a local data file when the application is ready to quit. A method called `writelocalHighScores()` will do this, opening the local data file and writing each of the local scores to that file (the application doesn't need to write global high scores; those come from the transmitter as part of the channel update).

`HighScoreFrame`

The second of the major classes for managing high scores is `HighScoreFrame`. `HighScoreFrame` is an instance of the `PlayerFrame` class for displaying the `highscore.gui` presentation and interpreting the data you get from that window. The `highscore.gui` dialog is shown in Figure 13.3.

Figure 13.3. The High Score Dialog.

The `WordSmithWidget` class is responsible for creating and showing this dialog, once the game has been won and the widget has determined that the new score merits being added to the high score list. After entering their name and e-mail address into this window, the user clicks OK, an event which is trapped by the `handleEvent()` method in the `HighScoreFrame` class, which calls the `handleOKButton()` method. The contents of `handleOKButton()` is shown in Listing 13.3

Listing 13.3. The handleOKButton() method from HighScoreFrame.

     1:public void handleOKButton() {

     2:   TextBoxWidget name_tbw =

     3:     (TextBoxWidget)player.getPresentation().getWidget("name");

     4:   String name = name_tbw.getText().trim();

     5:   hsm.lastNameUsed = name;

     6:   if (name.equals(""))

     7:       name = "<empty>";

     8:   TextBoxWidget email_tbw =

     9:      (TextBoxWidget)player.getPresentation().getWidget("email");

    10:   String email = email_tbw.getText().trim();

    11:   hsm.laste-mailUsed = email;

    12:   if (email.equals(""))

    13:       email = "<empty>";

    14:   HighScore hs = new HighScore(score, name, email, new Date());

    15:   hsm.insert(hs);

    16:

    17:   dispose();

    18:}

The `handleOKButton` is responsible for two main things: first, it builds a HighScore object with the information from the dialog's widgets and the score from the `WordSmithWidget`. Then it calls the `insert()` method in `HighScoreManager` (line 15) to insert the new `HighScore` object in the right spot in the list. That done, it calls `dispose()` to get rid of itself.

#### HighScoreListFrame

The only high score class left to discuss is the `HighScoreListFrame` class, which uses the `highscorelist.gui` presentation to display the current list of local and global high scores. This class is creates in response to the Show High Scores menu item. All it does is display the `highscores.gui` presentation and fills in the list widget in that presentation with the scores information stored in the `HighScoreManager` class. See the code for `HighScoreListFrame.java` for details.

### The Main Application

If the `WordSmithWidget` class is responsible for all the actual game play, and the `HighScoreManager` processes all the high score data, what's left for the main application to do? The main application ties together all the basic behavior of the widget and the high-score manager, builds and processes a menubar for various options in the application, and manages live updates from the transmitter.

The file `WordSmithApplication.java` in the `wordsmith` directory contains the source for the WordSmith channel. `WordSmithApplication` is a subclass of `ApplicationPlayerFrame`, and implements all the channel methods you've come to know and love: `setContext()`, `start()`, `stop()`, `notifyAvailable()`, `notifyInstall()`, and `handleEvent()`.

Initializing the Channel

Let's start with the `setContext()` method, as that's the first method that's called when the channel starts up. `setContext()` looks like this:

    public void setContext(ApplicationContext context) {

       super.setContext(context);

       highScores = new HighScoreManager();

       WordSmithWidget wsw = (WordSmithWidget)util.getWidget("wordsmith");

       wsw.setContext(context);

       wsw.setHighScoreManager(highScores);

    }

The `setContext()` method does three main things to initialize the channel:

- It calls `super.setContext()`, as all channels should, so that the `ApplicationContext` can be saved into the `context` instance variable.
- It initializes an instance of `HighScoreManager`, and stores it in the `highScores` variable.
- It gets ahold of `WordSmithWidget`, and passes the context and the high score manager to that widget.

The next step is the `start()` method:

    public void start() {

       super.start();

       highScores.setContext(context);

       highScores.readLocalHighScores();

       highScores.readGlobalHighScores();

    }

Here's where the high-score manager gets initialized, and where the local and global high-score lists are read into memory. After `start()` finishes executing, the game is ready to be played.

> ####

> Note

> The `start()` method actually contains two more lines I've edited out here: a call to a method called `readState()` and a call to `show()`. The former is a local method that is not implemented in this version of the channe (or rather, it's implemented empty) and the latter is actually already called in `super.start()` so it's redundant to call it here.

#### Handling Updates

Live updates to the WordSmith channel happen in the form of updated global high-score lists. An update can occur both because the update interval for the channel has passed, and also in response to a high score being logged by the `HighScoreManager`.(Remember that `HighScoreManager` calls the `context.update()` method explicitly.) To handle the new high score data, the `WordSmithApplication` channel overrides both `notifyAvailable()` and `notifyInstall()`.

Both these methods should look extremely familiar to you; I used similar versions in the Weather channel class from Chapter 11, "Managing Updates and User Information." Listing 13.4 shows both `notifyAvailable()` and `notifyInstall()`.

Listing 13.4. Update methods in `WordSmithApplication`.

     1: public void notifyAvailable(String dir) {

     2:    if ("restart".equals(context.getParameter("update.action"))) {

     3:        context.restart();

     4:    }

     5:

     6:    context.installData(dir);

     7: }

     8:

     9: public void notifyInstall(String dir) {

    10:    highScores.readGlobalHighScores();

    11: }

The `notifyAvailable()` method, as you know, is called after an update has occurred, and there's new data to be installed. In this method, if the channel has been published with the `Data Available` property as `restart()`, the channel will be restarted. In any case, however, we'll want the new data to be installed, so `context.installData` is called at the end of that method.

The `notifyInstall()` method processes the new data. In this case, the high-score manager is responsible for managing the global high scores that have arrived as part of the update, so you call the `readGlobalHighScores()` method in that class to re-read the list of high scores.

The combination of these two methods, in addition to the methods in the `HighScoreManager` class, ensure that the running channel has the most up-to-date version of the high score list at all times.

The Menubar

The `WordSmithApplication` also has one other feature we haven't covered in this book: it has a menubar with several menus and menu items.

Adding menus and menubars for a channel that uses a presentation is extremely similar to adding a menubar to a normal Java application, with one significant difference: instead of using the `java.awt` classes for handling menus, you use special classes in the `marimba.desktop` package (part of the `bongo.zip` library). These special classes include `AppMenuBar`, `AppMenu`, `AppMenuItem`, and `AppCheckboxMenuItem` and are very similar to their counterparts in the `java.awt` package. You can find out more about these classes (and about adding menus to presentations) in the Official Marimba Guide to Bongo.

The `WordSmithApplication` adds the menubar to the channel as part of the constructor for the class; because it's initialization code for the window itself and doesn't fit into `setContext()` very well, this is a good place for it. You can see the actual code in the Java file for the WordSmith application.

The `handleEvent()` method for the `WordSmithApplication` class also manages menu events, calling different methods in different objects for each different menu item. Listing 13.5 shows the part of the `handleEvent()` method that deals with menus. Note in particular the "Playing WordSmith" and "About WordSmith" menu items in lines 11 through 29, which display the `help.gui` and `about.gui` presentations in their own frames, respectively.

Listing 13.5. A partial `handleEvent()`. to handle menu events.

    1:if ((""+evt.arg).equalsIgnoreCase("quit")) {

     2:   context.stop();

     3:}

     4:else if ((""+evt.arg).equalsIgnoreCase("high scores...")) {

     5:   showHighScoreList();

     6:}

     7:else if ((""+evt.arg).equalsIgnoreCase("New Game")) {

     8:   WordSmithWidget wsw = (WordSmithWidget)util.getWidget("wordsmith");

     9:   wsw.newGame();

    10:}

    11:else if ((""+evt.arg).equalsIgnoreCase("Playing WordSmith...")) {

    12:  try {

    13:    PlayerFrame pf = new PlayerFrame();

    14:    pf.util.setPresentation(new URL(context.getBase(), "help.gui"));

    15:    pf.show();

    16:  }

    17:  catch(MalformedURLException e) {

    18:     e.printStackTrace();

    19:  }

    20:}

    21:else if ((""+evt.arg).equalsIgnoreCase("About WordSmith...")) {

    22:  try {

    23:     PlayerFrame pf = new PlayerFrame();

    24:     pf.util.setPresentation(new URL(context.getBase(), "about.gui"));

    25:     pf.show();

    26:  }

    27:  catch(MalformedURLException e) {

    28:     e.printStackTrace();

    29:  }

    30:}

Stopping the Channel

Once users gets bored playing the game, they select Quit from menu channel's menu, the channel stops, and the `stop()` method is called. Here's that `stop()` method, which called the `writeLocalHighScores()` method in `HighScoreManager` to save the state of the local high scores. The `hide()` and `dispose()` methods are part of the `super.stop()` implementation of this method; you could just have easily called `super.stop()` here instead:

    public void stop() {

       highScores.writeLocalHighScores();

       hide();

       dispose();

    }

> ####

> Note

> As with the `start()` method, I've deleted references called `writeState()` from this method. In the version of WordSmith I used for this chapter, `writeState()` doesn't do anything.

### The Plug-In

When a user scores well enough in the WordSmith game to merit being listed in the high scores file, the high-score manager logs the score they got and calls `update()`. That log data is sent to the transmitter as feedback data; a plug-in on the transmitter site will then process that global high score data.

The plug-in for the WordSmith channel is contained in the `plugin` directory of the channel, as it should be. The code itself for the channel, the file `WordSmithPlugin.java` is in the `wordsmith` directory inside `plugin`; the code is defined as inside the `wordsmith` package, so it needs to be in that directory. The properties.txt file for the plug-in takes that package into account:

`main=wordsmith.WordSmithPlugin`

The `WordSmithPlugin` class follows all the basic rules for a plug-in that you learned in the last chapter, including implementing `init()` and `processRequest()` events. In addition, because the procedures the plug-in uses to insert scores into the transmitter's version of the high-scores list are very similar to those the-high score manager uses to insert scores into its version of the list, a lot of the code is very similar to that of the `HighScoreManager` class. I'll point out the similarities as they appear.

Let's start with `init()` shown in Listing 13.6.

Listing 13.6. The `init()` method from `WordSmithPlugin.java.`

     1:public synchronized void init() {

     2:   highScoreFile = getDataFile(HIGHSCOREFILE);

     3:   // initialize array

     4:   for (int i = 0 ; i < HighScoreManager.MAXHIGHSCORES ; i++) {

     5:       HighScore hs = new HighScore();

     6:       highScores.addElement(hs);

     7:   }

     8:   readHighScores();

     9:

    10:   highScoreChecksum = calculateChecksum(scoresToBytes());

    11:    }

    12:

The `init()` method here does essentially the same thing the `HighScoreManager` class does to initialize itself: it sets up a vector object to store the scores, initializes it, and then reads the `highscore.txt` file into that vector (using the `readHighScores()` method). Notice that the plug-in only has one set of high scores to deal with, and it stores that file in the local data directory for the plug-in (line 2).

To finish up, the `init()` method calculates a checksum for the existing high-score data (the plug-in will keep a running checksum throughout the channel). The `scorestoBytes()` method is a utility method that converts the scores vector into a byte array.

One important point to note here is that the `init()` method uses the `HighScore` class for each entry in the high scores list. Although this may not seem surprising, it does demonstrate a feature of plug-ins: the `CLASSPATH` for a plug-in includes both the plug-in directory and the channel directory itself—so you can share files such as `HighScore` between the channel and the plug-in.

With the plug-in initialized with the current high scores, the plug-in waits for an update request. `processRequest()`, shown in Listing 13.7, handles each request:

Listing 13.7. `processRequest()` from `WordSmithPlugin.java.`

    1:public synchronized void processRequest(RequestContext context)

     2:   throws IOException {

     3:   Vector logs = getLogEntries(context);

     4:   for (Enumeration e = logs.elements() ; e.hasMoreElements() ; ) {

     5:     LogEntry le = (LogEntry)e.nextElement();

     6:     String s = new String(le.data, 0);

     7:     add(s);

     8:

     9:     highScoreChecksum = calculateChecksum(scoresToBytes());

    10:   }

    11:

    12:   byte b[] = scoresToBytes();

    13:   context.addFile(HIGHSCOREFILE, b, highScoreChecksum, PERSISTENT);

    14:

    15:   try {

    16:      if (highScoreFile.exists() == false) {

    17:         File dir = new File(highScoreFile.getParent());

    18:         if (dir.exists() == false) {

    19:            if (dir.mkdirs()){

    20:            }

    21:         }

    22:      }

    23:      FastOutputStream fos = new FastOutputStream(highScoreFile);

    24:      for (Enumeration e = highScores.elements() ; e.hasMoreElements() ; ) {

    25:         HighScore hs = (HighScore)e.nextElement();

    26:         fos.println(hs.toString());

    27:      }

    28:      fos.close();

    29:   }

    30:   catch(IOException e) {

    31:       e.printStackTrace();

    32:   }

    33:   super.processRequest(context);

    34:}

This method should look familiar; it's almost identical to the `processRequest()` method I used in the last chapter for the survey channel. Here are the most important parts of this method:

- Lines 3 through 7 read the log entries from the reqeust context and process them into `HighScore` objects.0 (Actually, the `add()` method, which I haven't shown here, processes each entry into a `HighScore` object and then inserts that `HighScore` object into the current list of high scores at the right spot.)
- Line 9 calculates the new checksum each time a new high score is added.
- Lines 12 and 13 add the new high-score list to the channel index using the `context.addFile()` method. Note that the file disposition for this file is `PERSISTENT`, which means that proxies will cache this file for requests that happen immediately after this one, to speed things up a bit.
- Lines 15 through 32 simply save out the new list of high scores to the local data file.

After the plug-in is finished processing the request, the data for the new high-scores file is sent back to the channel, which uses `notifyInstall()` to install the new data. In this way, a central high-scores list can be updated from each running channel and the changes can be pushed back out to each one.

Functional Flow

Got all that? The WordSmith channel is fairly complicated, involving several different classes and widgets, all interacting, to accomplish its purpose. Now that you have a basic idea of the various parts, here's an overview of how the typical flow of control works in the WordSmith channel as it's played:

- The channel starts up, and the `setContext()` and `init()` methods are called in the `WordSmithApplication` class. The menubar for the application is initialized, the high-scores manager reads in the local and global high scores, and game play starts in the `WordSmithWidget`.
- The user plays the game until the end. `WordSmithWidget` checks with the high-score manager to see if the current score is a local high score (using the `place()` method). If it isn't a high score, the widget displays a congratulatory message.
- If the score is indeed a high score, the widget launches the `HighScoreFrame` to prompt the user for their name and e-mail address. After the user clicks the OK button, the `HighScoreFrame` class packages up that data into a `HighScore` object and send it to the high score manager via the `insert()` method.
- The high score manager inserts the high score into the local high-score list and, if the score merits being added to the global high-score list, logs it as feedback data and calls `update()`.
- The feedback data is sent to the transmitter plug-in via the update reqeust (let's assume the WordSmith channel continues running at this point). The plug-in processes the new high score, inserts it into the high-score list, and returns the new high scores to the channel.
- The `WordSmithApplication` class handles the update by instructing the high-score manager to re-read the global high-score list.
- When the user quits the channel, the high-score manager saves the local high scores to a file, ready to be used again the next time WordSmith starts up.

Channel Properties

The last part of the WordSmith channel to go over are the actual properties for this channel. Figure 13.4 shows the general properties.

Figure 13.4. General Properties for WordSmith.

The only new thing here is the name of the main class for the application—because the channel is contained in the wordsmith package and inside the `wordsmith` directory, you'll have to include the name of that package here so that the right class can be launched.

The icon properties for the channel, shown in Figure 13.5 are also worth examining.

Figure 13.5. Icon properties.

There are four icons for this channel: the GIF and BMP icons are each a 64-pixel square icon that is used as the icon for the channel (there's two different formats, one for Windows and one for Unix), and two thumbnail icons for the channel in the tuner. All of these icons are stored in the `image` directory.

### Summary

WordSmith is a reasonably complex channel that makes use of most of the features of the Castanet and Bongo technologies: Bongo presentations, custom widgets, channel classes, local state, automatic updates, and channel feedback (whew!). Throughout this chapter you've explored how this channel uses all these features, and how the various different parts of the channel interact. At this point, you should be able to create your own Java channels that use any of these features. Good luck!