Comparison of JETT to jXLS

JETT is similar to jXLS, because JETT performs the same function -- translating Excel template spreadsheets into spreadsheets with the desired data populated. Here, JETT 0.9.0 is compared with the latest version of jXLS, 1.0.6:

Feature JXLS JETT
Excel File Formats Supported 97-2003 (.xls), 2007+ (.xlsx) 97-2003 (.xls), 2007+ (.xlsx)
Excel Spreadsheet API Used Apache POI Apache POI
Expressions Support Apache Commons JEXL Apache Commons JEXL
Tagless Export of Collections -- Implicit Collections Processing

Supported; can be disabled, but only overall, not on a per-collection name basis. It can be controlled in the following ways, with metadata, e.g. ${collName.property}//:2, Label://collName.

Include a "//collName" suffix to include cells in the repeating block that should be included, even though they don't explicitly reference the collection name in an Expression.

Include a "//:n" suffix to indicate how many rows should be included in each repeating block.

Supported; can be disabled on a per-collection name basis. It can be controlled in the following ways:

Include a "?@" metadata suffix, plus key/value pairs, e.g. ${collName.property}?@extraRows=1;left=1;right=2.

  • left=n and/or right=n Include other columns to the left and/or the right in the repeating block.
  • extraRows=n Include extra rows in each repeating block.
  • pastEndAction=action State what action is to be taken when processing Collections with less data than other Collections.
  • replaceValue=value When the pastEndAction is "replaceExpr", specify a replacement value for affected expressions.
  • groupDir=dir Create an Excel Group (Outline) with rows or columns on the processed data.
  • collapse=true/false When creating an Excel Group (Outline), decide whether to collapse the group.
  • copyRight=true/false Copy blocks to the right, instead of downward, the default.
  • fixed=true/false Mark this as "fixed", meaning that the processing assumes that there is space in which to place copied blocks of data, so it won't attempt to shift content out of the way.
  • indexVar=varName Place a zero-based index looping variable with this name in the beans map for further reference.
  • limit=n Limit the number of items displayed for any and all affected collections.
  • onProcessed=var Specify a TagListener that will be called when processing is complete.
  • onLoopProcessed=var Specify a TagLoopListener that will be called when the processing of each loop iteration is complete.
  • varStatus=var Place an looping variable with this name in the beans map that contains the current iteration index, plus the start and end indices.
Tagless Export of Collections - Implicit Sheet Cloning Not supported

Supported; can be disabled on a per-collection name basis. It can be controlled in the following ways:

Include a "$@" metadata suffix, plus key/value pairs, e.g. ${coll.prop}$@i=n;l=10.

  • i=n Like indexVar from implicit collections processing.
  • l=n Like limit from implicit collections processing.
  • r=value Like replaceValue from implicit collections processing. (pastEndAction=replaceValue is assumed.)
  • v=n Like varStatus from implicit collections processing.
Formulas

Supported, format $[formula(cellref)]. Optionally, provide a default value in case the cell references get deleted: $[formula(cellref)@defValue].

Supported, format $[formula(cellref)]. Optionally, provide a default value in case the cell references get deleted: $[formula(cellref||defValue)].

Additionally, create dynamic formulas with the formula tag.

Aggregate Expressions Limited support, e.g. ${Func(field):collection}, where "Func" is one of "sum", "min", "max", "avg", and "count". Supported, with jAgg integration, using Aggregate Expressions, e.g. ${jagg:eval(list, 'Func(field)')} -- and the agg tag. "Func" can be any one of 20 Aggregators built-in to the jAgg library, including "Sum", "Min", "Max", "Avg", "Count", and many more, or a custom Aggregator.
Merged Regions Takes into account merged regions when shifting and copying cells. Takes into account merged regions when shifting and copying cells.
Preserve Excel Features Preserves Excel macros, charts, drawings, images. Preserves Excel macros, charts, drawings, images, rich text strings.
Extracting Data From an Excel Spreadsheet jXLS contains the jxls-reader module Not supported
Transformer
Transformer Class (API Entry Point) XLSTransformer transformer = new XLSTransformer(); ExcelTransformer transformer = new ExcelTransformer();
Transform (Basic)
  • transformer.
    transformXLS(String srcFilePath, Map beanParams, String destFilePath);
  • transformer.
    transformXLS(InputStream is, Map beanParams);
  • transformer.
    transform(InputStream is, Map<String, Object> beans);
  • transformer.transform(String inFilename, String outFilename, Map<String, Object> beans)
  • transformer.transform(Workbook workbook, Map<String, Object> beans);
Transform One Template Sheet Into Many Result Sheets
  • transformer.
    transformMultipleSheetsList(
    InputStream is, List objects, List newSheetNames, String beanName, Map beanParams, int startSheetNum);
  • transformer.
    transform(InputStream is, List<String> templateSheetNamesList, List<String> newSheetNamesList, List<Map<String, Object>> beansList);
  • transformer.
    transform(String inFilename, String outFilename, List<String> templateSheetNamesList, List<String> newSheetNamesList, List<Map<String, Object>> beansList)
  • transformer.
    transform(Workbook workbook, List<String> templateSheetNamesList, List<String> newSheetNamesList, List<Map<String, Object>> beansList);
Fixed Collection Names transformer.
markAsFixedSizeCollection
("employees");
transformer.
addFixedSizeCollectionName
("employees");
Turn Off Implicit Collections Processing transformer.
setJexlInnerCollectionsAccess
(true);
transformer.
turnOffImplicitCollectionProcessing
("employees");
Evaluate all formulas after transformation Not supported transformer.setEvaluateFormulas(true);
Force formula recalculation upon opening Not supported transformer.setForceRecalculationOnOpening(boolean);
Tags
Tag Bodies

All tags that have their end tag below their start tag must have both tags exist alone on their respective rows. The entire rows are removed prior to processing, and any content below the tags is shifted up.

All tags that have their end tag to the right of their start tag must have both tags exist on the same row. The cells containing the tags are removed prior to processing, and any content to the right is shifted left. This can interfere with an attempt to preserve things like column widths.

To display multiple rows of Collection output, repeating to the right, one must use one tag per row.

All tags can coexist in the same cell as other content, eliminating the need to shift other content because of the removal of tag content.

Many tags can explicitly set directionality, so that it is not dependent on where the end tag is, relative to the start tag.

To display multiple rows of Collection output, repeating to the right, one can use one tag and explicitly state that the additional blocks should be copied right.

Looping Tag to Display a Collection

jx:forEach tag

  • items attribute - The Collection of items to display.
  • var attribute - The looping variable introduced into the beans map.
  • groupBy attribute - Display groups of items, based on one property.
  • groupOrder attribute - Sort groups of items, ascending or descending.
  • select attribute - Filter the display list.
  • varStatus attribute - Index of item currently being iterated.
  • No limit attribute.
  • No fixed atttribute, but one can use the "fixed size collection" feature.
  • No pastEndAction attribute.
  • No groupDir or collapse attributes.

jt:forEach tag

  • items attribute - The Collection of items to display.
  • var attribute - The looping variable introduced into the beans map.
  • groupBy attribute - Display groups of items, based on one or more properties.
  • orderBy attribute - Order the entire collection, grouped or not, specifying multiple sort fields.
  • where attribute - Filter the display list.
  • indexVar attribute - Index of item currently being iterated.
  • limit attribute - Stop display after a certain number of items.
  • fixed attribute - Don't shift other content out of the way; assume that the spreadsheet has "room" for the collection content.
  • pastEndAction attribute - Controls what happens when the iterations proceeds beyond the end of a Collection.
  • replaceValue attribute - Controls the replacement value if pastEndAction is "replaceExpr".
  • groupDir attribute - After processing, create an Excel Group (Outline) around all of the processed data, in rows or columns.
  • collapse attribute - If creating an Excel Group (Outline), decide whether to collapse the group.
  • varStatus attribute - Object indicating iteration index, start index, and end index of the loop.
Looping Tag to Display Multiple Collections At Once No such tag exists, but it is possible with implicit collections processing. jt:multiForEach tag or implicit collections processing.
Loop Over Arbitrary Numbers Not supported directly. To workaround, supply a Collection of numbers to the beans map and use a jx:forEach tag. jt:for tag
Display Content Conditionally

jx:if tag - Must have a body.

  • test attribute - The condition to display the content.

jt:if tag

  • test attribute - The condition to display the content.
  • elseAction attribute - What to do when the condition is false to remove the content.
  • (bodiless form only) then attribute - The content to display if the condition is true.
  • (bodiless form only) else attribute - The content to display if the condition is false.
Grouping Excel Rows

jx:outline tag, grouping rows only

  • detail attribute - Whether to show the newly created Excel Group (Outline) as uncollapsed.

jt:group tag, grouping rows or columns

  • groupDir attribute - Whether to create the Excel Group (Outline) in rows or columns.
  • collapse attribute - Whether to show the newly created Excel Group (Outline) as collapsed.

It is also possible to create an Excel Group (Outline) around a dynamically determined range of rows or columns with looping tags such as "forEach".

Excel Comment Creation Not supported

jt:comment tag

  • value attribute - The normal content of the Cell in which to create a Comment.
  • author attribute - Specify the author of the comment.
  • comment attribute - Specify the content of the comment.
  • visible attribute - Control whether the comment is initially visible.

Because JETT is built on Apache POI, creating Comments on a Sheet will most likely corrupt any pre-existing Charts or other Drawings, but only for .xls spreadsheets (97-2003 versions).

Hyperlink Creation Not supported

jt:hyperlink tag

  • type attribute - Control the type of the hyperlink: url, email, file, or doc.
  • address attribute - The address of the hyperlink.
  • value attribute - The contents of the Cell with the hyperlink, acting as the label for the hyperlink.
Dynamic Formula Creation Not supported

jt:formula tag

Dynamic Named Range Manipulation Not supported

jt:name tag

Dynamic Style Not supported

jt:style tag, where the "style" attribute controls:

  • horizontal and vertical alignment
  • borders and border colors
  • data format string
  • fill background and foreground colors, and fill pattern
  • "hidden" and "locked"
  • number of characters text is indented
  • number of degrees text is rotated
  • whether text is wrapped to the next line within a cell
  • font:
    • bold
    • charset
    • color
    • size
    • font name
    • italic
    • strikeout
    • subscript/superscript
    • underline

... by specifying style properties directly, or by specifying a class name that is defined in a CSS-like file with the "class" attribute.

Expression Output jx:out tag or ${expression} ${expression} only
Hide Columns, Rows, and Sheets
  • Before jXLS 2: could hide individual columns
  • jXLS 2: can hide entire sheets
jt:hideCols, jt:hideRows, jt:hideSheet tags
Perform SQL-like "group by" Operations on Data Not supported jt:agg tag, which also supports super-aggregate operations such as rollups and cube
Tag Support for Aggregate Expressions Not supported jt:total tag
Perform Analytic Operations on Data Not supported jt:ana tag
Don't Transform an Expression Not supported jt:null tag, and also \${expression}
Other
SQL Execution in the Template Supported, with the ReportManager bean. Supported, with the JDBCExecutor bean. Also supported are "?" placeholders in the query, plus additional arguments passed as bind variable values.
Access to POI Objects in the Template workbook, sheet, hssfRow workbook, sheet, cell
Execution Hooks for Custom Processing CellProcessor, PropertyPreprocessor, RowProcessor CellListener, TagListener, TagLoopListener, SheetListener
Fixed Size Collections Supported on a collection name basis Supported on a collection name basis, plus the "fixed" attribute for looping tags, plus implicit collections processing.
Custom Tag Libraries Not documented, but it is possible by supplying a subclass of Configuration to the XLSTransformer. This subclass registers a custom TagLib. Supported, through the direct registration of a custom TagLibrary in the ExcelTransformer.
Expose JEXL Engine Properties Not supported. Silent, Lenient, and Debug flags are exposed. Controlling the size of the JEXL Engine parse cache is exposed. Adding JEXL custom namespace functions is exposed.

Guide to Converting Templates from jXLS to JETT

While there are some features of JETT that do not exist in jXLS, and some features of jXLS that do not exist in JETT, there are lots of features that are common, but sometimes the syntax varies. Here is a guide comparing those commonly used features:

Collections Display

Here is an example of jXLS's "forEach" tag. Notice how separate rows are needed for the "jx:forEach" tag, and separate cells are needed for the "jx:if" tag. Each cell is less cluttered, but jXLS needs to perform extra shifting when removing the tags.

Employee Salary Manager    
<jx:forEach items="${employees}" var="employee" select="${employee.salary >= 900}" varStatus="status">        
${status.index + 1}. ${employee.lastName}, ${employee.firstName} ${employee.salary} <jx:if test="${employee.getManager() != null}"> ${employee.manager.lastName}, ${employee.manager.firstName} </jx:if>
</jx:forEach>        

Here is the equivalent JETT "forEach" tag. The cells with tags are more cluttered, but no shifting to remove tags is needed.

Employee Salary Manager
<jt:forEach items="${employees}" var="employee" where="${employee.salary >= 900}" indexVar="index">${index + 1}. ${employee.lastName}, ${employee.firstName} ${employee.salary} <jt:if test="${employee.getManager() != null}" then="${employee.manager.lastName}, ${employee.manager.firstName}"/></jt:forEach>

Both templates would, in their respective frameworks, yield the following transformation. Notice how Elmer Fudd, salary $800, is not present.

Employee Salary Manager
1. Stack, Robert $1000.00  
2. Queue, Suzie $900.00 Stack, Robert
3. Bunny, Bugs $1500.00  

Displaying Collections Data to the Right

Here is an example of jXLS's "forEach" tag. Notice how one "forEach" tag is needed per row.

Employee <jx:forEach items="${employees}" var="employee"> ${employee.lastName}, ${employee.firstName} </jx:forEach>    
Salary <jx:forEach items="${employees}" var="employee"> ${employee.salary} </jx:forEach>    
Manager <jx:forEach items="${employees}" var="employee"> <jx:if test="${employee.getManager() != null}"> ${employee.manager.lastName}, ${employee.manager.firstName} </jx:if> </jx:forEach>

Here is the equivalent template in JETT, using only one "forEach" tag. Notice the use of the "copyRight" attribute to determine directionality.

Employee <jt:forEach items="${employees}" var="employee" copyRight="true">${employee.lastName}, ${employee.firstName}        
Salary ${employee.salary}        
Manager <jt:if test="${employee.getManager() != null}">${employee.manager.lastName}, ${employee.manager.firstName}</jt:if></jt:forEach>        

Both templates would, in their respective frameworks, yield the following transformation.

Employee Stack, Robert Queue, Suzie Fudd, Elmer Bunny, Bugs  
Salary $1,000.00 $900.00 $800.00 $1,500.00  
Manager   Stack, Robert Bunny, Bugs    

Implicit Collections Processing

Here is an example of jXLS's implicit collections processing. Notice the use of "//collName" and "//:2" on different cells to control the processing.

Company: ${companyName} Name://employees ${employees.lastName}, ${employees.firstName}//:2 Department Name://departments ${departments.name}//:2 ID: ${departments.id}
  Salary: ${employees.salary} Bonus: ${employees.bonus} Number of Employees://departments ${departments.numEmployees}  

Here is an example of JETT's implicit collections processing. Notice the use of metadata keys "extraRows", "left", and "right", all on one cell, to control the processing.

Company: ${companyName} Name: ${employees.lastName}, ${employees.firstName}?@extraRows=1;left=1;right=3 Department Name: ${departments.name} ID: ${departments.id}
  Salary: ${employees.salary} Bonus: ${employees.bonus} Number of Employees: ${departments.numEmployees}  

Both templates would, in their respective frameworks, yield the following transformation. Notice how the company name is not copied with the rest of the collections.

Company: Whatsit Name: Stack, Robert Department Name: Application Development ID: 101
  Salary: 1000 Bonus: 200 Number of Employees: 3  
  Name: Queue, Suzie Department Name: Human Resources ID: 102
  Salary: 900 Bonus: 300 Number of Employees: 2  
  Name: Fudd, Elmer Department Name: Quality Assurance ID: 103
  Salary: 800 Bonus: 400 Number of Employees: 2  
  Name: Bunny, Bugs      
  Salary: 1500 Bonus: 500      

JDBC Query Execution in the Template

Here is an example of the usage of the ReportManager in jXLS:

    Connection conn = getConnection();
    Map<String, Object> beans = new HashMap<String, Object>();
    ReportManager rm = new ReportManagerImpl(conn, beans);
    beans.put("rm", rm);
    Workbook workbook = transformer.transformXLS(inputStream, beans);
<jx:forEach items="${rm.exec('SELECT first_name, last_name, salary, title FROM employee')}" var="employee">

JETT's JDBCExecutor is very similar:

    Connection conn = getConnection();
    Map<String, Object> beans = new HashMap<String, Object>();
    JDBCExecutor jdbc = new JDBCExecutor(conn);
    beans.put("jdbc", jdbc);
    Workbook workbook = transformer.transform(inputStream, beans);
<jt:forEach items="${jdbc.execQuery('SELECT first_name, last_name, salary, title FROM employee')}" var="employee">

However, a JDBCExecutor also has the ability to run JDBC select statements that contain ? placeholders. There must be one additional argument passed to the execQuery method for each ?.

<jt:forEach items="${jdbc.execQuery('SELECT first_name, last_name, salary, title FROM employee WHERE title = ?', myTitle)}" var="employee">