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

Filterable JRDataSource


ktrinad

Recommended Posts

By: Chuck Deal - cdeal

Filterable JRDataSource

2004-05-19 19:01

OK, I took a shot at a possible implementation that does not require source changes, per se. You should be able to cut the following code into a java file and add it to your classpath or compile it with the existing Jasper source to make a new jar file, etc.

 

DISCLAIMER: I haven't test this yet. I am putting it up here in the hopes that someone who needs this fuinctionality (C-Box, for instance) will work with it and give me some feedback. It is implemented as a wrapper class so that it is easier to use the existing JRDataSource implementations.

 

There is one restriction when sending the Map of filters, the key needs to be an instance of JRField. I had trouble creating a JRField instace (or subclass) in the next() method. If someone knows how to get a JRField instace given a fieldName, I would be interested it the code.

 

I am looking for any and all feedback. Does it work? Is it missing a feature? Anything?

=================

package dori.jasper.engine.data;

 

import dori.jasper.engine.JRDataSource;

import dori.jasper.engine.JRException;

import dori.jasper.engine.JRField;

import dori.jasper.engine.JRRewindableDataSource;

 

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

 

/**

* An implementation of a Filterable JRDataSource. This implementation is a wrapper for

* any of the existing JRDataSource implementations. This should aid in testing and allow

* for backwards compatibility.

*

*

* @author Chuck Deal cdeal@users.sourceforge.net

*/

public class JRFilterableDataSourceWrapper implements JRRewindableDataSource {

/*

* Private reference to an instantiated JRDataSource

*/

private JRDataSource ds = null;

 

/*

* Place to store our filters

*/

private Map filters = null;

 

/**

* JRFilterableDataSourceWrapper Constructor

*

* @param dataSource

*/

public JRFilterableDataSourceWrapper(JRDataSource dataSource) {

super();

 

ds = dataSource;

}

 

/**

* Delegate to the instantiated JRDataSource

*

* @see dori.jasper.engine.JRDataSource#getFieldValue(dori.jasper.engine.JRField)

*/

public Object getFieldValue(JRField jrField) throws JRException {

return ds.getFieldValue(jrField);

}

 

/**

* Return the Map of filters.

* This method makes sure a valid Map object is always returned.

*

* @return java.util.Map

*/

public Map getFilters() throws JRException {

if (filters == null) {

filters = new HashMap();

}

 

return filters;

}

 

/**

* Delegate to the instantiated JRRewindableDataSource

*

* @see dori.jasper.engine.JRRewindableDataSource#moveFirst()

*/

public void moveFirst() throws JRException {

if (ds instanceof JRRewindableDataSource) {

((JRRewindableDataSource)ds).moveFirst();

}

else {

throw new JRException(ds.getClass().getName() + " does not implement the JRRewindableDataSource interface!");

}

}

 

/**

* This method calls the instantiated JRDatasource object's next() method and

* checks a set of filters against the data in the row. If any filter fails or

* ds.next() returns false, then we will return false.

*

* @see dori.jasper.engine.JRDataSource#next()

*/

public boolean next() throws JRException {

boolean valid;

 

Map f = getFilters();

 

Iterator keys = null;

Object key = null;

Object value = null;

 

/*

* As long as next() returns a valid row...

*/

while ((valid = ds.next())) {

keys = f.keySet().iterator();

 

/*

* Loop thru the filters

*/

while (keys.hasNext()) {

key = keys.next();

value = f.get(key);

 

/*

* Test each filter against the field it represents

*/

if (!value.equals(getFieldValue((JRField)key))) {

// failed the test

valid = false;

break;

}

}

 

/*

* if valid == true, then all of the filters passed the test

*/

if (valid) {

break;

}

}

 

/*

* true: when has next row AND all filters passed

* false: either there is not a next row OR at least one filter failed

*/

return valid;

}

 

/**

* Set the filters to use for this JRDataSource. The filter key must be

* an instance of JRField.

*

* @param map

* @throws JRException

*/

public void setFilters(Map map) throws JRException {

filters = map;

 

if (filters != null) {

Iterator keys = filters.keySet().iterator();

Object key = null;

while (keys.hasNext()) {

key = keys.next();

 

if (!(key instanceof JRField)) {

throw new JRException("Filter key must be an instance of JRField");

}

}

}

}

}

======================

 

 

By: C-Box - c-box

RE: Filterable JRDataSource

2004-05-24 06:59

Hi Chuck,

 

i just found your post today (sorry) .. thanks a lot so far, I will test it as soon as possible.

 

As far as I've read, it loops through a map of filters and compares every field with the current filterfield's value. Sounds good.

 

I've just to find out where to call the setFilters Method at runtime. Perhaps with another constructor that contains the map as parameter?

 

till later

C-Box

 

 

 

 

By: Chuck Deal - cdeal

RE: Filterable JRDataSource

2004-05-24 07:22

I was trying to make this a low-impact wrapper. So, I would expect you to instantiate your JRTableModelDataSource and send that as a parameter to my JRFilterableDataSourceWrapper . Then, once you have an instance of my DataSource, you could send in the HashMap. Perhaps, you could have a Scriplet method that returns an appropriate HashMap.

 

<dataSourceExpression>((JRFilterableDataSourceWrapper)$P{subDataSource}).setFilters(Scriptlet.getParameterMap()</dataSourceExpression>

 

Besides testing if this is a useful class, please consider any aspects that may be clumsy. Maybe we can work out those issues. If you can't get it to work, please respond with what doesn't appear to be working. I'll take your comments and come up with a better, more detailed usage plan (if I can).

 

I know that using this wrapper is a little heavier than having a full-fledged filtering implementation of the current DataSources. But, this allows us to test just the filtering code until we are satisfied. If everything works out with this wrapper class, then we can implement some better datasources based on the existing JRDataSource objects.

 

Please let me know how you make out!

 

 

By: C-Box - c-box

RE: Filterable JRDataSource

2004-06-02 00:17

Hi Chuck,

 

I thought about your filterable DataSource and I've one Problem with the filter-keys (again).

When running the whole report with multi subreports on it, I don't have the filterfields in the masterreports DataSource because I have to put for example the field-value of MasterReportsDataSource "ID" (e.g. an integer value like 1) to subreports datasource where I have to compare it to the field-value of SubReportDataSource "HEADID" (e.g. all records that also match the given interger value 1)

 

So I have to put the field "HEADID" into the filter-map with the current value of the field "ID"... but on runtime the field "HEADID" doesn't exists in MasterReport....so it can't be an instance of "JRField" but more of "String".

 

I tried following:

 

I build up a container-class that holds for the whole report ALL SubReports (as private attributes, or if necessary and more flexible way in a private hashmap-attribute) and ALL FilterableDataSources (also as private attributes or if necessary in a private hashmape-attribute).

 

Before filling the MasterReport I have to fill the container... where I do put all the contained SubReports and it's filterable DataSources into that container.

 

Then I pass that container into the parametermap when filling the masterreport.

 

in SubReportExpression I do use following statement to retrieve the needed SubReport:

 

<subreportExpression class="dori.jasper.engine.JasperReport"><![CDATA[$P{DataContainer}.getSRPositions()]]></subreportExpression>

 

where I have a fixed method (in my case all reports consists of certain fixed subreport-elements so that I can use the private-attribute and it's get-method - it could also be a method that looks in a hashmap) "getSRPositions" that returns the compiled SubReport.

 

for datasource-expression I use following:

 

<dataSourceExpression><![CDATA[$P{AFPSDataContainer}.getFilterableDataSource("SRPositionen","dokumentId",$F{Id})]]></dataSourceExpression>

 

where I have again a method "getFilterableDataSource" that get's the name of the FilterableDataSource and it's filtercriteria (just ONE because I don't know how to fill a hashmap at runtime and for my purpose, one is enough (first)) and returns a FilterableDataSource instance with it's filter-criteria.

 

So you see I've to use String as FilterField because the corresponding JRField doesn't exists and would throw an exception.

 

You suggestion with the scriptlet-usage could perhaps work with the filtermap and the JRField as FilterField-type, but then I can't edit the filter in a designer (for example iReport). But that I've to clarify first if it's at all necessary yet. If not, than I'll test my container with a scriptlet that fills the filter-hash-map.

 

till later

C-Box

Link to comment
Share on other sites

  • Replies 0
  • Created
  • Last Reply

Top Posters In This Topic

Popular Days

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