diff --git a/easypoi-base/src/main/java/org/jeecgframework/poi/excel/entity/TemplateExportParams.java b/easypoi-base/src/main/java/org/jeecgframework/poi/excel/entity/TemplateExportParams.java index e9cc3cf..bc37195 100644 --- a/easypoi-base/src/main/java/org/jeecgframework/poi/excel/entity/TemplateExportParams.java +++ b/easypoi-base/src/main/java/org/jeecgframework/poi/excel/entity/TemplateExportParams.java @@ -47,6 +47,10 @@ public class TemplateExportParams extends ExcelBaseParams { * Excel 导出style */ private Class style = ExcelExportStylerDefaultImpl.class; + /** + * FOR EACH 用到的局部变量 + */ + private String tempParams = "t"; /** * 默认构造器 @@ -168,4 +172,12 @@ public class TemplateExportParams extends ExcelBaseParams { this.scanAllsheet = scanAllsheet; } + public String getTempParams() { + return tempParams; + } + + public void setTempParams(String tempParams) { + this.tempParams = tempParams; + } + } diff --git a/easypoi-base/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java b/easypoi-base/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java index 3b381d2..0f2953d 100644 --- a/easypoi-base/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java +++ b/easypoi-base/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java @@ -27,7 +27,11 @@ import org.jeecgframework.poi.excel.export.base.ExcelExportBase; import org.jeecgframework.poi.excel.export.styler.IExcelExportStyler; import org.jeecgframework.poi.exception.excel.ExcelExportException; import org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum; + +import static org.jeecgframework.poi.util.PoiElUtil.*; + import org.jeecgframework.poi.util.PoiPublicUtil; +import org.jeecgframework.poi.util.PoiSheetUtility; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,34 +44,34 @@ import org.slf4j.LoggerFactory; */ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { - private static final Logger LOGGER = LoggerFactory - .getLogger(ExcelExportOfTemplateUtil.class); + private static final Logger LOGGER = LoggerFactory + .getLogger(ExcelExportOfTemplateUtil.class); - private static final String START_STR = "{{"; - private static final String END_STR = "}}"; - private static final String NUMBER_SYMBOL = "N:"; /** * 缓存TEMP 的for each创建的cell ,跳过这个cell的模板语法查找,提高效率 */ - private Set tempCreateCellSet = new HashSet(); + private Set tempCreateCellSet = new HashSet(); + /** + * 模板参数,全局都用到 + */ + private TemplateExportParams teplateParams; /** * 往Sheet 填充正常数据,根据表头信息 使用导入的部分逻辑,坐对象映射 * - * @param params + * @param teplateParams * @param pojoClass * @param dataSet * @param workbook */ - private void addDataToSheet(TemplateExportParams params, Class pojoClass, - Collection dataSet, Sheet sheet, Workbook workbook) - throws Exception { + private void addDataToSheet(Class pojoClass, Collection dataSet, Sheet sheet, + Workbook workbook) throws Exception { if (workbook instanceof XSSFWorkbook) { super.type = ExcelType.XSSF; } // 获取表头数据 - Map titlemap = getTitleMap(params, sheet); + Map titlemap = getTitleMap(sheet); Drawing patriarch = sheet.createDrawingPatriarch(); // 得到所有字段 Field[] fileds = PoiPublicUtil.getClassFields(pojoClass); @@ -77,17 +81,17 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { targetId = etarget.value(); } // 创建表格样式 - setExcelExportStyler((IExcelExportStyler) params.getStyle().getConstructor(Workbook.class) - .newInstance(workbook)); + setExcelExportStyler((IExcelExportStyler) teplateParams.getStyle() + .getConstructor(Workbook.class).newInstance(workbook)); // 获取实体对象的导出数据 List excelParams = new ArrayList(); getAllExcelField(null, targetId, fileds, excelParams, pojoClass, null); // 根据表头进行筛选排序 sortAndFilterExportField(excelParams, titlemap); short rowHeight = getRowHeight(excelParams); - int index = params.getHeadingRows() + params.getHeadingStartRow(), titleHeight = index; + int index = teplateParams.getHeadingRows() + teplateParams.getHeadingStartRow(), titleHeight = index; //下移数据,模拟插入 - sheet.shiftRows(params.getHeadingRows() + params.getHeadingStartRow(), + sheet.shiftRows(teplateParams.getHeadingRows() + teplateParams.getHeadingStartRow(), sheet.getLastRowNum(), getShiftRows(dataSet, excelParams), true, true); if (excelParams.size() == 0) { return; @@ -150,7 +154,8 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { Workbook wb = null; // step 2. 判断模板的Excel类型,解析模板 try { - wb = getCloneWorkBook(params); + this.teplateParams = params; + wb = getCloneWorkBook(); // step 3. 解析模板 for (int i = 0, le = params.isScanAllsheet() ? wb.getNumberOfSheets() : params .getSheetNum().length; i < le; i++) { @@ -166,8 +171,7 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { if (dataHanlder != null) { needHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields()); } - addDataToSheet(params, pojoClass, dataSet, wb.getSheetAt(params.getDataSheetNum()), - wb); + addDataToSheet(pojoClass, dataSet, wb.getSheetAt(params.getDataSheetNum()), wb); } } catch (Exception e) { LOGGER.error(e.getMessage(), e); @@ -179,46 +183,30 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { /** * 克隆excel防止操作原对象,workbook无法克隆,只能对excel进行克隆 * - * @param params + * @param teplateParams * @throws Exception * @Author JueYue * @date 2013-11-11 */ - private Workbook getCloneWorkBook(TemplateExportParams params) throws Exception { - return ExcelCache.getWorkbook(params.getTemplateUrl(), params.getSheetNum(), - params.isScanAllsheet()); - - } + private Workbook getCloneWorkBook() throws Exception { + return ExcelCache.getWorkbook(teplateParams.getTemplateUrl(), teplateParams.getSheetNum(), + teplateParams.isScanAllsheet()); - /** - * 获取参数值 - * - * @param params - * @param map - * @return - */ - private String getParamsValue(String params, Map map) throws Exception { - if (params.indexOf(".") != -1) { - String[] paramsArr = params.split("\\."); - return String - .valueOf(PoiPublicUtil.getValueDoWhile(map.get(paramsArr[0]), paramsArr, 1)); - } - return map.containsKey(params) ? map.get(params).toString() : ""; } /** * 获取表头数据,设置表头的序号 * - * @param params + * @param teplateParams * @param sheet * @return */ - private Map getTitleMap(TemplateExportParams params, Sheet sheet) { + private Map getTitleMap(Sheet sheet) { Row row = null; Iterator cellTitle; Map titlemap = new HashMap(); - for (int j = 0; j < params.getHeadingRows(); j++) { - row = sheet.getRow(j + params.getHeadingStartRow()); + for (int j = 0; j < teplateParams.getHeadingRows(); j++) { + row = sheet.getRow(j + teplateParams.getHeadingStartRow()); cellTitle = row.cellIterator(); int i = row.getFirstCellNum(); while (cellTitle.hasNext()) { @@ -235,10 +223,14 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { } private void parseTemplate(Sheet sheet, Map map) throws Exception { + deleteCell(sheet, map); Row row = null; int index = 0; while (index <= sheet.getLastRowNum()) { row = sheet.getRow(index++); + if (row == null) { + continue; + } for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { if (row.getCell(i) != null && !tempCreateCellSet.contains(row.getRowNum() + "_" @@ -249,6 +241,40 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { } } + /** + * 先判断删除,省得影响效率 + * @param sheet + * @param map + * @throws Exception + */ + private void deleteCell(Sheet sheet, Map map) throws Exception { + Row row = null; + Cell cell = null; + int index = 0; + while (index <= sheet.getLastRowNum()) { + row = sheet.getRow(index++); + if (row == null) { + continue; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) { + cell = row.getCell(i); + if (row.getCell(i) != null + && (cell.getCellType() == Cell.CELL_TYPE_STRING || cell.getCellType() == Cell.CELL_TYPE_NUMERIC)) { + cell.setCellType(Cell.CELL_TYPE_STRING); + String text = cell.getStringCellValue(); + if (text.contains(IF_DELETE)) { + if (Boolean.valueOf(eval( + text.substring(text.indexOf(START_STR) + 2, text.indexOf(END_STR)) + .trim(), map).toString())) { + PoiSheetUtility.deleteColumn(sheet, i); + } + cell.setCellValue(""); + } + } + } + } + } + /** * 给每个Cell通过解析方式set值 * @@ -263,12 +289,11 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { String oldString; cell.setCellType(Cell.CELL_TYPE_STRING); oldString = cell.getStringCellValue(); - if (oldString != null && oldString.indexOf(START_STR) != -1 - && !oldString.contains("foreach||")) { + if (oldString != null && oldString.indexOf(START_STR) != -1 && !oldString.contains(FOREACH)) { // step 2. 判断是否含有解析函数 - String params; + String params = null; boolean isNumber = false; - if (oldString.contains(NUMBER_SYMBOL)) { + if (isNumber(oldString)) { isNumber = true; oldString = oldString.replace(NUMBER_SYMBOL, ""); } @@ -276,8 +301,8 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { params = oldString.substring(oldString.indexOf(START_STR) + 2, oldString.indexOf(END_STR)); - oldString = oldString.replace(START_STR + params + END_STR, - getParamsValue(params.trim(), map)); + oldString = oldString.replace(START_STR + params + END_STR, eval(params, map) + .toString()); } //如何是数值 类型,就按照数值类型进行设置 if (isNumber) { @@ -288,11 +313,15 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { } } //判断foreach 这种方法 - if (oldString != null - && (oldString.trim().startsWith("foreach||") || oldString.trim().startsWith( - "!foreach||"))) { + if (oldString != null && oldString.contains(FOREACH)) { addListDataToExcel(cell, map, oldString.trim()); } + + } + + private boolean isNumber(String text) { + return text.startsWith(NUMBER_SYMBOL) || text.contains("{" + NUMBER_SYMBOL) + || text.contains(" " + NUMBER_SYMBOL); } /** @@ -304,10 +333,12 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { */ private void addListDataToExcel(Cell cell, Map map, String name) throws Exception { - boolean isCreate = !name.startsWith("!"); - Collection datas = (Collection) map.get(name.substring(name.indexOf("||") + 2, - name.indexOf(START_STR))); - List columns = getAllDataColumns(cell, name); + boolean isCreate = !name.contains(FOREACH_NOT_CREATE); + name = name.replace(FOREACH_NOT_CREATE, EMPTY).replace(FOREACH, EMPTY) + .replace(START_STR, EMPTY); + String[] keys = name.replaceAll("\\s{1,}", " ").trim().split(" "); + Collection datas = (Collection) PoiPublicUtil.getParamsValue(keys[0], map); + List columns = getAllDataColumns(cell, name.replace(keys[0], EMPTY)); if (datas == null) { return; } @@ -317,7 +348,7 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { //处理当前行 if (its.hasNext()) { Object t = its.next(); - setForEeachCellValue(isCreate, cell.getRow(), cell.getColumnIndex(), t, columns); + setForEeachCellValue(isCreate, cell.getRow(), cell.getColumnIndex(), t, columns, map); } while (its.hasNext()) { Object t = its.next(); @@ -329,20 +360,32 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { row = cell.getRow().getSheet().createRow(rowIndex - 1); } } - setForEeachCellValue(isCreate, row, cell.getColumnIndex(), t, columns); + setForEeachCellValue(isCreate, row, cell.getColumnIndex(), t, columns, map); } } private void setForEeachCellValue(boolean isCreate, Row row, int columnIndex, Object t, - List columns) throws Exception { + List columns, Map map) + throws Exception { for (int i = 0, max = columnIndex + columns.size(); i < max; i++) { if (row.getCell(i) == null) row.createCell(i); } for (int i = 0, max = columns.size(); i < max; i++) { - String val = String.valueOf(PoiPublicUtil.getValueDoWhile(t, columns.get(i) - .split("\\."), 0)); - row.getCell(i + columnIndex).setCellValue(val); + boolean isNumber = false; + String tempStr = new String(columns.get(i)); + if (isNumber(tempStr)) { + isNumber = true; + tempStr = tempStr.replace(NUMBER_SYMBOL, ""); + } + map.put(teplateParams.getTempParams(), t); + String val = eval(tempStr, map).toString(); + if (isNumber) { + row.getCell(i + columnIndex).setCellValue(Double.parseDouble(val)); + row.getCell(i + columnIndex).setCellType(Cell.CELL_TYPE_NUMERIC); + } else { + row.getCell(i + columnIndex).setCellValue(val); + } tempCreateCellSet.add(row.getRowNum() + "_" + (i + columnIndex)); } @@ -357,20 +400,23 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { private List getAllDataColumns(Cell cell, String name) { List columns = new ArrayList(); if (name.contains(END_STR)) { - columns.add(name.substring(name.indexOf(START_STR) + 2, name.indexOf(END_STR)).trim()); + columns.add(name.replace(END_STR, EMPTY)); cell.setCellValue(""); return columns; } - columns.add(name.substring(name.indexOf(START_STR) + 2).trim()); + columns.add(name.trim()); int index = cell.getColumnIndex(); Cell tempCell; while (true) { tempCell = cell.getRow().getCell(++index); + if (tempCell == null) { + break; + } String cellStringString; - try {//不允许为空 + try {//允许为空,单表示已经完结了,因为可能被删除了 cellStringString = tempCell.getStringCellValue(); if (StringUtils.isBlank(cellStringString)) { - throw new ExcelExportException(); + break; } } catch (Exception e) { throw new ExcelExportException("for each 当中存在空字符串,请检查模板"); @@ -381,7 +427,12 @@ public final class ExcelExportOfTemplateUtil extends ExcelExportBase { columns.add(cellStringString.trim().replace(END_STR, "")); break; } else { - columns.add(cellStringString.trim()); + if (cellStringString.trim().contains(teplateParams.getTempParams())) { + columns.add(cellStringString.trim()); + } else { + //最后一行被删除了 + break; + } } } diff --git a/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiElUtil.java b/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiElUtil.java index 90543d9..a4873ef 100644 --- a/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiElUtil.java +++ b/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiElUtil.java @@ -11,13 +11,18 @@ import org.jeecgframework.poi.exception.excel.ExcelExportException; */ public final class PoiElUtil { - private static final String LENGTH = "le:"; - private static final String FORMAT_DATE = "fd:"; - private static final String FORMAT_NUMBER = "fn:"; - private static final String IF_DELETE = "!if:"; - private static final String EMPTY = ""; - private static final String LEFT_BRACKET = "("; - private static final String RIGHT_BRACKET = ")"; + public static final String LENGTH = "le:"; + public static final String FOREACH = "fe:"; + public static final String FOREACH_NOT_CREATE = "!fe:"; + public static final String START_STR = "{{"; + public static final String END_STR = "}}"; + public static final String NUMBER_SYMBOL = "n:"; + public static final String FORMAT_DATE = "fd:"; + public static final String FORMAT_NUMBER = "fn:"; + public static final String IF_DELETE = "!if:"; + public static final String EMPTY = ""; + public static final String LEFT_BRACKET = "("; + public static final String RIGHT_BRACKET = ")"; private PoiElUtil() { } diff --git a/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java b/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java index 0a6f632..f4aaf7a 100644 --- a/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java +++ b/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java @@ -37,6 +37,7 @@ import org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants; import org.jeecgframework.poi.word.entity.WordImageEntity; import org.jeecgframework.poi.word.entity.params.ExcelListEntity; import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; + /** * EASYPOI 的公共基础类 * @author JueYue @@ -362,12 +363,16 @@ public final class PoiPublicUtil { * @param map * @return */ - public static Object getParamsValue(String params, Map map) throws Exception { + @SuppressWarnings("rawtypes") + public static Object getParamsValue(String params, Object object) throws Exception { if (params.indexOf(".") != -1) { String[] paramsArr = params.split("\\."); - return getValueDoWhile(map.get(paramsArr[0]), paramsArr, 1); + return getValueDoWhile(object, paramsArr, 0); + } + if (object instanceof Map) { + return ((Map) object).get(params); } - return map.containsKey(params) ? map.get(params) : ""; + return getMethod(params, object.getClass()).invoke(object, new Object[] {}); } /** diff --git a/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiSheetUtility.java b/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiSheetUtility.java new file mode 100644 index 0000000..6cec624 --- /dev/null +++ b/easypoi-base/src/main/java/org/jeecgframework/poi/util/PoiSheetUtility.java @@ -0,0 +1,90 @@ +package org.jeecgframework.poi.util; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +/** + * 国外高手的,不过也不好,慎用,效率不行 + * Helper functions to aid in the management of sheets + */ +public class PoiSheetUtility extends Object { + + /** + * Given a sheet, this method deletes a column from a sheet and moves + * all the columns to the right of it to the left one cell. + * + * Note, this method will not update any formula references. + * + * @param sheet + * @param column + */ + public static void deleteColumn(Sheet sheet, int columnToDelete) { + int maxColumn = 0; + for (int r = 0; r < sheet.getLastRowNum() + 1; r++) { + Row row = sheet.getRow(r); + + // if no row exists here; then nothing to do; next! + if (row == null) + continue; + + // if the row doesn't have this many columns then we are good; next! + int lastColumn = row.getLastCellNum(); + if (lastColumn > maxColumn) + maxColumn = lastColumn; + + if (lastColumn < columnToDelete) + continue; + + for (int x = columnToDelete + 1; x < lastColumn + 1; x++) { + Cell oldCell = row.getCell(x - 1); + if (oldCell != null) + row.removeCell(oldCell); + + Cell nextCell = row.getCell(x); + if (nextCell != null) { + Cell newCell = row.createCell(x - 1, nextCell.getCellType()); + cloneCell(newCell, nextCell); + } + } + } + + // Adjust the column widths + for (int c = 0; c < maxColumn; c++) { + sheet.setColumnWidth(c, sheet.getColumnWidth(c + 1)); + } + } + + /* + * Takes an existing Cell and merges all the styles and forumla + * into the new one + */ + private static void cloneCell(Cell cNew, Cell cOld) { + cNew.setCellComment(cOld.getCellComment()); + cNew.setCellStyle(cOld.getCellStyle()); + + switch (cNew.getCellType()) { + case Cell.CELL_TYPE_BOOLEAN: { + cNew.setCellValue(cOld.getBooleanCellValue()); + break; + } + case Cell.CELL_TYPE_NUMERIC: { + cNew.setCellValue(cOld.getNumericCellValue()); + break; + } + case Cell.CELL_TYPE_STRING: { + cNew.setCellValue(cOld.getStringCellValue()); + break; + } + case Cell.CELL_TYPE_ERROR: { + cNew.setCellValue(cOld.getErrorCellValue()); + break; + } + case Cell.CELL_TYPE_FORMULA: { + cNew.setCellFormula(cOld.getCellFormula()); + break; + } + } + + } +} \ No newline at end of file diff --git a/easypoi-test/src/main/java/org/jeecgframework/poi/test/excel/doc/foreach.xlsx b/easypoi-test/src/main/java/org/jeecgframework/poi/test/excel/doc/foreach.xlsx index 09a895e..bc13fc6 100644 Binary files a/easypoi-test/src/main/java/org/jeecgframework/poi/test/excel/doc/foreach.xlsx and b/easypoi-test/src/main/java/org/jeecgframework/poi/test/excel/doc/foreach.xlsx differ diff --git a/easypoi-test/src/main/java/org/jeecgframework/poi/test/excel/template/TemplateForEachTest.java b/easypoi-test/src/main/java/org/jeecgframework/poi/test/excel/template/TemplateForEachTest.java index 8d765b8..36f574f 100644 --- a/easypoi-test/src/main/java/org/jeecgframework/poi/test/excel/template/TemplateForEachTest.java +++ b/easypoi-test/src/main/java/org/jeecgframework/poi/test/excel/template/TemplateForEachTest.java @@ -3,6 +3,7 @@ package org.jeecgframework.poi.test.excel.template; import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,6 +38,9 @@ public class TemplateForEachTest { list.add(entity); } map.put("entitylist", list); + map.put("letest", "12345678"); + map.put("fntest", "12345678.2341234"); + map.put("fdtest", new Date()); List> mapList = new ArrayList>(); for (int i = 0; i < 4; i++) { Map testMap = new HashMap();