Data Source

JasperReports can be built on any data having visual representation. In order to make the data "understandable" to the report object, it should be supplied through a JRDataSource interface. The JasperReports package supplies several implementations, which can be used with a RDBMS, xml, csv and object data sources.

In order to represent db4o objects in the most convenient way, we will build a special JRDataSource implementation - ObjectDataSource - using reflection to obtain object field values.

ObjectDataSource will accept data as a list of objects, because this is the way it is returned from a db4o query:

List <Pilot> pilots = objectContainer.query(pilotPredicate);

ObjectDataSource.java: ObjectDataSource
01/** 02 * ObjectDataSource class is used to extract object field values for the report. 03 * <br><br> 04 * usage:<br> 05 * List pilots = ...<br> 06 * ObjectDataSource dataSource = new ObjectDataSource(pilots);<br> 07 * In the report (*.jrxml) you will need to define fields. For example: <br> 08 * <field name="Name" class="java.lang.String"/><br> 09 * where field name should correspond to your getter method:<br> 10 * "Name" - for getName()<br> 11 * "Id" - for getId()<br> 12 * 13 */ 14public class ObjectDataSource implements JRDataSource { 15 16 private Iterator iterator; 17 18 private Object currentValue; 19 20 public ObjectDataSource(List list) { 21 this.iterator = list.iterator(); 22 }

ObjectDataSource must implement 2 methods:

public boolean next()

and

public Object getFieldValue(JRField field)

The next() implementation is very simple: it just moves the current pointer to the next object in the list:

ObjectDataSource.java: next
1public boolean next() throws JRException { 2 currentValue = iterator.hasNext() ? iterator.next() : null; 3 return (currentValue != null); 4 }

getFieldValue method should return the value for the specified field. The field is defined in *.jrxml file and is passed to the JRDataSource as a JRField. In the case of an object list datasource the objective is to correspond field names to the object field values. One of the ways to do this is to correspond the name of the field in the report to the name of the getter method in the object class. For example:

<field name="Name" class="java.lang.String"/>

class Pilot

{

...

    public String getName(){

        return name;

    }

}

The method name is "get" + JRField#getName() or "get" + "Name". Knowing the method name, we can invoke it using reflection and obtain the value of the object field:

ObjectDataSource.java: getFieldValue
01public Object getFieldValue(JRField field) throws JRException { 02 Object value = null; 03 try { 04 // getter method signature is assembled from "get" + field name 05 // as specified in the report 06 Method fieldAccessor = currentValue.getClass().getMethod("get" + field.getName(), null); 07 value = fieldAccessor.invoke(currentValue, null); 08 } catch (IllegalAccessException iae) { 09 iae.printStackTrace(); 10 } catch (InvocationTargetException ite) { 11 ite.printStackTrace(); 12 } catch (NoSuchMethodException nsme) { 13 nsme.printStackTrace(); 14 } 15 return value; 16 }
The full code of the class can be downloaded from ObjectDataSource.