32. Alternative Simulation Frameworks

Part of CS:2820 Object Oriented Software Development Notes, Fall 2015
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

 

An Archaic Framework

Discrete-event simulation was first developed in the 1960s, before anyone understood object-oriented programming. Much of the early work was done in the FORTRAN programming language. FORTRAN, at that time, had very limited control structures and a very limited data model. Variables could be integers, floating-point numbers, or double-precision floating point numbers. There was no character data type, but characters could be processed as integers. There was nothing resembling structures, or classes, but there were arrays. This was very limiting, but still sufficient.

If we were building our road network simulation in FORTRAN back the, we might begin by designing the event set. Each event might be described by a logical record containing:

Of course, since FORTRAN did not have record structures of any sort, we'd have to make do with arrays. For each integer value of I, the array entries ETIME(I), EVEHICLE(I), EROAD(I), EINTERS(I), and ENEXT(I), would describe one event. The time field is floating point, while the other fields are all integers. Logically, there is an array of vehicles indexed by vehicle number, an array of roads, indexed by road number, and an array of intersections indexed by intersection number. Events themselved are arranged in a logical array of events indexed by event number, but note, event numbers are arbitrary. The next field organizes events into a linked list, sorted in chronological order, with the head of the list being the head of the event set, the next event to simulate.

That's a data structure. ........ Javadoc, or rather, the style of commenting that Javadoc uses, is an example of a broad class of techniques described as literate programming. The old, low-tech approach to programming is to develop code and documentation as entirely separate documents. You write the program here, and you create a web page or other document describing the program there, using unrelated tools and unrelated languages. Perhaps you write the program in C++ and then write the documentation using Microsoft Word.

In 1984, Donal Knuth published a book, Literate Programming, that encourages a different approach. In Knuth's model, the programmer creates a single document that incorporates program fragments woven into the documentation for that program. Knuth provided two tools for processing this document. One tool traverses the document, weaving together the program fragments into a program in the target programming language, while the other tool traverses the document, typesetting human readable text to document that program.

Knuth's tools for supporting literate programming were WEB (for Pascal programs) and later CWEB (for C programs). Noweb was derived from this as a language-independent literate programming environment, and many others have followed in this direction.

Javadoc is not quite the same as Knuth's idea, but it is in the same broad category. Unlike Knuth's idea, Javadoc does not allow the order or structure of the Java program to be assembled "out of order" or rather, in the order that makes sense to the programmer, as opposed to the language. It does, however, support the idea of creating a single document from which documentation and code can be derived.

A Javadoc Example

Let's look at a simplified small example class from our highway network simulator:

// ScanSupport.java

import java.util.regex.Pattern;
import java.util.Scanner;

public class ScanSupport {
        public interface ErrorMessage {
                abstract String myString();
        }

        public static void lineEnd( Scanner sc, ErrorMessage message ) {
                String skip = sc.nextLine();
                if (!"".equals( skip )) {
                        Errors.warning(
                                message.myString() +
                                " -- expected a newline"
                        );
                }
        }

        private static final Pattern name = Pattern.compile( "[A-Za-z]\\w*" );

        public static String nextName( Scanner sc, ErrorMessage message ) {
                if (sc.hasNext( name )) {
                        return sc.next( name );
                } else {
                        Errors.warning(
                                message.myString() +
                                " -- expected a name"
                        );
                        return null;
                }
        }
}

The only comment above is the one identifying the file name. There are numerous places in this code where we can put Javadoc

// ScanSupport.java

import java.util.regex.Pattern;
import java.util.Scanner;

/** Input scanning support methods
 *  @author Douglas W. Jones 
 *  @version 4/11/2016
 *  @see java.util.Scanner
 */
public class ScanSupport {

The documentation output of Javadoc will use this text to build the global structure of the document, but each public component of the class should also be documented. The first component of our example class is worth significant documentation:

        /** Interface allowing error messages passed as lambda expressions
         *  The {@code ErrorMessage} interface is used implicitly in the
         *  parameter lists to methods of class {@code ScanSupport},
         *  but is rarely explicitly mentioned.
         *  The implicit mechanism for passing error message strings creates
         *  a subclass of {@code ErrorMessage} for each message.
         */
        public interface ErrorMessage {
                /** Return the part of the error message text giving the context
                 *  each call to a {@code ScanSupport} method will typically
                 *  provide a different implementation of this function.
                abstract String myString();
        }

Following this, each public method of our class is worthy of documentation. Here is an example:

        /** Force there to be a line end here, complain if not
         *  @param sc, the {@link Scanner} from which the input text is
         *             being scanned
         *  @param message, the {@link ErrorMessage} to use if the input text
         *             is not currently positioned at a line end.
         *  Typically, the {@code message} parameter is given as a lambda
         *  expression, for example: {@code LineEnd(sc,()->"Line:"+n);}
         *  The use of a lambda expression here means that the computations
         *  (for example, string concatenations) are not done unless there
         *  is not a line end where one was expected.
         */
        public static void lineEnd( Scanner sc, ErrorMessage message ) {
                String skip = sc.nextLine();
                if (!"".equals( skip )) {
                        // Bug:  do we want to allow comments here
                        Errors.warning(
                                message.myString() +
                                " -- expected a newline"
                        );
                }
                // Bug:  what if sc.nextLine() was illegal (illegal state)
        }

The extended text above could be replicated for each method, but we can cross reference text within the documentation, so we can simplify the presentation for later methods:

        /** Get the next name, or complain if there isn't one
         *  @param sc, the {@link Scanner} from which the input text is
         *             being scanned
         *  @param message, the {@link ErrorMessage} to use if the input text
         *             is not currently positioned at a line end.
         *  Typically, the {@code message} parameter is given as a lambda
         *  expression, for example: {@code NextName(sc,()->"Line:"+n);}
         *  See {@link lineEnd} for the reason a lambda expression is used here.
         *  Names are defined as a letter followed by any number of letters
         *  or digits using Java's {@link Pattern} recognition mechanisms.
         *  @see java.util.regex.Pattern
         */
        public static String nextName( Scanner sc, ErrorMessage message ) {
                if (sc.hasNext( name )) {
                        return sc.next( name );
                } else {
                        Errors.warning(
                                message.myString() +
                                " -- expected a name"
                        );
                        return null;
                }
        }

Using Javadoc

Once you have a file that contains Javadoc comments, you can comple it with the Java compiler using the javac command, and you can create an HTML (world wide web) document describing the contents of that file using the javadoc command. For example, you could type:

[HawkId ~]$ javadoc ScanSupport.java

If you do this, it will output any error messages resulting from misformed Javadoc comments, and then it will create a flood of interlinked .html files, as well as a javascript file (.js suffix) and a style sheet (.css suffix).

You can do this one file at a time when you are developing your code, but the real power of Javadoc comes to the fore when you use Javadoc on the main class of your project or on the set of all .java files that make up your project. When you do that, the set of files generated by Javadoc becomes a complete web site describing your project.

In fact, if you use a web browser to view the output of Javadoc, what you will see is documentation that, at least in style, resembles the on-line documentation provided by Oracle for the Java libraries. That is because Oracle uses Javadoc to prepare this documentation as they maintain their libraries.