Mail Merge with Regions using IMailMergeDataSource

I’m trying to perform a Mail Merge with Regions using the IMailMergeDataSource and I’m not sure how to define my data source with multiple tables. I tried using the ResultSetHashMap object but soon realised that wouldn’t work:

GenericMailMergeDataSource gmmdsSS = new GenericMailMergeDataSource(siteSurvey);
GenericMailMergeDataSource gmmdsSSA = new GenericMailMergeDataSource(siteSurvey.getSiteSurveyAttributes());
ResultSetHashMap rshm = new ResultSetHashMap();
rshm.put("SiteSurvey", gmmdsSS );
rshm.put("SiteSurveyAttribute", gmmdsSSA );
doc.getMailMerge().executeWithRegions(rshm);

My GenericMailMergeDataSource class currently only supports one object type/table name - do I need to extend this so that it returns different table names after each moveNext() operation? If so are there any rules for how I look through my multiple record sets?

If you need me to explain more please let me know.

Thanks

Further to my last post I’ve done some more experimentation and, from what I can gather, the MailMerge.executeWithRegions(IMailMergeDataSource) method does not support multiple regions, i.e. mutliple table names. I’ve tried writing my implementation of IMailMergeDataSource so that the table name changes after each moveNext() call but it seems the getTableName() method is only called once.

Am I missing something or do I need to implement my own way of identifying merge fields in different regions, e.g. by prefxing them with some ID?

Thanks

Hi, Rob,
First, I suppose you have multiple tables as a data source for mail merge. Second, are these tables standard java.sql.ResultSets? Third, are table names agree with mail merge region names?
Depending on answers we have several options for mail merge from multiple tables (in Java).
In common case we use:

  1. executeWithRegions(java.sql.ResultSet dataTable) with each source table (as many times as many tables).
  2. executeWithRegions(java.sql.ResultSet[] dataSet) only once with simple array of your tables.

In cases when even only one of ResultSets can’t correctly return a table name or when the table name doesn’t correspond to a mail merge region name (and we don’t want to change these names), we use:

  1. executeWithRegions(java.lang.String tableName, java.sql.ResultSet resultSet) with each source table.
  2. executeWithRegions(ResultSetHashMap resultSetHashMap) – before call the method we have to compose ResultSetHashMap from ours tables. By the way, ResultSetHashMap is just a HashMap and introduced only for compatibility with jdk1.4.

The last case is when we have to use an arbitrary data source (not ResultSets at all). In that case we have to implement IMailMergeDataSource by a class that will serve as the data source and use:

  1. executeWithRegions(IMailMergeDataSource dataSource)

As you see, you confuse two different approaches: IMailMergeDataSource and ResultSetHashMap.
Best Regards,

Hi Konstantin,

Thanks for your reply. As I think is a bit more clear from my second post, I am trying to use the last case, i.e. executeWithRegions(IMailMergeDataSource dataSource). I have to use this as I want to work with Java objects, not jdbc result sets.

I have created a class that implements IMailMergeDateSource and used it successfully with a simple (i.e. without regions) Word document. However, when I try to use Regions, because I have to specify a different table name for each regiond I don’t seem to be able to do this using IMailMergeDataSource.

Let me give you an example. I have a Customer object which contains a collection of Orders and a collection of Accounts. e.g.

class Customer {
    private String name;
    private String postCode;
    private ArrayList orders;
    private ArrayList accounts;
    …
}

I want to produce a Customer Summary document with a section at the top containing basic Customer properties like name and address and two sections (regions) below this listing the customer’s orders and his accounts.

I want to create a class that implements IMailMergeDataSource that I can pass into executeWithRegions() and that will interpret the merge fields in each region and replace them with the Customer data. I am able to do this for a simple document using a class similar to the one below but I’m struggling with the Multi-Region/Multi-Table version.

class Custome implements IMailMergeDataSource {
    private List customers;
    int currIndex = -1;

    public CustomerMailMerge ( List customers ) {
        this.customers = customers;
    }

    public getTableName() throws Exception {
        return "Customer";
    }

    public boolean moveNext() throws Exception {
        currIndex++;
        if ((currIndex + 1) > customers.size())
            return false;
        else
            return true;
    }

    public boolean getValue(String arg0, Object[] arg1) throws Exception {
        if ( arg0.equals("name") ) {
            return ((Customer)customers.get(currIndex)).getName();
        }
        else if ( arg0.equals("postCode") ) {
            return ((Customer)customers.get(currIndex)).getPostCode();
        }
        return true;
    }

}

This works fine for the customer primitive properties but how do I iterate through the Customer.orders and Customer.accounts properties and associate the resultant data to the separate regions on my document?

Thanks

Thanks for your interest in Aspose.Words for Java. Now it’s more clear for me:)
First. IMailMergeDataSource works just as a single table – one table with one name. So you need in additional IMailMergeDataSource’s for orders and accounts. And you have to launch executeWithRegions(IMailMergeDataSource) as many times as many IMailMergeDataSource’s you have. MailMerge will fill only fields with specific region name each time you launch particular executeWithRegions(). In common this works exactly like executeWithRegions(ResultSet).
Second. I don’t believe that your IMailMergeDataSource.getValue(String fieldName, Object[] fieldValue) works. The method should return a boolean (success or not) while the value should be returned inside a fieldValue[0] (it is emulation of .Net out parameter).
Best Regards,

Konstantin is right - IMailMergeDataSource corresponds to one “table”. If you want data from more than one table in your document (e.g Customer and CustomerDetail) you need to have two IMailMergeDataSource objects and need to execute mail merge twice.
It is normal to execute mail merge more than once with Aspose.Words as only the fields that match get replaced with data. The rest of the mail merge fields remain in the document.
For example, the Dinner Invitation demo does just that. It is a letter that invites people to a party. The letter has the sender and recipient details. The sender details are populated first from one-row data source (an array of objects), then the receiver details are populated during a second mail merge (one record per receiver). Since this demo does not use mail merge regions - whole document (whole letter) is filled for every customer.
Same situation when you use mail merge regiosn. You can do several mail merges. Like shown in the Sales Invoice demo. There is a mail merge region for the Customer, another for Order Details and yet another for Order Totals. Three mail merges are invoked.
In an ideal world, Customer - CustomerDetails is a parent-child relationship and it would be great to be able to make a single data set that contains all data for this and pass to Aspose.Words in a single mail merge, but since we don’t support parent-child mail merges yet, you have to simply do these steps one by one.

Thank you both for your response - that makes perfect sense now.

PS. You’re right, the getValue() method is incorrect - it was just a simple example for the purposes of explaining my question and not something I actually used.