Easypoi_4.2.0源码修改 word模板导出 增加表格第一列非循环语句时word导出
1. 写在前面
之前写过easypoi修改源码的文章,写的很细,但是发现基本没什么反响。这篇文章不会太详细的介绍如何定位问题,只会简单记录,最后把修改完的代码贴出来,并实现功能。
2. 开始寻找突破口
首先定位到问题,当word模板中存在表格的情况下,并且表格的第一列是固定,如下图中蓝框中的内容是固定,这时使用 !fe: 遍历的时候,会发现毫无效果。问题定位到了,那就一步一步来找到原因所在吧。
3. 解决问题
首先找到代码入口。查看该方法exportWord07
WordExportUtil.exportWord07("班级.docx", map)
经过一系列的调试,定位到了ParseWord07类中的parseThisTable方法。源码如下:
private void parseThisTable(XWPFTable table, Map<String, Object> map) throws Exception {
for(int i = 0; i < table.getNumberOfRows(); ++i) {
XWPFTableRow row = table.getRow(i);
List<XWPFTableCell> cells = row.getTableCells();
Object listobj = this.checkThisTableIsNeedIterator((XWPFTableCell)cells.get(0), map);
if (listobj == null) {
this.parseThisRow(cells, map);
} else if (listobj instanceof ExcelListEntity) {
(new ExcelEntityParse()).parseNextRowAndAddRow(table, i, (ExcelListEntity)listobj);
i = i + ((ExcelListEntity)listobj).getList().size() - 1;
} else {
ExcelMapParse.parseNextRowAndAddRow(table, i, (List)listobj);
i = i + ((List)listobj).size() - 1;
}
}
}
我们需要改的地方就是第五行,之后替换表格内容的代码最重要的就是listobj参数。这源代码中第五行很明显,他只是根据第一列的内容来获取listobj,而我们的模板循环语句并未在第一列上,因此导致word导出时,无法替换内容。原因讲完了,下面都是动手节了。
首先将ParseWord07类中的parseThisTable方法 修改成以下代码。
private void parseThisTable(XWPFTable table, Map<String, Object> map) throws Exception {
for (int i = 0; i < table.getNumberOfRows(); ++i) {
XWPFTableRow row = table.getRow(i);
List<XWPFTableCell> cells = row.getTableCells();
//此部分为修改的源代码
//作用 用于识别word中表格非第一列的循环语法
Object listobj = null;
int col = 0;
for (XWPFTableCell cell : cells) {
listobj = this.checkThisTableIsNeedIterator(cell, map);
if (listobj != null) {
break;
}
col++;
}
if (listobj == null) {
this.parseThisRow(cells, map);
} else if (listobj instanceof ExcelListEntity) {
(new ExcelEntityParse()).parseNextRowAndAddRow(table, i, (ExcelListEntity) listobj);
i = i + ((ExcelListEntity) listobj).getList().size() - 1;
} else {
LcExcelMapParse.parseNextRowAndAddRow(table, i, (List) listobj, col);
i = i + ((List) listobj).size() - 1;
}
}
}
再找到ExcelMapParse类中的parseNextRowAndAddRow方法,修改成以下代码
public static void parseNextRowAndAddRow(XWPFTable table, int index, List<Object> list, int col) throws Exception {
XWPFTableRow currentRow = table.getRow(index);
String[] params = parseCurrentRowGetParams(currentRow);
String listname = params[col];
boolean isCreate = !listname.contains("!fe:");
listname = listname.replace("!fe:", "").replace("$fe:", "").replace("fe:", "").replace("{{", "");
String[] keys = listname.replaceAll("\\s{1,}", " ").trim().split(" ");
params[col] = keys[1];
List<XWPFTableCell> tempCellList = new ArrayList();
tempCellList.addAll(table.getRow(index).getTableCells());
// int cellIndex = false;
Map<String, Object> tempMap = Maps.newHashMap();
LOGGER.debug("start for each data list :{}", list.size());
Iterator var11 = list.iterator();
while (var11.hasNext()) {
Object obj = var11.next();
currentRow = isCreate ? table.insertNewTableRow(index++) : table.getRow(index++);
tempMap.put("t", obj);
//如果有合并单元格情况,会导致params越界,这里需要补齐
String[] paramsNew = (String[]) ArrayUtils.clone(params);
if (params.length < currentRow.getTableCells().size()) {
for (int i = 0; i < currentRow.getTableCells().size() - params.length; i++) {
paramsNew = (String[]) ArrayUtils.add(paramsNew, 0, "placeholderLc_" + i);
}
}
String val;
int cellIndex;
for (cellIndex = 0; cellIndex < currentRow.getTableCells().size(); ++cellIndex) {
val = PoiElUtil.eval(paramsNew[cellIndex], tempMap).toString();
//源代码的bug 此方法无法删除单元格中的内容
//currentRow.getTableCells().get(cellIndex).setText("");
//使用此方法清空单元格内容
if (!Strings.isNullOrEmpty(val)) {
currentRow.getTableCells().get(cellIndex).getParagraphs().forEach(p -> p.getRuns().forEach(r -> r.setText("", 0)));
}
PoiWordStyleUtil.copyCellAndSetValue(cellIndex >= tempCellList.size() ? tempCellList.get(tempCellList.size() - 1) : tempCellList.get(cellIndex)
, currentRow.getTableCells().get(cellIndex), val);
}
while (cellIndex < paramsNew.length) {
val = PoiElUtil.eval(paramsNew[cellIndex], tempMap).toString();
PoiWordStyleUtil.copyCellAndSetValue((XWPFTableCell) tempCellList.get(cellIndex), currentRow.createCell(), val);
++cellIndex;
}
}
table.removeRow(index);
}
至此源代码全部修改完毕,这次没有解释太多,因为估计真有遇到需要修改源代码的时候,大部分人选择的是放弃使用这个第三方类库,修改源码也确实有一定难度,所以这个基本用于记录自己解决问题的整个流程,方便以后看得时候能快速想起来。