Jump to content
We've recently updated our Privacy Statement, available here ×

How to implement custom datasource with input parameters (solved ?)


ddaitarn304gmail.com

Recommended Posts

So, I have installed Jaspersoft Studio as Eclipse plugin, and nice! It's integrated into my application, so I want to
create a java class to provide my data for a report. Do a google search and implement a custom JRDataSourceProvider.
Let's say, I implement my "RptMyCustomReport", give report fields and data and this works fine.
Next step, I want to filter my data based on report input parameter ... but I can't use my custom provider, I have to
use QueryExecuterFactory ... but now I loose my original class, or worse, I try to use JRDataSourceProvider (as provider for the report, useful to get report fields)
together with QueryExecuterFactory (as language for the report) with no luck.

What I wanted, was to give an interface to Jasper for implementing my report, an object that can provide any information to Jasper to build the report,
for example, "report field ?"  "report input parameters?" "report data for these filters ?" but Jasper doesn't work in this mode.

So, to partially simulate that behavior, I found the solution below.

First, reports have these elements:
- a provider: this is used at design time to get report fields. It's not used a runtime.
- a query language to execute the query: this links the QueryExecuterFactory
- a query sql: the "only" data stored as is in the report

I implement the provider to get the reports fields to Jasper.
I put my "RptMyCustomReport" class in the query sql
I implement the custom QueryExecuterFactory to parse the sql as java class and create custom data at runtime

Here a basic example:

// this class shows only that you can pass a parameter to your costum data source

public class TestDataSourceFiltered implements JRDataSource {     String[]             fields;    String[][]           data;    String               sampleFilter;       private Map<String, Integer> fieldIndex = new HashMap<>();          public TestDataSourceFiltered(String[] fields, String[][] data) {        this.fields = fields;        this.data = data;               for( int i=0; i<fields.length; i++ )            fieldIndex.put(fields[i], i);    }    public void setParamFilters(Map<String, Object> params) {        sampleFilter = (String) params.get("TEST_FILTER_PARAM");    }       int pos = -1;       @Override    public boolean next() throws JRException {        for(;;)  {            pos ++;            if( pos >= data.length )                return false;                       String[] row = data[pos];            if( sampleFilter == null || row[0].equals(sampleFilter) )                return true;        }    }    @Override    public Object getFieldValue(JRField jrField) throws JRException {        return data[pos][fieldIndex.get(jrField.getName())];    }}[/code]




// this is the provider:
// design time: get the report fields
// design / runtime: return null on data source, to pass the  baton to QueryExecuterFactory

public class TestDataSourceProviderFiltered implements JRDataSourceProvider {    public static class MyField extends JRBaseField  {        public MyField(String name, String desc, Class<?> clazz) {            this.name = name;            this.description = desc;            this.valueClass = clazz;            this.valueClassName = clazz.getName();        }    }    @Override    public boolean supportsGetFieldsOperation() {        return true;    }      // create java class from the query  in the report    private static TestDataSourceFiltered getInstanceFrom(JRReport report)  {        if( report == null || report.getQuery() == null )            return null;               String s = report.getQuery().getText();               if( s.isEmpty() )            return null;        try {            return (TestDataSourceFiltered) Class.forName(s).newInstance();        } catch( InstantiationException | IllegalAccessException | ClassNotFoundException e ) {            e.printStackTrace();            return null;        }    }       @Override    public JRField[] getFields(JasperReport report) throws JRException, UnsupportedOperationException {        TestDataSourceFiltered ds = getInstanceFrom(report);        if( ds == null )  return new JRField[0];               JRField[] res = new JRField[ds.fields.length];        for( int i=0; i<ds.fields.length; i++ )  {            res[i] = new MyField(ds.fields[i], ds.fields[i], String.class);        }        return res;    }    // wanted: return null !! The data source will be provided by query executor factory    @Override    public JRDataSource create(JasperReport report) throws JRException {        return null;    }    // used at design time and at run time    public static JRDataSource createWithParams(JRReport report, Map<String, Object> params) throws JRException {        TestDataSourceFiltered ds = getInstanceFrom(report);        ds.setParamFilters(params);               return ds;    }       @Override    public void dispose(JRDataSource dataSource) throws JRException {    }}[/code]
public class TestQueryExecuterFactoryFiltered extends AbstractQueryExecuterFactory {    @Override    public Object[] getBuiltinParameters() {        return null;    }    Map<String, Object> unwrap(Map<String, ? extends JRValueParameter> in )  {        HashMap<String, Object> res = new HashMap<>();        for( Entry<String, ? extends JRValueParameter> t: in.entrySet() )   {            JRValueParameter value = t.getValue();                       res.put( t.getKey(), value != null ? value.getValue() : null );        }        return res;    }       @Override  public JRQueryExecuter createQueryExecuter(JasperReportsContextjasperReportsContext, JRDataset dataset, Map<String, ? extendsJRValueParameter> parameters) throws JRException {        JRValueParameter paramReport = parameters.get(JRParameter.JASPER_REPORT);        if( paramReport != null )  {            JRReport jreport =  (JRReport) paramReport.getValue();                       if( jreport != null )  {                return new JRQueryExecuter() {                    @Override                    public JRDataSource createDatasource() throws JRException {                        return TestDataSourceProviderFiltered.createWithParams( jreport, unwrap(parameters) );                    }                                       @Override                    public void close() {                    }                                       @Override                    public boolean cancelQuery() throws JRException {                        return false;                    }                };            }        }               throw new JRException("no provider for dataset: " + dataset.getName());    }    @Override    public boolean supportsQueryParameterType(String className) {        return false;    }}[/code]



How to use at design time:
1) register TestQueryExecuterFactoryFiltered in Eclipse
2) create a custom report using TestDataSourceProviderFiltered as provider
3) edit "query language" to point to  TestQueryExecuterFactoryFiltered
4) create "RptMyCustomReport" derived from TestDataSourceFiltered
5) put in the sql editor the full qualified name of "RptMyCustomReport"
6) exit, save the report (little bug, until you save the report, JSS doesn't give you the query sql) and reopen, click on "read fields", and you get your report fields

How to use at runtime

public class MainTest {    public static void main(String[] args) throws JRException {      DefaultJasperReportsContext.getInstance().setProperty("net.sf.jasperreports.query.executer.factory.TEST","test.TestQueryExecuterFactoryFiltered");               Map<String, Object> params = new HashMap<>();        params.put("TEST_FILTER_PARAM", "A");               File sourceFile = new File("./src/test/Blank_A4.jasper");        JasperReport jasperReport = (JasperReport)JRLoader.loadObject(sourceFile);             JasperPrint jr = JasperFillManager.fillReport(jasperReport, params,TestDataSourceProviderFiltered.createWithParams(jasperReport, params));        JasperExportManager.exportReportToPdfFile(jr, "./test.pdf");    }}[/code]

 

Link to comment
Share on other sites

  • 1 year later...
  • Replies 1
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...