Creating HTML output

exercise No. 17

Q:

Instead of transforming our simple catalog into textual output in Figure 800, “Accessing an XML Tree purely by DOM methods. ” we may also create XHTML pages like:

<!DOCTYPE html>
<!-- Static content section--> <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Available articles</title>
  </head>
  <body>
    <h1>Available articles</h1>
    <table>
      <tbody>
        <tr />
        <th>Article Description</th>
        <th>Order Number</th>
        <!-- End of static, beginning of dynamic section--> <tr>
          <td align="left">Swinging headset</td><td>3218</td>
        </tr>
        <tr>
          <td align="left">200W Stereo Amplifier</td><td>9921</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

Rather then just coding...println(<html>\n\t<head>...) statements you are expected to implement a more sophisticated solution: We may combine Figure 800, “Accessing an XML Tree purely by DOM methods. ” and A sub structured <title> . The idea is parsing the XML catalog instance to a Java DOM object as before. Then construct a second DOM tree representing the desired HTML output and fill in the article information from the first DOM tree accordingly.

Tip

The desired HTML output does contain both static ❶ and dynamic content ❷ with respect to a given XML catalog input.

The static content may be implemented as in Figure 797, “XML document creation from scratch. ”. Regarding dynamic content you'll have to parse your catalog input and construct the HTML's table lines in a similar fashion by iterating over <item orderNo="...">...</item> elements .

A:

We introduce a class dom.HtmlTree:

package dom;

import java.io.IOException;
import java.io.PrintStream;

import org.jdom2.DocType;

 ...

public class HtmlTree {

   private final Element tableBody;

   /**
    * Create a HTML skeleton (&lt;html&gt;&lt;head&gt;...&lt;body&gt; ...&lt;/body&gt;&lt;/head&gt;
    * to be filled in later by calling {@link #appendItem(String, String)}.
    *
    * @param titleText
    *   The document's title, e.g. "Available articles"
    *
    * @param tableHeaderFields
    *   The articles will be displayed as HTML table with these header fields
    *   e.g. {"Article Description", "Order Number" }
    */
   public HtmlTree(final String titleText,
         final String[] tableHeaderFields) { 

      final DocType doctype =  new DocType("html",
            "-//W3C//DTD XHTML 1.0 Strict//EN",
            "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd");

      final Element htmlRoot = new Element("html");

      final Document htmlOutput = new Document (htmlRoot, doctype);
      htmlOutput.addContent(0, new Comment(" Static content section"));

      // We create a HTML skeleton including a yet empty table
      final Element
            head = new Element("head"),
            body = new Element("body"),
            table = new Element("table");

      htmlRoot.addContent(head).addContent(body);

      head.addContent(new Element("title").addContent(new Text(titleText)));

      body.addContent(new Element("h1").addContent(new Text(titleText)));

      body.addContent(table);

      tableBody = new Element("tbody");
      table.addContent(tableBody);

      final Element tr = new Element("tr");
      tableBody.addContent(tr);
      for (final String headerField:  tableHeaderFields) {
         tr.addContent(new Element("th").addContent(new Text(headerField)));
      }

      tableBody.addContent(new Comment(" End of static, beginning of dynamic section"));
   }

   /**
    * Inserting an &lt;item&gt; as a new table row with two &lt;td&gt; elements.
    *
    * @param itemName The item's name (e.g. Tennis racket)
    * @param orderNo The item's order number
    */
   public void appendItem(final String itemName, final String orderNo) { 
      final Element tr = new Element("tr");
      tableBody.addContent(tr);
      tr.addContent(new Element("td").addContent(new Text(itemName)));
      tr.addContent(new Element("td").addContent(new Text(orderNo)));
   }
   /**
    * Serialize the HTML DOM to a stream
    * @param out Output destination.
    */
   public void serialize(final PrintStream out){

      // Set formatting for the XML output
      final Format outFormat = Format.getPrettyFormat();

      // Serialize to console
      final XMLOutputter printer = new XMLOutputter(outFormat);
      try {
         printer.output(tableBody.getDocument(), System.out);
      } catch (IOException e) {
         e.printStackTrace();
         System.exit(1);
      }
   }
   /**
    * @return the table's &lt;tbody&gt; element
    */
   public Element getTable() {
      return tableBody;
   }
}

A basic HTML skeleton is is being created:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
                    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>Available articles</title>
    </head>
    <body>
        <h1>Available articles</h1>
        <table>
            <tbody>
              <th>Article Description</th>
              <th>Order Number</th>
              <!-- Data to be inserted here during <item> iteration -->
            </tbody>
        </table>
    </body>
</html>

The table containing the product's data is empty at this point and thus invalid.

Calling solve.dom.HtmlTree.appendItem(String,String) once per <item> completes the creation of our HTML DOM tree:

...
  </tr>
  <tr>
    <td>Swinging headset</td>
    <td>3218</td>
  </tr>
  <tr>
    <td>200W Stereo Amplifier</td>
    <td>9921</td>
  </tr>
</tbody> ...

The class solve.dom.Article2Html reads the catalog data:

package dom;

import ...

public class Article2Html {

  private final SAXBuilder builder = new SAXBuilder();
  private final HtmlTree htmlResult;

  /**
   * Instances of this class allow for reading XML catalogs and delegate
   * XHTML transformation to a {@link HtmlTree} object,
   * see {@link #process(String, PrintStream)}.
   */
  public Article2Html() {

     builder.setErrorHandler(new MySaxErrorHandler(System.out));

     htmlResult = new HtmlTree("Available articles", new String[] { 
        "Article Description", "Order Number" });
  }

  /** Read an Xml catalog instance and insert product names among with their order numbers
   *  into a HTML DOM. Then serialize the resulting HTML tree to a stream.
   *
   * @param
   *   filename of the Xml source.
   * @param out
   *    The output stream for HTML serialization.
   * @throws IOException In case filename cannot be opened
   * @throws JDOMException Parsing error
   *
   */
  public void process(final String filename, final PrintStream out) throws JDOMException, IOException{
    final List<Element> items =
//          builder.build(filename).getRootElement().getChildren();
    builder.build(getClass().getClassLoader().getResource(filename)).getRootElement().getChildren();

    for (final Element item : items) { 
       htmlResult.appendItem(item.getText(),
                 item.getAttributeValue("orderNo")); 
    }
    htmlResult.serialize(out); 
  }
}

Create an instance holding a HTML DOM with a table header containing the strings Article Description and Order Number.

Iterate over all product nodes.

Insert the product's name an order number into the HTML DOM.

Serialize the completed HTML DOM tree to the output stream.