ddaitarn304gmail.com Posted September 9, 2015 Share Posted September 9, 2015 So, I have installed Jaspersoft Studio as Eclipse plugin, and nice! It's integrated into my application, so I want tocreate 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 touse 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 reportI implement the provider to get the reports fields to Jasper.I put my "RptMyCustomReport" class in the query sqlI implement the custom QueryExecuterFactory to parse the sql as java class and create custom data at runtimeHere a basic example:// this class shows only that you can pass a parameter to your costum data sourcepublic 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 QueryExecuterFactorypublic 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 Eclipse2) create a custom report using TestDataSourceProviderFiltered as provider3) edit "query language" to point to TestQueryExecuterFactoryFiltered4) create "RptMyCustomReport" derived from TestDataSourceFiltered5) 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 fieldsHow to use at runtimepublic 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 More sharing options...
ramonc Posted August 17, 2017 Share Posted August 17, 2017 Can someone explain points 4 and 5 ? Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now