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

4

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




// 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 {
    }
}
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(JasperReportsContext
jasperReportsContext, JRDataset dataset, Map<String, ? extends
JRValueParameter> 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;
    }
}



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");
    }
}

ddaitarn304's picture
Joined: Sep 9 2015 - 6:39am
Last seen: 2 months 3 weeks ago

Can someone explain points 4 and 5 ?

ramonc - 1 year 8 months ago

0 Answers:

No answers yet
Feedback