BIRT, Design API and rptLibrary
The BIRT Design Engine API (DEAPI) provides the Java developers added flexibility to create, explore or modify BIRT report designs programmatically. For example, the BIRT Designer is built using this API. To use the DEAPI in your application, you’ll need the design engine SDK. More detailed information are available on the BIRT website.
For example let’s try to move a table column on a design using the DEAPI in a RCP (or RAP) application.
First, create an instance of the design engine and get a SessionHandle.
-
IDesignEngineFactory designEngineFactory = (IDesignEngineFactory) Platform
-
.createFactoryObject(IDesignEngineFactory.EXTENSION_DESIGN_ENGINE_FACTORY);
-
IDesignEngine designEngine = designEngineFactory .createDesignEngine(new DesignConfig());
-
SessionHandle session = designEngine.newSessionHandle(null);
Then, open the desired report design.
-
ReportDesignHandle designHandle = session.openDesign(“sample.rptDesign”);
Next, retrieve the table and the columns to move thanks to their design id.
-
TableHandle table = (TableHandle) findElementHandle(designHandle.getBody(), tableDesignId);
-
int sourceColIndex = ((ColumnHandle) findElementHandle(table.getColumns(), sourceColDesignId)).getIndex();
-
int destinationColIndex = ((ColumnHandle) findElementHandle(table.getColumns(), destinationColDesignId)).getIndex();
Where :
-
private static DesignElementHandle findElementHandle(SlotHandle slotHandle, long designId) {
-
// search into the slot content for the element corresponding to the given design id
-
for (DesignElementHandle elementHandle : (List<DesignElementHandle>) slotHandle.getContents()) {
-
// if found at this level, return it
-
if (elementHandle .getID() == designId) {
-
return elementHandle ;
-
}
-
// else do the same recursively in its children
-
else {
-
int elementSlotCount = element.getDefn().getSlotCount();
-
for (int i = 0; i < elementSlotCount ; i++) {
-
DesignElementHandle handle = findElementHandle(element.getSlot(i), designId);
-
if (handle != null) {
-
return handle;
-
}
-
}
-
}
-
}
-
return null;
-
}
Finally, call the appropriate given method on these elements :
-
if (sourceColIndex > destColumnIndex) {
-
table.shiftColumn(sourceColIndex + 1, destinationColIndex);
-
}
-
else {
-
table.shiftColumn(sourceColIndex + 1, destinationColIndex + 1);
-
}
The report design can be ran and rendered again to verify the modification. It can also be save and close to persist it.
-
designHandle.save();
-
designHandle.close();
BIRT allow developers to store and share design elements thanks to a report library. So as designs, libraries can be manipulated via the DEAPI. But what will happen if a design composed of elements from a Library is trying to be altered by the DEAPI ?
Let’s imagine that our Table is now an extension of a referenced report library :
<table name="Table" id="10" extends="library.Library_Table">
Executing the same code as described previously will lead to the following error :
org.eclipse.birt.report.model.api.elements.SemanticError: The paste operation on the element "Table" is forbidden. at org.eclipse.birt.report.model.api.ColumnBandShiftAction.shiftColumnBand(ColumnBandShiftAction.java:155) at org.eclipse.birt.report.model.api.TableHandle.shiftColumn(TableHandle.java:556)
Indeed, as the table is inherited from the library, it can’t be modified when the DEAPI is working on the report design. This change can only be applied on the report library itself. This behavior can be a restrictive argument to develop an application which is changing dynamically design, like an interactive viewer… At least, it prevents from using library…
One idea to avoid this is to copy the extended elements from the library to the design while opening it, always thanks to DEAPI.
-
/* retrieve report libraries and put them in a map by namespace */
-
Map<String, LibraryHandle> libraries = new HashMap<String, LibraryHandle>();
-
for (LibraryHandle library : (List<LibraryHandle>) designHandle.getLibraries()) {
-
libraries.put(library.getNamespace(), library);
-
}
-
replaceExtendsElementHandle(designHandle.getBody(), libraries);
Where :
-
private static void replaceExtendsElementHandle(SlotHandle slotHandle,
-
Map<String, LibraryHandle> libraries) throws SemanticException {
-
/* iterates over slot content to find an extend element */
-
for (int i = 0; i < slot.getCount(); i++) {
-
DesignElementHandle elementHandle = slotHandle.get(i);
-
ElementRefValue extendsRefValue = (ElementRefValue) elementHandle.getElement().
-
getLocalProperty(elementHandle.getModule(), IDesignElementModel.EXTENDS_PROP);
-
/* if not null drop and copy */
-
if (extendsRefValue != null) {
-
slotHandle.drop(i);
-
slotHandle.add(libraries.get(extendsRefValue.getLibraryNamespace()).
-
findElement(elementHandle.getName()), i);
-
}
-
// else search into its children for extend element
-
else {
-
int slotCount = elementHandle.getDefn().getSlotCount();
-
for (int j = 0; j < slotCount; j++) {
-
replaceExtendsElementHandle(elementHandle.getSlot(j), libraries);
-
}
-
}
-
}
-
}
But if you execute this code, the following error will occur :
org.eclipse.birt.report.model.api.IllegalOperationException: The module is read-only and operation is forbidden.
The last trick to finally obtain the wanted result is to take a copy of the declared library :
-
for (LibraryHandle library : (List<LibraryHandle>) designHandle.getLibraries()) {
-
libraries.put(library.getNamespace(), (LibraryHandle) library.copy().
-
getHandle(library.getModule()));
-
}
I want to thanks the BIRT newsgroup and more precisely Jason for helping me while working on this case.
Bonjour!
I’ve tried your code, and something doesn’t work for me… Seems like the copied elements from the library doesn’t have the extendsRefValue set to true…
Did you face also something like this?
I’d greatly appreciate a clue 🙂
God bless 🙂