RReport (com.java4less.rreport)

 

Introduction

Wellcome to RReport, the package that will allow you to include printing, reporting and prevewing capablities into yourJava [TM] or Web applications (Java Development Kit 1.1 or newer) in a few minutes. RReport is located in the package com.java4less.rreport

The main features or RReport are:

 

Creating a report

Before you start reading this section you must note that there are 2 ways for creating reports:

  1. you can programatically create all the objects that make a report and link them together. This documentation is focussed on this approach.
  2. or you can use RReport Visual Builder in order to visually create a report. This will be exported to a definition file *.rep that you can import in your application.

You can, of course, also combine both approaches. For example, you can create a report template (*.rep file) and modify it at runtime.

There are 4classes that represent a report

  1. RReport: basic class. It uses Java 1.1 printing API. This version is Java platform 1.1 compatible.
  2. RReportImp: this is a subclass of RReport that will allow you to read a definition file (*.rep) created with RReport Visual Builder. For this purpose it includes a importReport() method.
  3. RReportJ2: this is a subclass of RReportImp that uses Java 2 printing API (printerJob). This version has the following advantages:
  4. RReportJ2X: Subclass of RReport that uses features only available in the Java Development Kit 1.4 or later. For example, it allows you to select a given printer by program.

 

A Rreport is made of Rareas. A RArea is a group of objects (RObject) that will be printed together (in the same page). Typical examples of areas in a report are:

 

The source code to create an area looks like this:

// create report
Rreport Rep=new RReport(MainWindow);
// if you use java 1.2 or later you can use
// Rep=new RReportJ2(null);


RArea HDR_PurchaseOrder= new RArea();
HDR_PurchaseOrder.width=16.429;
HDR_PurchaseOrder.height=4.074;
Rep.addArea(HDR_PurchaseOrder);

As already stated, an RArea contains of a set of objects (RObject). These are the elements that are actually printed. The following Robjects are currently available:

The following example creates the page footer for the Purchase order report:

RArea PFOOTER_PurchaseOrder= new RArea();
RField TextPageNumber=new RField();
TextPageNumber.name="TextPageNUmber";
TextPageNumber.setConstant(false);
TextPageNumber.setdefaultValue("[Page]");
TextPageNumber.x=0.238;
TextPageNumber.y=0.185;
TextPageNumber.width=3;
TextPageNumber.height=0.423;
TextPageNumber.Align=RField.ALIGN_LEFT;
TextPageNumber.Expand=false;
TextPageNumber.Compress=false;
TextPageNumber.FontColor=java.awt.Color.black;
TextPageNumber.FontType=new java.awt.Font("Arial",java.awt.Font.PLAIN,8);
PFOOTER_PurchaseOrder.add(TextPageNumber);

rep.setPageFooter(PFOOTER_PurchaseOrder);

The code:

Actually this would not be necessary. When you create a new RReport it already contains a default pager footer like this one.

Note: if you are using RReport Visual Builder you will not do this, you will read the definition of your report from a file.

If you want to modify a RField in a report created with the Visual Builder you can do it like this:

RField f=(RField) report.getAreaByName(name).getItemByName(elementName);

 

Nested (linked) Areas

Areas can be nested by linking them to a superarea (see RArea.setLinkedArea() ). After a repetition of an area is printed, all subareas are printed automatically.

For example:

detailArea.setLinkedArea(headerArea);

will result in detailArea being printed after each repetition of headerArea. See RJDBCSource to learn how to retrieve the information from a database and define relationships between tables.

Let's suppose your report has 2 areas (header and detail). You have 2 options:

 

 

Group of Areas

You can create a group of areas using RReport Visual Builder. A group of areas is made of:

The groups of areas will allow you to automatically print a header and/or a footer based on the values of one or more fields.

The groups of areas must be used together with a RSource (normally a RJDBCSource) and you should set a value for the "GroupByFields" property of the detail area, there is no other special treatment, you can print the group's detail area as you would print any other area.

RReport will automatically print the group header and footer areas whenever the values of the "group by" fields change. Note that the header and footer area share the same RSource as the detail area.

For example , if you are printing the following data in you group detail:

Article Id =1 , Name=Printer , Category=Hardware
Article Id =2 , Name=Scanner , Category=Hardware
Article Id =3 , Name=Antivirus , Category=Software
Article Id =4 , Name=Editor , Category=Software
Article Id =5 , Name=Linux , Category=Software

and you set Grouo by "Category". The result will be:

  1. Group header is printed
  2. article 1 is printed
  3. article 2 is printed
  4. Group footer is printed
  5. Group header is printed
  6. article 3 is printed
  7. article 4 is printed
  8. article 5 is printed
  9. Group footer is printed

Note that RReport will NOT SORT the values. You must provide the data for the detail area in the correct order. In the previous example you would use a sql statement like: "select * from Articles order by Category" to make sure all articles in the same category come together.

You can find an example of use of groups in the examples/groups folder.

Printing a report

After you have defined your report (either programatically or with the Visual Builder) , you will need to print it. First you must decide if you want to print it directy or you want to preview it.

If you want to preview the report you need to put the RReport object on a component. The RReport object is a java.awt.Panel so you can put it on any Java container. Then you must call the report‘s method setPreview(MainWindow).

You can create your own preview window however we include two preview windows. One of them is a pure AWT component (RReportWindow) and the second is a Swing component (RReportJWindow). The code to use the window is:

Win= new RReportWindow(rep,MainWindow);
// if you use SWING:
// Win= new RReportJWindow(rep,MainWindow);

report.prepare();


// Print here ....


report.endReport();
Win.show();

In order to print a report you must follow these steps:

  1. Call prepare(). This will print your report header.
  2. Print the areas. You can either print the areas by assigning the values to the elements yourself (programatically) or you can provide a data source (RSource).
  3. Call endReport(). This will print your report footer.

Note: report header anf footer and page header and footers are printed automatically. If you need to execute your own code before these areas a printed you must use a RAreaListener.

Tip: if you make all your areas dependent of the report header using the setLinkedArea(), then all areas will be printed automatically when you call prepare(). (i.e. the header will be printed and all its subareas).

Note: the prepare() method will print one repetition of the report header. If you assign a RSource (array, table in database ...) to the report header, several repetitions of the header and all subareas will be printed. For each new report header repetition a page break and a initialisation of the page number is automatically performed. In this way you can print more than 1 report at once.

 

The preview Window

RReport comes with two implementations of a preview window (RReportWindow and RReportJWindow ). However if you don‘t like them you can create your own preview window. RReport is an extension from Panel so you can add it to any of your windows, panels etc..

The following methods in RReport will help you building your own user interface:

Data sources (Rsource)

Areas are programatically printed by:

  1. Assigning a value to the elements in the area. The following command assigns the value "Article1" to the element "TextDescription".
    DETAIL_PurchaseOrder.getItemByName("TextDescription").setruntimeValue("Article1");
  2. and Calling the report’s printArea() method: rep.printArea(DETAIL_PurchaseOrder);
  3. or if they are linked to a superarea, after the superarea is printed.

However you can also use a Rsource object. In this case the report will automatically assing the values to the fields and print the area as many times as needed. There are 3 implemented Rsource classes, but you can implement your own class.

The following example uses the class RArraySource to print the lines of a Purchase Order:

private static void Example2() {


// example: array as data source
Win= new RReportWindow(rep,MainWindow);
DETAIL_PurchaseOrder.setDataSource(new RArraySource(columnsNames,columnsData));
rep.prepare();

//print here
rep.printArea(DETAIL_PurchaseOrder);

rep.endReport();
Win.show();

}

The array columnsNames contains the names of the fields and the array columnsData contanins the values. See Examples or exampleOrder.java.

Note: you can use a RAreaListener in order to execute your own Java code before each repetition of the area is printed.

Note: objects defined as constant (see RObject.setConstant()) will not be modified by the data source (RSource).

 

Reading from database (RJDBCSource)

The class RJDBCSource is a subclass of RSouce. It will therefore allow you to print the contents of a table in a database. Furthermore RJDBCSource supports nested areas. This means you can print an area that contains subareas.

Let's suppose you must print 2 Invoices. Invoice number 1 and invoice number 2. You have a database that contains two tables: INVOICES and INVOICESLINES.

You have defined the following areas for this:

in order to programatically print the two invoices you must:

  1. Retrieve the header's data for invoice 1 (you should query the database like "SELECT * FROM INVOICES WHERE KEY=1")
  2. print header 1
  3. Retrieve the lines for invoice 1 (you should query the database like "SELECT * FROM INVOICESLINES WHERE KEY=1")
  4. print all lines for invoice 1
  5. Retrieve the header's data for invoice 1 (you should query the database like "SELECT * FROM INVOICES WHERE KEY=2")
  6. print header 2
  7. Retrieve the lines for invoice 2 (you should query the database like "SELECT * FROM INVOICESLINES WHERE KEY=2")
  8. print all lines for invoice 2

You can easily simplify this process by using RJDBCSource in the following way.

 

// create source table
RJBCSource headerTable=new RJBCSource(statement,"SELECT * FROM INVOICES");
headerArea.setDataSource(headerTable);

// create source table
RJBCSource linesTable =new RJBCSource(statement,"SELECT * FROM INVOICESLINES");
linesArea.setDataSource(linesTable);

// link areas
linesArea.setLinkArea(headerArea);

// link fields
linesTable.setToFields("ID");
linesTable.setFromFields("ID");

// link tables
linesTable.setLink(headerTable);

This means the following:

  1. Assign the table INVOICES as source for headerArea.
  2. Assign the table INVOICESLINES as source for linesArea.
  3. area linesArea (subarea) is linked to headerArea (superarea). This means, after each headerArea, the linesArea will be printed.
  4. The field TO (KEY) is the name of the field/s in the subtable (linesTable)
  5. The field FROM (KEY) is the name of the field/s in the supertable (headerTable). You can specifiy several fields separated by "|".
  6. the RJDBCSource in linesArea must be linked to the RJDBCSource in headerArea.

You can also use parameters in your SQL statement. For example:

myJDBCSource=new RJDBCSource(statement, "SELECT * FROM INVOICES where IDate>'[%dateParam]'");

in this case you must provide a value for the parameter before you run the report:

myJDBCSource.setParameter("dateParam","01/01/2001");

 

Printing a JTable

If you want to print a JTable you don‘t have to define the report’s layout field by field, the RJTable class can do it for you.

This class performs the following tasks:

The following example show how to print a Jtable in 5 minutes:

private static void Example5() {

// example: printing a JTable
Win= new RReportWindow(rep);
Win.show();

javax.swing.JTable dataTable = new javax.swing.JTable(columnsData, columnsNames);

RJTable RT=new RJTable(dataTable,0,0,10,2,1.5);
RT.createAreas();
rep.addArea(RT.getHeaderArea());
rep.addArea(RT.getDetailArea());

rep.prepare();

//print here
rep.printArea(RT.getHeaderArea());
rep.printArea(RT.getDetailArea());

rep.endReport();
Win.refreshPageNumber();

}

You first create the RJTable object and provide some information about the size. Then call the createAreas() method and last you add the new areas to you report.

 

Creating HTML

This method is provided for backwards compatibility only, we advise you to use DHTML instead.

Rreport is also capable of exporting your report as HTML. This process works in the following way:

<table BORDER="0" CELLSPACING="0" CELLPADDING"0">
<tr HEIGHT=31>
<td WIDTH=5></td>
<td WIDTH=164 <%%lblPurchaseOrder%%> </td>
</tr>
</table>

 

<table BORDER="0" CELLSPACING="0" CELLPADDING"0">
<tr HEIGHT=31>
<td WIDTH=5></td>
<td WIDTH=164 ALIGN="LEFT"><font SIZE="2" FACE="Arial" COLOR="#000000"><STRONG>Purchase Order</STRONG></font></td>
</tr>
</table>

 

where <%%lblPurchaseOrder%%> has been replaced with the actual value.

This means you can change the way the HTML is created. If you dont like how your HTML report looks like, you can override the area’s getHTML() methods an create you own layout. But remember to include "<%%fieldname%%>" where the elements must be inserted!

You could also override the Robject’s toHTML() method to change the way a Robject is converted to HTML.

 

Creating DHTML

RReport can also export your report to DHTML. This is usefull if you are using rreport in a web environment. We advise you to use DHTML instead of HTML because it produces better quality output.

In order to export to DHTML you must call the following methods:

rep.setHTMLActive(true); // active HTML / DHTML output
rep.setDHTMLActive(true); // select DHTML mode
rep.disablePrinting(true); // do not send output to printer

/ / print report now
rep.prepare();
....
rep.endReport();

// save DHTML output to file
String htmlString=rep.getHTML();
try {

java.io.OutputStreamWriter out2= new java.io.OutputStreamWriter(new java.io.FileOutputStream(fileName),System.getProperty("file.encoding"));
out2.write(htmlString,0,htmlString.length());
out2.close();

} catch (Exception e) { e.printStackTrace(); }

 

The class DHTMLLayer is in charge of converting the reports to DHTML code.

Whenever you export to DHTML you must take into account that the browser requires all to be stored in files. If you create a chart or a barcode, the resulting image must be stored in a image file. The following properties are used for the configuration of this:

report.exportImagesFormat="jpg"; // store the chart or barcode as jpg file
report.exportDirectory="c:\web\images"; // store the file in this subdirectory
report.imagesHTMLPrefix="images\"; // the resulting DHTML will look like this: <IMG SRC="images/XXXXXX.jpg" >

If you want to create GIF or PNG file you must include the following packages in your classpath:

If you are using a RPicture object for printing images, the "ImageHTMLAddress" property will be used when exporting to DHTML. In RReport Visual Builder the property is called "HTML image".

For example:

picture.ImageHTMLAddress="/images/file.jpg"

will generatee <IMG SRC="/images/file.jpg" > as part of the DHTML code. In the case of RPicture you must place the file in the correct directory, RReport will not create the file.

 

Creating PDF

RReport can also export your report to PDF. This is usefull if you are using rreport in a web environment. In order to export to DHTML you must call the following methods:

rep.setPDFFile("output.pdf") // set pdf output file
rep.disablePrinting(true); // do not send output to printer

/ / print report now
rep.prepare();
....
rep.endReport();

RReport uses a free Java library called iText that creates PDF files. Click here to download the pdf library. You must install the PDF library by including the iText.jar file in your class path or in the same directory as rreport.

The class PDFLayer is in charge of converting your report to PDF using iText.

Web Reports (Servlets)

If you are developing a web application you can also use RReport to create your reports. The reports can be created as DHTML pages or PDF files. In both cases the result can be displayed in the user's browser.

RReport provides a generic servlet call RReportServlet. The Servlet load a Java class that implements the IWebReport interface. This interface is used by the servlet to create and run the report.

The servlet has the following parameters:

The IWebReport interface

As already stated the servlet always loads a java class that implements the IWebReport interface. This interface only has 2 methods:

The servlet performs the following steps:

  1. load the class specified in the REPORT parameter of the servlet
  2. call the createReport() method on the class in order to create the report object.
  3. Activate DHTML or PDF output as needed.
  4. call runReport() method of the class.
  5. Send report output (DHTML or PDF) to the user's browser.

RReport provides a ready to use implementation of IWebReport called "WebReportJDBC" which is used to execute reports whose data is read from a database.

 

The WebReportJDBC class

This class is the defaut implementation of IWebReport. It works the following way:

  1. it loads the report template file (*.rep) specified in the REPORTFILE parameter.
  2. All servlet parameters starting with "PAR_" are forwarded to the RSource objects defined in the template file.
  3. It runs the report by calling prepare() and endReport().

This class is used to load and execute reports that can read data from database. The SQL statements in the template file can contain parameters.

For example, suppose you have a report that prints employee's information. Your template file could use the following SQL statement in order to read the employee's data:

Select * from Employees where EmployeeId=[%Id]

Then the servlet should have a parameter called "PAR_ID" which provides the value for the %Id% parameter in the SQL.

You would for example, execute the servlet like this:

http://localhost:8080/rreport/servlet/RReportServlet?REPORTFILE=file://employee.rep&PAR_ID=14&FORMAT=PDF

in order to print the information of employee 14.

Installation on Tomcat

In order to install RReportServlet on Tomcat you must follow the following steps:

Now you can test the installation

 

Examples

The following examples are delivered in app.java :

These are examples that programatically defined reports that will help you understand how RReport works, but it is not the best way to create reports. Read the RReport Visual Builder documentation in order to see examples of report created with RReport Visual Designer.

The following examples are included in this document:

 

Changing the HTML

 

The following source code creates a new type of Area. This area has a horizontal separator in the HTML version.

Public class myRArea extends Rarea
{
public String getHTML() {
String html=super.getHTML();
Html=html+"<hr>"; // html tag for displaying a horizontal line
Return HTML;
}

}

 

You can also change the way a Robject is converted to HTML. The following code changes the HTML version of Rcombo:

Public class myCombo extends RCombo
{
public String toHTML(Object Value) {
String s;

s="";

if (Align==ALIGN_RIGHT) s=s+" ALIGN=\"RIGHT\"";
if (Align==ALIGN_CENTER) s=s+" ALIGN=\"CENTER\"";
if (Align==ALIGN_LEFT) s=s+" ALIGN=\"LEFT\"";

s=s+">";

s=s+"<font SIZE=\""+super.fontSizetoHTML(FontType)+"\" COLOR=\""+super.fontColortoHTML(FontColor)+"\">"+super.fontValuetoHTML(FontType,((String) Value))+"</font></td>";

return s;
}
}

 

Creating your own RObject

 

The following source code creates a new Robject class for printing triangles:

import java.awt.*;

public class RTriangle extends RObject {

// (x1,y1),(x2,y2) and (x3,y3) make the 3 corners
public double x1=0.5;
public double y1=0;
public double x2=0;
public double y2=1;
public double x3=1;
public double y3=1;

public java.awt.Color color=java.awt.Color.black;

public RTriangle(){
constant=true; }

public void print(Graphics g,double px, double py,Object Value) {

int[] X=new int[3];
int[] Y=new int[3];

// convert from CM to pixels
X[0]=(int) (px+(x1*this.resolution));
Y[0]=(int) (py+(y1*this.resolution));
X[1]=(int) (px+(x2*this.resolution));
Y[1]=(int) (py+(y2*this.resolution));
X[2]=(int) (px+(x3*this.resolution));
Y[2]=(int) (py+(y3*this.resolution));

g.setColor(color);
g.drawPolygon(X,Y,3);
}

}

In order to create you own RObject you must extend the RObject class and implement the function:

public void print(Graphics g,double px, double py,Object Value)

you must then draw your object in the graphics context "g" at the position (px, py). The parameter "Value" can be any runtime object you need. For example, in the case of RField, "Value" is the text that should be printed and in the case of RPicture "Value" is the Image to be printed. In the case of RTriangle we don't use that parameter.

See RObject for more information about field you may need to use in your object.

Optionally you can implement the importFile() function if you want your object to be able to read itself from a report definition file:

 

public void importLine(String key,String val) {

super.importLine(key,val);

if (key.compareTo("COLOR")==0) {
this.color=convertColor(val); }

if (key.compareTo("X1")==0) this.x1=new Double(val).doubleValue();
if (key.compareTo("Y1")==0) this.y1=new Double(val).doubleValue();
if (key.compareTo("X2")==0) this.x2=new Double(val).doubleValue();
if (key.compareTo("Y2")==0) this.y2=new Double(val).doubleValue();
if (key.compareTo("X3")==0) this.x3=new Double(val).doubleValue();
if (key.compareTo("Y3")==0) this.y3=new Double(val).doubleValue();
}

 

This function receives a "key" that contains the name of the field read from the file and the value "val". See also Creating your own objects in RReport Visual Builder.

Text wrapping

You can use the properties expand and compress to specify how the Rfield class will behave if the text is too long or too short:

 

Grids and frames

You can very easily draw a frame around the page,an area, or all areas of the same type.

The following example draws a frame around the report’s page:

rep.setPageFrameStyle(new RLineStyle(0.2f,java.awt.Color.black,RLineStyle.LINE_NORMAL));

The following example draws a frame around the report’s header:

PHDR_PurchaseOrder.setFrameType(RArea.FRAME_AREA);

PHDR_PurchaseOrder.setFrameStyle(new RLineStyle(0.2f,java.awt.Color.black,RLineStyle.LINE_NORMAL));

The following example draws a frame around the report’s header

DETAIL_PurchaseOrder.setFrameType(RArea.FRAME_PAGE);

DETAIL_PurchaseOrder.setFrameStyle(new RLineStyle(0.2f,java.awt.Color.black,RLineStyle.LINE_NORMAL));

Note that in this example only one frame for all the lines of the "purchase order" will be created. If we use RArea.FRAME_AREA instead, a frame for each line is created.

If you want to print a table you may also want to print a grid to separate rows and columns inside this table.

DETAIL_PurchaseOrder.setGrid({8,10,12});

DETAIL_PurchaseOrder.setHorizontalGrid(true);

DETAIL_PurchaseOrder.setGridStyle(new RLineStyle(0.2f,java.awt.Color.black,RLineStyle.LINE_NORMAL));

This code will:

If you use the RJTable class to construct your report, you dont have to worry about this. Just call:

MyRJTable.SetHRDFrameStyle(new RLineStyle(0.2f,java.awt.Color.black,RLineStyle.LINE_NORMAL));

MyRJTable.SetLINFrameStyle(new RLineStyle(0.2f,java.awt.Color.black,RLineStyle.LINE_NORMAL));

MyRJTable.SetLINGridFrameStyle(new RLineStyle(0.2f,java.awt.Color.black,RLineStyle.LINE_NORMAL));

To create frames and grid for the table.

 

 


Java, JSP, JDBC, JDK and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries. J4L Components is independent of Sun Microsystems, Inc.