# LAB-5569: ODFDOM - Changing ODF documents using the new opensourced multi-tiered API

Expected Duration: 100 minutes
Contacts: Svante.Schubert Steffen Grund

Introduction | Exercise 0 | Exercise 1 | Exercise 2 | Exercise 3 | Exercise 4 | Summary
5. Introduction Steps to Follow Challenge Summary ## Exercise 2: Create Web Application Project (30 minutes) The goal of this exercise is to let you build a simple Netbeans Web Application Project by following step by step instructions. ### Steps to Follow Step 1: Creating a web based Project If NetBeans is not already running, start it. Select File Menu Select New Project Select Category 'Java Web' and Project 'Web Application' Press 'Next' Figure-01: New Project: Choose Project In the next dialog, do the following Set 'Project Name' to CustomerInvoice Set 'Project Location' to '<lab_root>' - note for JavaOne attendees: this is correct as it is - just keep the default. Press 'Next' Figure-02: New Web Application: Name and Location In the next dialog, do the following Set 'Server' to 'Glassfish V2' Set 'Java EE Version' to 'Java EE 5' Press 'Finish' Figure-03: New Web Application: Server and Settings After the project creation, the index.jsp file is automatically opened by NetBeans. This is how it looks like: Figure-04: New Web Application: Project View The following code creates a web application form to get data. Replace the complete content of the index.jsp file with it:
       <%--
           Document   : index
           Created on : June 04, 2009, 01:19:08 PM
           Author     : your name
       --%>

       <%@page contentType="text/html" pageEncoding="UTF-8"%>
       <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
           "http://www.w3.org/TR/html4/loose.dtd">

       <html>
           <head>
               <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
               <title>Customer Invoice</title>
           </head>
           <body>
               <h2>Create a Customer Invoice</h2>
               <form action="createInvoice.jsp" method="POST">
                   <table border="0">
                       <tbody>
                           <tr>
                               <td>Name, First Name</td>
                               <td><input type="text" name="myName" value=""/></td>
                           </tr>
                           <tr>
                               <td>Street</td>
                               <td><input type="text" name="myStreet" value=""/></td>
                           </tr>
                           <tr>
                               <td>Zip Code</td>
                               <td><input type="text" name="myZip" value=""/></td>
                           </tr>
                           <tr>
                               <td>City</td>
                               <td><input type="text" name="myCity" value=""/></td>
                           </tr>
                       </tbody>
                   </table>
                   <table border="0">
                       <thead>
                           <tr>
                               <th>Product ID</th>
                               <th>Product Name</th>
                               <th>Quantity</th>
                               <th>Single Price</th>
                           </tr>
                       </thead>
                       <tbody>
                           <tr>
                               <td><input type="text" name="myProdID" value=""/></td>
                               <td><input type="text" name="myProdName" value=""/></td>
                               <td><input type="text" name="myProdQuantity" value=""/></td>
                               <td><input type="text" name="myProdPrice" value=""/></td>
                           </tr>
                           <tr>
                               <td><input type="text" name="myProdID" value=""/></td>
                               <td><input type="text" name="myProdName" value=""/></td>
                               <td><input type="text" name="myProdQuantity" value=""/></td>
                               <td><input type="text" name="myProdPrice" value=""/></td>
                           </tr>
                           <tr>
                               <td><input type="text" name="myProdID" value=""/></td>
                               <td><input type="text" name="myProdName" value=""/></td>
                               <td><input type="text" name="myProdQuantity" value=""/></td>
                               <td><input type="text" name="myProdPrice" value=""/></td>
                           </tr>
                       </tbody>
                   </table>
                   <input type="submit" value="Send" />
               </form>
           </body>
       </html>
9. Do not forget to save the file. We are adding some jar libraries to our project - for future usage. Right-click the CustomerInvoice project and choose 'Properties'. In the 'Libraries' category, click 'Add JAR/Folder'. Add the following libraries to the project: <lab_root>/resources/odfdom/jars/xercesImpl.jar <lab_root>/resources/odfdom/jars/odfdom.jar For JavaOne attendees, the libraries are here: /export/home/lab5569/odfdom/resources/odfdom/jars/xercesImpl.jar /export/home/lab5569/odfdom/resources/odfdom/jars/odfdom.jar Click "OK" afterwards. Figure-05: Add required libraries to the project Press F6 to start the project. Verify the form in your browser. Note that it may take a while to start glassfish. Figure-06: Verify the form in the browser In Netbeans, right-click the 'Web Pages' folder and create a new JSP file named "createInvoice". Use the default settings in the 'New JSP File' dialog. Figure-07: createInvoice JSP File You have to add a couple of 'imports' to the "createInvoice.jsp" file:
       <%@page import="java.text.*,
                       java.util.*,
                       org.w3c.dom.*,
                       org.odftoolkit.odfdom.dom.OdfNamespace,
                       org.odftoolkit.odfdom.dom.type.OdfPositiveInteger,
                       org.odftoolkit.odfdom.doc.OdfDocument,
                       org.odftoolkit.odfdom.doc.OdfFileDom,
                       org.odftoolkit.odfdom.doc.element.text.*,
                       org.odftoolkit.odfdom.doc.element.office.*,
                       org.odftoolkit.odfdom.doc.element.table.*,
                       javax.xml.xpath.*" %>
11. Figure-08: Add required imports Now change the text of the h1 element in "createInvoice.jsp" to 'Customer invoice generated.' Store the data which has been passed from the web front end form in variables. The data can be accessed using the getParameter() method of the request object. The parameter of the getParameter method is the name of the text input form which has been assigned in index.jsp. You will need to define the following variables:
            <%
                // Read received post parameter
                String name = request.getParameter("myName");
                String street = request.getParameter("myStreet");
                String zip = request.getParameter("myZip");
                String city = request.getParameter("myCity");
                String[] productIds = request.getParameterValues("myProdID");
                String[] productNames = request.getParameterValues("myProdName");
                String[] productQuantities = request.getParameterValues("myProdQuantity");
                String[] productPrices = request.getParameterValues("myProdPrice");
            %>
12. This code has to be inserted after the h1 element and has to be enclosed by '<%' and '%>' to denote that this is Java code. Figure-09: Get data from request We will extend the Java part in the "createInvoice.jsp" file. Now we start using the ODFDOM API. First we want to open a template bill file and get access to the content dom.
                  ////////////////////////////////////////////////////////////////
                  // Start ODFDOM handling
                  ////////////////////////////////////////////////////////////////
                  // 1. Load invoice template
                  // if you are not attending JavaOneplease edit the <lab_root>
                  OdfDocument document = OdfDocument.loadDocument("/export/home/lab5569/odfdom/resources/invoice_template.odt");
                  // 1.1 Get the Content DOM of this Text Document
                  OdfFileDom content = document.getContentDom();
13. Please note that you have to adjust the file location passed to the loadDocument method. Have a look at the template bill document. This document contains 4 placeholder fields: _NAME_, _STREET_, _ZIPCITY_, and _CURRENTDATE_. We want to replace the address placeholder fields with the data from the web front end and the date placeholder field should be replaced with the current date. For this we first create 4 new OdfSpan objects:
                  ////////////////////////////////////////////////////////////////
                  // 2. Create OdfSpan elements for curstomer address and current date
                  // 2.1 Add Name
                  OdfSpan spanName = new OdfSpan(content);
                  spanName.setTextContent(name);
                  // 2.2 Street
                  OdfSpan spanStreet = new OdfSpan(content);
                  spanStreet.setTextContent(street);
                  // 2.2 Zip And City
                  OdfSpan spanCity = new OdfSpan(content);
                  spanCity.setTextContent(zip + " " + city);
                  // 2.3 Current Date
                  OdfSpan spanDate = new OdfSpan(content);
                  SimpleDateFormat dateFormater = new SimpleDateFormat();
                  String dateStr = dateFormater.getDateInstance(DateFormat.FULL).format(new Date());
                  spanDate.setTextContent(dateStr);
14. In order to find the placeholder fields in the template document, we use a xpath. The result is returned in a NodeList. Please note that the namespace context has to be set to the OdfNamespace. Alternatively, we could use
                  ////////////////////////////////////////////////////////////////
                  // 3. Find all placeholder elements
                  XPath xpath = XPathFactory.newInstance().newXPath();
                  xpath.setNamespaceContext(new OdfNamespace());
                  NodeList placeholder = (NodeList)xpath.evaluate("//text:placeholder", content, XPathConstants.NODESET);
15. Now we can iterate over the found placeholder fields and replace them with our new spans:
                  // 3.1 Apply Address Details and Date
                  for(int i=0; i<placeholder.getLength(); i++) {
                      OdfPlaceholder pl = (OdfPlaceholder)placeholder.item(i);
                      OdfParagraph parentParagraph = (OdfParagraph)pl.getParentNode();
                      String placeholderText = pl.getTextContent();
                      if(placeholderText.indexOf("_NAME_") > 0) {
                          parentParagraph.replaceChild(spanName, pl);
                      } else if(placeholderText.indexOf("_STREET_") > 0) {
                          parentParagraph.replaceChild(spanStreet, pl);
                      } else if(placeholderText.indexOf("_ZIPCITY_") > 0) {
                          parentParagraph.replaceChild(spanCity, pl);
                      } else if(placeholderText.indexOf("_CURRENTDATE_") > 0) {
                          parentParagraph.replaceChild(spanDate, pl);
                      }
                  }
16. We also want to fill the table in the template bill with the data from the web front end. So we first need to get access to the table in the template bill content dom:
                  ///////////////////////////////////////////////////////
                  // 4. Calculate Product Details and add them as Table Rows
                  // 4.1 Find the Billing Table
                  xpath = XPathFactory.newInstance().newXPath();
                  xpath.setNamespaceContext(new OdfNamespace());
                  OdfTable table = (OdfTable)xpath.evaluate("//table:table[1]", content, XPathConstants.NODE);
17. Since there's only one table in the template document, we can access this directly without using a NodeList. We append a new table row for each set of data obtained from the web frontend. Each of these rows will consist of 5 table cells containing a paragraph with the following data: the product ID the product name the product quantity the product single price and the total price The style for the table cells should be a copied from the respective cells in the first table row. The style for the paragraphs in the table cells should be "Table_20_Contents". This style is a document style of the bill template document. The last row of the table should state the overall price in the last table cell.
                  // 4.2 Find the first Row get the style info for table cell
                  OdfTableRow firstRow = (OdfTableRow)table.getElementsByTagName("table:table-row").item(0);
                  // 4.3 Calculate row sum to insert
                  float priceOverall = 0;
                  // 4.4 Insert rows for billing items
                  for(int i=0; i < productIds.length; i++) {
                      OdfTableRow billingItemRow = new OdfTableRow(content);
                      // 4.4.1 Add cells billing item details
                      for(int j=0; j < 5; j++) {
                          // Create new Cell
                          OdfTableCell cell = new OdfTableCell(content);
                          // Get and set correct style name for this cell
                          cell.setStyleName( ((OdfTableCell)firstRow.getCellAt(j)).getStyleName() );
                          // Create new Paragraph
                          OdfParagraph paragraph = new OdfParagraph(content);
                          // Set correct style name for this cell
                          paragraph.setStyleName( "Table_20_Contents" );
                          // Apply text regarding the current cell index
                          if(j == 0) {
                              paragraph.setTextContent(productIds[i]);
                          } else if(j == 1) {
                              paragraph.setTextContent(productNames[i]);
                          } else if(j == 2) {
                              paragraph.setTextContent(productQuantities[i]);
                          } else if(j == 3) {
                              paragraph.setTextContent(productPrices[i]);
                          } else if(j == 4) {
                              float priceTotal = Float.parseFloat(productPrices[i])*Float.parseFloat(productQuantities[i]);
                              priceOverall += priceTotal;
                              paragraph.setTextContent(String.valueOf(priceTotal));
                          }
                          cell.appendChild(paragraph);
                          billingItemRow.appendCell(cell);
                      }
                      table.appendRow(billingItemRow);
                  }
18. The last row of the table should state the overall price for the customer bill. It shall consist of two cells, the first of which has a column span of 4 and the second contains a new paragraph with the overall price. The paragraph style for this is "SumPrice".
                  // 4.5 Add last row showing overall price
                  OdfTableRow lastRow = new OdfTableRow(content);
                  OdfTableCell cell = new OdfTableCell(content);
                  cell.setNumberColumnsSpanned(OdfPositiveInteger.valueOf("4"));
                  lastRow.appendCell(cell);
                  cell = new OdfTableCell(content);
                  OdfParagraph paragraphFullPrice = new OdfParagraph(content);
                  paragraphFullPrice.setStyleName("SumPrice");
                  paragraphFullPrice.setTextContent(String.valueOf(priceOverall));
                  cell.appendChild(paragraphFullPrice);
                  lastRow.appendCell(cell);
                  table.appendRow(lastRow);
19. Finally, we save the file:
                  ///////////////////////////////////////////////////////
                  // 5. Save the resulting file
                  // Save to the project directory
                  // Edit the path if you are NOT a JavaOne attendee
                  document.save("/export/home/lab5569/NetBeansProjects/CustomerInvoice/invoice_final.odt");
20. The whole "ceateInvoice.jsp" file can be viewed here:
        <%--
            Document   : createInvoice
            Created on : Mar 31, 2009, 01:41:35 PM
            Author     : your name
        --%>

        <%@page contentType="text/html" pageEncoding="UTF-8"%>
        <%@page import="java.text.*,
                        java.util.*,
                        org.w3c.dom.*,
                        org.odftoolkit.odfdom.dom.OdfNamespace,
                        org.odftoolkit.odfdom.dom.type.OdfPositiveInteger,
                        org.odftoolkit.odfdom.doc.OdfDocument,
                        org.odftoolkit.odfdom.doc.OdfFileDom,
                        org.odftoolkit.odfdom.doc.element.text.*,
                        org.odftoolkit.odfdom.doc.element.office.*,
                        org.odftoolkit.odfdom.doc.element.table.*,
                        javax.xml.xpath.*"%>
        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
           "http://www.w3.org/TR/html4/loose.dtd">

        <html>
            <head>
                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
                <title>JSP Page</title>
            </head>
            <body>
                <h1>Customer invoice generated.</h1>
              <%
                  // Read received post parameter
                  String name = request.getParameter("myName");
                  String street = request.getParameter("myStreet");
                  String zip = request.getParameter("myZip");
                  String city = request.getParameter("myCity");
                  String[] productIds = request.getParameterValues("myProdID");
                  String[] productNames = request.getParameterValues("myProdName");
                  String[] productQuantities = request.getParameterValues("myProdQuantity");
                  String[] productPrices = request.getParameterValues("myProdPrice");

                  ////////////////////////////////////////////////////////////////
                  // Start ODFDOM handling
                  ////////////////////////////////////////////////////////////////
                  // 1. Load invoice template
                  // if you are not attending JavaOneplease edit the <lab_root>
                  OdfDocument document = OdfDocument.loadDocument("/export/home/lab5569/odfdom/resources/invoice_template.odt");
                  // 1.1 Get the Content DOM of this Text Document
                  OdfFileDom content = document.getContentDom();
                  ////////////////////////////////////////////////////////////////
                  // 2. Create OdfSpan elements for curstomer address and current date
                  // 2.1 Add Name
                  OdfSpan spanName = new OdfSpan(content);
                  spanName.setTextContent(name);
                  // 2.2 Street
                  OdfSpan spanStreet = new OdfSpan(content);
                  spanStreet.setTextContent(street);
                  // 2.2 Zip And City
                  OdfSpan spanCity = new OdfSpan(content);
                  spanCity.setTextContent(zip + " " + city);
                  // 2.3 Current Date
                  OdfSpan spanDate = new OdfSpan(content);
                  SimpleDateFormat dateFormater = new SimpleDateFormat();
                  String dateStr = dateFormater.getDateInstance(DateFormat.FULL).format(new Date());
                  spanDate.setTextContent(dateStr);
                  ////////////////////////////////////////////////////////////////
                  // 3. Find all placeholder elements
                  XPath xpath = XPathFactory.newInstance().newXPath();
                  xpath.setNamespaceContext(new OdfNamespace());
                  NodeList placeholder = (NodeList)xpath.evaluate("//text:placeholder", content, XPathConstants.NODESET);
                  // 3.1 Apply Address Details and Date
                  for(int i=0; i<placeholder.getLength(); i++) {
                      OdfPlaceholder pl = (OdfPlaceholder)placeholder.item(i);
                      OdfParagraph parentParagraph = (OdfParagraph)pl.getParentNode();
                      String placeholderText = pl.getTextContent();
                      if(placeholderText.indexOf("_NAME_") > 0) {
                          parentParagraph.replaceChild(spanName, pl);
                      } else if(placeholderText.indexOf("_STREET_") > 0) {
                          parentParagraph.replaceChild(spanStreet, pl);
                      } else if(placeholderText.indexOf("_ZIPCITY_") > 0) {
                          parentParagraph.replaceChild(spanCity, pl);
                      } else if(placeholderText.indexOf("_CURRENTDATE_") > 0) {
                          parentParagraph.replaceChild(spanDate, pl);
                      }
                  }
                  ///////////////////////////////////////////////////////
                  // 4. Calculate Product Details and add them as Table Rows
                  // 4.1 Find the Billing Table
                  xpath = XPathFactory.newInstance().newXPath();
                  xpath.setNamespaceContext(new OdfNamespace());
                  OdfTable table = (OdfTable)xpath.evaluate("//table:table[1]", content, XPathConstants.NODE);
                  // 4.2 Find the first Row get the style info for table cell
                  OdfTableRow firstRow = (OdfTableRow)table.getElementsByTagName("table:table-row").item(0);
                  // 4.3 Calculate row sum to insert
                  float priceOverall = 0;
                  // 4.4 Insert rows for billing items
                  for(int i=0; i < productIds.length; i++) {
                      OdfTableRow billingItemRow = new OdfTableRow(content);
                      // 4.4.1 Add cells billing item details
                      for(int j=0; j < 5; j++) {
                          // Create new Cell
                          OdfTableCell cell = new OdfTableCell(content);
                          // Get and set correct style name for this cell
                          cell.setStyleName( ((OdfTableCell)firstRow.getCellAt(j)).getStyleName() );
                          // Create new Paragraph
                          OdfParagraph paragraph = new OdfParagraph(content);
                          // Set correct style name for this cell
                          paragraph.setStyleName( "Table_20_Contents" );
                          // Apply text regarding the current cell index
                          if(j == 0) {
                              paragraph.setTextContent(productIds[i]);
                          } else if(j == 1) {
                              paragraph.setTextContent(productNames[i]);
                          } else if(j == 2) {
                              paragraph.setTextContent(productQuantities[i]);
                          } else if(j == 3) {
                              paragraph.setTextContent(productPrices[i]);
                          } else if(j == 4) {
                              float priceTotal = Float.parseFloat(productPrices[i])*Float.parseFloat(productQuantities[i]);
                              priceOverall += priceTotal;
                              paragraph.setTextContent(String.valueOf(priceTotal));
                          }
                          cell.appendChild(paragraph);
                          billingItemRow.appendCell(cell);
                      }
                      table.appendRow(billingItemRow);
                  }
                  // 4.5 Add last row showing overall price
                  OdfTableRow lastRow = new OdfTableRow(content);
                  OdfTableCell cell = new OdfTableCell(content);
                  cell.setNumberColumnsSpanned(OdfPositiveInteger.valueOf("4"));
                  lastRow.appendCell(cell);
                  cell = new OdfTableCell(content);
                  OdfParagraph paragraphFullPrice = new OdfParagraph(content);
                  paragraphFullPrice.setStyleName("SumPrice");
                  paragraphFullPrice.setTextContent(String.valueOf(priceOverall));
                  cell.appendChild(paragraphFullPrice);
                  lastRow.appendCell(cell);
                  table.appendRow(lastRow);
                  ///////////////////////////////////////////////////////
                  // 5. Save the resulting file
                  // Save to the project directory
                  // Edit the path if you are NOT a JavaOne attendee
                  document.save("/export/home/lab5569/NetBeansProjects/CustomerInvoice/invoice_final.odt");

              %>

            </body>
        </html>
You can now run the web application by pressing F6. If the application does not what you expect it to do, you can debug the code by pressing STRG+F5 When you fill out the web form and click on send, the invoice_final.odt file is generated. Verify this by starting OpenOffice.org and load the invoice. It could look like this: Figure-10: The created customer invoice ### Challenge The example above produces an exception when not all fields in the table are filled. Fix this. Hint: Debug the project to verify where the exception actually happens. ### Summary In this exercise, you learned how to create a new Netbeans Web Application Project that gathers some data that is written into a document using the ODF Toolkit. Back to top Next exercise