HTML
Document Generation
JavaDoc is useful documentation, but insufficient to give potential users how a tool or package is to be used. HTML is
a standard medium and language for doing this. And in such
documentation, code examples and their inputs and outputs should be
given to illustrate key ideas and idiomatic usage. Writing such
documentation is hard enough -- but keeping the examples up-to-date
with ongoing package updates and refactorings without tool support is close to impossible.
HTML documentation for MDELite was produced by a document generator
-- a simple Java class that extracts code examples + input/output files
from regression tests and inserts them at designated locations into HTML templates to yield
up-to-date documentation. The entire process of document
generation is part of MDELite regression tests, which not only runs the
afore-mentioned regression tests, it also generates the HTML output.
The file MDELite.DocumentGenerator offers a single static method to
accomplish this:
void main( Source sourceCodeFile, String htmlTemplateFile, String htmlOutputFile )
where:
- sourceCodeFile
is the path to a source file of a Java regression test that
has examples to be read.
- htmTemplateFIle is
the path to an .htm template file that has "holes" for examples and for
input/output files.
- htmlOutputFIle is
the path to a .html file that has the "holes" filled in.
Table of Contents
Examples of DocumentGeneration can be found in the MDELite Netbeans Project in files: test/DML/Prolog/Doc*.java.
Source
Java Code File Format
Regression
tests are Java files. A code example in such a file looks
like:
@Test
public void toTableTest() {
RegTest.Utility.redirectStdOut(errorfile);
toTable();
RegTest.Utility.validate(errorfile, correct + "toTable.txt", false);
}
///toTable
void toTable() {
DB db = DB.read(testdata + "dogOwner.do.pl");
Table dog = db.getTableEH("dog");
// Step 1: stream dog, and collect into a new dog table
Table dog2 = dog.stream().collect(new TuplesToTable());
boolean result = dog2.equals(dog);
System.out.format("dog2 %s dog", result ? "=" : "!=");
}
///toTable
The
JUnit regression test is indicated by the @Test annotation.
The code example is inbetween the ///toTable markers,
where the example is named "toTable".
The DocumentGenerator reads a JUnit test file and harvests
the text of examples and stores them by their name.
HTM
Template File
An
HTM template file ends in ".htm". It is a standard HTML file
with the followning exceptions:
- A
"hole" in which a code example is to be place is written as 3 lines of
preformatted text, a blank line, followed by an indented,
bold line with a ---exampleName---
marker in Courier font, followed by blank line:
---toTable---
- A
"hole" for the contents of an entire file is to be placed is written as 3 lines of
preformatted text, a blank line, followed by an indented,
bold line with a threeColon filename threeColon marker in Courier font, followed by blank line:
:::DocGen/figures/fileHole.txt:::
That's
it. The purpose of the template file is to provide text to
explain examples and illustrate input and outputs (files) to these
examples.
HTML Output File
As
said above, the Template file with its holes are read in. So too
is a single Java regression test with examples. The
DocumentGenerator fills these "holes" with named code examples that it
harvested and the contents of files that it read. The resulting
text is written to the "html output file".
Usage
The
typical usage of DocumentGenerator is a single call, written as JUnit
test (so when regression tests are run, up-to-date documentation is
produced):
@Test
public void families() {
main("src/LectureExamples/FamiliesM2M.java", "DocGen/FamiliesDemo.htm", "Docs/FamiliesDemo.html");
}
Sometimes
code examples are taken from several Java regression tests. In
such cases, html output files are placed in a directory that ultimately
will be deleted, such as this example where a template file requires
code examples from three different sources:
@Test
public void allegories() {
main("src/LectureExamples/allegory/Main.java", "DocGen/AllegoryDemo.htm", "DELETE.htm");
main("src/LectureExamples/allegory/PDD.java", "DELETE.htm", "DELETE1.htm");
main("test/LectureExamples/Correct/x.PDD.pl", "DELETE1.htm", "Docs/AllegoryDemo.html");
}
Here's another idiom: A tool is invoked to produce a file whose output is to be inserted into an HTMLOutputFile.
void yumlSetup() {
Yuml.ClassParser.main("test/BuildDocumentation/Test/x.yuml.yuml", "outYuml1.txt");
Yuml.ClassParser.main("test/BuildDocumentation/Test/y.yuml.yuml", "outYuml2.txt");
}
@Test
public void yuml() {
yumlSetup();
main("test/BuildDocumentation/test/nothing.jav", "DocGen/YumlManualTemplate.htm", "Docs/YumlManual.html");
}
The method yumlSetup() calls the Yuml.ClassParser to parse two different files (x.yuml.yuml and y.yuml.yuml) into separate output files. These files are then read by the YumlManualTemplate.htm
template file to fill "file holes". Interestingly, in this case,
there is no Java regression test from which to harvest code examples --
that is, the template file contains holes only for files. So a
"nothing.jav" file -- literally an empty file -- is given as the Java
file to harvest.
JUnit Test Structure
Nothing
fancy is needed. You should have a JUnit setUpClass()
method to create a generated "Docs" directory in red below (which
you should delete prior to running regression tests as part of ANT
makefiles). Then you should have a series of HTML file
generation tests in purple below, terminating with the creation of an
index.html file from a template that, in this case, has no code or file
holes to fill:
public class BuildDocTest extends DocumentGenerator {
@BeforeClass
public static void setUpClass() {
new File("Docs").mkdir();
}
@Test
public void m2m() {
main("test/DML/PrologDB/DocExamplesTest.java", "DocGen/M2MTemplate.htm", "Docs/M2MPrograms.html");
}
...
@Test
public void index() throws Exception {
main("test/BuildDocumentation/test/nothing.jav", "DocGen/indexTemplate.htm", "Docs/index.html");
}
Note: if a hole cannot be
filled, either because there is no code example with the given name or
that the file referenced does not exist, the marker of the code example
or file will remain unchanged.