Eclipse's BIRT: Dynamic Report
Join the DZone community and get the full member experience.
Join For FreeMotivation
Eclipse's BIRT is using XML files for the report layout description which have suffixes like: rptdesign, rptlibrary, rpttemplate. The rptdesign files, directly used for report generation, are created for any new report structure. When the project generates many different reports then the number of the design files is increasing rapidly which means to manage them is much more time intensive. Thats why this situation needs to be avoid. The implementation of this idea in not intuitive if you are new to BIRT and the next step for its implementation is the search for new information about it.
Inspiration
In the book about eclipses BIRT "BIRT: A Field Guide to Reporting" (ISBN-10: 0321733584 ISBN-13: 978-0321733580) is a good explanation how to organize the architecture of reporting when the report project is more complex and it is build on many files such as rptlibraries and rpttemplates.The suggestion there was to use different rptlibraries which were grouped depend on the purpose they were used for. One group of the rptlibraries should contain only general elements which are used from any report. The other group of rptlibrary are made of elements that are report specific. The final result is that the rptdesign file contains elements from the general and from the project specific rptlibraries.
This architecture description gave me idea to split the elements into general and specific ones, an idea which I needed for the later steps and the final goal.
Implementation
API functionaliy
Testing the functionality of the Desing Engine API I found some deprecated methods which were used for coping report elements from loaded rptlibrary object into a new created report design object. This way I could create reportdesign obejct on runtime and first load the content from the default library where the general report elements were located and then if nessessery to override the report items with the testspecific from the testspecific rptlibrary.
Structure
For this purpose strict defined structure of the reports was needed. Every section has own master page and grid element as root element named meaningully for better association since the names are used as id's and they are used often for report manupilation programming purposes.
Substitution
One rptlibrary for the general report section and many rptlibraries for the special cases were created. The section were interpreted in the report library as grid's i.e. the report cover page section is a grid item, toc page too and so on. The grid name are also the id's which are used for layout manipulating purposes via programming. The specific rptlibrary, or new sections in the given specific rptlibraries, are defined in case they are needed.
If one report has a section which differs from the same section of the default rptlibrary then whole section will be replaced. This way only for the difference between the needed and default section new rptlibraries will be created, which means less rptlibraries to manage.
An simple graphical example how the substitution is intended to be:
(has 4 sections) (has 1 sections)
standard library specific library
Section 1 - Grid A
Section 2 - Grid B
Section 3 - Grid C <---substitute---> Section 3 Grid C
Section 4 - Grid D
Description
In this part of the article one implementation example will be described.
The program ist build like the following list:
- creating the report desing object
- copy the parts of the default library to the report desing object
- datasource
- oda dataset
- master page
- grid handle
- substitute the default report parts
- datasource
- oda dataset
- master page
- grid handle
The main process of the programm can look like:
// creating the secton SessionHandle session = DesignEngine.newSession(null); // creating the report design ReportDesignHandle reportDesignHandle = session.createDesign(); // loading the library from file system into object LibraryHandle defaultLibraryHandle = session.openLibrary(getLibraryPath(reportTypeEnum.DEFAULT)); // setting the elements from the library object to the design object setDefaultLibraryElements(reportDesignHandle, defaultLibraryHandle); // binding the report design to the return value design = engine.openReportDesign(reportDesignHandle); // if the report type is not the default then subtitute the sections if (!ReportTypeEnum.DEFAULT.equals(reportTypeEnum)) { setExtraLibraryElements(reportTypeEnum, reportDesignHandle); } // order the sections orderSections(reportDesignHandle);
Setting the default data set to the report design object can look like (from the method setDefaultLibraryElements
):
for (Object defaultScriptDataSetHandle : defaultLibraryHandle.getDataSets().getContents()) { if (defaultScriptDataSetHandle instanceof OdaDataSetHandle) { try { reportDesignHandle.getDataSets().add((OdaDataSet)((OdaDataSetHandle)defaultScriptDataSetHandle).copy()); } catch (ContentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NameException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
If you use Scripted DataSet then this should be used instead of the ODA DataSet.
Example of replacing the default grid with the special one can be coded like:
private static void replaceCopyComponents( ReportDesignHandle reportDesignHandle, LibraryHandle extratLibraryHandle) { try { for (Object extraGridHandleObject : extratLibraryHandle.getComponents().getContents()) { if (extraGridHandleObject instanceof GridHandle) { for (Object defaultGridHandleObject : reportDesignHandle.getBody().getContents()) { if (extraGridHandleObject instanceof GridHandle) { if ( ((GridHandle)extraGridHandleObject).getName().equals( ((GridHandle)defaultGridHandleObject).getName()) ) { int pos = reportDesignHandle.getBody().findPosn((DesignElementHandle)(defaultGridHandleObject)); reportDesignHandle.getBody().drop((DesignElementHandle)defaultGridHandleObject); reportDesignHandle.getBody().add((DesignElement)((GridHandle)extraGridHandleObject).copy(), pos); extratLibraryHandle.getComponents().drop((DesignElementHandle)extraGridHandleObject); } } } } } for (Object extraGridHandleObject : extratLibraryHandle.getComponents().getContents()) { if (extraGridHandleObject instanceof GridHandle) { reportDesignHandle.getBody().add((DesignElement)((GridHandle)extraGridHandleObject).copy()); } } } catch (SemanticException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
As further information, in the example a kind of report section sorting is also presented where programmaticaly the section as grids are sorted in the wanted order.
private static void orderSections(ReportDesignHandle reportDesignHandle) { List<String> gridSectionNames = Arrays.asList("ZeroSectionGrid", "FirstSectionGrid", "SecondSectionGrid"); Map<String, DesignElement> map = new HashMap<String, DesignElement>(); for (Object bodyContent : reportDesignHandle.getBody().getContents()) { if (bodyContent instanceof GridHandle) { GridHandle gridHandle = (GridHandle)bodyContent; String name = gridHandle.getName(); DesignElement designElement = (DesignElement)gridHandle.copy(); map.put(name, designElement); } } for (int i = 0; i < gridSectionNames.size(); i++) { if (map.containsKey(gridSectionNames.get(i))) { try { reportDesignHandle.findElement(gridSectionNames.get(i)).drop(); reportDesignHandle.getBody().add(map.get(gridSectionNames.get(i)), i); } catch (SemanticException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
GitHub
Posting the whole code here would not look good, that's why I have posted it at github [ref]. The source code of the project "dynamicreports" is about this article.
The example was made with early version of eclipse Luna IDE using maven for the library dependencies.
Résumé
This article describes one way to create dynamic reports using a light way of cascading report sections. Other title which I wanted to give to this article was "another architecture" because it describes other way of creating reports. Now after knowing the solution of my task I can say that this approach is not complicated if you know the DE API well, if you know the methods which are doing the copy process. Knowing all that now was a motivation for writing this article.
Opinions expressed by DZone contributors are their own.
Trending
-
Reactive Programming
-
How To Approach Java, Databases, and SQL [Video]
-
Building a Flask Web Application With Docker: A Step-by-Step Guide
-
Chaining API Requests With API Gateway
Comments