Статьи

BIRT в Eclipse: использование API Design Engine

Представьте, что вы определили таблицу как элемент отчета в файле дизайна отчета с именем «клиенты». Как следует из названия, таблица используется для отображения всех клиентов из образца базы данных. Кроме того, он имеет одну группу таблиц, которая используется для группировки элементов по странам, и несколько столбцов, в которых перечислены свойства ограниченного набора данных.

При удалении одного столбца из таблицы ширина других столбцов, которые не имеют явно определенной ширины столбца, пересчитывается способом, который не является удовлетворительным в качестве конечного результата. Отсутствие большого контроля над вычислением ширины в этой ситуации стало причиной решения этой проблемы с помощью API Design Engine путем манипулирования файлом rptdesign до его использования механизмом BIRT.

Перед основным объяснением я хочу представить вам несколько API, которые можно использовать для управления файлами отчетов, а также для расширения BIRT:

API Chart Engine

С помощью API Chart Engine вы можете создавать собственные диаграммы. Вы должны понимать программные интерфейсы, которые должны использоваться для реализации функциональности диаграммы, и следовать строгим правилам iz для создания диаграммы. Я думаю, что результат будет похож на другие диаграммы, где дизайнер rpt может использовать 3-вкладки для настроек диаграммы. Одна из особенностей API Chart Engine заключается в том, что его можно использовать отдельно от BIRT. Это означает, что вы можете связать его библиотеки с любым Java-приложением, не используя часть BIRT.

API механизма отчетов

Для создания пользовательских элементов отчета вам нужно использовать REAPI. Выходные данные элемента отчета включают изображения в нескольких форматах, таких как, JPG, PNG или SVG. Поскольку у вас есть больше возможностей для разработчиков, чем для реализации API Chart Engine, вы можете разрабатывать диаграммы как пользовательский элемент. В качестве библиотек рендеринга рекомендуется использовать библиотеки Java2D [ref] [ref]   или ApacheBatik [ref] , если для вывода используется SVG.

API Design Engine

DEAPI служит для создания или управления файлами отчетов, такими как rptlibrary, rptdesign или rpttemplate. С помощью этого API новый объект дизайна отчета может быть создан во время выполнения или данный XML-файл отчета может быть преобразован в объекты Java, необходимые для дальнейшей манипуляции в Java. В этой статье описывается один вариант использования, в котором этот API выполняет действие

Использование DEAPI для манипулирования таблицами

В этом случае ширины столбцов имеют фиксированные значения в пикселях, что видно из кода. Пересчет ширины будет выполнен после того, как будет сделан один холодный педиатр. Но как мы можем удалить столбец через API?

Шаг 1 — преобразование файла rptdesign в объект Java.
Самым первым шагом для этого решения является преобразование текущего файла rptdesign в объект Java. После нахождения табличного объекта приведение необходимо, потому что нам нужно работать дальше над табличным объектом. Приведение основанного элемента общего отчета в TableHandle выполняет эту работу.

... 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();
}

Шаг 2 — найти таблицу
. Второй шаг — найти нужную таблицу из файла rptdesign. Для этого был написан метод findElement из класса ReportDesinger. Вы можете выполнить поиск, а в случае обнаружения вы можете получить любой элемент объекта отчета из отчета как собственный объект. Идея, лежащая в основе этого, аналогична методу getById, используемому javascript для объекта DOM, но здесь вместо идентификаторов атрибут name используется как уникальное свойство идентификации.

Следующие шаги — поиск и удаление столбца. Перед этим нам нужно удалить ячейки, которые размещены в столбце. Также необходимо удалить ячейки из группы измерений. Этот подход может быть достигнут с помощью следующих шагов.

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

Шаг 3 — найти и удалить заголовок и нижний колонтитул
группы. Объект группы таблиц является частью объекта таблицы. API имеет свои собственные методы для получения его как 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();
				}
			}
		}
	}
}

Шаг 4 — найдите и бросьте верхний, нижний и нижний колонтитулы

//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.