/* gnu.classpath.tools.doclets.htmldoclet.HtmlDoclet
Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of GNU Classpath.
GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
package gnu.classpath.tools.doclets.htmldoclet;
import gnu.classpath.tools.IOToolkit;
import gnu.classpath.tools.doclets.AbstractDoclet;
import gnu.classpath.tools.doclets.DocletOption;
import gnu.classpath.tools.doclets.DocletOptionFile;
import gnu.classpath.tools.doclets.DocletOptionFlag;
import gnu.classpath.tools.doclets.DocletOptionString;
import gnu.classpath.tools.doclets.PackageGroup;
import gnu.classpath.tools.doclets.TagletPrinter;
import gnu.classpath.tools.taglets.TagletContext;
import gnu.classpath.tools.java2xhtml.Java2xhtml;
import com.sun.javadoc.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
public class HtmlDoclet
extends AbstractDoclet
{
private static String filenameExtension = ".html";
private void printNavBar(HtmlPage output, String currentPage, ClassDoc currentClass)
{
output.beginDiv(CssClass.NAVBAR_TOP);
boolean overviewLevel = ("overview".equals(currentPage)
|| "full-tree".equals(currentPage)
|| "alpha-index".equals(currentPage));
if ("overview".equals(currentPage)) {
output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE);
output.print("Overview");
output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE);
}
else {
output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED);
output.beginAnchor(output.getPathToRoot() + "index-noframes" + filenameExtension);
output.print("Overview");
output.endAnchor();
output.endSpan(CssClass.NAVBAR_ITEM_ENABLED);
}
output.print(" ");
if (!overviewLevel) {
if ("package".equals(currentPage)) {
output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE);
output.print("Package");
output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE);
}
else {
output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED);
output.beginAnchor("index" + filenameExtension);
output.print("Package");
output.endAnchor();
output.endSpan(CssClass.NAVBAR_ITEM_ENABLED);
}
}
else {
output.beginSpan(CssClass.NAVBAR_ITEM_DISABLED);
output.print("Package");
output.endSpan(CssClass.NAVBAR_ITEM_DISABLED);
}
if (optionUse.getValue() || optionLinkSource.getValue()) {
output.print(" ");
if (null != currentClass) {
if ("class".equals(currentPage)) {
output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE);
output.print("Class");
output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE);
}
else {
output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED);
output.beginAnchor(currentClass.name() + filenameExtension);
output.print("Class");
output.endAnchor();
output.endSpan(CssClass.NAVBAR_ITEM_ENABLED);
}
}
else {
output.beginSpan(CssClass.NAVBAR_ITEM_DISABLED);
output.print("Class");
output.endSpan(CssClass.NAVBAR_ITEM_DISABLED);
}
if (optionUse.getValue()) {
output.print(" ");
if (null != currentClass) {
output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED);
output.beginAnchor(currentClass.name() + "-uses" + filenameExtension);
output.print("Use");
output.endAnchor();
output.endSpan(CssClass.NAVBAR_ITEM_ENABLED);
}
else {
output.beginSpan(CssClass.NAVBAR_ITEM_DISABLED);
output.print("Use");
output.endSpan(CssClass.NAVBAR_ITEM_DISABLED);
}
}
if (optionLinkSource.getValue()) {
output.print(" ");
if (null != currentClass) {
output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED);
String targetClassName = currentClass.name();
String targetAnchor = "";
if (null != currentClass.containingClass()) {
targetClassName = getOuterClassDoc(currentClass).name();
targetAnchor = "#line." + currentClass.position().line();
}
output.beginAnchor(targetClassName + "-source" + filenameExtension + targetAnchor);
output.print("Source");
output.endAnchor();
output.endSpan(CssClass.NAVBAR_ITEM_ENABLED);
}
else {
output.beginSpan(CssClass.NAVBAR_ITEM_DISABLED);
output.print("Source");
output.endSpan(CssClass.NAVBAR_ITEM_DISABLED);
}
}
}
if (!optionNoTree.getValue()) {
output.print(" ");
if ("tree".equals(currentPage) || "package-tree".equals(currentPage)) {
output.beginSpan(CssClass.NAVBAR_ITEM_ACTIVE);
output.print("Tree");
output.endSpan(CssClass.NAVBAR_ITEM_ACTIVE);
}
else {
output.beginSpan(CssClass.NAVBAR_ITEM_ENABLED);
output.beginAnchor("tree" + filenameExtension);
output.print("Tree");
output.endAnchor();
output.endSpan(CssClass.NAVBAR_ITEM_ENABLED);
}
}
output.print(" ");
String indexName;
if (optionSplitIndex.getValue()) {
indexName = "alphaindex-1";
}
else {
indexName = "alphaindex";
}
output.beginAnchor(output.getPathToRoot() + indexName + filenameExtension);
output.print("Index");
output.endAnchor();
if (!optionNoDeprecatedList.getValue()) {
output.print(" ");
output.beginAnchor(output.getPathToRoot() + "deprecated" + filenameExtension);
output.print("Deprecated");
output.endAnchor();
}
if (!optionNoHelp.getValue()) {
output.print(" ");
output.beginAnchor(output.getPathToRoot() + "help" + filenameExtension);
output.print("Help");
output.endAnchor();
}
output.print(" ");
output.beginAnchor(output.getPathToRoot() + "about" + filenameExtension);
output.print("About");
output.endAnchor();
output.endDiv(CssClass.NAVBAR_TOP);
}
private void printNavBarTop(HtmlPage output, String currentPage)
{
printNavBarTop(output, currentPage, null);
}
private void printNavBarTop(HtmlPage output, String currentPage, ClassDoc currentClass)
{
if (!optionNoNavBar.getValue()) {
output.beginTable(CssClass.NAVBAR_TOP);
output.beginRow();
output.beginCell();
printNavBar(output, currentPage, currentClass);
output.endCell();
if (null != optionHeader.getValue()) {
output.beginCell();
output.print(optionHeader.getValue());
output.endCell();
}
output.endRow();
output.endTable();
}
}
private void printNavBarBottom(HtmlPage output, String currentPage)
{
printNavBarBottom(output, currentPage, null);
}
private void printNavBarBottom(HtmlPage output, String currentPage, ClassDoc currentClass)
{
if (!optionNoNavBar.getValue()) {
output.beginDiv(CssClass.NAVBAR_BOTTOM_SPACER);
output.print(" ");
output.endDiv(CssClass.NAVBAR_BOTTOM_SPACER);
output.beginTable(CssClass.NAVBAR_BOTTOM);
output.beginRow();
output.beginCell();
printNavBar(output, currentPage, currentClass);
output.endCell();
if (null != optionFooter.getValue()) {
output.beginCell();
output.print(optionFooter.getValue());
output.endCell();
}
output.endRow();
output.endTable();
}
if (null != optionBottom.getValue()) {
output.hr();
output.print(optionBottom.getValue());
}
}
private void printPackagePageClasses(HtmlPage output, ClassDoc[] classDocs, String header)
{
if (classDocs.length > 0) {
output.beginTable(CssClass.PACKAGE_SUMMARY);
output.rowDiv(CssClass.TABLE_HEADER, header);
for (int i=0; i 0) {
for (int i=0; i");
if (null == rootNode) {
rootNode = new TreeNode(null);
treeMap.put("", rootNode);
}
rootNode.children.add(node);
}
}
return node;
}
private void printPackageTreeRec(HtmlPage output, TreeNode node)
{
output.beginElement("li");
if (node.classDoc.isIncluded()) {
output.print(node.classDoc.containingPackage().name());
output.print(".");
printType(output, node.classDoc);
}
else {
output.print(node.classDoc.qualifiedName());
}
output.endElement("li");
output.beginElement("ul");
Iterator it = node.children.iterator();
while (it.hasNext()) {
TreeNode child = (TreeNode)it.next();
printPackageTreeRec(output, child);
}
output.endElement("ul");
}
private void printClassTree(HtmlPage output, ClassDoc[] classDocs)
{
Map classTreeMap = new HashMap();
for (int i=0; i");
if (null != interfaceRoot) {
Iterator it = interfaceRoot.children.iterator();
if (it.hasNext()) {
output.div(CssClass.PACKAGE_TREE_SECTION_TITLE, "Interface Hierarchy");
output.beginElement("ul");
while (it.hasNext()) {
TreeNode node = (TreeNode)it.next();
printPackageTreeRec(output, node);
}
output.endElement("ul");
}
}
}
private void printPackageTreePage(File packageDir, String pathToRoot, PackageDoc packageDoc)
throws IOException
{
HtmlPage output = new HtmlPage(new File(packageDir,
"tree" + filenameExtension),
pathToRoot);
output.beginPage(packageDoc.name() + " Hierarchy");
output.beginBody();
printNavBarTop(output, "package-tree");
output.div(CssClass.PACKAGE_TREE_TITLE, "Hierarchy for Package " + packageDoc.name());
ClassDoc[] classDocs = packageDoc.allClasses();
printClassTree(output, classDocs);
printInterfaceTree(output, classDocs);
printNavBarBottom(output, "package-tree");
output.endBody();
output.endPage();
output.close();
}
private void printFullTreePage()
throws IOException
{
HtmlPage output = new HtmlPage(new File(getTargetDirectory(),
"tree" + filenameExtension),
"");
output.beginPage("Hierarchy");
output.beginBody();
printNavBarTop(output, "full-tree");
output.div(CssClass.PACKAGE_TREE_TITLE, "Hierarchy");
ClassDoc[] classDocs = getRootDoc().classes();
printClassTree(output, classDocs);
printInterfaceTree(output, classDocs);
printNavBarBottom(output, "full-tree");
output.endBody();
output.endPage();
output.close();
}
private void printIndexEntry(HtmlPage output, Doc entry)
{
output.beginDiv(CssClass.INDEX_ENTRY);
String anchor = null;
String description = null;
if (entry instanceof PackageDoc) {
output.beginAnchor(getPackageURL((PackageDoc)entry) + "index" + filenameExtension);
output.print(entry.name());
output.endAnchor();
output.print(" - package");
}
else if (entry instanceof ClassDoc) {
ClassDoc classDoc = (ClassDoc)entry;
output.beginAnchor(getClassURL(classDoc));
output.print(entry.name());
output.endAnchor();
output.print(" - ");
if (entry.isInterface()) {
output.print("interface ");
}
else if (entry.isException()) {
output.print("exception ");
}
else if (entry.isError()) {
output.print("error ");
}
else {
output.print("class ");
}
output.print(classDoc.containingPackage().name());
output.print(".");
printType(output, classDoc);
}
else {
ProgramElementDoc memberDoc = (ProgramElementDoc)entry;
output.beginAnchor(getMemberDocURL(output, memberDoc));
output.print(entry.name());
if (memberDoc instanceof ExecutableMemberDoc) {
output.print(((ExecutableMemberDoc)memberDoc).signature());
}
output.endAnchor();
output.print(" - ");
if (memberDoc.isStatic()) {
output.print("static ");
}
if (entry.isConstructor()) {
output.print("constructor for class ");
}
else if (entry.isMethod()) {
output.print("method in class ");
}
else if (entry.isField()) {
output.print("field in class ");
}
ClassDoc containingClass = memberDoc.containingClass();
output.print(containingClass.containingPackage().name());
output.print(".");
printType(output, containingClass);
}
output.beginDiv(CssClass.INDEX_ENTRY_DESCRIPTION);
printTags(output, entry.firstSentenceTags());
output.endDiv(CssClass.INDEX_ENTRY_DESCRIPTION);
output.endDiv(CssClass.INDEX_ENTRY);
}
private void printFrameSetPage()
throws IOException
{
HtmlPage output = new HtmlPage(new File(getTargetDirectory(),
"index" + filenameExtension),
"",
HtmlPage.DOCTYPE_FRAMESET);
String title;
if (null == optionWindowTitle.getValue()) {
title = "Generated API Documentation";
}
else {
title = optionWindowTitle.getValue();
}
output.beginPage(title);
output.beginElement("frameset", "cols", "20%,80%");
output.beginElement("frameset", "rows", "25%,75%");
output.atomicElement("frame",
new String[] { "src", "name" },
new String[] { "all-packages" + filenameExtension, "packages" });
output.atomicElement("frame",
new String[] { "src", "name" },
new String[] { "all-classes" + filenameExtension, "classes" });
output.endElement("frameset");
output.atomicElement("frame",
new String[] { "src", "name" },
new String[] { "index-noframes" + filenameExtension, "content" });
output.endElement("frameset");
output.endPage();
output.close();
}
private void printPackagesMenuPage()
throws IOException
{
HtmlPage output = new HtmlPage(new File(getTargetDirectory(),
"all-packages" + filenameExtension),
"");
output.beginPage("Package Menu");
output.beginBody();
output.div(CssClass.PACKAGE_MENU_TITLE, "Packages");
output.beginDiv(CssClass.PACKAGE_MENU_LIST);
PackageDoc[] packageDocs = getRootDoc().specifiedPackages();
for (int i=0; i 0) {
if (classDoc.isInterface()) {
deprecatedInterfaces.add(classDoc);
}
else if (classDoc.isException()) {
deprecatedExceptions.add(classDoc);
}
else if (classDoc.isError()) {
deprecatedErrors.add(classDoc);
}
else {
deprecatedClasses.add(classDoc);
}
}
}
ConstructorDoc[] constructors = classDoc.constructors();
for (int j=0; j 0) {
deprecatedConstructors.add(constructors[j]);
}
}
MethodDoc[] methods = classDoc.methods();
for (int j=0; j 0) {
deprecatedMethods.add(methods[j]);
}
}
FieldDoc[] fields = classDoc.fields();
for (int j=0; j 0) {
deprecatedFields.add(fields[j]);
}
}
}
output.beginDiv(CssClass.DEPRECATION_TOC);
output.div(CssClass.DEPRECATION_TOC_HEADER, "Contents");
output.beginDiv(CssClass.DEPRECATION_TOC_LIST);
if (!deprecatedInterfaces.isEmpty()) {
output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY);
output.anchor("#interfaces", "Deprecated Interfaces");
output.endDiv(CssClass.DEPRECATION_TOC_ENTRY);
}
if (!deprecatedClasses.isEmpty()) {
output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY);
output.anchor("#classes", "Deprecated Classes");
output.endDiv(CssClass.DEPRECATION_TOC_ENTRY);
}
if (!deprecatedExceptions.isEmpty()) {
output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY);
output.anchor("#exceptions", "Deprecated Exceptions");
output.endDiv(CssClass.DEPRECATION_TOC_ENTRY);
}
if (!deprecatedErrors.isEmpty()) {
output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY);
output.anchor("#errors", "Deprecated Errors");
output.endDiv(CssClass.DEPRECATION_TOC_ENTRY);
}
if (!deprecatedFields.isEmpty()) {
output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY);
output.anchor("#fields", "Deprecated Fields");
output.endDiv(CssClass.DEPRECATION_TOC_ENTRY);
}
if (!deprecatedFields.isEmpty()) {
output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY);
output.anchor("#methods", "Deprecated Methods");
output.endDiv(CssClass.DEPRECATION_TOC_ENTRY);
}
if (!deprecatedFields.isEmpty()) {
output.beginDiv(CssClass.DEPRECATION_TOC_ENTRY);
output.anchor("#constructors", "Deprecated Constructors");
output.endDiv(CssClass.DEPRECATION_TOC_ENTRY);
}
output.endDiv(CssClass.DEPRECATION_TOC_LIST);
output.endDiv(CssClass.DEPRECATION_TOC);
output.anchorName("interfaces");
printDeprecationSummary(output, deprecatedInterfaces, "Deprecated Interfaces");
output.anchorName("classes");
printDeprecationSummary(output, deprecatedClasses, "Deprecated Classes");
output.anchorName("exceptions");
printDeprecationSummary(output, deprecatedExceptions, "Deprecated Exceptions");
output.anchorName("errors");
printDeprecationSummary(output, deprecatedErrors, "Deprecated Errors");
output.anchorName("fields");
printDeprecationSummary(output, deprecatedFields, "Deprecated Fields");
output.anchorName("methods");
printDeprecationSummary(output, deprecatedMethods, "Deprecated Methods");
output.anchorName("constructors");
printDeprecationSummary(output, deprecatedConstructors, "Deprecated Constructors");
printNavBarBottom(output, "deprecated");
output.endBody();
output.endPage();
output.close();
}
private void printOverviewPage()
throws IOException
{
HtmlPage output = new HtmlPage(new File(getTargetDirectory(),
"index-noframes" + filenameExtension),
"");
output.beginPage("Overview");
output.beginBody();
printNavBarTop(output, "overview");
String overviewHeader;
if (null == optionDocTitle.getValue()) {
overviewHeader = "Overview";
}
else {
overviewHeader = optionDocTitle.getValue();
}
output.div(CssClass.OVERVIEW_TITLE, overviewHeader);
/*
output.beginDiv(CssClass.PACKAGE_DESCRIPTION_TOP);
printTags(output, packageDoc.firstSentenceTags());
output.endDiv(CssClass.PACKAGE_DESCRIPTION_TOP);
*/
List packageGroups = getPackageGroups();
if (packageGroups.isEmpty()) {
printOverviewPackages(output, getRootDoc().specifiedPackages(),
"All Packages");
}
else {
Iterator it = packageGroups.iterator();
while (it.hasNext()) {
PackageGroup packageGroup = (PackageGroup)it.next();
printOverviewPackages(output,
(PackageDoc[])packageGroup.getPackages().toArray(new PackageDoc[0]),
packageGroup.getName());
}
}
/*
output.anchorName("description");
output.beginDiv(CssClass.PACKAGE_DESCRIPTION_FULL);
printTags(output, packageDoc.inlineTags());
output.endDiv(CssClass.PACKAGE_DESCRIPTION_FULL);
*/
printNavBarBottom(output, "overview");
output.endBody();
output.endPage();
output.close();
}
private void printOverviewPackages(HtmlPage output, PackageDoc[] packageDocs, String header)
{
output.beginTable(CssClass.OVERVIEW_SUMMARY);
output.rowDiv(CssClass.TABLE_HEADER, header);
for (int i=0; i 0) {
output.beginDiv(CssClass.CLASS_SYNOPSIS_IMPLEMENTS);
output.print("implements ");
for (int i=0; i0) {
output.print(", ");
}
printType(output, interfaces[i]);
}
output.endDiv(CssClass.CLASS_SYNOPSIS_IMPLEMENTS);
}
output.endDiv(CssClass.CLASS_SYNOPSIS);
output.hr();
output.beginDiv(CssClass.CLASS_DESCRIPTION);
printTags(output, classDoc.inlineTags());
output.endDiv(CssClass.CLASS_DESCRIPTION);
printTaglets(output, classDoc.tags(), TagletContext.TYPE);
printProgramElementDocs(output, classDoc.fields(),
"Field Summary");
printProgramElementDocs(output, classDoc.constructors(),
"Constructor Summary");
printProgramElementDocs(output, classDoc.methods(),
"Method Summary");
ClassDoc superClassDoc = classDoc.superclass();
while (null != superClassDoc) {
printInheritedMembers(output, superClassDoc.methods(),
"Methods inherited from " + superClassDoc.qualifiedName());
superClassDoc = superClassDoc.superclass();
}
printMemberDetails(output, classDoc.fields(),
"Field Details");
printMemberDetails(output, classDoc.constructors(),
"Constructor Details");
printMemberDetails(output, classDoc.methods(),
"Method Details");
printNavBarBottom(output, "class", classDoc);
output.endBody();
output.endPage();
output.close();
}
private void printInheritedMembers(HtmlPage output,
ProgramElementDoc[] memberDocs, String header)
{
if (memberDocs.length > 0) {
output.beginTable(CssClass.CLASS_SUMMARY);
output.rowDiv(CssClass.TABLE_HEADER, header);
output.beginRow();
output.beginCell();
for (int i=0; i 0) {
output.print(", ");
}
output.beginAnchor(getMemberDocURL(output, memberDoc));
output.print(memberDoc.name());
output.endAnchor();
}
output.endCell();
output.endRow();
output.endTable();
}
}
private void printMemberDetails(HtmlPage output,
ProgramElementDoc[] memberDocs, String header)
{
if (memberDocs.length > 0) {
output.div(CssClass.TABLE_HEADER, header);
for (int i=0; i0) {
output.hr();
}
ProgramElementDoc memberDoc = memberDocs[i];
output.anchorName(getMemberAnchor(memberDoc));
output.beginDiv(CssClass.MEMBER_DETAIL);
output.div(CssClass.MEMBER_DETAIL_NAME, memberDoc.name());
output.beginDiv(CssClass.MEMBER_DETAIL_SYNOPSIS);
output.print(getFullModifiers(memberDoc));
if (memberDoc.isMethod()) {
output.print(" ");
printType(output, ((MethodDoc)memberDoc).returnType());
}
output.print(" ");
if (optionLinkSource.getValue() && null != memberDoc.position()) {
ClassDoc containingClass = memberDoc.containingClass();
while (null != containingClass.containingClass()) {
containingClass = containingClass.containingClass();
}
String href = containingClass.name() + "-source" + filenameExtension + "#line." + memberDoc.position().line();
output.beginAnchor(href);
output.print(memberDoc.name());
output.endAnchor();
}
else {
output.print(memberDoc.name());
}
if (memberDoc.isConstructor() || memberDoc.isMethod()) {
printParameters(output, (ExecutableMemberDoc)memberDoc);
}
output.endDiv(CssClass.MEMBER_DETAIL_SYNOPSIS);
output.beginDiv(CssClass.MEMBER_DETAIL_DESCRIPTION);
printTags(output, memberDoc.inlineTags());
output.endDiv(CssClass.MEMBER_DETAIL_DESCRIPTION);
if (memberDoc.isConstructor() || memberDoc.isMethod()) {
ExecutableMemberDoc execMemberDoc
= (ExecutableMemberDoc)memberDoc;
Parameter[] parameters = execMemberDoc.parameters();
if (parameters.length > 0) {
output.beginDiv(CssClass.MEMBER_DETAIL_PARAMETER_LIST);
output.div(CssClass.MEMBER_DETAIL_PARAMETER_HEADER, "Parameters:");
for (int j=0; j 0) {
output.beginDiv(CssClass.MEMBER_DETAIL_THROWN_LIST);
output.div(CssClass.MEMBER_DETAIL_THROWN_HEADER, "Throws:");
for (int j=0; j 0) {
output.print(", ");
}
printType(output, parameters[j].type());
output.print(" ");
output.print(parameters[j].name());
}
output.print(")");
}
private void printProgramElementDocs(HtmlPage output,
ProgramElementDoc[] memberDocs, String header)
{
if (memberDocs.length > 0) {
output.beginTable(CssClass.CLASS_SUMMARY);
output.rowDiv(CssClass.TABLE_HEADER, header);
for (int i=0; i