|
@@ -1,225 +1,295 @@
|
|
|
-//package com.yaoyicloud.render;
|
|
|
+package com.yaoyicloud.render;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+
|
|
|
+import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import com.yaoyicloud.config.FilerepoProperties;
|
|
|
+import com.yaoyicloud.message.FxyProtos;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFRun;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFTable;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFTableCell;
|
|
|
+import org.apache.poi.xwpf.usermodel.XWPFTableRow;
|
|
|
+import com.deepoove.poi.XWPFTemplate;
|
|
|
+import com.deepoove.poi.config.Configure;
|
|
|
+import com.deepoove.poi.config.ConfigureBuilder;
|
|
|
+import com.deepoove.poi.exception.RenderException;
|
|
|
+import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
|
|
+import com.deepoove.poi.policy.RenderPolicy;
|
|
|
+import com.deepoove.poi.template.ElementTemplate;
|
|
|
+import com.deepoove.poi.template.run.RunTemplate;
|
|
|
+import com.deepoove.poi.util.TableTools;
|
|
|
+import com.google.protobuf.util.JsonFormat;
|
|
|
+import com.yaoyicloud.message.FxyProtos.FinancialInfo;
|
|
|
+import com.yaoyicloud.tools.Util;
|
|
|
+
|
|
|
+/**
|
|
|
+ * FinancialInfo渲染器
|
|
|
+ *
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+public final class FinancialInfoRender extends AbstractRender {
|
|
|
+ private final FilerepoProperties filerepoProperties;
|
|
|
+ public FinancialInfoRender(String cwd, FilerepoProperties filerepoProperties, FilerepoProperties filerepoProperties1) {
|
|
|
+ super(cwd);
|
|
|
+ this.filerepoProperties = filerepoProperties1;
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ protected String getBasicPath() throws IOException {
|
|
|
+ return filerepoProperties.getBasePath();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected String getReportImagePath() {
|
|
|
+ return filerepoProperties.getReportImagePath();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Docx 渲染
|
|
|
+ *
|
|
|
+ * @param info 数据
|
|
|
+ * @param templateFileContent 模板内容
|
|
|
+ * @return 本地文件目录
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
+
|
|
|
+ public String renderDocx(String info, Map<String, Object> addtionalMap, byte[] templateFileContent, String relationId) throws IOException {
|
|
|
+ log.info("开始渲染附件模块,relationId: {}", relationId);
|
|
|
+
|
|
|
+ // 配置POI-TL渲染器
|
|
|
+ ConfigureBuilder builder = Configure.builder();
|
|
|
+ RenderPolicy indicatorsRenderPolicy = this.indicatorsRenderPolicy();
|
|
|
+ builder.bind("basicInfoChecks", indicatorsRenderPolicy);
|
|
|
+ FxyProtos.BasicInfo.Builder basicInfoBuilder = FxyProtos.BasicInfo.newBuilder();
|
|
|
+ JsonFormat.parser().merge(info, basicInfoBuilder);
|
|
|
+
|
|
|
+ FxyProtos.BasicInfo defaultInstance = FxyProtos.BasicInfo.getDefaultInstance();
|
|
|
+ FxyProtos.BasicInfo mergedProto = defaultInstance.toBuilder()
|
|
|
+ .mergeFrom(basicInfoBuilder.build())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ String completeJson = JsonFormat.printer()
|
|
|
+ .includingDefaultValueFields()
|
|
|
+ .print(mergedProto);
|
|
|
+ ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+ Map<String, Object> data = objectMapper.readValue(completeJson, new TypeReference<Map<String, Object>>() {});
|
|
|
+ data.replaceAll((k, v) -> v.equals("") ? "-" : v);
|
|
|
+
|
|
|
+ if (addtionalMap != null) {
|
|
|
+ data.putAll(addtionalMap);
|
|
|
+ }
|
|
|
+ fillBasicDefaultValues(data);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 渲染文档
|
|
|
+ String resultPath = this.renderDocx(data, templateFileContent, builder, relationId);
|
|
|
+ log.info("渲染附件模块成功,文件路径: {}", resultPath);
|
|
|
+ return resultPath;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("渲染附件模块失败,relationId: {}", relationId, e);
|
|
|
+ throw new IOException("文档渲染失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 填充默认值,确保所有必要字段都存在
|
|
|
+ */
|
|
|
+ private void fillBasicDefaultValues(Map<String, Object> data) {
|
|
|
+ Map<String, Object> basicInfoSummary = (Map<String, Object>) data.get("basicInfoSummary");
|
|
|
+ basicInfoSummary.replaceAll((k, v) -> v.equals("") ? "-" : v);
|
|
|
+ Map<String, Object> platformExt = (Map<String, Object>) data.get("platformExt");
|
|
|
+ platformExt.replaceAll((k, v) -> v.equals("") ? "-" : v);
|
|
|
+ Object certReceived = data.get("certReceived");
|
|
|
+ if (certReceived.equals("-")) {
|
|
|
+ data.put("certReceived", "否");
|
|
|
+ }
|
|
|
+ Object bankLicense = data.get("bankLicense");
|
|
|
+ if (bankLicense.equals("-")) {
|
|
|
+ data.put("bankLicense", "否");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+// public String renderDocx(FinancialInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
|
|
|
//
|
|
|
-//import java.io.IOException;
|
|
|
-//import java.nio.file.Paths;
|
|
|
-//import java.util.HashMap;
|
|
|
-//import java.util.List;
|
|
|
-//import java.util.Map;
|
|
|
-//import java.util.UUID;
|
|
|
+// // 不需要定制展示逻辑的时候,使用protobuf的转json方法
|
|
|
+// String jsonStr = JsonFormat.printer().print(info);
|
|
|
//
|
|
|
-//import org.apache.poi.xwpf.usermodel.XWPFParagraph;
|
|
|
-//import org.apache.poi.xwpf.usermodel.XWPFRun;
|
|
|
-//import org.apache.poi.xwpf.usermodel.XWPFTable;
|
|
|
-//import org.apache.poi.xwpf.usermodel.XWPFTableCell;
|
|
|
-//import org.apache.poi.xwpf.usermodel.XWPFTableRow;
|
|
|
-//import com.deepoove.poi.XWPFTemplate;
|
|
|
-//import com.deepoove.poi.config.Configure;
|
|
|
-//import com.deepoove.poi.config.ConfigureBuilder;
|
|
|
-//import com.deepoove.poi.exception.RenderException;
|
|
|
-//import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
|
|
-//import com.deepoove.poi.policy.RenderPolicy;
|
|
|
-//import com.deepoove.poi.template.ElementTemplate;
|
|
|
-//import com.deepoove.poi.template.run.RunTemplate;
|
|
|
-//import com.deepoove.poi.util.TableTools;
|
|
|
-//import com.google.protobuf.util.JsonFormat;
|
|
|
-//import com.yaoyicloud.message.FxyProtos.FinancialInfo;
|
|
|
-//import com.yaoyicloud.tools.Util;
|
|
|
+// // 注: 报告模板的模板变量按照json序列化的结果命名
|
|
|
+// // 注: 目前的实现假设:一个session对应一个cwd目录
|
|
|
+// ConfigureBuilder builder = Configure.builder();
|
|
|
+// builder.bind("indicators", new LoopRowIncludeStatisticsTableRenderPolicy("values"));
|
|
|
+// builder.bind("financialDataSeq", new LoopColumnStaticTableRenderPolicy("[", "]", false, true, 2));
|
|
|
+// builder.bind("financialCheckDetails", new LoopRowCutAndMergeFirstColTableRenderPolicy());
|
|
|
+// // 注意使用了SpringEL之后,每个模板变量都要设置值,不然会报错
|
|
|
+// builder.useSpringEL();
|
|
|
//
|
|
|
-///**
|
|
|
-// * FinancialInfo渲染器
|
|
|
-// *
|
|
|
-// */
|
|
|
-//public final class FinancialInfoRender extends AbstractRender {
|
|
|
-//
|
|
|
-// public FinancialInfoRender(String cwd) {
|
|
|
-// super(cwd);
|
|
|
-// }
|
|
|
-//
|
|
|
-// /**
|
|
|
-// * Docx 渲染
|
|
|
-// *
|
|
|
-// * @param info 数据
|
|
|
-// * @param templateFileContent 模板内容
|
|
|
-// * @return 本地文件目录
|
|
|
-// * @throws IOException
|
|
|
-// */
|
|
|
-//// public String renderDocx(FinancialInfo info, Map<String, Object> addtionalMap, byte[] templateFileContent) throws IOException {
|
|
|
-////
|
|
|
-//// // 不需要定制展示逻辑的时候,使用protobuf的转json方法
|
|
|
-//// String jsonStr = JsonFormat.printer().print(info);
|
|
|
-////
|
|
|
-//// // 注: 报告模板的模板变量按照json序列化的结果命名
|
|
|
-//// // 注: 目前的实现假设:一个session对应一个cwd目录
|
|
|
-//// ConfigureBuilder builder = Configure.builder();
|
|
|
-//// builder.bind("indicators", new LoopRowIncludeStatisticsTableRenderPolicy("values"));
|
|
|
-//// builder.bind("financialDataSeq", new LoopColumnStaticTableRenderPolicy("[", "]", false, true, 2));
|
|
|
-//// builder.bind("financialCheckDetails", new LoopRowCutAndMergeFirstColTableRenderPolicy());
|
|
|
-//// // 注意使用了SpringEL之后,每个模板变量都要设置值,不然会报错
|
|
|
-//// builder.useSpringEL();
|
|
|
-////
|
|
|
-//// this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
|
|
|
-//// Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
|
|
|
-//// return this.docxResultPath;
|
|
|
-//// }
|
|
|
-//
|
|
|
-// /**
|
|
|
-// * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
|
|
|
-// */
|
|
|
-// public class LoopColumnStaticTableRenderPolicy implements RenderPolicy {
|
|
|
-//
|
|
|
-// private String prefix;
|
|
|
-// private String suffix;
|
|
|
-// private boolean onSameLine;
|
|
|
-// private boolean reverse;
|
|
|
-// private int valColIndex;
|
|
|
-//
|
|
|
-// public LoopColumnStaticTableRenderPolicy() {
|
|
|
-// this(false);
|
|
|
-// }
|
|
|
-//
|
|
|
-// public LoopColumnStaticTableRenderPolicy(boolean onSameLine) {
|
|
|
-// this("[", "]", onSameLine);
|
|
|
-// }
|
|
|
-//
|
|
|
-// public LoopColumnStaticTableRenderPolicy(String prefix, String suffix) {
|
|
|
-// this(prefix, suffix, false);
|
|
|
-// }
|
|
|
-//
|
|
|
-// public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine) {
|
|
|
-// this(prefix, suffix, onSameLine, false);
|
|
|
-// }
|
|
|
-//
|
|
|
-// public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse) {
|
|
|
-// this(prefix, suffix, onSameLine, false, 1);
|
|
|
-// }
|
|
|
-//
|
|
|
-// public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse,
|
|
|
-// int valRowIndex) {
|
|
|
-// this.prefix = prefix;
|
|
|
-// this.suffix = suffix;
|
|
|
-// this.onSameLine = onSameLine;
|
|
|
-// this.reverse = reverse;
|
|
|
-// this.valColIndex = valRowIndex;
|
|
|
-// }
|
|
|
-//
|
|
|
-// @Override
|
|
|
-// public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
|
|
|
-// RunTemplate runTemplate = (RunTemplate) eleTemplate;
|
|
|
-// XWPFRun run = runTemplate.getRun();
|
|
|
-// try {
|
|
|
-// if (!TableTools.isInsideTable(run)) {
|
|
|
-// throw new IllegalStateException(
|
|
|
-// "The template tag " + runTemplate.getSource() + " must be inside a table");
|
|
|
-// }
|
|
|
-// XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
|
|
|
-// XWPFTable table = tagCell.getTableRow().getTable();
|
|
|
-// run.setText("", 0);
|
|
|
-//
|
|
|
-// int templateColIndex = getTemplateColIndex(tagCell);
|
|
|
-// // 模版变量列总是写在左边
|
|
|
-// int minIndex = templateColIndex;
|
|
|
-// int maxIndex = table.getRows().get(valColIndex).getTableCells().size() - 1;
|
|
|
-// int currIndex = reverse ? maxIndex : minIndex;
|
|
|
-// int indexDelta = reverse ? -1 : 1;
|
|
|
-//
|
|
|
-// // 目前expression当作数据Map的key来对待,将来可以当作POI-TL变量一致化的处理
|
|
|
-// Map<Integer, String> idx2Expression = new HashMap<>();
|
|
|
-// for (int i = 0; i < table.getRows().size(); i++) {
|
|
|
-// XWPFTableCell cell = table.getRows().get(i).getCell(templateColIndex);
|
|
|
-// String text = cell.getText().trim();
|
|
|
-// if (text.startsWith(prefix) && text.endsWith(suffix)) {
|
|
|
-// idx2Expression.put(i, text.substring(1, text.length() - 1));
|
|
|
-// cell.setText("");
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// int rowSize = table.getRows().size();
|
|
|
-// @SuppressWarnings("unchecked")
|
|
|
-// List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
|
|
|
-//
|
|
|
-// for (Map<String, Object> realData : mpData) {
|
|
|
-// for (int i = 0; i < rowSize; i++) {
|
|
|
-// if (!idx2Expression.containsKey(i)) {
|
|
|
-// continue;
|
|
|
-// }
|
|
|
-// XWPFTableRow row = table.getRow(i);
|
|
|
-// XWPFTableCell valueCell = row.getCell(currIndex);
|
|
|
-// String valStr = realData.getOrDefault(idx2Expression.get(i), "-").toString();
|
|
|
-// valueCell.setText(valStr);
|
|
|
-// }
|
|
|
-// currIndex += indexDelta;
|
|
|
-// }
|
|
|
-//
|
|
|
-// } catch (Exception e) {
|
|
|
-// throw new RenderException("HackLoopTable for " + eleTemplate + "error: " + e.getMessage(), e);
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// private int getTemplateColIndex(XWPFTableCell tagCell) {
|
|
|
-// return onSameLine ? Util.getColIndexOfFirstRow(tagCell) : (Util.getColIndexOfFirstRow(tagCell) + 1);
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// public class LoopRowCutAndMergeFirstColTableRenderPolicy extends LoopRowTableRenderPolicy {
|
|
|
-//
|
|
|
-// @Override
|
|
|
-// public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
|
|
|
-// RunTemplate runTemplate = (RunTemplate) eleTemplate;
|
|
|
-// XWPFRun run = runTemplate.getRun();
|
|
|
-// XWPFTable table = null;
|
|
|
-// try {
|
|
|
-// if (!TableTools.isInsideTable(run)) {
|
|
|
-// throw new IllegalStateException(
|
|
|
-// "The template tag " + runTemplate.getSource() + " must be inside a table");
|
|
|
-// }
|
|
|
-// // Reserve the first two rows
|
|
|
-// XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
|
|
|
-// table = tagCell.getTableRow().getTable();
|
|
|
-// for (int i = table.getNumberOfRows() - 1; i > 1; i--) {
|
|
|
-// table.removeRow(i);
|
|
|
-// }
|
|
|
-// } catch (Exception e) {
|
|
|
-// throw new RenderException(
|
|
|
-// "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
|
|
|
-// }
|
|
|
-//
|
|
|
-// // in case data not sorted by rank
|
|
|
-// @SuppressWarnings("unchecked")
|
|
|
-// List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
|
|
|
-// mpData.sort((a, b) -> Integer.valueOf(a.getOrDefault("rank", "0").toString())
|
|
|
-// - Integer.valueOf(b.getOrDefault("rank", "0").toString()));
|
|
|
-//
|
|
|
-// super.render(eleTemplate, data, template);
|
|
|
-//
|
|
|
-// try {
|
|
|
-// // merge the first column
|
|
|
-// Util.mergeFirstNColSimple(table, 1, 0);
|
|
|
-// } catch (Exception e) {
|
|
|
-// throw new RenderException(
|
|
|
-// "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-//
|
|
|
-// public class LoopRowIncludeStatisticsTableRenderPolicy extends LoopRowTableRenderPolicy {
|
|
|
-//
|
|
|
-// private String valueTag;
|
|
|
-//
|
|
|
-// public LoopRowIncludeStatisticsTableRenderPolicy(String valueTag) {
|
|
|
-// this.valueTag = valueTag;
|
|
|
-// }
|
|
|
-//
|
|
|
-// @Override
|
|
|
-// public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
|
|
|
-// @SuppressWarnings("unchecked")
|
|
|
-// List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
|
|
|
-// for (Map<String, Object> row : mpData) {
|
|
|
-// @SuppressWarnings("unchecked")
|
|
|
-// List<String> values = (List<String>) row.get(valueTag);
|
|
|
-// row.put("avg", values.stream().mapToLong(Long::valueOf).average().orElse(Double.NaN));
|
|
|
-// }
|
|
|
-//
|
|
|
-// super.render(eleTemplate, data, template);
|
|
|
-// }
|
|
|
+// this.docxResultPath = this.renderDocx(jsonStr, addtionalMap, templateFileContent, builder,
|
|
|
+// Paths.get(cwd, UUID.randomUUID().toString() + ".docx").toString());
|
|
|
+// return this.docxResultPath;
|
|
|
// }
|
|
|
-//}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 这些render policy类都应当是共享的 重要设计假设: data的类型cast都可以建立在json通用反序列化后的基本类型基础上。
|
|
|
+ */
|
|
|
+ public class LoopColumnStaticTableRenderPolicy implements RenderPolicy {
|
|
|
+
|
|
|
+ private String prefix;
|
|
|
+ private String suffix;
|
|
|
+ private boolean onSameLine;
|
|
|
+ private boolean reverse;
|
|
|
+ private int valColIndex;
|
|
|
+
|
|
|
+ public LoopColumnStaticTableRenderPolicy() {
|
|
|
+ this(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public LoopColumnStaticTableRenderPolicy(boolean onSameLine) {
|
|
|
+ this("[", "]", onSameLine);
|
|
|
+ }
|
|
|
+
|
|
|
+ public LoopColumnStaticTableRenderPolicy(String prefix, String suffix) {
|
|
|
+ this(prefix, suffix, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine) {
|
|
|
+ this(prefix, suffix, onSameLine, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse) {
|
|
|
+ this(prefix, suffix, onSameLine, false, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ public LoopColumnStaticTableRenderPolicy(String prefix, String suffix, boolean onSameLine, boolean reverse,
|
|
|
+ int valRowIndex) {
|
|
|
+ this.prefix = prefix;
|
|
|
+ this.suffix = suffix;
|
|
|
+ this.onSameLine = onSameLine;
|
|
|
+ this.reverse = reverse;
|
|
|
+ this.valColIndex = valRowIndex;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
|
|
|
+ RunTemplate runTemplate = (RunTemplate) eleTemplate;
|
|
|
+ XWPFRun run = runTemplate.getRun();
|
|
|
+ try {
|
|
|
+ if (!TableTools.isInsideTable(run)) {
|
|
|
+ throw new IllegalStateException(
|
|
|
+ "The template tag " + runTemplate.getSource() + " must be inside a table");
|
|
|
+ }
|
|
|
+ XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
|
|
|
+ XWPFTable table = tagCell.getTableRow().getTable();
|
|
|
+ run.setText("", 0);
|
|
|
+
|
|
|
+ int templateColIndex = getTemplateColIndex(tagCell);
|
|
|
+ // 模版变量列总是写在左边
|
|
|
+ int minIndex = templateColIndex;
|
|
|
+ int maxIndex = table.getRows().get(valColIndex).getTableCells().size() - 1;
|
|
|
+ int currIndex = reverse ? maxIndex : minIndex;
|
|
|
+ int indexDelta = reverse ? -1 : 1;
|
|
|
+
|
|
|
+ // 目前expression当作数据Map的key来对待,将来可以当作POI-TL变量一致化的处理
|
|
|
+ Map<Integer, String> idx2Expression = new HashMap<>();
|
|
|
+ for (int i = 0; i < table.getRows().size(); i++) {
|
|
|
+ XWPFTableCell cell = table.getRows().get(i).getCell(templateColIndex);
|
|
|
+ String text = cell.getText().trim();
|
|
|
+ if (text.startsWith(prefix) && text.endsWith(suffix)) {
|
|
|
+ idx2Expression.put(i, text.substring(1, text.length() - 1));
|
|
|
+ cell.setText("");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ int rowSize = table.getRows().size();
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
|
|
|
+
|
|
|
+ for (Map<String, Object> realData : mpData) {
|
|
|
+ for (int i = 0; i < rowSize; i++) {
|
|
|
+ if (!idx2Expression.containsKey(i)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ XWPFTableRow row = table.getRow(i);
|
|
|
+ XWPFTableCell valueCell = row.getCell(currIndex);
|
|
|
+ String valStr = realData.getOrDefault(idx2Expression.get(i), "-").toString();
|
|
|
+ valueCell.setText(valStr);
|
|
|
+ }
|
|
|
+ currIndex += indexDelta;
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RenderException("HackLoopTable for " + eleTemplate + "error: " + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private int getTemplateColIndex(XWPFTableCell tagCell) {
|
|
|
+ return onSameLine ? Util.getColIndexOfFirstRow(tagCell) : (Util.getColIndexOfFirstRow(tagCell) + 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public class LoopRowCutAndMergeFirstColTableRenderPolicy extends LoopRowTableRenderPolicy {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
|
|
|
+ RunTemplate runTemplate = (RunTemplate) eleTemplate;
|
|
|
+ XWPFRun run = runTemplate.getRun();
|
|
|
+ XWPFTable table = null;
|
|
|
+ try {
|
|
|
+ if (!TableTools.isInsideTable(run)) {
|
|
|
+ throw new IllegalStateException(
|
|
|
+ "The template tag " + runTemplate.getSource() + " must be inside a table");
|
|
|
+ }
|
|
|
+ // Reserve the first two rows
|
|
|
+ XWPFTableCell tagCell = (XWPFTableCell) ((XWPFParagraph) run.getParent()).getBody();
|
|
|
+ table = tagCell.getTableRow().getTable();
|
|
|
+ for (int i = table.getNumberOfRows() - 1; i > 1; i--) {
|
|
|
+ table.removeRow(i);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RenderException(
|
|
|
+ "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
|
|
|
+ }
|
|
|
+
|
|
|
+ // in case data not sorted by rank
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
|
|
|
+ mpData.sort((a, b) -> Integer.valueOf(a.getOrDefault("rank", "0").toString())
|
|
|
+ - Integer.valueOf(b.getOrDefault("rank", "0").toString()));
|
|
|
+
|
|
|
+ super.render(eleTemplate, data, template);
|
|
|
+
|
|
|
+ try {
|
|
|
+ // merge the first column
|
|
|
+ Util.mergeFirstNColSimple(table, 1, 0);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RenderException(
|
|
|
+ "LoopRowCutAndMergeFirstColTable for " + eleTemplate + " error: " + e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public class LoopRowIncludeStatisticsTableRenderPolicy extends LoopRowTableRenderPolicy {
|
|
|
+
|
|
|
+ private String valueTag;
|
|
|
+
|
|
|
+ public LoopRowIncludeStatisticsTableRenderPolicy(String valueTag) {
|
|
|
+ this.valueTag = valueTag;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void render(ElementTemplate eleTemplate, Object data, XWPFTemplate template) {
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ List<Map<String, Object>> mpData = (List<Map<String, Object>>) data;
|
|
|
+ for (Map<String, Object> row : mpData) {
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ List<String> values = (List<String>) row.get(valueTag);
|
|
|
+ row.put("avg", values.stream().mapToLong(Long::valueOf).average().orElse(Double.NaN));
|
|
|
+ }
|
|
|
+
|
|
|
+ super.render(eleTemplate, data, template);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|