Implicit Collections Processing

Explicit collections processing takes place by using the ForEach and MultiForEach tags. However, use of the tags can clutter the text box used for each cell.

If JETT detects that an Expression contains operations on a Collection, then it processes an implicit MultiForEach tag. By default, implicit collections processing occurs on the entire row where the collection or collections are found.

In this example, "employees" is a List of Employees. The List doesn't have the properties "lastName", "firstName", "salary", and "manager" (methods getLastName(), getFirstName(), getSalary(), and getManager()). The implicit collections processing gets these properties from each item of the "employees" list.

Employee Salary Manager
${employees.lastName}, ${employees.firstName} ${employees.salary} <jt:if test="${employees.getManager() != null}" then="${employees.manager.lastName}, ${employees.manager.firstName}"/>

...gets transformed into...

Employee Salary Manager
Stack, Robert $1000.00  
Queue, Suzie $900.00 Stack, Robert
Fudd, Elmer $800.00 Stack, Robert
Bunny, Bugs $1500.00  

Changing Behavior with Metadata

To change the default behavior of implicit collections processing, include metadata at the end of an Expression that contains a reference to a Collection. Metadata may influence the affected block of cells, either restricting the affected columns, including additional rows, or both. To include metadata, append the string "?@" plus key/value pairs to the end of the Expression, separated by semicolons.

Metadata Keys

  • left: int Optional. Specify exactly how many cells to the left of the current cell to include in implicit collections processing, defaulting to zero.
  • right: int Optional. Specify exactly how many cells to the right of the current cell to include in implicit collections processing, defaulting to zero.
  • If neither "left" nor "right" is present, then JETT will include the entire row of data for implicit collections processing.
  • extraRows: int Optional. Specify how many extra rows are to be included in the block for implicit collections processing below the current row, defaulting to zero.
  • copyRight: Boolean Optional. If true, then the block will be copied to the right, instead of downward. Default: false (downward). This acts just like the "copyRight" attribute of looping tags.
  • fixed: Boolean Optional. If true, then the content outside of the block will not be shifted out of the way to make room for additional data. Default: false (shifting will occur). This acts just like the "fixed" attribute of looping tags.
  • pastEndAction: String Optional. Determines what happens to Cells that reference Collections that are exhausted and beyond iteration. This acts just like the "pastEndAction" attribute of looping tags. Here are the possible values:
    • clear This works just like the "elseAction" attribute of the if tag: remove the contents of the cells. This is the default.
    • remove This works just like the "elseAction" attribute of the if tag: remove the cells, including cell contents, formatting, borders, and merged regions.
    • replaceExpr Don't delete the contents of the entire cell. Replace any expressions that reference collections that have been exhausted with the result of evaluating the replaceValue attribute.
  • replaceExpr: String Optional. If the pastEndAction is replaceExpr, then all expressions that reference collections that have been exhausted are replaced with this value. This is ignored if pastEndAction is not replaceExpr. Default: an empty string. This acts just like the "replaceExpr" attribute of looping tags.
  • groupDir: String Optional. Create an Excel Group (Outline) on the resultant rows or columns. This acts just like the "groupDir" attribute of looping tags. Here are the possible values:
    • rows Create an Excel Group (Outline) out of the resultant rows.
    • cols Create an Excel Group (Outline) out of the resultant columns.
    • none Do not create an Excel Group (Outline). This is the default.
  • collapse: boolean Optional. This works just like the "collapse" attribute of looping tags: This determines whether the Excel Group (Outline) created is in a collapsed state. The default is false (not collapsed).
  • indexVar: int Optional. Expose a zero-based "looping" variable name in the beans map. This acts just like the "indexVar" attribute of the forEach tag and of the multiForEach tag.
  • limit: int Optional. Limit the number of collection items displayed to this number, for all collections found. This acts just like the "limit" attribute of the forEach tag and of the multiForEach tag.
  • onProcessed: TagListener Optional. Specify a TagListener or a fully-qualified Java class name so that a TagListener will be called when processing is complete. This acts just like the "onProcessed" attribute of all built-in tags.
  • onLoopProcessed: TagLoopListener Optional. Specify a TagLoopListener or a fully-qualified Java class name so that a TagLoopListener will be called each time a loop iteration has been processed. This acts just like the "onLoopProcessed" attribute of looping tags.
  • varStatus: String Optional. Expose a variable in the beans map that contains information about the current loop iteration -- the 0-based iteration index, whether it's the first iteration, and whether it's the last iteration. This acts just like the "varStatus" attribute of looping tags.

Once JETT has determined the desired block of cells, it will scan the entire block for any references to any Collections in any Expression. Then, it will process an implicit multiForEach tag on the entire block.

In this example, notice how "Company: Whatsit" is not copied with the rest of the block.

Company: Whatsit First Name: ${employees.firstName}?@extraRows=1;left=1;right=2 Last Name: ${employees.lastName}
  Salary: ${employees.salary} Manager: <jt:if test="${employees.getManager() != null}" then="${employees.managerlastName}, ${emmployees.manager.firstName}"/>

...gets transformed into...

Company: Whatsit First Name: Robert Last Name: Stack
  Salary: $1000.00 Manager:  
  First Name: Suzie Last Name: Queue
  Salary: $900.00 Manager: Stack, Robert
  First Name: Elmer Last Name: Fudd
  Salary: $800.00 Manager: Stack, Robert
  First Name: Bugs Last Name: Bunny
  Salary: $1500.00 Manager:  

Turn Off Implicit Collections Processing

To access Collection items explicitly in an Expression, it is desirable to turn off implicit collections processing on a collection name basis. To do this, use the following method available in the ExcelTransformer class:

public void turnOffImplicitCollectionProcessing(String collName);
            

In this example, the following code is run before the transform method:

transformer.turnOffImplicitCollectionProcessing("employees");
            
The highest ranked employee is: ${employees.get(0).fullName}

... gets transformed into...

The highest ranked employee is: Bugs Bunny

If implicit collections processing was allowed to occur on the "employees" collection, then four rows would be generated, one for each employee, each attempting to call "get(0)" directly on the Employee object, resulting in an error.

However, certain properties and method calls on Collections are recognized to be actual Collections operations, so if any of the following operations are performed on a Collection, then implicit collections processing does not occur:

  • capacity
  • contains
  • element
  • equals
  • get
  • hashCode
  • indexOf
  • isEmpty
  • lastIndexOf
  • size
  • toString