# JavaOne Hands-On Lab ## ODFDOM - Changing ODF documents using the new opensourced multi-tiered API ## LAB-5569 ### Copyright 2009 Sun Microsystems

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

Expected Duration: 100 minutes
Contacts: Svante Schubert Steffen Grund

Last Updated: May 13, 2009

The OpenDocument format (ODF) is an XML based, ISO standardized file format for electronic office documents such as spreadsheets, charts, presentations and word processing documents.

The new opensource library ODFDOM is a free Java 5 framework sponsored by Sun Microsystems Inc. to easily create and manipulate such ODF documents.

This workshop will give the opportunity to get in touch with this exciting new API and the possibility to talk with their architects.

Aside of the exercises, insights about further future goals of ODFDOM and its language independence are topic of this workshop.

Copyright

    Copyright  2008 Sun Microsystems, Inc. All rights reserved. Sun, Sun Microsystems,
    the Sun logo, Solaris, Java, the Java Coffee Cup logo, JavaOne, the JavaOne logo,
    and all Solaris-based and Java-based marks and logos are trademarks or registered
    trademarks of Sun Microsystems, Inc. in the United States and other countries.

Authors
Svante Schubert
Steffen Grund

### Prerequisites

This hands-on lab assumes you have some basic knowledge of, or programming experience in, the following technologies:

- Java™ language programming

### System Requirements

- Supported OSes: Solaris™M 8/9/10 Operating System (OS), OpenSolaris™ Operating System (OS), Linux, Windows, Mac OS X 10.4+
- Memory requirement: 512MB
- Disk space requirement: 300MB

### Software Needed For This Lab

Please install the following set of software. If you have any questions on installation, please feel free to send questions to the lab forum mentioned below.

- OpenOffice.org 3.0 (or more recent)
  - The OpenOffice.org SDK (for example 4)
- NetBeans IDE 6.5.1 (or more recent) Java Bundle (including Glassfish)
  - Netbeans Maven plugin (if NetBeans IDE prior 6.7)
  - OpenOffice plugin (for example 4)
- Java SE 5.0 (or more recent)
- ODFDOM.jar pre-0.7 (May 13th or more recent)

### Notations Used in This Documentation

- `<lab_root>` - directory into which lab zip file is unzipped
  - This document uses `<lab_root>` to denote the directory under which you have unzipped the lab zip file of this hands-on lab. The name of the lab zip file of this hands-on lab is 5569_odfdom.zip.
  - Once you unzipped the lab zip file under `<lab_root>`, it will create a subdirectory called odfdom. For example, under Solaris, if you have unzipped the lab zip file in the /home/myUser directory, it will create /home/myUser/odfdom directory.

- The lab machines are running Java Desktop System over Solaris 10
- In order to open a terminal window in Java Desktop System, right click any point in the background of the desktop, and select Open Terminal in the pop-up menu.
- The following source code editors are provided on the lab machines
  - vi (type vi in a terminal window)
  - emacs (type emacs in a terminal window)
  - jedit (type jedit in a terminal window)
  - NetBeans IDE (either click NetBeans IDE icon or type netbeans in a terminal window)

### Lab Exercises

- Exercise 0: Install and configure the lab environment
- Exercise 1: Create Web Application Project
- Exercise 2: Implement Web Front-End
- Exercise 3: Store Data in ODT File
- Exercise 4: Export Data From OpenOffice.org

### Additional Resources

- ODFDOM (from ODF Toolkit)
- LAB-5569 FAQ

### Where To Go For Help

- You can send technical questions via email to the authors of this Hands-on lab (and experts on the subject) or you can send your questions to the public users alias:
  - dev@odftoolkit.odftoolkit.org
- For general questions and feedback on JavaOne Hands-On Labs, please use the JavaOne2009_HOL_Forum

### Exercise 0: Install and Configure Lab Environment

For JavaOne attendees using the lab machines provided, the steps in Exercise 0 are done for you as part of the login process. So please proceed to Exercise 1.

Install OpenOffice.org

- Go to the OpenOffice.org download page and download the latest version. Follow the OpenOffice.org install instructions.
- For exercise 4, additionally download and install the OpenOffice.org SDK. Also follow the OpenOffice.org SDK install instructions.

Install and Configure NetBeans

- Go to the NetBeans download page and download the latest (at least 6.5.1) NetBeans version. Make sure that Glassfish V2.1 (or newer) support is included. Follow the NetBeans' install instructions.
- Start NetBeans: either click on the NetBeans icon or open a shell, go to the NetBeans installation directory and type `netbeans`.

- In NetBeans, make sure that the Maven plugin is installed and enabled. Go to Tools - Plugins - Installed. If the Maven plugin is in this list and marked as active, everything is ok. It should look like Figure-01.

  Figure-01: Correctly installed Maven plugin

- If this is not the case, you have to download and install the Maven plugin. Click on the "Available Plugins" tab. Enter `maven` in the search field. Mark the Maven plugin in the list and click on "Install". Follow the steps of the installation. At the end, click "Close" to close the window.

  Figure-02: Install the Maven plugin

- For exercise 4, an addional plugin for NetBeans is needed. It is available in the NetBeans Beta update center. Open "Tools - Plugins" and download and install the "OpenOffice.org API plugin for Netbeans".
  All steps work accordingly to the Maven plugin above.

  Refer to http://plugins.netbeans.org for more information on NetBeans plugins, and to http://wiki.services.openoffice.org for more information on the OpenOffice.org plugin for NetBeans.
  Notice that you may have to configurte the plugin with the installed OpenOffice.org and OpenOffice.org SDK. Confer http://wiki.services.openoffice.org

Download and Install the ODFDOM

Note: you do not have to download ODFDOM. The authors of this lab have included a compiled and working version of ODFDOM in the lab: odfdom.jar
The reason for this is that the API of ODFDOM is a work in progress and may not be compatible with the code presented in the exercises.
We do encourage the download of the latest version, though, to benefit from additions and bugfixes.

- Download the source code of the ODFDOM. This is available in a Mercurial repository. The following instructions explain everything on the NetBeans side. Confer ODFDOM build environment using NetBeans IDE

- In NetBeans, open "Versioning - Mercurial - Clone Other...". Enter the following Repository URL: `https://odftoolkit.org/hg/odfdom~developer`. Click "Next >"

  Figure-03: Download ODFDOM 1

- The following paths are already ok, so click "Next >" again.

  Figure-04: Download ODFDOM 2

- Enter "<lab_root>/odfdom" as Parent Directory. Note: "/home/javaone2009" is only a placeholder. Click "Finish". You may have to wait a little bit until Mercurial finishes.

  Figure-05: Download ODFDOM 3

- When cloning the project has finished, you may get the message box shown in Figure-06 - depending on your system. Click on "Yes" to ignore Mercurial's own files. If the settings here are already done on your machine, go directly to the next point.

  Figure-06: Download ODFDOM finished

- Finally, the maybe irritating message shown in Figure-07 will appear. Click on "Open Project..."
  This will open the project and set it as Main project.

  Figure-07: Open ODFDOM project

- If you run into a reference problem as displayed in Figure-08, that is a known bug in the NetBeans platform (and already fixed in NetBeans 6.7). This problem occurs when you have Java 6 installed, because the ODFDOM has Java 5 as official baseline. Fortunately there is a workaround available.

  Figure-08: Reference Problem

- Open the project's properties by right clicking on the project. Click on "Properties". Note that "Resolve Reference Problems..." is only helpful when there is also Java 5 available on your system.

  Figure-09: Open Project Properties

- Go to the "Libraries" category. Notice the missing Java platform.

  Figure-10: Open Project Properties

- Simply select one of the available Java platforms, make sure it is "JDK 1.5". Click "OK"

  Figure-11: Select the correct platform

- You will be asked to regenereate build-impl.xml. Click on "Regenerate"

  Figure-12: Regenerate build-impl.xml

Optional configuration

The following configuration steps are not mandatory to do the exercises, but they may help in working with ODFDOM. The authors of the lab strongly encourage to do the following to make working with the lab easier.
Add the javadoc of ODFDOM to NetBeans.

- Open "Tools - Library" Click on "New Library..."

  Figure-13: Library Manager

- Enter "ODFDOM" as name. Click "OK" next.

  Figure-14: Create new Library

- In the still open Library Manager dialog, the new library is added. Click on "Add JAR/Folder"and choose "<lab_root>/resources/odfdom/jars/odfdom.jar"

  Figure-15: Library Mabager

- Back again, click on "Javadoc" and then on "Add Zip/Folder". Choose the folder "<lab_root>/resources/odfdom/javadoc"

  Figure-16: Library Manager

- Click "OK" now to close the dialog. When you type a "." after a variable or class name from the odfdom.jar package, NetBeans will open the Javadoc in a popup window. Example:

  Figure-17: Javadoc Example

Now you are all set to proceed to the exercises!

## Exercise 1: Exploring the ODFDOM layers (20 minutes)

The goal of this exercise is to get an impression of the ODFDOM layers following step by step instructions.

### Steps to Follow

Step 1: Opening the Project

1. If NetBeans is not already running, start it.
   1. Select File Menu
   2. Select New Project
   3. Select Category 'Java' and Project 'Java Application'
   4. Press 'Next'

   Figure-01: New Project: Choose Project

2. In the next dialog, do the following
   1. Set 'Project Name' to OdfdomLayers
   2. Set 'Project Location' to '<lab_root>' - note for JavaOne attendees: this is correct as it is - just keep the default.
   3. Enter 'odfdom.example1.RunExample1' as main class.
   4. Press 'Finish'

   Figure-02: New Java Application: Name and Location

3. 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-03: Add required libraries to the project

4. After the project creation, the main class is automatically opened by NetBeans. When you enter the following code inside of the `main(...)` method, do not forget to set the `LAB_ROOT` variable. For JavaOne attendees, the line in the following code snippet is already correct. The second part of the exercise is commented at first since this project will be run twice.

           // For JavaOne the place for the project is this; change it for your machine
           private static final String LAB_ROOT = "/export/home/lab5569/odfdom/";
           private static final Logger LOG = Logger.getLogger(RunExample1.class.getName());

           /**
            *  Executes the first lab exercise.
            *
            * @param args the command line arguments
            */
           public static void main(String[] args) {
               try {
                   // 1st ODFDOM LAYER - PACKAGE / PHYSICAL LAYER:
                   // Creating an ODF package containing an image not shown in the text
                   firstExercicePart1();
               } catch (Exception ex) {
                   LOG.log(Level.SEVERE, "Something weired happened in the first part..", ex);
               }

       //        try {
       //            // 2nd ODFDOM LAYER - TYPED DOM / XML LAYER:
       //            // Creating an ODF Text document showing the image from the package
       //            firstExercicePart2();
       //        } catch (Exception ex) {
       //            LOG.log(Level.SEVERE, "Something weired happened in the second part..", ex);
       //        }
       //
       //        try {
       //            // 3rd ODFDOM LAYER - CONVENIENCE LAYER:
       //            // Quite similar to the previous DOM handling, as a DOC layer is always a child
       //            // of a DOM layer object therefore as well a DOM tree.
       //            //
       //            //
       //            // Two things are done for the user behind the scene:
       //            //
       //            // 1) The package handling to insert the image file
       //            //
       //            // 2) The default size is received from the image via AWT helper and
       //            // added to the attributes of the sourrounding draw:frame.
       //            firstExercicePart3();
       //        } catch (Exception ex) {
       //            LOG.log(Level.SEVERE, "Something weired happened in the third part..", ex);
       //        }
           }

5. Now implement the first exercise method. Notice that the output directory without path is the root of your current NetBeans project.
   The file is saved twice here with different extensions. With the .odt extension, you can open the file in OpenOffice.org, and with the .zip extension, the content can be viewed in NetBeans.

           /**
            * 1st ODFDOM LAYER - PACKAGE / PHYSICAL LAYER:
            * Creating an ODF package containing an image not shown in the text
            *
            * @throws java.lang.Exception
            */
           private static void firstExercicePart1() throws Exception {
               // ODF KNOW-HOW: One ODF Package is only a zipped container for arbitrary files of some document.
               OdfPackage pkg = OdfPackage.loadPackage(LAB_ROOT + "resources/"e1-input.odt");
               // Inserting an image into the 'Pictures' directory within the ODF package.
               pkg.insert(new FileInputStream(LAB_ROOT + "resources/e1-01_DukeToEmbed.png"),
                       "Pictures/e1-01_DukeToEmbed.png");

               // Save the package with the picture; no path used here, will be stored in project root
               pkg.save("output_1_seems-empty.odt");
               // zip can be opened with NetBeans
               pkg.save("output_1_seems-empty.zip");

               // ODF-KNOW-HOW: Only images referenced from the text will be viewed in ODF.
               //              Therefore if the document is being opened (for instance with OpenOffice.org)
               //              The document appears to be empty.
               LOG.info("SUCCESS: You were able to save an ODF Package containing an image, which is not shown!");
           }

6. Press CTRL+S to save your file. Then press CTRL+SHIFT+I to fix the missing imports from the libraries.

7. Your project should now be able to run. Press F6 for this. If it does not work, verify your code or ask a proctor. The result is shown in Figure-04. Activate the "Files" tab where the resulting document is displayed. NetBeans offers a simple way to browse into zip files: just click on the symbol before the file, then open the "Pictures" folder.
   You can open the .odt file in OpenOffice.org from here, too: click right on the document and select "Open With OpenOffice.org"

   Figure-04: Verify the correct document in Netbeans

8. We implement the second exercise method. Please uncomment the execution of `firstExercisePart2();` in the `main(...)` method and implement the method like this:

           /**
            * 2nd ODFDOM LAYER - TYPED DOM / XML LAYER
            * Creating an ODF Text document showing the image from the package
            *
            * @throws java.lang.Exception
            */
           private static void firstExercicePart2() throws Exception {
               // ODF-KNOW-HOW: An ODF Package contains one or more ODF Documents.
               //               The ODF XML elements part of an ODF Document.
               // Load the ODF Package as an ODF Document.
               OdfDocument odfDoc = OdfDocument.loadDocument("output_1_seems-empty.odt");

               // Get the ODF XML content as DOM tree representation.
               OdfFileDom odfContent = odfDoc.getContentDom();

               // ODF KNOW-HOW: The element name of a paragraph is <text:p>
               // Receiving the first paragraph  "//text:p[1]" (using XPath functionality from JDK).
               XPath xpath = getXPathForODF();
               OdfParagraphElement para = (OdfParagraphElement) xpath.evaluate("//text:p[1]", odfContent, XPathConstants.NODE);

               // ODF KNOW-HOW: An image consists always of a <draw:frame> parent and an embedded <draw:image> child.
               // Create the <draw:frame> ODF element.
               OdfFrameElement odfFrame = (OdfFrameElement) OdfElementFactory.createOdfElement(odfContent,
               OdfFrame.ELEMENT_NAME);

               // Add the created frame to the earlier found first paragraph.
               para.appendChild(odfFrame);

               // create the <draw:image> ODF element viewing the previous (Part1 )inserted image
               OdfImageElement odfImage = (OdfImageElement) OdfElementFactory.createOdfElement(odfContent,
               OdfImage.ELEMENT_NAME);

               // Add the created image to the earlier created frame.
               odfFrame.appendChild(odfImage);

               // setting the xlink:href attribute
               odfImage.setHref("Pictures/e1-01_DukeToEmbed.png");

               // Save the document; no path used here, will be stored in project root
               odfDoc.save("output_2_image-visible-small.odt");
               // zip can be opened with NetBeans
               odfDoc.save("output_2_image-visible-small.zip");
               LOG.info("SUCCESS: You were able to save an ODF Document again, now showing the earlier added image (small)!!");

           }

9. For the second exercise method to work, we need some helper. This is the helper function:

           private static XPath mXPath;

           private static XPath getXPathForODF() {
               if (mXPath == null) {
                   // W3C XPath initialization (i.e. JDK5 functionality)  -
                   // XPath is the path within the XML file
                   mXPath = XPathFactory.newInstance().newXPath();
                   mXPath.setNamespaceContext(new OdfNamespace());
               }
               return mXPath;
           }

10. Again press CTRL+S to save your file, followed by CTRL+SHIFT+I to fix the missing imports. This time you will be asked for correct classes. Figure-05 shows the correct imports.

    Figure-05: Correct imports for the used classes

11. Your project should again be able to run. Press F6 for this. If it does not work, verify your code or ask a proctor. The result is shown in Figure-06. This time we use OpenOffice.org to verify: Open OpenOffice.org and click on "File - Open". Browse to your NetBeans project and open the file "e1-output-2.odt":

    Figure-06: Image is visiable, but small

12. In the last layer, the convenient layer ODFDOM adds enhanced functionality and eases the developer's life. We implement the third exercise method. Please uncomment the execution of `firstExercisePart3();` in the `main(...)` method and implement the method like this:

          /**
             * 3rd ODFDOM LAYER - CONVENIENCE LAYER:
             * Quite similar to the previous DOM handling, as a DOC layer is always a child
             * of a DOM layer object therefore as well a DOM tree.
             *
             *
             * Two things are done for the user behind the scene:
             *
             * 1) The package handling to insert the image file
             *
             * 2) The default size is received from the image via AWT helper and
             * added to the attributes of the sourrounding draw:frame.
             *
             * @throws java.lang.Exception
             */
            private static void firstExercicePart3() throws Exception {

                OdfTextDocument odfText = OdfTextDocument.createTextDocument();
                // Get the ODF XML content as DOM tree representation.
                OdfFileDom odfContent = odfText.getContentDom();

                // ODF KNOW-HOW: The element name of a paragraph is <text:p>
                // Receiving the first paragraph  "//text:p[1]" (using XPath functionality from JDK).
                XPath xpath = getXPathForODF();
                OdfParagraph para = (OdfParagraph) xpath.evaluate("//text:p[1]", odfContent, XPathConstants.NODE);

                // ODF KNOW-HOW: An image consists always of a <draw:frame> parent and an embedded <draw:image> child.
                // Create the <draw:frame> ODF element.
                OdfFrame odfFrame = (OdfFrame) OdfElementFactory.createOdfElement(odfContent, OdfFrame.ELEMENT_NAME);

                // Add the created frame to the earlier found first paragraph.
                para.appendChild(odfFrame);

                // create the <draw:image> ODF element to view the similar image file
                OdfImage odfImage = (OdfImage) OdfElementFactory.createOdfElement(odfContent, OdfImage.ELEMENT_NAME);

                // Add the created image to the earlier created frame.
                odfFrame.appendChild(odfImage);

                // NOTE: Loading the image (relativ to <LABROOT>/solution/solution1
                // Adding the image to the package, getting the default
                // size via AWT helper and setting the size at the draw:frame parent.
                odfImage.insertImage(new URI("../../odfdom/resources/e1-01_DukeToEmbed.png"));

                // Save the document; no path used here, will be stored in project root
                odfText.save("output_3_image-visible-big.odt");
                // zip can be opened with NetBeans
                odfText.save("output_3_image-visible-big.zip");
                LOG.info("SUCCESS: You were able to add an image into an ODF Text Document using the convenient API!!");

            }

13. Again press CTRL+S to save your file, followed by CTRL+SHIFT+I to fix the missing imports.

14. Your project should again be able to run. Press F6 for this. If it does not work, verify your code or ask a proctor. The result is shown in Figure-07. This time we use OpenOffice.org to verify: Open OpenOffice.org and click on "File - Open". Browse to your NetBeans project and open the file "e1-output-3.odt":

    Figure-07: Image is visable with it's default size

15. For troubleshooting, the complete example should look like this (with first example method enabled):

        /*
         * To change this template, choose Tools | Templates
         * and open the template in the editor.
         */

        package odfdom.example1;

        import java.io.FileInputStream;
        import java.net.URI;
        import java.util.logging.Level;
        import java.util.logging.Logger;
        import javax.xml.xpath.XPath;
        import javax.xml.xpath.XPathConstants;
        import javax.xml.xpath.XPathFactory;
        import org.odftoolkit.odfdom.doc.OdfDocument;
        import org.odftoolkit.odfdom.doc.OdfFileDom;
        import org.odftoolkit.odfdom.doc.OdfTextDocument;
        import org.odftoolkit.odfdom.doc.element.OdfElementFactory;
        import org.odftoolkit.odfdom.doc.element.draw.OdfFrame;
        import org.odftoolkit.odfdom.doc.element.draw.OdfImage;
        import org.odftoolkit.odfdom.doc.element.text.OdfParagraph;
        import org.odftoolkit.odfdom.dom.OdfNamespace;
        import org.odftoolkit.odfdom.dom.element.draw.OdfFrameElement;
        import org.odftoolkit.odfdom.dom.element.draw.OdfImageElement;
        import org.odftoolkit.odfdom.dom.element.text.OdfParagraphElement;
        import org.odftoolkit.odfdom.pkg.OdfPackage;

        /**
         *
         * @author sg128468
         */
        public class RunExample1 {

            // For JavaOne the place for the project is this; change it for your machine
            private static final String LAB_ROOT = "/export/home/lab5569/odfdom/";
            private static final Logger LOG = Logger.getLogger(RunExample1.class.getName());

            /**
             * @param args the command line arguments
             */
            public static void main(String[] args) {
               try {
                    // 1st ODFDOM LAYER - PACKAGE / PHYSICAL LAYER:
                    // Creating an ODF package containing an image not shown in the text
                    firstExercicePart1();
                } catch (Exception ex) {
                    LOG.log(Level.SEVERE, "Something weired happened in the first part..", ex);
                }

                try {
                    // 2nd ODFDOM LAYER - TYPED DOM / XML LAYER:
                    // Creating an ODF Text document showing the image from the package
                    firstExercicePart2();
                } catch (Exception ex) {
                    LOG.log(Level.SEVERE, "Something weired happened in the second part..", ex);
                }

                try {
                    // 3rd ODFDOM LAYER - CONVENIENCE LAYER:
                    // Quite similar to the previous DOM handling, as a DOC layer is always a child
                    // of a DOM layer object therefore as well a DOM tree.
                    //
                    //
                    // Two things are done for the user behind the scene:
                    //
                    // 1) The package handling to insert the image file
                    //
                    // 2) The default size is received from the image via AWT helper and
                    // added to the attributes of the sourrounding draw:frame.
                    firstExercicePart3();
                } catch (Exception ex) {
                    LOG.log(Level.SEVERE, "Something weired happened in the third part..", ex);
                }
            }

            /**
             * 1st ODFDOM LAYER - PACKAGE / PHYSICAL LAYER:
             * Creating an ODF package containing an image not shown in the text
             *
             * @throws java.lang.Exception
             */
            private static void firstExercicePart1() throws Exception {
                // ODF KNOW-HOW: One ODF Package is only a zipped container for arbitrary files of some document.
                OdfPackage pkg = OdfPackage.loadPackage(LAB_ROOT + "resources/e1-input.odt");
                // Inserting an image into the 'Pictures' directory within the ODF package.
                pkg.insert(new FileInputStream(LAB_ROOT + "resources/e1-01_DukeToEmbed.png"),
                        "Pictures/e1-01_DukeToEmbed.png");

                // Save the package with the picture; no path used here, will be stored in project root
                pkg.save("output_1_seems-empty.odt");
                // zip can be opened with NetBeans
                pkg.save("output_1_seems-empty.zip");

                // ODF-KNOW-HOW: Only images referenced from the text will be viewed in ODF.
                //              Therefore if the document is being opened (for instance with OpenOffice.org)
                //              The document appears to be empty.
                LOG.info("SUCCESS: You were able to save an ODF Package containing an image, which is not shown!");
            }

            /**
             * 2nd ODFDOM LAYER - TYPED DOM / XML LAYER
             * Creating an ODF Text document showing the image from the package
             *
             * @throws java.lang.Exception
             */
            private static void firstExercicePart2() throws Exception {
                // ODF-KNOW-HOW: An ODF Package contains one or more ODF Documents.
                //               The ODF XML elements part of an ODF Document.
                // Load the ODF Package as an ODF Document.
                OdfDocument odfDoc = OdfDocument.loadDocument("output_1_seems-empty.odt");

                // Get the ODF XML content as DOM tree representation.
                OdfFileDom odfContent = odfDoc.getContentDom();

                // ODF KNOW-HOW: The element name of a paragraph is <text:p>
                // Receiving the first paragraph  "//text:p[1]" (using XPath functionality from JDK).
                XPath xpath = getXPathForODF();
                OdfParagraphElement para = (OdfParagraphElement) xpath.evaluate("//text:p[1]", odfContent, XPathConstants.NODE);

                // ODF KNOW-HOW: An image consists always of a <draw:frame> parent and an embedded <draw:image> child.
                // Create the <draw:frame> ODF element.
                OdfFrameElement odfFrame = (OdfFrameElement) OdfElementFactory.createOdfElement(odfContent,
                OdfFrame.ELEMENT_NAME);

                // Add the created frame to the earlier found first paragraph.
                para.appendChild(odfFrame);

                // create the <draw:image> ODF element viewing the previous (Part1 )inserted image
                OdfImageElement odfImage = (OdfImageElement) OdfElementFactory.createOdfElement(odfContent,
                OdfImage.ELEMENT_NAME);

                // Add the created image to the earlier created frame.
                odfFrame.appendChild(odfImage);

                // setting the xlink:href attribute
                odfImage.setHref("Pictures/e1-01_DukeToEmbed.png");

                // Save the document; no path used here, will be stored in project root
                odfDoc.save("output_2_image-visible-small.odt");
                // zip can be opened with NetBeans
                odfDoc.save("output_2_image-visible-small.zip");
                LOG.info("SUCCESS: You were able to save an ODF Document again, now showing the earlier added image (small)!!");

            }

         /**
             * 3rd ODFDOM LAYER - CONVENIENCE LAYER:
             * Quite similar to the previous DOM handling, as a DOC layer is always a child
             * of a DOM layer object therefore as well a DOM tree.
             *
             *
             * Two things are done for the user behind the scene:
             *
             * 1) The package handling to insert the image file
             *
             * 2) The default size is received from the image via AWT helper and
             * added to the attributes of the sourrounding draw:frame.
             *
             * @throws java.lang.Exception
             */
            private static void firstExercicePart3() throws Exception {

                OdfTextDocument odfText = OdfTextDocument.createTextDocument();
                // Get the ODF XML content as DOM tree representation.
                OdfFileDom odfContent = odfText.getContentDom();
                // ODF KNOW-HOW: The element name of a paragraph is <text:p>
                // Receiving the first paragraph  "//text:p[1]" (using XPath functionality from JDK).
                XPath xpath = getXPathForODF();
                OdfParagraph para = (OdfParagraph) xpath.evaluate("//text:p[1]", odfContent, XPathConstants.NODE);

                // ODF KNOW-HOW: An image consists always of a <draw:frame> parent and an embedded <draw:image> child.
                // Create the <draw:frame> ODF element.
                OdfFrame odfFrame = (OdfFrame) OdfElementFactory.createOdfElement(odfContent, OdfFrame.ELEMENT_NAME);

                // Add the created frame to the earlier found first paragraph.
                para.appendChild(odfFrame);

                // create the <draw:image> ODF element to view the similar image file
                OdfImage odfImage = (OdfImage) OdfElementFactory.createOdfElement(odfContent, OdfImage.ELEMENT_NAME);

                // Add the created image to the earlier created frame.
                odfFrame.appendChild(odfImage);

                // NOTE: Loading the image (relativ to <LABROOT>/solution/solution1
                // Adding the image to the package, getting the default
                // size via AWT helper and setting the size at the draw:frame parent.
                odfImage.insertImage(new URI("../../odfdom/resources/e1-01_DukeToEmbed.png"));

                // Save the document; no path used here, will be stored in project root
                odfText.save("output_3_image-visible-big.odt");
                // zip can be opened with NetBeans
                odfText.save("output_3_image-visible-big.zip");
                LOG.info("SUCCESS: You were able to add an image into an ODF Text Document using the convenient API!!");

            }

            private static XPath mXPath;

            private static XPath getXPathForODF() {
                if (mXPath == null) {
                    // W3C XPath initialization (i.e. JDK5 functionality)  -
                    // XPath is the path within the XML file
                    mXPath = XPathFactory.newInstance().newXPath();
                    mXPath.setNamespaceContext(new OdfNamespace());
                }
                return mXPath;
            }

        }

### Summary

In this exercise, you learned what the different layers of ODFDOM offer in regard of inserting an image file.

## 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

1. If NetBeans is not already running, start it.
   1. Select File Menu
   2. Select New Project
   3. Select Category 'Java Web' and Project 'Web Application'
   4. Press 'Next'

   Figure-01: New Project: Choose Project

2. In the next dialog, do the following
   1. Set 'Project Name' to CustomerInvoice
   2. Set 'Project Location' to '<lab_root>' - note for JavaOne attendees: this is correct as it is - just keep the default.
   3. Press 'Next'

   Figure-02: New Web Application: Name and Location

3. In the next dialog, do the following
   1. Set 'Server' to 'Glassfish V2'
   2. Set 'Java EE Version' to 'Java EE 5'
   3. Press 'Finish'

   Figure-03: New Web Application: Server and Settings

4. 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

5. 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>

   Do not forget to save the file.

6. 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

7. 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

8. 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

9. 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.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.*" %>

   Figure-08: Add required imports

10. Now change the text of the h1 element in "createInvoice.jsp" to 'Customer invoice generated.'

11. 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");
            %>

    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

12. 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();

    Please note that you have to adjust the file location passed to the loadDocument method.

13. 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);

    Since there's only one table in the template document, we can access this directly without using a NodeList.

17. 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(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.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(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>

21. 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.

## Exercise 3: Build a Web Report from Spreadsheet Data (20 minutes)

The goal of this exercise is to create a web page from the data available in a spreadsheet document. For this, the ODFDOM API will be used.

### Steps to Follow

1. Create a new Netbeans Web Application Project named 'Report' like in exercise 2.

   Figure-01: Create a web based project.

2. Add the odfdom.jar and xercesImpl.jar libraries to the project like in exercise 2.

   - <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

3. Add the imports to the 'index.jsp' file like in exercise 2.

        <%@page import="java.text.*,
                        java.util.*,
                        org.w3c.dom.*,
                        org.odftoolkit.odfdom.dom.OdfNamespace,
                        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.*" %>

4. Change the text of the h2 element to 'Address Data Report' and insert an empty table behind it.

5. Inside the table tag, enter the java code to open the report.ods spreadsheet document in the <lab_root>/resources folder. Get the content file dom from the document. Note: Make sure to enclose the java code using '<%' and '%>'.

                    ////////////////////////////////////////////////////////////////
                    // Start ODFDOM handling
                    // 1 Load source document
                    OdfDocument document = OdfDocument.loadDocument("/export/home/lab5569/odfdom/resources/report_data.ods");
                    // 2 Get the Content DOM of this source document
                    OdfFileDom content = document.getContentDom();

6. Use a xpath expression to receive a NodeList of all table rows:

                    // 3 Find the data rows
                    XPath xpath = XPathFactory.newInstance().newXPath();
                    xpath.setNamespaceContext(new OdfNamespace());
                    NodeList rows = (NodeList)xpath.evaluate("//table:table[1]//table:table-row", content, XPathConstants.NODESET);

7. Iterator over the table rows and cells. If a new row starts, use 'out.print("<tr>")' to start a new table row. If a row ends, use 'out.print("</tr>")' to close the respective table row tag. If a new cell starts, use 'out.print("<th>")' or 'out.print("<td>")'to start a new table (header) cell. If a cell ends, close the respective cell tag. Inside the cell you have to print the cell contents:

                    // 4 Print data to report table
                    out.print("<table border=\"1\">");
                    for (int i = 0; i < rows.getLength(); ++i){
                       OdfTableRow row = (OdfTableRow)rows.item(i);
                       out.print("<tr>");
                       NodeList tableRowChilds = row.getChildNodes();
                       for (int j = 0; j < tableRowChilds.getLength(); ++j){
                           OdfTableCell cell = (OdfTableCell)tableRowChilds.item(j);
                           out.print(i == 0 ? "<th>" : "<td>");
                           String textContent = cell.getTextContent();
                           out.print(textContent);
                           out.print(i == 0 ? "</th>" : "</td>");
                       }
                       out.print("</tr>");
                    }
                    out.print("</table>");

8. 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. The output should look like this:

   Figure-03: The created report

### Summary

In this exercise, you learned how to use the ODFDOM API to load, manipulate and store documents.

## Exercise 4: Export Data from OpenOffice.org as .csv (30 minutes)

In this exercise, an export function (with some additions, it could develop into a filter) for OpenOffice.org is written, using the odf toolkit. This exercise shows how data from a spreadsheet can be manipulated with Java inside of the OpenOffice.org process.

For this, an Extension for OpenOffice.org is created. Extensions can bring additional functionalities to OpenOffice.org that are missing from the original program. OpenOffice.org can be customized with Extensions.

### Steps to Follow

1. Configure the "OpenOffice.org API plugin for Netbeans". See http://wiki.services.openoffice.org

2. Create a new OpenOffice.org Add-On project

   Click "Next".

   Figure-01: Create an Add-On project.

3. Enter the following names in the correct places. Confer to the screenshot, if you are in doubt. Project Name should be "ExportExample". The Main Class Name will change accordingly. Enter "odfdom.example4" as Java package.

   Note: Do not forget to change <lab_root> with something meaningful.

   Finally make sure to have "Create Toolbar" enabled and "Create Menu" disabled. This will produce an extension to OpenOffice.org that brings its own toolbar with it.

   Click "Next" again afterwards.

   Figure-02: Define Project Names

4. Enter "Export" in the spaces shown in the screenshot.

   Click "Next" again afterwards.

   Figure-03: Define Function Name

5. Simply enable the "Spreadsheet" context here.

   Click "Finish".

   Figure-04: Enable Spreadsheet

6. Import the same jar files as described in exercise 2, step 6.

7. You do not need to add any new files, since everything is generated by NetBeans and the plugin. Open "ExportExample.java". Look for the `dispatch(...)` method. When someone clicks on the button that was defined during project creation, this method will be executed. Add the following lines:

       try {
           exportFile();
       } catch (Exception ex) {
           Logger.getLogger(ExportExample.class.getName()).log(Level.SEVERE, null, ex);
       }

   The complete `dispatch(...)` method should look like this:

       // com.sun.star.frame.XDispatch:
       public void dispatch(com.sun.star.util.URL aURL,
               com.sun.star.beans.PropertyValue[] aArguments) {
           if (aURL.Protocol.compareTo("odfdom.example4.ExportExample:") == 0) {
               if (aURL.Path.compareTo("Export") == 0) {
                   try {
                       exportFile();
                   } catch (Exception ex) {
                       Logger.getLogger(ExportExample.class.getName()).log(Level.SEVERE, null, ex);
                   }
               }
           }

8. Now implement the `exportFile()` function. Do not forget to edit the <lab_root> path. In this function lies the challenge of this exercise, but more to that later.

   First store the current file as a temporary document.

           /**
            * Export the csv file
            */
           private void exportFile() throws Exception {
               ////////////////////////////////////////////////////////////////
               // OpenOffice.org API handling
               // 0 save file first
               XModel xModel = m_xFrame.getController().getModel();
               XStorable xStorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, xModel);
               File file = File.createTempFile("temp", ".ods");
               String url = getUrl(file);
               xStorable.storeToURL(url, new PropertyValue[0]);

   Then create a file to write the content to.

               ////////////////////////////////////////////////////////////////
               // 1 Create a file for output
               String exportFileName = "output.csv";
               FileWriter exportFileWriter = new FileWriter(exportFileName);

               // CHALLENGE!

               // close the writer
               exportFileWriter.close();
           }

9. Implement the `getUrl()` helper function.

           /**
            * Get OpenOffice.org URL from a system file
            * @param file a file on the file system
            * @return the path of the file as OpenOffice.org URL
            */
           private String getUrl(File file) {
               String path = file.getPath().replace('\\', '/');
               if (!path.startsWith("/")) {
                   path = "/".concat(path);
               }
               path = "file://".concat(path);
               return path;
           }

10. This is the complete "ExportExample.java" file.

        package odfdom.example4;

        import com.sun.star.beans.PropertyValue;
        import com.sun.star.frame.XModel;
        import com.sun.star.frame.XStorable;
        import com.sun.star.uno.UnoRuntime;
        import com.sun.star.uno.XComponentContext;
        import com.sun.star.lib.uno.helper.Factory;
        import com.sun.star.lang.XSingleComponentFactory;
        import com.sun.star.registry.XRegistryKey;
        import com.sun.star.lib.uno.helper.WeakBase;
        import java.io.File;
        import java.io.FileWriter;
        import java.io.IOException;
        import java.util.logging.Level;
        import java.util.logging.Logger;
        import javax.xml.xpath.XPath;
        import javax.xml.xpath.XPathConstants;
        import javax.xml.xpath.XPathFactory;
        import org.odftoolkit.odfdom.doc.OdfDocument;
        import org.odftoolkit.odfdom.doc.OdfFileDom;
        import org.odftoolkit.odfdom.doc.element.table.OdfTableCell;
        import org.odftoolkit.odfdom.doc.element.table.OdfTableRow;
        import org.odftoolkit.odfdom.dom.OdfNamespace;
        import org.w3c.dom.NodeList;

        public final class ExportExample extends WeakBase
                implements com.sun.star.lang.XServiceInfo,
                com.sun.star.frame.XDispatchProvider,
                com.sun.star.lang.XInitialization,
                com.sun.star.frame.XDispatch {

            private final XComponentContext m_xContext;
            private com.sun.star.frame.XFrame m_xFrame;
            private static final String m_implementationName = ExportExample.class.getName();
            private static final String[] m_serviceNames = {
                "com.sun.star.frame.ProtocolHandler"};

            public ExportExample(XComponentContext context) {
                m_xContext = context;
            }

            ;

            public static XSingleComponentFactory __getComponentFactory(String sImplementationName) {
                XSingleComponentFactory xFactory = null;

                if (sImplementationName.equals(m_implementationName)) {
                    xFactory = Factory.createComponentFactory(ExportExample.class, m_serviceNames);
                }
                return xFactory;
            }

            public static boolean __writeRegistryServiceInfo(XRegistryKey xRegistryKey) {
                return Factory.writeRegistryServiceInfo(m_implementationName,
                        m_serviceNames,
                        xRegistryKey);
            }

            // com.sun.star.lang.XServiceInfo:
            public String getImplementationName() {
                return m_implementationName;
            }

            public boolean supportsService(String sService) {
                int len = m_serviceNames.length;

                for (int i = 0; i < len; i++) {
                    if (sService.equals(m_serviceNames[i])) {
                        return true;
                    }
                }
                return false;
            }

            public String[] getSupportedServiceNames() {
                return m_serviceNames;
            }

            // com.sun.star.frame.XDispatchProvider:
            public com.sun.star.frame.XDispatch queryDispatch(com.sun.star.util.URL aURL,
                    String sTargetFrameName,
                    int iSearchFlags) {
                if (aURL.Protocol.compareTo("odfdom.example4.ExportExample:") == 0) {
                    if (aURL.Path.compareTo("Export") == 0) {
                        return this;
                    }
                }
                return null;
            }

            // com.sun.star.frame.XDispatchProvider:
            public com.sun.star.frame.XDispatch[] queryDispatches(
                    com.sun.star.frame.DispatchDescriptor[] seqDescriptors) {
                int nCount = seqDescriptors.length;
                com.sun.star.frame.XDispatch[] seqDispatcher =
                        new com.sun.star.frame.XDispatch[seqDescriptors.length];

                for (int i = 0; i < nCount; ++i) {
                    seqDispatcher[i] = queryDispatch(seqDescriptors[i].FeatureURL,
                            seqDescriptors[i].FrameName,
                            seqDescriptors[i].SearchFlags);
                }
                return seqDispatcher;
            }

            // com.sun.star.lang.XInitialization:
            public void initialize(Object[] object)
                    throws com.sun.star.uno.Exception {
                if (object.length > 0) {
                    m_xFrame = (com.sun.star.frame.XFrame) UnoRuntime.queryInterface(
                            com.sun.star.frame.XFrame.class, object[0]);
                }
            }

            // com.sun.star.frame.XDispatch:
            public void dispatch(com.sun.star.util.URL aURL,
                    com.sun.star.beans.PropertyValue[] aArguments) {
                if (aURL.Protocol.compareTo("odfdom.example4.ExportExample:") == 0) {
                    if (aURL.Path.compareTo("Export") == 0) {
                        try {
                            exportFile();
                        } catch (Exception ex) {
                            Logger.getLogger(ExportExample.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
            }

            public void addStatusListener(com.sun.star.frame.XStatusListener xControl,
                    com.sun.star.util.URL aURL) {
                // add your own code here
            }

            public void removeStatusListener(com.sun.star.frame.XStatusListener xControl,
                    com.sun.star.util.URL aURL) {
                // add your own code here
            }

            private void exportFile() throws Exception {
                ////////////////////////////////////////////////////////////////
                // OpenOffice.org API handling
                // 0 save file first
                XModel xModel = m_xFrame.getController().getModel();
                XStorable xStorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, xModel);
                File file = File.createTempFile("temp", ".ods");
                String url = getUrl(file);
                xStorable.storeToURL(url, new PropertyValue[0]);
                // 1 Craete a file for output
                String exportFileName = "output.csv";
                FileWriter exportFileWriter = new FileWriter(exportFileName);

                exportFileWriter.close();
            }

            private String getUrl(File file) {
                String path = file.getPath().replace('\\', '/');
                if (!path.startsWith("/")) {
                    path = "/".concat(path);
                }
                path = "file://".concat(path);
                return path;
            }
        }

11. Select the "Projects" tab and then click right on the project node and select "Debug in Target OpenOffice.org" to verify that the Add-On works and to debug it.

    Figure-05: Debug the project in OpenOffice.org

12. To trigger the functionality, open Calc in OpenOffice.org and click on "Export" to execute the new function.
    For the actual functionality, take a look at the challenge.

    Figure-06: Result

### Challenge

The example until now is meaningless. Use the created variable `exportFileWriter` to write data to the csv file, and get the data from the stored ods document.

Hint: reuse code from example 3 to collect the data from the spreadsheet (ods file).

### Summary

In this exercise, the toolkit was used as a part of the OpenOffice.org functionality to add new functions to the OpenOffice.org feature set.

Congratulations! You have successfully completed LAB-5569: ODFDOM - Changing ODF documents using the new opensourced multi-tiered API.

Where to send questions or feedbacks on this lab and public discussion forums:

- You can send technical questions via email to the authors of this Hands-on lab (and experts on the subject) or you can post the questions to the web.

  Please post questions that are relevant only to this hands-on lab.

  - 5569_odfdom forum at hands-on-labs.dev.java.net

- You can send your other questions to the public users alias:

  - dev@odftoolkit.odftoolkit.org