Developer Notes

Tools

Getting Started

Image200 maintains two separate VSS projects: "Image2000" is the base product and "I2kCorePlugin" has the core plugins.

To configure your PC for Image2000 development:

  1. "Get latest" on the entire Image2000 VSS project into your own directory tree.
  2. Change the file attributes on i2k.jpr to be writable. (Note: currently, we do not checkin/checkout the i2k.jpr project file.)
  3. In JBuilder, open the i2k.jpr project file at the top of your tree.
  4. Under "project parameters", configure the paths for the XML, Rhino, and JAI jar files.
  5. Select gov.nasa.gsfc.i2k.main.Image2000 as the class to "run".
  6. Make sure that all of the Image2000 packages in your VSS "get latest" tree have been added to the project.
  7. Under "project parameters", set the command line parameter -Di2k.root=your-root, where "your-root" is the absolute path to the top of your directory tree. (Operationally, the i2k.root property will be set automatically--for now, you must hard-code the absolute path.)
  8. Build and run using JBuilder.
  9. If Image2000 does not come up, use JBuilder to display the "execution log".

To configure your PC for Image2000 Core plugin development, do "get latest" on the I2kCorePlugin project and follow the steps above.

Plugin Development

See Plugins for a description of the plugin architecture and a cookbook for developing and packaging a plugin.

Launcher

The launcher is a host-native executable that starts Image2000 running within the Java VM. You should run Image2000 using the launcher rather than running the VM directly. Under Win32, the file path for the launcher is bin/Image2000w.exe. There is also the console version bin/Image2000.exe that performs the same function but assumes the existence of a DOS console for standard output; the console form is typically used by programmers.

The bin/Image2000w.exe executable is suitable for use as a Windows shortcut icon. Dragging image, script, or DAG files to such a shortcut produces the standard result.

Properties Set by Launcher

Before running the Java VM, the launcher sets the values of the following Java properties:

Launcher Command Line Options

The Image2000 command line has the following form:

    Image2000[w] [javaVmOptions] [files] 

The javaVmOptions are the same as for running the JDK Java VM; see The Java Application Launcher. Image2000, however, supplies the class name automatically. In addition to the standard Java VM options, the following are available:

The file list may include image, DAG, and/or JavaScript files. If a file ends with ".dag", it is opened as a DAG; if the file ends with ".js", it is executed as a script; otherwise, the file is opened as an image. Files listed on the command line are subjected to macro substitution, i.e., {propertyName} is replaced with the value of the property.

Note: I2K properties defined on the command line using the -D option override the corresponding property value from a property file; this rule applies only to property names that start with i2k..

Image2000 Properties

See Tailorability for a description of how Image2000 reads property files. Image2000 honors properties from those files as follows:

There is a JavaScript script, show-properties.js, that displays the list of current property values.

Cookbook for Adding an Action

To add an action written in Java:

  1. For each action, there is a corresponding definition in some plugin directory. To add an action to a plugin, edit an existing spec.xml file or start a new plugin directory. Some spec.xml files use XML "external entities" to include action definitions, in which case you will edit some file referenced by the spec.xml file.
  2. Place the action on some menu by editing the corresponding file in the menubars directory.
  3. Optional: place the action on a toolbar by editing the corresponding file in the toolbars directory.
  4. Optional: put an icon for the action in the images directory and reference the icon from the XML file.
  5. Write a Java subclass of AbstractAction to implement the action. Use one of the gov.nasa.gsfc.i2k.main Action classes as a guide.
  6. When applicable, your Action class should make provisions for enabling/disabling the Action based on context.

MDI Child Windows

Each child window (JInternalFrame) should be a subclass of gov.nasa.gsfc.i2k.main.Child. The following considerations apply:

Document Handling

The Child class assumes that each subclass is manipulating a "document" that may be saved and restored from disk. The type of the document is determined by the type of the Child subclass as determined by the second argument to the Child's constructor. Example: the type of the "DAG Editor" window is "dag".

If the document has a text representation on disk, then the subclass should implement the writeTextDoc (PrintWriter) method to write the document. If the document has a binary form, then the subclass should implement the writeDocfile(File) method.

The Child framework handles the Save and SaveAs menu entries, presenting the appropriate file dialog and calling the subclass's writeTextDoc or writeDocFile as needed.

The Child framework also handles the prompt for "do you want to save changes?" when the window is closed or the application is exited. To assist the framework in this regard, the subclass must maintain the state of the "dirty" flag by calling setDocDirty(true) whenever edits are made to the document. If the subclass uses the framework's undo/redo mechanism, then setDocDirty is called automatically when the subclass posts a document change via postEdit.

For each Child frame type ("dag", etc), there are the following properties that relate to document handling:

Undo/Redo Support

The Child framework supports undo/redo according to the javax.swing.undo package. The framework maintains an UndoManager for each Child window and manages the enable/disable state of the Undo/Redo actions. Each Child subclass has the following undo/redo repsonsibility: after performing an action that changes the state of the Child's document, the Child must call postEdit(UndoableEdit). The UndoableEdit argument is an object--constructed by the subclass--that knows how to undo and redo the action. In this regard, a Child has several options:

Composing UndoableEdit objects is non-trivial. The javax.swing.undo.StateEdit classes provide a general-purpose approach for constructing UndoableEdit objects and, for many applications, StateEdit provides considerable simplication. The standard paradigm for a StateEdit is:

// capture state of the document, make the change, annnouce change: StateEdit stateEdit = new StateEdit (document, "TypeOfChangeHere"); changeTheDocument(); stateEdit.end(); postEdit (stateEdit);

There is a test application (gov.nasa.gsfc.i2k.main.testapp.Frame) that demonstrates undo/redo working within the I2k framework. The application presents a list of items within a Child window, with buttons for adding, deleting, or modifying an entry in the list. All three actions are undoable. If you set the i2k.<frame>.undo.limit property to some number greater than one, then the actions are undoable/redoable through that number of levels. To run testapp, run the undo-redo-demo.js script. Run several copies of testapp to demonstrate that an UndoManager exists for each window.

testapp demonstrates both the UndoableEdit and the StateEdit approaches. By default, testapp uses custom UndoableEdit objects; if the i2k.testapp.stateEdit property is set to "true", then testapp uses the StateEdit approach.

The following notes apply to the undo/redo framework:

Cut/Copy/Paste Support

The Child class provides methods for the popular edit operations: cut, paste, copy, etc. The Child implementations of these methods do nothing.

Wait Cursor Paradigm

The gov.nasa.gsfc.i2k.util.ShowWaitCursor class does the work of showing the wait cursor on the Image2000 frame. The paradigm for using ShowWaitCursor is as follows:

    ShowWaitCursor waitCursor = new ShowWaitCursor();
    try {
        do_stuff_with_returns_or_exceptions();
    }
    finally {
        waitCursor.finish();
    }

Note that the finally clause gets control no matter how the body of the try clause exits, i.e., whether by return, exception, or normal termination.

Using VSS Header Strings

Every Java source file should have the following statement:

    private static String ident = "$Header: $";

When fetched from VSS, the Header string gets substituted with the VSS version string for the source file, containing file name, date/time of the most recent checkin, and version number. The Header string may be queried in source, class, uncompressd jar file with the gnu ident utility. Examples;

    ident.exe *.java  
    ident.exe *.class
    ident.exe *.jar

For a compressed jar file, the info-zip unzip utility provides a convenient way to list Header strings:

    unzip -p *.jar >jar.tmp
    ident jar.tmp

Note: For some unknown reason, piping directly from "unzip -p" to ident does not work.

The gnu ident utility is carried under VSS in the "tools" project.

There is a perl utility that will attempt to add the Header strings automatically:

    perl \i2k\tools\plant-header.pl *.java

Logging

General

The gov.nasa.gsfc.i2k.util.Log class manages the PrintStream Log.out that I2k uses for logging. If logging is disabled, Log.out contains an inert PrintStream so that you may code "Log.out.println();" without a null check. By default, Image2000 directs logging to System.out.

(Note that the public static variable, Log.out, violates several coding conventions (e.g., accessors, naming conventions). The thinking is that "Log.out.println(...)" is a useful, readable, terse paradigm.)

Generally, you should not write directly to System.out. Use Log.out instead.

When logging is disabled, your println logic still incurs overhead, especially the formatting of the string argument to println. In those special cases where the overhead is significant, you could conditionalize the Log.out method calls on Log.out.isLoggingEnabled().

Categories

Categories provide for conditional logging. If you want conditional logging, use the category argument to println or print. A category is an arbitrary string that names a type of logging, e.g., "dag.xml.parsing". To use conditional logging, pass a category string to println or print and the output is ignored except when the category is enabled.

Programmers create categories simply by placing a category string in a log method call. By convention, the first component of the category should be the i2k package name, e.g., "dag" in the "dag.xml.parsing" category.

Categories may be enabled via the i2k.log.categories property (below). You may also enable a category programmatically by calling Log.out.enableCategory(String name, boolean state).

When logging is disabled for a category, your println logic still incurs overhead, especially the formatting of the string argument to println. In those special cases where the overhead is significant, you could conditionalize the Log.out method calls on Log.out.isCategoryEnabled().

General convention: Severe errors should be logged unconditionally.

Properties

i2k.log.path specifies the path for the log file. If the path is relative, I2K roots the path to the i2k.root directory. If the path is "none", then no logging output is produced. If i2k.log.path is not present, then Image2000 logs to stdout.

Warning: When specifying paths in Windows, note that backslashes must be doubled in property files. However, the Windows implementation of Image2000 accepts forward slashes as the file separator and such characters do not have to be doubled.

i2k.log.append should be "true" to append to the log file upon Image2000 activation. By default, Image2000 overwrites the previous log file.

i2k.log.tee should be "true" to write to the log file and to standard out. This provides "tee" output as in UNIX. This property only applies when i2k.log.path specifies that logging should be written to a file.

i2k.log.categories may be used to enable categories. The value of this property is a list of categories to be enabled. Separate element in the list with comma, semi-colon, or colon.

Scripting

In a script, the log is known as i2k.log. Scripts may use the standard println method, e.g., i2k.log.println("hello"), or the category method, e.g., i2k.log.println("myCat", "hello").

Miscellaneous