Skip to main content
Windward

Step 1

Overview

Implementing basic functionality.

Step 1: Getting the basics in place.

To implement the basic functionality, all you want to handle is out, if, and forEach. Most of the work is handling forEach.

Handling a forEach

It is tempting to handle just out and if in step 1, but forEach affects your entire design and skipping it now means a lot of repeat work later.

You best bet is to look at Dom4jDataSource.java (723 lines) or JdbcDataSource.java (850 lines), depending on which model is closer to your proprietary data. Complete source for each is in WindwardReports.jar, as are all Java files for net.windward.datasource and all sub-packages. They are well commented, and the code at the end of each for substituting variables can often be copied across to your own implementation.

Do not worry in step 1 about implementing ${variables}, unless you have a JDBC-type implementation where you must have it for the out tag.

DataSourceProvider

Create a constructor that connects to your data. The parameters passed to this and how it connects are totally dependent on your data source and is up to you. You can create several constructors if you wish. You will use these constructors to pass DataSourceProvider object(s) to Windward Reports to create a report.

Make sure that the only exceptions that your constructor and any other methods in your data source code throw are run time exceptions and DataSourceException. DataSourceException can be passed an inner exception, but the exception thrown must be DataSourceException.

Implement the close() method as necessary for your data source. The dump() method is for your own use only, so implement it as you see fit. For the setMap() method, just save the map inside your DataSourceProvider object.

parse and SimpleEvaluator

In both Dom4jDataSource.java and JdbcDataSource.java you will find the private method parse. This method and the sub-methods it uses are about 150 lines of code and you should copy one of them to your implementation.

This code (which also uses SimpleEvaluator) could have been placed outside of the data source package as it is common to both the XML and SQL implementations, despite slight differences. This code was left in the data source implementation in case you need to handle the select attribute parsing in a different manner.

The best case scenario is that you can copy it across. And even if you do have to write your own parse code (or add to the existing code), you should use the SimpleEvaluator class to handle any evaluations needed of select attributes.

DataSourceNode

Your DataSourceNode class will generally work best if it is an inner class inside your DataSourceProvider class. This gives it access to all member variables in the DataSourceProvider class.

First add do nothing methods for:

addQuery()

getBitmap()

getEscape()

getHtml()

getImport()

getLink()

Second, implement getIterator(). This requires implementing the DataSourceIterator class. This should be an inner class of DataSourceNode. Your DataSourceNode class can hold the iterator it is presently using as a variable, because it will only be asked for one iterator at a time. This is returned by the method getIterator().

All you really need to implement at first is the constructor, hasNext(), and next(). The rest can have hard-coded values. The index, count, first, & last properties are only called if the associated ${status.index}, etc. values are in a select.

Also do not worry about implementing steps (only returning every Nth row). You do not ever need to implement steps unless you are going to use them. But if you don’t, make sure you throw an exception if you get a step value other than 1.

Keep in mind that DataSourceIterator has a very simple job.

· Determine if there is another row to return.

· Return the next row as a DataSourceNode.

Handling an if

The good news is you now have the hardest part behind you – you are handling forEach loops and you are handling variable substitution. Handling an if is the only remaining part of any difficulty and is easier.

You now need to implement isIf() and isExistingNode(). They are very similar but there are differences. First implement isExistingNode(). This returns if a node exists and has no rules or attributes that impose special conditions. This method is called a lot so you want it to be fast. This is also the one method that can be called before any tag, is not mapped to any specific tag, and can be called at any time.

Keep in mind that if multiple data sources are applied to a template, isExistingNode() is used to determine if a tag’s node is for the data source it is on. If the node does not exist, and it is not the last data source to be applied, then the tag is left as is for the next data source.

This means isExistingNode() can get passed select statements that are totally illegal for your data source and may cause it to throw an exception or have unwanted side effects. You must return false in these cases. (Test isExistingNode() by passing it XPath and SQL select=”” tags.)

Next, implement the isIf() tag. Keep in mind that this can be either a test=”” or select=”” if, and they are generally two very different tests. You need to implement both. You also need to implement the empty test appropriately on a select test. What constitutes empty in the case of your data source is a decision you need to make.

If you are dying to test at this point, you can now handle a template that has just forEach and if tags in it. However, we strongly recommend that you get through the out tag first.

Handling an out

Handling an out is relatively easy. Return the value of the data as identified by the select. The only issue worth noting is to return null if you have no value for the tag and there is no default (as opposed to returning an empty string).

There is one other critical point for the out tag. If the out is a value=”foobar” instead of a select=”foobar”, there is no way to determine if the value is for the data set presently being processed. Therefore, the rule is if after parsing and formatting, there is still a “${“ in the string, then the assumption is that this tag is not for this data source and a null is returned.

You also will want to use OutTag.formatTag() which takes care of all formatting of the output string (used primarily for numbers and dates). This method also handles using the default value if the passed in value is empty.

Testing

You are now ready to test. First create a simple template with a single forEach, a single if, and two or three out tags. Run it through and debug as needed.

Next, create a nested test with an inner and outer loop. Test where each loop has 0, 1, 2 and 3 iterations. It is critical that you perform this test, as this is where you are most likely to find subtle problems.

Finally create a template with SQL and XPath tags in it, too. Run the test where your data source is not the lastData. It is fine if there is no lastData passed in and the SQL and XPath tags remain. This test is to ensure your isExistingNode() can handle SQL and XPath queries.

  • Was this article helpful?