Source code for a simple Java report application

This simple Java source file provides an example of a report program written in Java, using the Genero Report Writer for Java APIs.

This Java program completes the basic needs for any Genero Report Writer program:
  • Creates the data model.
  • Loads the data into memory.
  • Configures the output of the report.
  • Streams the data to the report engine.
The report is a simple example of a Java report program. In a real world situation, the data would be read from another source, and an iterator would read the data into memory and into the data stream one record at a time.
/*
 * FOURJS_START_COPYRIGHT(U,2003)
 * Property of Four Js*
 * (c) Copyright Four Js 2003, 2018. All Rights Reserved.
 * * Trademark of Four Js Development Tools Europe Ltd
 *   in the United States and elsewhere
 * 
 * Four Js and its suppliers do not warrant or guarantee that these samples are
 * accurate and suitable for your purposes.
 * Their inclusion is purely for information purposes only.
 * FOURJS_END_COPYRIGHT
 */

import com.fourjs.report.runtime.*;
import java.awt.Desktop;
import java.io.File;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.IOException;
import javax.xml.bind.JAXBException;
import org.xml.sax.SAXException;
import java.util.Vector;
import java.util.Date;
 
//The XmlRootElement annotation causes an global element declaration to be produced in the schema.
@XmlRootElement
public class Sales {
//The XmlElement annotation maps a property to an XML element.
//The Genero Engine does not support optional variables so setting "required=true" is mandatory.
//If a variable needs to be optional then use "nillable=true" and set the variable to null when
//there is no value. If a primitive type needs to be optional then use the corresponding 
//object wrapper instead (e.g java.lang.Integer for "int").
   @XmlElement(required = true, nillable = true)
   public String shopName;
   @XmlElement(required = true)
   public int zipCode;
   @XmlElement(required = true, nillable = true)
   public Date day;


//This and any other public collection will be serialized in the order they are declared descending 
//recursively into the classes contained in the collections.
    public Vector<SalesItem> items=new Vector<SalesItem>();

    public Sales(String shopName, int zipCode, Date day)
    {
        this.shopName=shopName;
        this.zipCode=zipCode;
        this.day=day;
        items.add(new SalesItem("Table lamp",SalesItem.Category.Furniture,23.00,null));
        items.add(new SalesItem("Table lamp",SalesItem.Category.Furniture,267.00, items.lastElement()));
        items.add(new SalesItem("Office chair",SalesItem.Category.Furniture,155.00, items.lastElement()));
        items.add(new SalesItem("Grandfather clock",SalesItem.Category.Furniture,329.00, items.lastElement()));
        items.add(new SalesItem("Scissors",SalesItem.Category.Supplies,19.00, items.lastElement()));
        items.add(new SalesItem("Measuring tape",SalesItem.Category.Supplies,23.00, items.lastElement()));
        items.add(new SalesItem("Sun glasses",SalesItem.Category.Travelling,15.95, items.lastElement()));
        items.add(new SalesItem("Pen knife",SalesItem.Category.Travelling,6.25, items.lastElement()));
        items.add(new SalesItem("Ornate angel",SalesItem.Category.Art,1.95, items.lastElement()));
    }

/** Default Constructor that is required for JAXB deserialization. 
    In this program this is never used.
*/
    public Sales() { assert false; }

/**
    Runs the report using the design file specified in args[0] or "SalesList.4rp" otherwise.
    The program creates the file "SalesList.pdf" and opens it using Desktop.open() which will typically
    invoke the Acrobat Reader.
*/
    public static void main(String[] args) throws JAXBException, IOException, SAXException
    {
        String designFile;
        String outputFilename = "SalesList.pdf";

        if (args.length == 0) {
            designFile = "../src/SalesList.4rp";
        } else {
            designFile = args[0];
        }

        FormatHandler handler = new FormatWriter(outputFilename);
        PDFRenderer renderer = new PDFRenderer(handler);
        FourRpLayouter report = new FourRpLayouter(designFile, renderer);

        report.setDebugLevel(9);
        Sales data = new Sales("Columbus Arts", 75038, new Date());

        report.runFromJAXBObject(data);

        // open the file
        File result = new File(outputFilename);
        Desktop desktop = Desktop.getDesktop();
        desktop.open(result);

  }

}

class SalesItem
{
    public enum Category {Furniture, Art, Supplies, Travelling };

    @XmlElement(required = true, nillable = true)
    public String articleName;
    @XmlElement(required = true, nillable = true)
    public Category category;
    @XmlElement(required = true)
    public double price;
    @XmlElement(required = true)
    public double runningTotal;

//The previous item is passed to allow computing the running total.
    public SalesItem(String articleName, Category category, double price, SalesItem previousItem)
    {
        this.articleName=articleName;
        this.category=category;
        this.price=price;
        this.runningTotal=previousItem==null?price:previousItem.runningTotal+price;
    }

/** Default Constructor that is required for JAXB deserialization. 
    In this program this is never used.
*/
    public SalesItem() { }
}

An overview of the simple report

Two Plain Old Java Objects (POJO) act as models to the list report. The POJOs are:
  • public class Sales - where Sales represents the sales of a particular shop on a particular day.
  • class SalesItem - where SalesItems represents the individual items sold in the shop on that day.

POJO means that the objects are not required to implement any specific interfaces or to be extensions of a specific class. These objects are annotated to hint to JAXB how these objects are to be serialized to XML.

The Java program contains:
  • The Report Model Objects, which specifies the data the report will include.
  • A main method to run the report using a simple list design and to view the result in PDF format.

Since Java 6, JAXB (Java architecture for XML Binding) is available as part of the J2SE standard distribution. It provides a means to serialize plain Java objects in a streaming manner, and it provides a schema generator capable of creating an XML schema from a set of annotated Java classes.

Using this API is the method proposed in this example.

Mandatory Java classes to be imported

import com.fourjs.report.runtime.*;
   // Specific class of Genero Report Writer
import java.io.File;
   // Generate the report file on disk
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.IOException;
import javax.xml.bind.JAXBException;
import org.xml.sax.SAXException;

XML annotations

XML annotations provide information in the XSD schema.
@XmlRootElement
The @XmlRootElement annotation causes a global element declaration to be produced in the schema.
...
@XmlRootElement
public class Sales {
...
@XmlElement
The @XmlElement annotation maps a property to an XML element.
   @XmlElement(required = true, nillable = true)
   public String shopName;
   @XmlElement(required = true)
   public int zipCode;
   @XmlElement(required = true, nillable = true)
   public Date day;
Note: The Genero Report Engine does not support optional values; setting required=true is mandatory. If a variable needs to be optional, use "nillable=true" and set the variable to null when there is no value.
@XmlAttribute
The @XmlAttribute annotation maps a property to an XML attribute.

Default Constructor required for JAXB deserialization

The default constructors required for JAXB deserialization are duplicates of the main constructors. While required, they are never used by the program.

Genero Report Writer APIs and Java code highlights

The following are excerpts of Java report code that feed the report data and control the output.
  • Use a handler object.
    FormatHandler handler = new FormatWriter(outputFilename);
  • Define a renderer according to the expected output format. In this example, the PDFRenderer is specified. See Change report output format (Java) for a summary of your choices.
    PDFRenderer  renderer = new PDFRenderer(handler);
  • Create the report object specifying the report design file and the renderer object.
    FourRpLayouter report = new FourRpLayouter(designFile, renderer);
  • Specify the data.
    Sales data = new Sales("Columbus Arts", 75038, new Date());
  • Run the report.
    report.runFromJAXBObject(data);

    The following is an alternative method for running a report, using a data file (.xml) instead of a database.

    In our simple program, no iterator is specified. All the data is in memory, and the standard iterator goes through that list in memory, done internally by RunFromJAXB. To see code that uses an iterator, examine the code in the OrderReportJava demo.

    report.runFromXML("my_data_file.xml");
  • Generate the report file on disk.
    File result = new File(outputFilename);