FileDocCategorySizeDatePackage
StockMIDlet.javaAPI DocJ2ME MIDP 2.051395Thu Nov 07 12:02:18 GMT 2002example.stock

StockMIDlet

public class StockMIDlet extends MIDlet

The MIDlet application class that we'll run for the Stock Demo.

author
Jeffrey Bacon

Fields Summary
private static int
OFFSET
Since there is no support in MIDP/CLDC for floating point numbers, and all of the stock data comes in the form ##.### (usually) then we have to find a way to store the decimals as well as the integer part of the quote data. Multiplying by the OFFSET will shift the decimal place far enough over to make the number an integer which we can store. Every time we retrieve quote data from our RecordStore we must make sure that we divide by the OFFSET to correctly display and use the value we actually want.
Display
display
The MIDlet's display object
private Ticker
stockTicker
The Ticker that scrolls along the top of the screen
private List
choose
A List of stocks
private List
view
The Stock Tracker menu
private List
menu
The Main menu
private List
alertList
The Alert menu
private List
settingsList
The Settings menu
private Form
whatif
The 'What If?' Form upon which we enter our query data
private Form
updatesForm
The Form to display the different update intervals settings
private TextBox
stockSymbolBox
Used to input a stock symbol
private TextBox
alertPriceBox
Used to enter the price at which the user wishes to be alerted to the stocks's value
private TextField
origPurchPriceField
The original price the user purchased the stock at, used on the What If? Form
private TextField
numSharesField
The number of shares the users wishes to sell, used on the What If? Form
private static final Command
BACK_COMMAND
Back Command
private static final Command
MAIN_MENU_COMMAND
Main Menu Command
private static final Command
DONE_COMMAND
Done Command
private static final Command
SET_COMMAND
Set Command
private static final Command
EXIT_COMMAND
Exit Command
private static final Command
CALC_COMMAND
Calc Command
private static final Command
ABOUT_COMMAND
About Command
private ChoiceGroup
updatesChoices
The radio buttons for the update interval time
private String
currentMenu
A textual reference to the current menu that is displayed onscreen to allow the StockCommandListener to decide what action to perform upon execution of a command
private String
stockSymbol
A reference to the stock that has been chosen from a list of stocks onscreen. Using this we can extract the correct stock's data from the StockDatabase
private StockDatabase
stocks
The reference to the StockDatabase that stores the stock data
private AlertDatabase
alerts
The reference to the AlertDatabase that stores the alerts
private String
quoteServerURL
The server from which the quotes are downloaded NOTE: Currently, only the quote.yahoo.com server is supported
private String
quoteFormat
The format parameter for the quote server to retrieve stock data NOTE: Currently, only this format is supported
private String
proxyURL
The proxy server that must be negotiated NOTE: Proxy server is optional and a blank string indicates that no proxy should be used
private Timer
stockRefresh
The Timer object that refreshes the stock quotes periodically
private StockRefreshTask
stockRefreshTask
The TimerTask that the Timer performs periodically. It refreshes the stock quotes
private int
refresh_interval
How often are the stocks' data updated off of the server?
Constructors Summary
public StockMIDlet()

Default constructor that is called by the application manager to create a new instance of this MIDlet after which the MIDlet enters the Paused state.

 // 1000 = 1 second

                                  
      
Methods Summary
private voidabout()

Show the about box with copyright info and image

        Runtime runtime = Runtime.getRuntime();
        /*
        StringBuffer props = new StringBuffer();
        props.append("\nSystem Properties\n");
        props.append("Used Memory = " 
		     + (runtime.totalMemory() - runtime.freeMemory()) + "\n");
        props.append("Garbage collecting . . .\n");

        runtime.gc();
        long free = runtime.freeMemory();
        long total = runtime.totalMemory();

        props.append("Used Memory = " + (total - free) + "\n");
        props.append("Free Memory = " + free + "\n");
        props.append("Total Memory = " + total + "\n");
        System.out.println(props.toString());
        */
        example.About.showAbout(display);
    
private booleanaddNewStock(java.lang.String tkrSymbol)

Add the stock to the database

  • first contact the quote server to get the stock quote
  • if the stock doesn't not exist, alert the user and return to the add screen
  • if the stock exists then add it to our list
  • if the addition of the stock was successful, return true otherwise, return false

  • This is the format returned by the quote.yahoo.com server in the format specified in the instance variable:

    NAME TIME PRICE CHANGE LOW HIGH OPEN PREV
    "SUNW","11:24AM - 79.0625",-3.0625,"26.9375 - 106.75",80.5,82.125
    

    This is what is returned if the stock is not found:

    "NAME" ,"N/A - 0.00" ,N/A ,"N/A - N/A" ,N/A ,N/A
    

    return
    whether or not the addition of the stock was successful
    param
    tkrSymbol The ticker symbol of the stock to add

            try {
    
                // When stocks.search() returns null, the stock doesn't yet exist
                if (stocks.search(tkrSymbol) == null) {
                    try {
                        stocks.add(getStockQuote(tkrSymbol));
                        stockTicker.setString(makeTickerString());
                    } catch (RecordStoreFullException rsf) {
                        error("Database is full.", 2000);
                        return false;
                    } catch (RecordStoreException rs) {
                        error("Failed to add " + tkrSymbol, 2000);
                        return false;
                    } catch (IOException ioe) {
                        error("Failed to download stock quote for \"" + 
    			  tkrSymbol + "\"", 2000);
                        return false;
                    } catch (NumberFormatException nfe) {
                        error("\"" + tkrSymbol + 
    			  "\" not found on server, or invalid data "
    			  + "received from server",
    			  2000);
                        return false;
                    }
    
                // The stock already exists so we'll just update it
                } else {
                    try {
                        stocks.update(tkrSymbol,
    				  getStockQuote(tkrSymbol).getBytes());
                        stockTicker.setString(makeTickerString());
                    } catch (RecordStoreFullException rsf) {
                        error("Database is full.", 2000);
                        return false;
                    } catch (RecordStoreException rs) {
                        error("Failed to update " + tkrSymbol, 2000);
                        return false;
                    } catch (IOException ioe) {
                        error("Failed to download stock quote for " 
    			  + tkrSymbol, 2000);
                        return false;
                    }
                }
            } catch (RecordStoreException rs) {
                error("Error accessing database.", 2000);
                return false;
            }
            return true;
        
    private voidaddStock()

    Show the screen to add a stock

            stockSymbolBox.setString("");
            display.setCurrent(stockSymbolBox);
            currentMenu = "AddStock";
        
    private voidalertForm(java.lang.String tkrSymbol)

    Show the form to add an alert

    param
    tkrSymbol The ticker symbol of the stock we're adding an alert to

            display.setCurrent(alertPriceBox);
            currentMenu = "AlertForm";
            stockSymbol = tkrSymbol;
        
    private voidalertMenu(boolean showAlert)

    Show the alert management menu

    param
    showAlert Indicate whether we should show an alert to indicate that we have just successfully added an alert

            display.setCurrent(alertList);
            currentMenu = "AlertMenu";
            if (showAlert) {
                Alert a = new Alert("", "\n\n\n   Saved!", null, null);
                a.setTimeout(2000);
                display.setCurrent(a, alertList);
            }
        
    private voidcalc()

    Calculate the profits in a What If? scenario by the formula:

    Profit = (CurrentPrice - OriginalPurchasePrice) * NumberOfShares

    First we retrieve the current price of the stock. Then parse the original purchase price that the user enters to format it to an integer. Next, retrieve the number of shares from the form that the user filled in and then calculate the profits and display a nice message (with the result) to the user onscreen.

            try {
                String s       = stocks.search(stockSymbol);
                int currPrice  = Stock.getPrice(s);
                int opp        = Stock.makeInt(origPurchPriceField.getString());
                int numShares  = 
    		Integer.valueOf(numSharesField.getString()).intValue();
                int profit     = ((currPrice - opp) * numShares);
    
                Form answerForm = new Form(Stock.getName(s) + 
    				       " " + Stock.getStringPrice(s));
                StringBuffer sb = 
    		new StringBuffer().append("Net profit (loss) is ")
    		    .append((profit >= 0) ? "$" : "($")
    		    .append((profit >= 0) ? Stock.convert(profit) :
    			    "-" + Stock.convert(profit))
    		    .append((profit >= 0) ? "" : ")")
    		    .append(" when selling ")
    		    .append(String.valueOf(numShares))
    		    .append(" shares at $")
    		    .append(Stock.convert(currPrice))
    		    .append(" per share.");
                answerForm.append(sb.toString());
                answerForm.addCommand(BACK_COMMAND);
                answerForm.addCommand(MAIN_MENU_COMMAND);
                answerForm.setCommandListener(new StockCommandListener());
                display.setCurrent(answerForm);
                currentMenu = "AnswerForm";
            }
            catch (Exception e) {
                error("Calculation Failed", 2000);
            }
        
    private voidcheckAlerts(java.lang.String tkrSymbol)

    Check the alerts to see if any are registered for tkrSymbol at the tkrSymbol's current price

    param
    tkrSymbol The name of the stock to check for alerts on

            try {
                int current_price = Stock.getPrice(stocks.search(tkrSymbol));
                RecordEnumeration re = alerts.enumerateRecords(tkrSymbol,
    						       current_price);
                while (re.hasNextElement()) {
                    String alert_string = new String(re.nextRecord());
                    int my_price = 
    		    Integer.valueOf(alert_string
    				    .substring(alert_string.indexOf(';")+1,
    					       alert_string.length()))
    		    .intValue();
                    Alert a = new Alert(tkrSymbol, "", null, AlertType.ALARM);
                    StringBuffer sb = new StringBuffer()
                                      .append(tkrSymbol)
                                      .append(" has reached your price point of $")
                                      .append(Stock.convert(my_price))
                                      .append(" and currently is trading at $")
                                      .append(Stock.getStringPrice(stocks
    						    .search(tkrSymbol)));
                    a.setString(sb.toString());
                    a.setTimeout(Alert.FOREVER);
                    display.setCurrent(a);
                    alerts.delete(alert_string);
                }
            } catch (RecordStoreNotOpenException rsno) {
            } catch (RecordStoreException rs) {
            }
        
    private voidchooseStock(boolean reload, java.lang.String menuType, int type, boolean prices)

    Show the list of stocks to pick from and set the menu type to indicate to the Listener what to do with the list choice

    param
    reload Indicates whether the list should be reloaded which should only happen if it is possible that the stocks have changed since it was last shown
    param
    menuType Which menu is this list representing
    param
    type Type of Choice to display
    param
    prices Indicates whether or not to show prices on the list

            if (reload) {
                choose = new List("Choose Stocks", type);
                choose.setTicker(stockTicker);
                choose.addCommand(BACK_COMMAND);
                if (menuType.equals("RemoveStock")) {
                    choose.addCommand(DONE_COMMAND);
                } else if (menuType.equals("WhatChoose") ||
                           menuType.equals("AddAlert")) {
                    choose.addCommand(MAIN_MENU_COMMAND);
                }
                choose.setCommandListener(new StockCommandListener());
    
                try {
                    RecordEnumeration re = stocks.enumerateRecords();
                    while (re.hasNextElement()) {
                        String theStock = new String(re.nextRecord());
                        if (prices) {
                            choose.append(Stock.getName(theStock)
    				      + " @ "
    				      + Stock.getStringPrice(theStock), null);
                        } else {
                            choose.append(Stock.getName(theStock), null);
                        }
                    }
                } catch (RecordStoreNotOpenException rsno) {
                } catch (RecordStoreException rs) {
                }
            }
            display.setCurrent(choose);
            currentMenu = menuType;
        
    private voiddeleteStock(java.lang.String tkrSymbol)

    Remove the stock from the StockDatabase

    param
    tkrSymbol The ticker symbol of the stock to delete

            try {
                stocks.delete(tkrSymbol);
                alerts.removeUselessAlerts(tkrSymbol);
                stockTicker.setString(makeTickerString());
            } catch (RecordStoreException rs) {
                error("Failed to delete " + tkrSymbol, 2000);
            }
        
    public voiddestroyApp(boolean unconditional)

    When the application management software has determined that the MIDlet is no longer needed, or perhaps needs to make room for a higher priority application in memory, is signals the MIDlet that it is a candidate to be destroyed by invoking the destroyApp(boolean) method. In this case, we need to destroy the RecordEnumerations so that we don't waste their memory and close the open RecordStores. If the RecordStores are empty, then we do not need them to be stored as they will be recreated on the next invokation of the MIDlet so we should delete them. At the end of our clean up, we must call notifyDestroyed which will inform the application management software that we are done cleaning up and have finished our execution and can now be safely terminated and enters the Destroyed state.

    param
    unconditional If true when this method is called, the MIDlet must cleanup and release all resources. If false the MIDlet may throw MIDletStateChangeException to indicate it does not want to be destroyed at this time.
    throws
    MIDletStateChangeException is thrown if the MIDlet wishes to continue to execute (Not enter the Destroyed state). This exception is ignored if unconditional is equal to true.
    see
    javax.microedition.midlet.MIDlet

    
            // If there is no criteria that will keep us from terminating
            if (unconditional) {
                synchronized (this) {
                    if (display == null) {
                        // If display == null, we are not initialized and
                        // we have nothing to destroy
                        return;
                    }
                    stockRefresh.cancel();
                    try {
                        stocks.close();
                        alerts.close();
                        RecordStore settings = 
                            RecordStore.openRecordStore("Settings", true);
                        try {
                            settings.setRecord(1, 
                                               String.valueOf(refresh_interval)
                                               .getBytes(), 
                                               0, String.valueOf(refresh_interval)
                                               .length());
                            
                            // First time writing to the settings file
                        } catch (RecordStoreException rse) {
                            settings.addRecord(String.valueOf(refresh_interval)
                                               .getBytes(),
                                               0, String.valueOf(refresh_interval)
                                               .length());
                        }
                        settings.closeRecordStore();
                    } catch (Exception e) {
                        // Ignore exception there is no place to report it
                    }
                    notifyDestroyed();
                    
                    // Something might make us not want to exit so check it 
                    // here before terminating
                } // synchronized
            } else {
            }
        
    private voiddisplayStock(java.lang.String tkrSymbol)

    Display the stock selected on the View menu with all its attributes shown (ie. Name, Price, etc.)

    param
    tkrSymbol The name of the stock to be deleted

            try {
                String theStock = stocks.search(tkrSymbol);
                Form stockInfo = new Form(Stock.getName(theStock));
                stockInfo.setTicker(stockTicker);
                StringBuffer sb = new StringBuffer()
                                  .append("Last Trade:\n         ")
                                  .append(Stock.getTime(theStock))
                                  .append("\n         ")
                                  .append(Stock.getStringPrice(theStock))
                                  .append("\nChange:  ")
                                  .append(Stock.getStringChange(theStock))
                                  .append("\nHigh:  ")
                                  .append(Stock.getStringHigh(theStock))
                                  .append("\nLow:  ")
                                  .append(Stock.getStringLow(theStock))
                                  .append("\nOpen:  ")
                                  .append(Stock.getStringOpen(theStock))
                                  .append("\nPrev:  ")
                                  .append(Stock.getStringPrevious(theStock));
                stockInfo.append(sb.toString());
                stockInfo.addCommand(BACK_COMMAND);
                stockInfo.addCommand(MAIN_MENU_COMMAND);
                stockInfo.setCommandListener(new StockCommandListener());
                display.setCurrent(stockInfo);
                currentMenu = "stockInfo";
            } catch (RecordStoreNotOpenException rsno) {
                error("Could not display stock.  ", 2000);
            } catch (RecordStoreException rs) {
                error("Could not display stock.  ", 2000);
            } catch (NullPointerException npe) {
                error("Could not display stock.  ", 2000);
            }
        
    private voiderror(java.lang.String message, int time)

    Display a message onscreen for a specified period of time

    param
    message The message to be displayed
    param
    time The delay before the message disappears

            if (!(display.getCurrent() instanceof Alert)) {
                Alert a = new Alert("Error", message, null, AlertType.ERROR);
                a.setTimeout(time);
                display.setCurrent(a, display.getCurrent());
            }
        
    private java.lang.StringgetStockQuote(java.lang.String tkrSymbol)

    This method actually contacts the server, downloads and returns the stock quote.

    NOTE: If PROXY support is added to HttpConnection that switch the code over to that as it will be pure MIDP instead of this hack

    return
    the stock quote
    param
    tkrSymbol The Stock to be requested from the server
    throws
    IOException is thrown if there is a problem negotiating a connection with the server
    throws
    NumberFormatException is thrown if trashed data is received from the server (or the Stock could not be found)

            String quoteURL = quoteServerURL + tkrSymbol + quoteFormat;
    
            StreamConnection c = (StreamConnection)
    	    Connector.open(quoteURL, Connector.READ_WRITE);
            InputStream is = c.openInputStream();
            int ch;
            StringBuffer sb = new StringBuffer();
            while ((ch = is.read()) != -1) {
                sb.append((char)ch);
            }
            Stock.parse(sb.toString());
    	is.close();
    	c.close();
            return sb.toString();
        
    private voidmainMenu()

    Display the main menu of the program

            display.setCurrent(menu);
            currentMenu = "Main";
        
    private java.lang.StringmakeTickerString()

    Generate a string (which concatenates all of the stock names and prices) which will be used for the Ticker.

    return
    The ticker string which concatenates all of the stock symbols and prices

    
            // the ticker tape string
            StringBuffer tickerTape = new StringBuffer();
    
            try {
                RecordEnumeration re = stocks.enumerateRecords();
                while (re.hasNextElement()) {
                    String theStock = new String(re.nextRecord());
                    tickerTape.append(Stock.getName(theStock))
                              .append(" @ ")
                              .append(Stock.getStringPrice(theStock))
                              .append("   ");
                }
            } catch (Exception e) {
                return "Error Accessing Database";
            }
            return tickerTape.toString();
        
    public voidpauseApp()

    This method is invoked by the application management software when the MIDlet no longer needs to be active. It is a stop signal for the MIDlet upon which the MIDlet should release any resources which can be re-acquired through the startApp method which will be called upon re-activation of the MIDlet. The MIDlet enters the Paused state upon completion of this method.

    see
    javax.microedition.midlet.MIDlet

            synchronized (this) {
                // free memory used by these objects
                display        = null;
                choose         = null;
                view           = null;
                menu           = null;
                alertList      = null;
                settingsList   = null;
                whatif         = null;
                updatesForm    = null;
                stockSymbolBox = null;
                alertPriceBox  = null;
                origPurchPriceField = null;
                numSharesField      = null;
                stockTicker         = null;
                stockRefresh.cancel();
                stockRefresh        = null;
                stockRefreshTask    = null;
                try {
                    stocks.close();
                    stocks = null;
                    alerts.close();
                    alerts = null;
                } catch (Exception e) {}
            } // synchronized
        
    private voidremoveAlert(java.lang.String choose_data)

    Remove the alert from our RecordStore referenced by index

    param
    choose_data A string with the symbol and price of the alert to remove in it

            try {
    
                // Separate the symbol and price from the data
                String symbol = choose_data.substring(0,
    						  choose_data.indexOf('@")-1);
                int sPrice = Stock.makeInt( 
                             choose_data.substring(choose_data.indexOf('@")+3,
                                                      choose_data.length()));
                System.out.println("Remove Alert: " + symbol + ";" + sPrice);
                // Remove the alert
                alerts.delete(symbol + ";" + sPrice);
            } catch (Exception e) {
                error("Failed to remove alert", 2000);
            }
        
    private voidsetAlert(java.lang.String Sprice)

    Set an alert for the selected stock at the specified price

    param
    Sprice String representation of the price of the stock that the user would like an alert for

            try {
                alerts.add((new StringBuffer()
                                .append(Stock.getName(stocks.search(stockSymbol)))
                                .append(';")
                                .append(Stock.makeInt(Sprice))).toString());
            } catch (Exception e) {
                error("Failed to add alert", 2000);
            }
        
    private voidsettings(boolean showAlert)

    Show the settings menu

    param
    showAlert Indicate whether we should show an alert to indicate that we have just successfully saved changes to the settings

            display.setCurrent(settingsList);
            currentMenu = "Settings";
            if (showAlert) {
                Alert a = new Alert("", "\n\n\n   Saved!", null, null);
                a.setTimeout(1500);
                display.setCurrent(a, settingsList);
            }
    
        
    public voidstartApp()

    This method is invoked when the MIDlet is ready to run and starts/resumes execution after being in the Paused state. The MIDlet acquires any resources it needs, enters the Active state and begins to perform its service, which in this case means it displays the main menu on the screen.

    The method proceeds like so:

  • open the StockDatabase and the AlertDatabase
  • read the settings data from the settings RecordStore
  • create the string to be scrolled across the Ticker on the top of the screens and instantiate the Ticker object using that string. That string will be constructed of the names and prices of the stocks in our database
  • get and store the MIDlet's Display object
  • create and show the main menu
  • instantiate the TimerTask and Timer and associate the two setting the refresh interval to the value of the refresh_interval variable
  • throws
    MIDletStateChangeException is thrown if the MIDlet cannot start now but might be able to start at a later time.
    see
    javax.microedition.midlet.MIDlet

            synchronized (this) {
                display = Display.getDisplay(this);
                
                // Open the Stocks file
                stocks = new StockDatabase();
                try {
                    stocks.open("Stocks");
                } catch (Exception e) {
                    try {
                        stocks.cleanUp("Stocks");
                    } catch (Exception e2) {}
                }
                
                // Open the Alerts file
                alerts = new AlertDatabase();
                try {
                    alerts.open("Alerts");
                } catch (Exception e) {
                    try {
                        alerts.cleanUp("Alerts");
                    } catch (Exception e2) {}
                }
                
                // Open the Settings file
                try {
                    RecordStore settings = 
                        RecordStore.openRecordStore("Settings", true);
                    refresh_interval = 
                        Integer.valueOf(new String(settings.getRecord(1)))
                        .intValue();
                    settings.closeRecordStore();
                    
                    // No settings file existed
                } catch (Exception e) {
                    refresh_interval = 900000;
                }
                
                // Make the ticker
                stockTicker = new Ticker(makeTickerString());
                
                // Create all the menus and forms
                origPurchPriceField = 
                    new TextField("Original Purchase Price:",     "", 5,  
                                  TextField.NUMERIC);
                numSharesField      = 
                    new TextField("Number Of Shares:",            "", 9,  
                                  TextField.NUMERIC);
                
                menu = new List("Stock Menu", Choice.IMPLICIT);
                menu.append("Stock Tracker", null);
                menu.append("What If?", null);
                menu.append("Alerts", null);
                menu.append("Settings", null);
                menu.addCommand(EXIT_COMMAND);
                menu.addCommand(ABOUT_COMMAND);
                menu.setCommandListener(new StockCommandListener());
                menu.setTicker(stockTicker);
                
                whatif = new Form("What If?");
                whatif.setTicker(stockTicker);
                whatif.append(origPurchPriceField);
                whatif.append(numSharesField);
                whatif.addCommand(BACK_COMMAND);
                whatif.addCommand(CALC_COMMAND);
                whatif.setCommandListener(new StockCommandListener());
                
                alertList = new List("Alert Menu", Choice.IMPLICIT);
                alertList.setTicker(stockTicker);
                alertList.append("Add", null);
                alertList.append("Remove", null);
                alertList.addCommand(BACK_COMMAND);
                alertList.setCommandListener(new StockCommandListener());
                
                settingsList = new List("Settings", Choice.IMPLICIT);
                settingsList.setTicker(stockTicker);
                settingsList.append("Updates", null);
                settingsList.append("Add Stock", null);
                settingsList.append("Remove Stock", null);
                settingsList.addCommand(BACK_COMMAND);
                settingsList.setCommandListener(new StockCommandListener());
                
                alertPriceBox = new TextBox("Alert me when stock reaches:", 
                                            "", 9, TextField.NUMERIC);
                alertPriceBox.setTicker(stockTicker);
                alertPriceBox.addCommand(DONE_COMMAND);
                alertPriceBox.addCommand(BACK_COMMAND);
                alertPriceBox.setCommandListener(new StockCommandListener());
                
                updatesForm = new Form("Updates");
                updatesChoices = new ChoiceGroup("Update Interval:", 
                                                 Choice.EXCLUSIVE);
                updatesChoices.append("Continuous", null); // will be 30 seconds
                updatesChoices.append("15 minutes", null); // default for JavaONE
                updatesChoices.append("30 minutes", null);
                updatesChoices.append("1 hour", null);
                updatesChoices.append("3 hours", null);
                switch (refresh_interval) {
                    case 30000:    updatesChoices.setSelectedIndex(0, true);
                        break;
                    case 1800000:  updatesChoices.setSelectedIndex(2, true);
                        break;
                    case 3600000:  updatesChoices.setSelectedIndex(3, true);
                        break;
                    case 10800000: updatesChoices.setSelectedIndex(4, true);
                        break;
                    case 900000:
                    default:       updatesChoices.setSelectedIndex(1, true);
                        break;
                }
                updatesForm.setTicker(stockTicker);
                updatesForm.append(updatesChoices);
                updatesForm.addCommand(BACK_COMMAND);
                updatesForm.addCommand(DONE_COMMAND);
                updatesForm.setCommandListener(new StockCommandListener());
                
                stockSymbolBox = new TextBox("Enter a Stock Symbol:",
                                             "", 5, TextField.ANY);
                stockSymbolBox.setTicker(stockTicker);
                stockSymbolBox.addCommand(DONE_COMMAND);
                stockSymbolBox.addCommand(BACK_COMMAND);
                stockSymbolBox.setCommandListener(new StockCommandListener());
                
                mainMenu();
                
                // Set up and start the timer to refresh the stock quotes
                stockRefreshTask = new StockRefreshTask();
                stockRefresh = new Timer();
                stockRefresh.schedule(stockRefreshTask, 0, refresh_interval);
                
                // FOR JavaONE -- ADD IN A COUPLE DEFAULT STOCKS
                /*
                  addNewStock("MOT");
                  addNewStock("SUNW");
                  addNewStock("NOK");
                  addNewStock("IBM");
                  addNewStock("AOL");
                  addNewStock("MSFT");
                  addNewStock("GM");
                  addNewStock("FORD");
                  addNewStock("ORCL");
                  addNewStock("SEEK");
                  addNewStock("AT&T");
                  addNewStock("LU");
                  addNewStock("HON");
                  addNewStock("CORL");
                  addNewStock("NOR");
                */
            } // synchronized
        
    private voidupdates()

    Show the updates choices

            display.setCurrent(updatesForm);
            currentMenu = "Updates";
        
    private voidviewAlerts()

    Show a list of all active alerts

            choose = new List("Current Alerts", Choice.MULTIPLE);
            choose.setTicker(stockTicker);
            try {
    
                // Get all the Alert records
                RecordEnumeration re = alerts.enumerateRecords("", 0);
                while (re.hasNextElement()) {
                    String a = new String(re.nextRecord());
                    String price = 
    		    Stock.convert(Integer.valueOf(a.substring(a.indexOf(';")+1,
    						      a.length())).intValue());
                    choose.append(a.substring(0, a.indexOf(';")) + " @ $"
    			      + price, null);
                }
            } catch (Exception e) {
                error("Error reading alerts", 2500);
            }
            choose.addCommand(BACK_COMMAND);
            choose.addCommand(DONE_COMMAND);
            choose.setCommandListener(new StockCommandListener());
            display.setCurrent(choose);
            currentMenu = "RemoveAlert";
        
    private voidwhatIfForm(java.lang.String tkrSymbol)

    Show the What If? form to investigate a hypothetical stock deal

    param
    tkrSymbol The name of the stock to perform the query with

            display.setCurrent(whatif);
            currentMenu = "WhatIfForm";
            stockSymbol = tkrSymbol;