I'm looking for a way to generate a grouped by report using Freemarker based on the dataType (int) from a list of data items.
So for example I have:
data.datatype = 1 where data has id = 1
data.datatype = 1 where data has id = 4
data.datatype = 1 where data has id = 7
data.datatype = 3 where data has id = 2
data.datatype = 3 where data has id = 3
data.datatype = 4 where data has id = 5
... and so on
The sequence is sorted by datatype but at the same time I need to keep the data items in their respective order (secondary sort order).
I'm also using macros like functions. Ideally I'd love to have <#macro rendertable filteredDatatypelist datatype>
In the past I did called a macro to render the header, footer, and row based on the current datatype but I'm trying to move away from that and have a single macro to render the whole table on the sublist for each datatype.
That is to say send the macro a list of the data items for the datatype. So for example in the above data I would call the rendertable
macro 3 times, once for datatype 1, 3, and 4.
I looked at trying to create a sublist using filter
but this seemed like it could be a performance issue with O(n) for each datatype. Not ideal but even then I'm not sure how it would work exactly. Also please note that I have no idea how many datatypes there are or what their values could be. I also looked at seq_index_of
and seq_last_index_of
but again I don't know what the different datatypes will be. I considered creating a list of unique datatypes first but I have no idea how to do that either. In other words I'm not sure how to even do something like
<#assign dataForDataType = dataList?filter(data.datatype = 1)>
<#list dataForDataType as x>$<@renderTable x 1></#list>
Except that in the case above I'm hardcoding the datatype and it only works for one datatype that is hardcoded. Even if it's O(n) I think I would be ok with it but the issue is that I don't know how to do it for a list of datatypes. Any help on how this can be achieved would be greatly appreciated.
What I ended up doing was creating a class that extended the interface TemplateMethodModelEx
that would take in the list of data and then return a list of datatypes. I then looped through the datatypes and then using another function got the list of data items for a specific datatypes through another TemplateMethodModelEx.
Something along the lines of:
<#assign datatypes = getDistinctDataTypes(dataList)/>
<#list datatypes as datatype>
Datatype: ${datatype.id}
<#list getDataForDatatype(dataList, datatype) as data>
${data.whatever}
</#list>
</#list>
Then I have classes such as this example:
public class GetDistinctDataTypes implements TemplateMethodModelEx {
@Override
public Object exec(List arguments) throws TemplateModelException
{
// Testing code is omitted for brevity and everything is assumed to just work and be correct.
DefaultListAdapter defaultListAdapter = ((DefaultListAdapter)arguments.get(0));
List<Data> dataList = (List<Data>)defaultListAdapter.getWrappedObject();
// Do what I need and return list of datatypes
return getDistinctDataTypesListFromDataList(dataList);
}
}
I then also added the method to the Configuration so that it can be used. You can do it in the freemarker file but I find it's better in the Java code.
CONFIGURATION.setSharedVariable("getDistinctDataTypes", new GetDistinctDataTypes());
You can find more details on the following Freemarker document page about method directives.