Last updated on: 07/25/02

Apex WorldBuilder Extensibility Guide

Contents

Introduction

Prerequisites

The idea

Deriving a class

Overwriting base class methods

Qualifier register();

boolean qualify(org.w3c.dom.Node node) throws SimObjectException;

void parseNode(org.w3c.dom.Node node) throws SimObjectException;

String toString();

Best Practices

Implementation Caveats

How it all works

Credits

Revision History

 


Introduction

Apex WorldBuilder (the WorldBuilder) application was originally designed to identify and process HTML hotspots and image maps in a set of HTML storyboard files into initialize.lisp file understandable by Apex.  One of the primary architectural concerns proposed for the WorldBuilder application was extensibility.  To that end, the WorldBuilder has been designed to make it easy to add support for new types of Apex simulation objects (simobjects).  This document will describe the process of adding support for translating new HTML tags into simobjects.

Prerequisites

To be able to extend this application a developer needs to be familiar with the following technologies:

  • Apex simobjects – The appropriate format in which to specify the desired simulation object in Apex.  This includes the general understanding of the syntax of initialize.lisp and the role it plays in the creation of Apex simulations.
  • HTML Tags – The name and attributes of the tag that is to be translated into a simobject.
  • Object-oriented (OO) programming – The developer should ideally be familiar with concepts of object oriented programming.  This document will rely on OO concepts such as inheritance and polymorphism.
  • C-based syntax – The developer must have at least a familiarity with C-based syntax to be able to implement the functions required.  Ideally, the developer would understand how to implement a simple class in Java.
  • Basic development and debugging skills – The WorldBuilder application is written in Java using JBuilder 6.  The document assumes that WorldBuilder extension authors will have access to this or a similar development environment.  It is not reasonable to expect that any new code will not work right after it is written.  Therefore the document also assumes that the extensions authors will be able to debug their code.  Finally, in order to release a new version of WorldBuilder, the Borg team has created release documentation along with several tools that span Windows and Macintosh platforms.  The document assumes that extensions authors is able follow these procedures.
  • Document Object Model (DOM) programming model – The developer must have familiarity with DOM in general and how to navigate it in Java in particular.  Later in the document, we provide some links that can help developers get up to speed in this area.

 


The idea

The basic task of extending Apex is to derive a new class from the existing base class and to overwrite its methods.  The first part involves deriving a new class.  It utilizes the OO principle of inheritance.  This principle guarantees that the derived class will have access to all functionality of the base class and to its member variables.  At the same time it allows the author of the new class to add, modify or suppress parts of the base class functionality.  The second part of the task involves overwriting the methods of the base class.  This utilizes the OO principle of polymorphism.  The methods of the base class form a contract with the rest of the WorldBulider application.  By overwriting the methods of the base class, the derived class can interact with the WorldBuilder according to the contract established by the base class.

 


Deriving a class

The name of the base class is wb.simobjects.SimObject.  It resides in SimObject.java file in wb\simobjects subfolder of the WorldBuilder project.  The prefix wb stands for WorldBuilder and denotes the encompassing Java package.  Notice that the name of the class matches the name of the object contained inside.  This rule should be followed for any new classes (e.g. SimObjectFoo should reside in wb\simobjects\SimObjectFoo.java).  New SimObject-derived classes should also reside in wb\simobjects subfolder and be declared as a part of wb.simobjects package.  It is recommended that the name of the new class starts with SimObject.

 

To reiterate all of these requirements, consider a hypothetical new class wb.SimObjectFoo.  To satisfy all of the above requirements wb.simobjects.SimObjectFoo should be implemented in SimObjectFoo.java file which resides in wb\simobjects subfolder of WorldBuilder project.  From this point on, the object will simply be referred to as SimObjectFoo for brevity.  The file containing SimObjectFoo code should start with the following code snippet:

 

// place this Java object inside wb.simobjects package

package wb.simobjects;

 

/**

 * Description: The class to parse and create Apex simobject

 * foo.

 * Copyright: <Your copyright goes here>

 * Company: <Your company name goes here>

 * @author <Your name goes here>

 * @version <Version information>

 */

 

// more imports may be required

import wb.Qualifier;

import wb.exceptions.SimObjectException;

import java.util.StringTokenizer;

import java.text.MessageFormat;

 

public class SimObjectFoo extends SimObject {

...

}

 


Overwriting base class methods

There are five methods that SimObjectFoo should implement.  These methods will be called by the internals of the WorldBuilder application.

Default Constructor – SimObjectFoo();

This method will be called whenever the WorldBuilder needs to create a new object.  This goal of the constructor is to initialize internal member variables.  As implementation caveat below describes, SimObjectFoo will be created and destroyed several times in the course of processing HTML storyboards.  Therefore it is recommended that no heavy processing is done in the constructor.  A better place for heavy processing is in parseNode() and toString().  The following is an example of a constructor:

 

public SimObjectButton() {

    m_x = m_y = m_cx = m_cy = -1;

}

Qualifier register();

This method is called by SimObjectFactory to build the table of qualifiers.  A qualifier is a record that establishes association between a tag along with its attributes and a class that processes this tag.  register() method returns the Qualifier record for its class (SimObjectFoo).

 

The Qualifier record in the table consists of the following entries: type, HTML tag, property (or attribute) name, property (or attribute) value and handler class name.  In HTML terminology, properties are referred to as attributes.  This document refers to HTML tag attributes as properties.  Property name and property value together form a property pair.

 

The possible types are: Qualifier.UNIQUE, Qualifier.PAIR, and Qualifier.FUNCTION.

 

When the type is Qualifier.UNIQUE, the class becomes solely responsible for processing all occurrences of the designated HTML tag encountered in all HTML storyboards.  In this case, property pair values are ignored by the WorldBuilder and should be set to null.  The following is an example of such record:

 

public Qualifier register() {

    return new Qualifier(Qualifier.UNIQUE, "area", null, null,

        getClass().getName());

}

 

This method indicates that the implementing class (SimObjectFoo) will be solely/uniquely responsible for "area" HTML tags in all HTML storyboards.  Notice that the two parameters for the property pair are null.  getClass().getName() provides an automatic way to return "wb.simobjects.SimObjectFoo".  Caveat:  When classes register themselves in Qualifier's table of SimObjectFactory, they form a community where one class does not know what the other ones are processing.  In this situation if one class registers itself as UNIQUE handler for a certain tag, it may preclude other SimObjects from getting a chance to participate in processing HTML storyboards.  (Notice the difference in notation between SimObject and simobject.  The former represents a SimObject-derived class, while the later defines Apex simulation object.  These concepts while similar are not the same.)  Since classes are registered in alphabetical order, in case of a collision, the first class in alphabetical order will prevail.  Use UNIQUE qualifiers with caution and check all other SimObjects for collisions before you decide to do that.  Currently, SimObjectButton implemented by the Borg team uses UNIQUE qualifier for gareah tag.  If new classes are added that parse gareah tag, SimObjectButton.register() will need to be modified to differentiate itself from these new classes.  One way to differentiate SimObjectButton is via PAIR qualifier, with gshapeh and grecth as property pair name and value respectively.

 

When the type is Qualifier.PAIR, the property pair is evaluated for the specified HTML tag and only tags that match the property pair criterion will be passed to the class for processing.  It is obvious that this is less restrictive than Qualifier.UNIQUE and allows SimObject authors to become more precise about which types of tags they want to process.  The following is an example of such record:

 

public Qualifier register() {

    return new Qualifier(Qualifier.PAIR, "area", "shape", "rect",

        getClass().getName());

}

 

This method indicates that the implementing class will only process "area" HTML tags in the form of <area ... shape="rect" ...>.  This creates an opportunity for another class to become responsible for <area ... shape="circle" ...> tags.  The idea behind this type of qualifiers was to add precision to the types of tags each SimObject can process.  It also lowers the chance for collision characteristic for UNIQUE qualifiers.  From the pragmatic standpoint, it can also simplify processing each SimObject has to do.  For example, shape="rect" implies that there will be four coordinates as in <area ... shape="rect" coords="100,100,200,200">; whereas shape="circle" implies that there will be only three coordinates as in <area ... shape="circle" coords="100,100,200">.  If "area" tags where handled through UNIQUE qualifier, the author of that SimObject would have to build in intelligence to distinguish between the two shape types.  PAIR qualifier automates and thus makes such a task easier.

 

Qualifier.FUNCTION provides the ultimate degree of flexibility for deciding which tags to process.  When this type of Qualifier is used, the WorldBuilder will call qualify() method in SimObjectFoo to ask the class whether it wants to process the tag or not.  qualify() function will be described in greater detail later in this document.  When FUNCTION qualifier is used, the author will be able to determine whether to process the tag based on an arbitrary number of property pairs or even query the properties of other tags in the HTML storyboard.  The following is an example of FUNCTION qualifier:

 

public Qualifier register() {

    return new Qualifier(Qualifier.FUNCTION, "area", null, null,

        getClass().getName());

}

 

Notice that the tag is still required for FUNCTION qualifier but both entries for property pair are set to null as they are not used.

boolean qualify(org.w3c.dom.Node node) throws SimObjectException;

This function works in conjunction with Qualifier.FUNCTION.  It will only be invoked by SimObjectFactory if register() returned FUNCTION qualifier.  You do NOT need to implement this function or feature it in your class in any way if your qualifier is not FUNCTION.

 

Since a Document Object Model (DOM) node is passed into the function, the author will be able to assess an arbitrary number of property pairs or even navigate to and check other tags in HTML storyboard.  The tutorial on how to access arbitrary property pairs and perform navigation between nodes (a.k.a. the programming model for DOM) is beyond the scope of this document.  The Borg team tried to simplify coding for most common scenarios as described in this best practices note.  If your needs go beyond capabilities of these APIs refer to SimObjectFactory.java for examples or check http://msdn.microsoft.com/workshop/author/dom/domoverview.asp or http://java.sun.com/j2se/1.4/docs/api/org/w3c/dom/Node.html or http://www.w3.org/TR/2000/WD-DOM-Level-1-20000929/ for background and references.  There are also a number of books published that explain programming DOM in details.  For example Dynamic HTML: The Definitive Reference by Danny Goodman, O'Reilly, ISBN 1-56592-494-0.

 

Suppose that some HTML storyboard has two types of circle hotspots: small and large, where large hotspots have radius greater than 100 pixels.  Furthermore, suppose that each type represents a different simobject and thus has its own LISP syntax.  Per WorldBuilder architecture the most intuitive approach for processing these simobjects would be to have two SimObject-derived classes.  The following is an example of qualify() function for large hotspots.  As such it only accepts area tags shaped as a circle with a radius greater than 100 pixels.  This type of qualification would be impossible to do with PAIR qualifier because two pairs are being assessed for their states.  Additionally, this code snippet demonstrates the power of the APIs described in the best practices note.

 

public boolean qualify(org.w3c.dom.Node node) throws SimObjectException {

    String sValue;

 

    // validate input parameter, bail out if it's bad

    if (null == node)

        return false;

 

    // first, check the value of the "shape" attribute,

    // bail out if it's not "circle". use getAttributeString

    // helper API to show how easy this is.

    sValue = getAttributeString(node, gshapeh);

    if (null == sValue || !sValue.equalsIgnoreCase("circle"))

        return false;

 

    // second, check the value of the "coords" attribute,

    // strip out the value for radius.

    // this is slightly harder but provides greater

    // flexibility.

    NamedNodeMap nnmAttrs;

 

    // get the list of all attributes node has, bail out if there are none

    nnmAttrs = node.getAttributes();

    if (0 >= nnmAttrs.getLength())

        return false;

 

    sValue = nnmAttrs.getNamedItem("coords").getNodeValue();

    // end of getting an attribute

 

    // parse attribute value to get the radius

    StringTokenizer st;

    st = new StringTokenizer(sValue, ",");

    st.nextToken(); // skip x coordinate

    st.nextToken(); // skip y coordinate

 

    // return true if the radius is greater than 100, false otherwise

    return (100 < Integer.valueOf(st.nextToken()).intValue());

}

 

If an error occurs that deserves customerfs attention, you can throw the exception that will be reported to the user.  Throwing an exception will terminate processing of HTML storyboards and customer will be given a choice to retry the processing or to exit WorldBuilder.

void parseNode(org.w3c.dom.Node node) throws SimObjectException;

This API is the workhorse of every SimObject-derived class.  Its purpose is to assess the state of tag property pairs and potentially even other tags and to initialize internal member variables so that LISP code can be produced.  This API can initialize several member variables inherited from the base class (SimObject).  These member variables are: m_sType, m_sLocale, and m_sName.  m_sType holds the Apex type of the simobject.  Currently, the base class constructor initializes m_sType to ginterface-objecth.  m_sLocale holds the locale that Apex associates with the simobject.  The base class constructor initializes m_sLocale to gworldh.  Since these two values are hardcoded, and set in the default base class constructor, the derived class (SimObjectFoo) normally does not need to initialize them.  m_sName holds the name by which Apex identifies the simobject.  In contrast to the two previous member variables, m_sName is user supplied, not hardcoded.  It is a good idea to initialize m_sName based on some attribute value for the tag (e.g. gtitleh) that simobject is responsible for.

 

m_sName is particularly important because it will be used by WorldBuilder to establish uniqueness of the object in the global object repository.  The global object repository is used by WorldBuilder to produce the final LISP code understandable by Apex.  Generally, it is a good idea to look at the implementation of SimObjectButton.parseNode() that resides in wb\imobjects\SimObjectButton.java for reference.  Since SimObjectButton.parseNode() is implemented by the Borg team it reflects the latest thinking and trends in the WorldBuilder codebase evolution.  The following is a sample implementation of parseNode():

 

public void parseNode(org.w3c.dom.Node node) throws SimObjectException {

    StringTokenizer st;

    String sValue;

    int    x1, x2, y1, y2;

 

    // initialize m_sName with the value of the attribute gtitleg

    sValue = getAttributeString(node, "title");

    if (null == sValue || 0 >= sValue.length())

        throw new SimObjectException(ERR_NO_TITLE);

 

    m_sName = sValue;

 

    // andrewgu: initialize arguments for all error

    // messages that can follow

    Object[] rgoErrorArgs = { m_sName };

 

    // get "coords" attribute for the tag

    sValue = getAttributeString(node, "coords");

    if (null == sValue || 0 >= sValue.length())

        throw new SimObjectException(MessageFormat.format(ERR_NO_COORDS,

            rgoErrorArgs));

 

    // parse retrieved coordinates into 4 integers corresponding to the

    // dimensions.  the implicit assumption is that the area tag has a

    // rectangular form (this is specified by gshape=recth attribute pair

    // inside gareah tag)

    st = new StringTokenizer(sValue, ",");

    x1 = Integer.valueOf(st.nextToken()).intValue();

    y1 = Integer.valueOf(st.nextToken()).intValue();

    x2 = Integer.valueOf(st.nextToken()).intValue();

    y2 = Integer.valueOf(st.nextToken()).intValue();

 

    // initialize m_x, m_y, m_cx, m_cy member variables

    // notice that m_x and m_y are set to the centroid of the rectangle

    // per Apex specification.

    m_cx = x2 - x1;

    m_cy = y2 - y1;

    m_x  = x1 + m_cx/2;

    m_y  = y1 + m_cy/2;

 

    // bail out if there is an error

    if (0 > m_x || 0 > m_y || 0 > m_cx || 0 > m_cy)

        throw new SimObjectException(MessageFormat.format(ERR_BAD_COORDS,

            rgoErrorArgs));

}

String toString();

This method works in a pair with parseNode().  parseNode() accumulates information into classfs internal member variables.  toString() is responsible for outputting LISP code base on these internal member variables.  Result produced by this method will be included into initialize.lisp.  Ideally, the output of this method should be formatted to make it human-readable.  The following is a sample implementation of this method:

 

public String toString() {

    String sFormat =

      "\t ({1} (make-instance ''{2} :name ''{1} :locale {3}{0}" +

      "\t\t:pos ''({4,number,0} {5,number,0}) " +

      ":dimensions ''({6,number,0} {7,number,0}))){0}";

    Object rgoArgs[] = {

        ms_sEOL,           // 0

        m_sName,           // 1

        m_sType,           // 2

        m_sLocale,         // 3

        new Integer(m_x),  // 4

        new Integer(m_y),  // 5

        new Integer(m_cx), // 6

        new Integer(m_cy)  // 7

    }

 

    return MessageFormat.format(sFormat, rgoArgs);

}

 

The resultant LISP code may look something like this:

 

    (MicrosoftWordItem (make-instance 'interface-object :name 'MicrosoftWordItem :locale world

        :pos '(163 256) :dimensions '(86 12)))

 

Notice that this API relies on a number of member variables: ms_sEOL, m_sName, m_sType, m_sLocale, m_x, m_y, m_cx, and m_cy.  These variables with the exception of ms_sEOL were initialized in a sample implementation of parseNode() above.

 

ms_sEOL is a static variable initialized only once by a the base class (SimObject).  This variable hold a platform-agnostic representation of a new line character(s).  By convention, it is always the first element of the arguments array.  By using ms_sEOL you will ensure that initialize.lisp has correct formatting with regard to line breaks.

 

Real SimObject-derived classes should follow the same concept of using local and inherited member variables.  Impelementation Caveats section also provides more general information about the use of MessageFormat class.

 


Best practices

  • Standard naming convention for member variables.  It is recommended that member variables start with m_ prefix where m stands for member.  It will also be beneficial if variable names start with encoded description of variable type, for example String m_sFoo, where s stands for String.

 

  • Use helper APIs provided by SimObject.  We presented the concept of inheritance in the beginning of this document.  In order to make coding of common task of determining the value of a property pair, the Borg team has implemented two helper APIs in SimObject class.  These APIs are available to any SimObject-derived class via inheritance.  The APIs are:
    • String getAttributeString(org.w3c.dom.Node node, String sAttrName).  A helper function to automate a common task of extracting String property value given a node and a property name.  Since you are likely to only need to call this API from inside qualify() or parseNode() you should use the node that came in as a parameter for these functions.
    • int getAttributeInt(org.w3c.dom.Node node, String sAttrName).   A helper function to automate a common task of extracting integer property value given a node and a property name.  Since you are likely to only need to call this API from inside qualify() or parseNode() you should use the node that came in as a parameter for these functions.

 

  • Use m1_debug.bat.  When creating new SimObject-derived classes it is likely that you will need to debug WorldBuilder.  The design of WorldBuilder requires that after each modification a new WorldBuilder.jar is produced to run the debugging session.  m1_debug.bat automates all preparation steps to run the debug session and is recommended for use in debugging situations.

 

  • Testing.  It is a good idea to create a collection of HTML storyboards that exercise all major features of WorldBuilder.  Before each release a sanity check should be performed by processing all storyboards from the collection with a new version of WorldBuilder and then examining the resultant initialize.lisp for correctness.

 

  • Use m1_release.htm.  This file describes in details all the steps one has to go through to prepare a new version release of WorldBuilder.  Thus m1_release.htm is a complimentary document to the Extensibility Guide.  The check list in that file should be used after design, implementatation and testing for new SimObject-derived classes is complete.

 


Implementation Caveats

  1. Multiple object instantiations.  Three separate SimObjectFoo instances will be created by the WorldBuilder to call (1) register(); (2) qualify() and (3) parseNode()/toString().  This requires that there can be no sharing of state other than by global means between register(), qualify() and parseNode()/toString().  If you must share state, do so via static variables.  The WorldBuilder will deposit the instance of SimObjectFoo after it calls its parseNode() method.  The WorldBuilder will later ask this object from the repository to output (via toString()) its content into initialize.lisp.  Sharing of state via regular member variables is possible between parseNode() and toString() and in fact is encouraged.

 

  1. Uniqueness in the repository based on name.  As mentioned above m_sName is a member variable of the base class SimObject.  All SimObject-derived classes will have this member variable via inheritance.  When the WorldBuilder adds SimObjects into its global repository, it first makes sure that the incoming object is unique.  It does so, by comparing the name with of the incoming object with the names of objects already in the repository.  This ensures that no duplicate entries appear in initialize.lisp.

 

  1. Scope of hyperlink following in HTML storyboards.  Currently only the hyperlinks found in tags that appear in SimObjectFactory table of qualifiers will be navigated by the WorldBuilder.  No other hyperlinks in HTML storyboards will be processed.  This behavior is by design.

 

  1. One HTML tag per one Apex object.  The architecture of the WorldBuilder promotes the one-to-one association between HTML tags and Apex objects.  It is possible to assess properties of more than one tag in parseNode() but there are no helper APIs provided by the WorldBuilder to do that.  The programming model will not be very intuitive and parseNode() implementation has a high chance of becoming messy.  The task of using more than one HTML tag for one Apex object should be considered advanced and avoided if possible.

 

  1. Hardcoded filenames.  Two files names are hardcoded into WorldBuilder application:

 

a)      The name of the archive has to be WorldBuilder.jar.  If you choose to change this name you should update WorldBuilder.FILE_THISJAR constant to reflect the new name.  Per naming specification outlined in the beginning of this document, the constant is located in wb\WorldBuilder.java file.

 

b)      The name of the files storing the version number and potentially other customizable strings is resources.txt.  This name is also hardcoded WorldBuilder.FILE_RESOURCES constant to reflect the new name.  This constant is located in the same file as a).

 

  1. Centroid coordinate specification for visual objects.  In parseNode() implementation if Lisp simobject description includes coordinates that represent dimensions of a visual object, the coordinates have to be that of a centroid.  For an example at how this is done, refer to the sample implementation of parseNode() above.

 

  1. Using MessageFormat class.  MessageFormat class provides a convenient and a powerful way to differentiate data and formatting for text message.  In particular, this class is useful in toString() implementations.  One thing the user of the class has to watch out for, however, is locale specific formatting.  For example, if a number is left without specifying additional formatting, on a US English system will have e,f to separate thousands (e.g. 2002 will be formatted into g2,002h.)  Luckily, the class provides an easy remedy of explicitly stating the required format such as {x,number, 0}, where x is argument index in an arguments array, number indicates that parameter is a number, and 0 indicates that it should be left unformatted.

 

  1. When structure of WorldBuilder changes.  If you make substantial changes to the structure of WorldBuilder application, it may become necessary to change the manifest file – manifest.txt.  Currently, manifest file prescribes that the main file (i.e. the file with main() function)of the application is wb.WorldBuilder.  Additionally, it establishes that Tidy.jar (JTidy implementation library) has to be in the same folder as WolderBuilder.jar.

 

  1. JBuilder build quirk (or why only 2 objects were processed).  In some development scenarios, it appears that when the entire application is rebuilt, wb\simobjects\SimObjectButton.java fails to build.  It is easy to detect if you fell victim to this build quirk: you will get a message that only 2 objects were processed regardless of the number of objects in HTML storyboard.  If this is unexpected, check where classes\wb\simobjects\SimObjectButton.class exist after a rebuild of the application.  If it does not, it is easy to build by right clicking on the SimObjectButton.java file in the Project window of JBuilder and selecting Make or Rebuild command for the context menu.

 


How it all works -- Overview of WorldBuilder flow of control

 

The following run-time view of the WorldBuilder application demonstrates component/connectors and their temporal interactions.  It should be noted that this diagram is somewhat of an abstraction that omits some mundate detail but still nicely captures the run-time aspect of the system.  The goal of this diagram is to enhance general understanding of WorldBuilder by developers who are extending it with more SimObject-derived classes.

 

The following is a brief description of temporal actions:

(1)         Main creates and initializes SimObjects repository.

(2)         Main creates and initializes SimObjectFactory object.  The factory will later create individual SimObjects based on HTML tags and the qualifiers table.  This is when register() method will be called on all SimObject-derived classes.

(3)         Main creates and initializes HTMLOverview object.  HTMLOverview object is responsible for describing simulation agents and interfaces and also provides a link to the first grealh HTML storyboard.

(4)         HTMLOverview loads and parses HTMLOverview file.  To accomplish this task, the object relies on JTidy library and on DOM interfaces the library returns.

(5)         After DOM representation is created for HTMLOverview file, HTMLOverview object parses the file and creates agent and interface SimObjects.

(6)         Previously created SimObjects are placed into the repository.

(7)         The first link in HTMLOverview file serves as an entry point into HTMLStoryboard processing.  HTMLStoryboard object is created and control passes to it.

(8)         HTMLOverview loads and parses HTMLOverview file.  To accomplish this task, the object relies on JTidy library and on DOM interfaces the library returns.

(9)         HTMLStoryboard get the collection of tags from the SimObjectFactory qualifiers table.  HTMLStoryboard parses HTMLStoryboard file and gives control to the factory to process the previously returned HTML tags of interest.

(10)     SimObjectFactory creates SimObject-derived objects and gives control to these objects to parse HTML tag information.  This is when qualify() method might be called and parseNode() method will be called for your SimObject-derived class.

(11)     Previously created SimObjects are placed into the repository.

(12)     Main creates and initializes LispFile object.  This object is applicationfs wrapper for initialize.lisp.

(13)     LispFile scans SimObjects repository and generates a textual representation of the SimObject-derived objects inside.  This is when toString() method will be called for your SimObject-derived class.

(14)     LispFile creates and saves initialize.lisp.

 

The processing of a set of HTML storyboards ends.

 


CREDITS

Project Initiator:

Professor Bonnie John, HCII, Carnegie Mellon University

Technical Advisor:

Mike Freed, Ames Research Center, NASA

 

MSE Studio Team:

 

Mike Lewis

Project Lead

Andrew Guletsky

Development Lead

J. Darby Mitchell

Planning Lead

Chiharu Kawatake

Quality/Process Lead

Cmdr. Dave Root

Studio mentor

Dr. Jim Tomayko

Studio mentor


REVISION HISTORY

Note:  The version number is the version of this file, not the application.

 

REVISION 1.0

Date: 06/10/2002

Updated By: Andrew Guletsky

Reason:  Created the file and incorporated feedback from the Borg team members.

 

REVISION 1.1

Date: 06/15/2002

Updated By: Andrew Guletsky

Reason:  Incorporated more feedback.  Added Best practices section.  Revised code samples.  Misc. other minor content improvements.

 

REVISION 1.2

Date: 07/25/2002

Updated By: Andrew Guletsky

Reason:  Incorporated feedback from Irene Tollinger.  Updated the content to match version 1.1.6.  Added several new Best Practices (3-5) and Implementation Caveats (5-9).  Added How it works diagram and explanation.  Made the document more Word-like after bad round-tripping from HTML.