Over a million developers have joined DZone.

Eclipse's BIRT: Using Design Engine API

· Big Data Zone

Learn how you can maximize big data in the cloud with Apache Hadoop. Download this eBook now. Brought to you in partnership with Hortonworks.

Imagine you have defined a table as report item in a report design file with the name "customers". As its name implies the table is used for showing all customers from the sample database. Further it has one table group which is used for grouping the items by country and several columns where the properties of the bounded dataset are listed.

When deleting one column from the table the width of the other columns which doesn't have explicitly defined column width are recalculated in a way which is not satisfactory as a final result. Not having much control over the width calculation in this situation was the reason for solving this problem via the Design Engine API by manipulate the rptdesign file before it is used by the BIRT engine.

Before the main explanation I want to introduce you few API's which can be used for manipulating report files and also extending BIRT:

Chart Engine API

With the Chart Engine API you can create custom chart's. You need to understand the program interfaces which must be used for the implementation of the chart functionality and follow izs strict rules for creating the chart. I guess that the result will look like the other charts where the rpt designer can use the 3-tabs for the chart settings. One specialty of the Chart Engine API is that it can be used separately out of BIRT, means you can bound its libraries to any java application without using the BIRT part.

Report Engine API

For creating custom report items you need to use the REAPI. The output of the report item include images in several formats like, jpg, png or svg. Because you have more free playground as developer than implementing the Chart Engine API you can develop charts with as custom item. As rendering libraries it is recommended Java2D[ref] [ref]  or the ApacheBatik[ref], libraries if the output is SVG, to be used.

Design Engine API

The DEAPI serves for creating or manipulating the report files such as rptlibrary, rptdesign or rpttemplate. With this API new report design object can be created on runtime or given XML report file can be converted into Java objects needed for further manipulation in Java. This article describes one use case where this API takes action.

Using the DEAPI for table manipulation

In this case the widths of the columns have fixed pixel values which can be seen from the code. The recalculation of the width will be executed after one coldropedum is . But how can we drop a column via the API?

Step 1 - converting the rptdesign file into Java Object
The very first step for this solution is to convert the current rptdesign file into a Java Object. After finding the table object casting is needed because we need to work further on the table object. Casting the founded generic report item into TableHandle does this work.

... IReportRunnable design
 ... int posn
//Step 1 - converting the XML file into java object
try {
	design = engine.openReportDesign("report" + File.separator
			+ "simpletable.rptdesign");
} catch (EngineException e) {
	e.printStackTrace();
}

Step 2 - find the table
The second step is to find the right table from the rptdesign file. The "findElement"-method from the class ReportDesinger was written for this purpose. You can search and if found you can get any report object item from the report back as own object. The idea behind this is analog to the "getById" method used by javascript on a DOM object, but here instead of the id's the name-attribute are used as unique identification property.

Next steps are finding and deleting the column. Before that we need to delete the cells which are placed on the column. Also dropping the cells from the dimension group are necessary. This approach can be reached with the following steps.

//Step 2 - find the table
TableHandle customerTable = (TableHandle)((ReportDesignHandle) design.getDesignHandle()).findElement("CustomerTable");

Step 3 - find and drop group header and footer
The table group object is part of the table object. The API has its own methods for getting it as Java Object

//Step 3 - find and drop group header and footer
for (int i = 0; i < customerTable.getGroups().getCount(); i++) {
	TableGroupHandle tableGroupHandle = (TableGroupHandle) customerTable
			.getGroups().get(i);
	iterateAndDeleteFrom(tableGroupHandle.getHeader(), posn);
	iterateAndDeleteFrom(tableGroupHandle.getFooter(), posn);			
}
...
public static void iterateAndDeleteFrom(SlotHandle slothandle, int columnNumber) {
	for (int j = 0; j < slothandle.getCount(); j++) {
		dropCell(slothandle.get(j), columnNumber);
	}
}
...
public static void dropCell(DesignElementHandle designElementHandle,
		int posn) {
	if (designElementHandle != null) {
		if (designElementHandle instanceof RowHandle) {
			RowHandle rowHandle = (RowHandle) designElementHandle;
			if (rowHandle != null && rowHandle.getCells() != null
					&& rowHandle.getCells().get(posn) != null) {
				try {
					rowHandle.getCells().get(posn).drop();
				} catch (SemanticException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

Step 4 - find and drop the header, detail and footer

//Step 4 - find and drop the header, detail and footer
iterateAndDeleteFrom(customerTable.getHeader(), posn);
iterateAndDeleteFrom(customerTable.getDetail(), posn);
iterateAndDeleteFrom(customerTable.getFooter(), posn);

Step 5 - delete the column
The next steps is finding and deleting the column vie the "drop"-method

//Step 5 - delete the column
try {
	if (customerTable.getColumns().get(posn) != null) {
		customerTable.getColumns().get(posn).drop();
		//Step 6 - recalculating the widths			
		recalculatingWidths(columnWidths, posn); 
	}
} catch (SemanticException e) {
	e.printStackTrace();
}

Step 6 - recalculating the widths

private static void recalculatingWidths(List<Integer> columnWidths, int posn) {
	int distribution = 2;
	if (posn == 0 || posn == 4) {
		distribution = 3;
	}
	int additionalWidth = Math.round(columnWidths.get(posn) / distribution);
	for (int i = 0; i < columnWidths.size(); i++) {
		if (i != 0 && i != 4 && i != posn) {
			columnWidths.set(i, columnWidths.get(i) + additionalWidth);
		}
	}		
	columnWidths.remove(posn);
}

Step 7 - setting the new with to the table
After dropping the one column we need to set the new widths to the given columns. 

for (int i = 0; i < columnWidths.size(); i++) {
	if (customerTable.getColumns().get(i) != null) {
		try {
			customerTable.getColumns().get(i).setProperty("width", columnWidths.get(i) + "px");
		} catch (SemanticException e) {
			e.printStackTrace();
		}
	}
}

Hint: The example table which is shown here has simple structure. It gets more complicated when col- and row-spans are used. Then you must to know which of the table cell elements exists and must be deleted. Having complex structure can be confusing. You can try to guess the result knowing the structure of the table very well by try and error like creating reports after changing the cell id which should be deleted or better you can debug the code iterating over all slothandlers like the rowhandler and the cellhandler and get their id to see exactly which elements are manipulated. The id's of the object handlers corresponds to the id's which can be found as XML attributes in the report files in the row, cell and any other XML Element.

For this example I have used the newest version of eclipse and BIRT. For the example three files were used, two Java and the rptdesign file as source which was created with the report designer.

  • Main java clas. (EngineMain.java)
  • The class used for declaring the manipulation logic for the table.(MyDesignHandler.java)
  • The report library file which was created via the report designer.(simpletable.rptdesign)

API vs Scrpting

Many reporting task can be solved by using the API or Scripting. I prefer to use scripting when the changes are not so complicated, like setting colors which depend on report parameter or dataset value. When a scenario comes like described in this article then the API solution is a good choice. Of cource you can use scripting for it but then whole logic should be implemented into the XML file and it will be evaluated and executed on the Run or Render task, and that is something what I want to avoid. For me this case is kind of preparation of the report design file for BIRT and should be done before the RunAndRender task runs.

Résumé

This article is dedicated to manipulation of tables as part of eclipse BIRT rptdesign.xml-files via the Designe Engine API. With this example you can get the first impressions what you can do with this API and why it exists. With this API you can access on the rpt-files via Java which gives you the posibility to manipulate or even create such files on runtime. Thouse functionality can be used for implementing complex preprocessing modules which can be used into your apllication like:

  • mixing report elements from other rpt-files
  • coping report elements from report libraries

into the current report design object.

GitHub

The sources for this example can be checkout from: https://github.com/kstojanovski/birt. Use the designengineapi folder and start the main method of the EngineMain class to see the processing described here.

Hortonworks DataFlow is an integrated platform that makes data ingestion fast, easy, and secure. Download the white paper now.  Brought to you in partnership with Hortonworks

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}