From b1290331c7df6f6104e896e7d314be5433c63297 Mon Sep 17 00:00:00 2001 From: chenjg <17688741996@163.com> Date: Thu, 8 Aug 2024 18:00:28 +0800 Subject: [PATCH 1/2] =?UTF-8?q?#[1217200747151360]=E6=8A=A5=E8=A1=A8?= =?UTF-8?q?=E5=AF=BC=E5=87=BAtable=E4=BC=98=E5=8C=96=20http://192.168.0.96?= =?UTF-8?q?:8090/demo/rdm.html#/story-detail/939050947543040/9390509475430?= =?UTF-8?q?42/1217200747151360?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/service/ReportServiceImpl.java | 282 ++++++++++++++++-- 1 file changed, 257 insertions(+), 25 deletions(-) diff --git a/src/main/java/neatlogic/module/report/service/ReportServiceImpl.java b/src/main/java/neatlogic/module/report/service/ReportServiceImpl.java index 1350264..695bf24 100644 --- a/src/main/java/neatlogic/module/report/service/ReportServiceImpl.java +++ b/src/main/java/neatlogic/module/report/service/ReportServiceImpl.java @@ -1,6 +1,7 @@ package neatlogic.module.report.service; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import neatlogic.framework.common.dto.BasePageVo; import neatlogic.framework.common.util.PageUtil; @@ -23,6 +24,15 @@ import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.poi.hssf.util.HSSFColor; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.streaming.SXSSFCell; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -508,7 +518,7 @@ public class ReportServiceImpl implements ReportService { List list = (List) object; for (Object obj : list) { if (obj instanceof Map) { - Map hashMap = new HashMap<>(); + Map hashMap = new LinkedHashMap<>(); for (Map.Entry entity : ((Map) obj).entrySet()) { hashMap.put((String) entity.getKey(), entity.getValue()); } @@ -625,31 +635,32 @@ public class ReportServiceImpl implements ReportService { @Override public Workbook getReportWorkbook(String content) { Map>> tableMap = getTableListByHtml(content); - if (MapUtils.isEmpty(tableMap)) { - throw new TableNotFoundInReportException(); - } - ExcelBuilder builder = new ExcelBuilder(SXSSFWorkbook.class); - for (Map.Entry>> entry : tableMap.entrySet()) { - String tableName = entry.getKey(); - List> tableBody = entry.getValue(); - Map map = tableBody.get(0); - List headerList = new ArrayList<>(); - List columnList = new ArrayList<>(); - for (String key : map.keySet()) { - headerList.add(key); - columnList.add(key); + if (MapUtils.isNotEmpty(tableMap)) { + ExcelBuilder builder = new ExcelBuilder(SXSSFWorkbook.class); + for (Map.Entry>> entry : tableMap.entrySet()) { + String tableName = entry.getKey().trim(); + List> tableBody = entry.getValue(); + Map map = tableBody.get(0); + List headerList = new ArrayList<>(); + List columnList = new ArrayList<>(); + for (String key : map.keySet()) { + headerList.add(key); + columnList.add(key); + } + SheetBuilder sheetBuilder = builder.withBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT) + .withHeadFontColor(HSSFColor.HSSFColorPredefined.WHITE) + .withHeadBgColor(HSSFColor.HSSFColorPredefined.DARK_BLUE) + .withColumnWidth(30) + .addSheet(tableName) + .withHeaderList(headerList) + .withColumnList(columnList); + sheetBuilder.addDataList(tableBody); } - SheetBuilder sheetBuilder = builder.withBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT) - .withHeadFontColor(HSSFColor.HSSFColorPredefined.WHITE) - .withHeadBgColor(HSSFColor.HSSFColorPredefined.DARK_BLUE) - .withColumnWidth(30) - .addSheet(tableName) - .withHeaderList(headerList) - .withColumnList(columnList); - sheetBuilder.addDataList(tableBody); + return builder.build(); + } else { + // 考虑报表内容配置有自定义表格 + return getReportWorkbookByTemplateTable(content); } - Workbook workbook = builder.build(); - return workbook; } /** @@ -683,7 +694,7 @@ public class ReportServiceImpl implements ReportService { * @return */ private Map>> getTableListByHtml(String content) { - Map>> tableMap = new LinkedHashMap(); + Map>> tableMap = new LinkedHashMap<>(); if (StringUtils.isNotBlank(content)) { Document doc = Jsoup.parse(content); /** 抽取所有带有tableName属性的元素 */ @@ -718,6 +729,12 @@ public class ReportServiceImpl implements ReportService { } valueList.add(map); } + if (tableMap.containsKey(tableName)) { + // 存在同名表格,增加空格区分存进Map + do { + tableName += " "; + } while (tableMap.containsKey(tableName)); + } tableMap.put(tableName, valueList); } } @@ -727,4 +744,219 @@ public class ReportServiceImpl implements ReportService { } return tableMap; } + + /** + * 解析内容配置里的不规范表格 + * 循环填充方式生成excel,兼容存在rowspan、colspan的表格 + * + * @param content + * @return + */ + private Workbook getReportWorkbookByTemplateTable(String content) { + SXSSFWorkbook workbook = new SXSSFWorkbook(); + if (StringUtils.isNotBlank(content)) { + Document doc = Jsoup.parse(content); + Elements tableElements = doc.getElementsByTag("table"); + if (CollectionUtils.isEmpty(tableElements)) { + // 没有table标签 + throw new TableNotFoundInReportException(); + } + + List>> sheetList = new ArrayList<>(); + List mergeList = new ArrayList<>(); + Map> rowList = null; + Map columnList = null; + JSONArray mergeJsonArray = null; + JSONObject mergeJsonObj = null; + boolean hasTableHead = false; + for (Element t : tableElements) { + // i是sheet号 + Elements trList = t.select("tr"); + + mergeJsonArray = new JSONArray(); + rowList = new HashMap<>(); + + for (int j = 0; j < trList.size(); j++) { + // 遍历表格内所有行 + Element r = trList.get(j); + + columnList = rowList.computeIfAbsent(j, k -> new HashMap<>()); + + List tdList = new ArrayList<>(); + tdList.addAll(r.select("th,td")); + + for (int k = 0; k < tdList.size(); k++) { + // 遍历此行内所有列 + Element d = tdList.get(k); + if (j == 0 && k == 0 && "th".equals(d.tag().normalName())) { + // 存在表头标题行 + hasTableHead = true; + } + + Element element = d.clone(); + int columnNum = getRightColumnIndex(columnList, k); + // Excel 最大的 cell size 为 32767 + if (element.text().length() >= 32000) { + columnList.put(columnNum, element.text().toString().substring(0, 32000)); + } else { + String trimStr = null; + Elements childrenEle = element.children(); + // 存在换行符 + for (Element o : childrenEle) { + if ("br".equals(o.tag().normalName())) { + if (trimStr == null) { + trimStr = element.html().trim(); + } + trimStr = trimStr.replaceAll("\\s*" + o + "+\\s*", "\n"); + } + } + if (trimStr == null) { + trimStr = element.text().trim(); + } + columnList.put(columnNum, trimStr); + } + + // 为了合并单元格 填充空值 以防重合 + int colspan = 1; + int rowspan = 1; + if (StringUtils.isNotBlank(d.attr("colspan"))) { + colspan = Integer.parseInt(d.attr("colspan").trim()); + } + if (StringUtils.isNotBlank(d.attr("rowspan"))) { + rowspan = Integer.parseInt(d.attr("rowspan").trim()); + } + + // 填充单元格 + // 先填充本行 + if (colspan > 1 || rowspan > 1) { + mergeJsonObj = new JSONObject(); + mergeJsonObj.put("firstRow", j); + mergeJsonObj.put("lastRow", j + rowspan - 1); + mergeJsonObj.put("firstCol", k); + mergeJsonObj.put("lastCol", k + colspan - 1); + //合并列 + if (colspan > 1) { + for (int m = k + 1; m < colspan + k; m++) { + mergeJsonObj = new JSONObject(); + mergeJsonObj.put("firstRow", j); + mergeJsonObj.put("lastRow", j + rowspan - 1); + mergeJsonObj.put("firstCol", columnList.size() - 1); + mergeJsonObj.put("lastCol", columnList.size() + colspan - 2); + if (CollectionUtils.isEmpty(mergeJsonArray) || mergeJsonArray.getJSONObject(mergeJsonArray.size() - 1).getInteger("lastCol") < columnList.size() - 1) { + mergeJsonArray.add(mergeJsonObj); + } + columnList.put(columnList.size(), ""); + } + } else { + mergeJsonArray.add(mergeJsonObj); + } + + // 合并行 + // 再填充后面行 + if (rowspan > 1) { + Map nextColumnList = null; + for (int p = j + 1; p < rowspan + j; p++) { + // 下一个行号为 p + nextColumnList = rowList.computeIfAbsent(p, k1 -> new HashMap<>()); + for (int m = k; m < colspan + k; m++) { + nextColumnList.put(m, ""); + } + } + } + + } + } + } + mergeList.add(mergeJsonArray); + sheetList.add(rowList); + } + + // 生成sheet及填充数据 + CellStyle headStyle = getDefualtHeadCellStyle(workbook); + CellStyle style = getDefualtCellStyle(workbook); + if (CollectionUtils.isNotEmpty(sheetList)) { + SXSSFSheet sheet = null; + SXSSFRow row = null; + SXSSFCell cell = null; + for (int index = 0; index < sheetList.size(); index++) { + Map> table = sheetList.get(index); + + sheet = workbook.createSheet(); + for (int i = 0; i < table.size(); i++) { + Map tr = table.get(i); + row = sheet.createRow(i); + for (int j = 0; j < tr.size(); j++) { + String cellValue = tr.get(j); + + cell = row.createCell((short) j); + if (i == 0 && hasTableHead) { + cell.setCellStyle(headStyle); + } else { + cell.setCellStyle(style); + } + cell.setCellValue(cellValue); + } + } + // 合并单元格 + mergeJsonArray = mergeList.get(index); + for (int p = 0; p < mergeJsonArray.size(); p++) { + mergeJsonObj = mergeJsonArray.getJSONObject(p); + int firstRow = mergeJsonObj.getInteger("firstRow"); + int lastRow = mergeJsonObj.getInteger("lastRow"); + int firstCol = mergeJsonObj.getInteger("firstCol"); + int lastCol = mergeJsonObj.getInteger("lastCol"); + + sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol)); + } + } + } + } + return workbook; + } + + private CellStyle getDefualtCellStyle(Workbook wb) { + CellStyle style = wb.createCellStyle(); + style.setBottomBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + style.setTopBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + style.setLeftBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + style.setRightBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + style.setBorderTop(BorderStyle.THIN); + return style; + } + + private CellStyle getDefualtHeadCellStyle(Workbook wb) { + CellStyle style = wb.createCellStyle(); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillBackgroundColor(HSSFColor.HSSFColorPredefined.DARK_BLUE.getIndex()); + style.setFillForegroundColor(HSSFColor.HSSFColorPredefined.DARK_BLUE.getIndex()); + + Font font = wb.createFont(); + font.setColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex()); + style.setFont(font); + style.setBottomBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + style.setTopBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + style.setLeftBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + style.setRightBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + + style.setAlignment(HorizontalAlignment.CENTER); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + style.setBorderTop(BorderStyle.THIN); + return style; + } + + private int getRightColumnIndex(Map columnList, int k) { + int columnNum; + if (!columnList.containsKey(k)) { + columnNum = k; + } else { + columnNum = getRightColumnIndex(columnList, ++k); + } + return columnNum; + } } -- Gitee From 6d9a516cea881f169f6e7173b32380c6ee4e4e1e Mon Sep 17 00:00:00 2001 From: chenjg <17688741996@163.com> Date: Fri, 9 Aug 2024 18:38:35 +0800 Subject: [PATCH 2/2] =?UTF-8?q?[=E4=BF=AE=E5=A4=8D]=20=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E5=90=88=E5=B9=B6=E5=8D=95=E5=85=83=E6=A0=BC?= =?UTF-8?q?=E7=9A=84=E6=83=85=E5=86=B5=E4=B8=8B=E4=BC=9A=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/service/ReportServiceImpl.java | 210 +++++++++--------- 1 file changed, 106 insertions(+), 104 deletions(-) diff --git a/src/main/java/neatlogic/module/report/service/ReportServiceImpl.java b/src/main/java/neatlogic/module/report/service/ReportServiceImpl.java index 695bf24..9b54543 100644 --- a/src/main/java/neatlogic/module/report/service/ReportServiceImpl.java +++ b/src/main/java/neatlogic/module/report/service/ReportServiceImpl.java @@ -762,46 +762,54 @@ public class ReportServiceImpl implements ReportService { throw new TableNotFoundInReportException(); } - List>> sheetList = new ArrayList<>(); - List mergeList = new ArrayList<>(); + // 表头样式 + CellStyle headStyle = getDefualtHeadCellStyle(workbook); + // 普通样式 + CellStyle style = getDefualtCellStyle(workbook); + // 标志表头 + Map> thMap = null; + // 表内所有单元格数据 Map> rowList = null; + // 单行单元格数据 Map columnList = null; + // 合并单元格数组 JSONArray mergeJsonArray = null; + // 合并单元格对象 JSONObject mergeJsonObj = null; - boolean hasTableHead = false; - for (Element t : tableElements) { - // i是sheet号 - Elements trList = t.select("tr"); + // 行数据列表 + Elements trList = null; + // 列数据列表 + Elements tdList = null; - mergeJsonArray = new JSONArray(); + for (Element t : tableElements) { + thMap = new HashMap<>(); rowList = new HashMap<>(); + mergeJsonArray = new JSONArray(); + // 遍历表格内所有行 + trList = t.select("tr"); for (int j = 0; j < trList.size(); j++) { - // 遍历表格内所有行 Element r = trList.get(j); + // 获取当前起始单元格行下标row, col + int rowIndex = getBottomRowIndex(rowList, j); + columnList = rowList.computeIfAbsent(rowIndex, k -> new HashMap<>()); - columnList = rowList.computeIfAbsent(j, k -> new HashMap<>()); - - List tdList = new ArrayList<>(); - tdList.addAll(r.select("th,td")); - + // 遍历此行内所有列 + tdList = r.select("th,td"); for (int k = 0; k < tdList.size(); k++) { - // 遍历此行内所有列 - Element d = tdList.get(k); - if (j == 0 && k == 0 && "th".equals(d.tag().normalName())) { - // 存在表头标题行 - hasTableHead = true; - } + Element element = tdList.get(k); + boolean isTh = "th".equals(element.tagName()); + // 获取当前起始单元格列下标 + int colIndex = getRightColumnIndex(columnList, 0); - Element element = d.clone(); - int columnNum = getRightColumnIndex(columnList, k); + // 当前单元格内容 + String trimStr = null; // Excel 最大的 cell size 为 32767 if (element.text().length() >= 32000) { - columnList.put(columnNum, element.text().toString().substring(0, 32000)); + trimStr = element.text().toString().substring(0, 32000); } else { - String trimStr = null; Elements childrenEle = element.children(); - // 存在换行符 + //
标签替换为\n换行符 for (Element o : childrenEle) { if ("br".equals(o.tag().normalName())) { if (trimStr == null) { @@ -810,105 +818,80 @@ public class ReportServiceImpl implements ReportService { trimStr = trimStr.replaceAll("\\s*" + o + "+\\s*", "\n"); } } - if (trimStr == null) { - trimStr = element.text().trim(); - } - columnList.put(columnNum, trimStr); + } + if (trimStr == null) { + trimStr = element.text().trim(); } - // 为了合并单元格 填充空值 以防重合 + // 合并单元格 填充空值 int colspan = 1; int rowspan = 1; - if (StringUtils.isNotBlank(d.attr("colspan"))) { - colspan = Integer.parseInt(d.attr("colspan").trim()); + if (StringUtils.isNotBlank(element.attr("colspan"))) { + colspan = Integer.parseInt(element.attr("colspan").trim()); } - if (StringUtils.isNotBlank(d.attr("rowspan"))) { - rowspan = Integer.parseInt(d.attr("rowspan").trim()); + if (StringUtils.isNotBlank(element.attr("rowspan"))) { + rowspan = Integer.parseInt(element.attr("rowspan").trim()); } - // 填充单元格 - // 先填充本行 if (colspan > 1 || rowspan > 1) { - mergeJsonObj = new JSONObject(); - mergeJsonObj.put("firstRow", j); - mergeJsonObj.put("lastRow", j + rowspan - 1); - mergeJsonObj.put("firstCol", k); - mergeJsonObj.put("lastCol", k + colspan - 1); - //合并列 - if (colspan > 1) { - for (int m = k + 1; m < colspan + k; m++) { - mergeJsonObj = new JSONObject(); - mergeJsonObj.put("firstRow", j); - mergeJsonObj.put("lastRow", j + rowspan - 1); - mergeJsonObj.put("firstCol", columnList.size() - 1); - mergeJsonObj.put("lastCol", columnList.size() + colspan - 2); - if (CollectionUtils.isEmpty(mergeJsonArray) || mergeJsonArray.getJSONObject(mergeJsonArray.size() - 1).getInteger("lastCol") < columnList.size() - 1) { - mergeJsonArray.add(mergeJsonObj); + for (int p = rowIndex; p < rowIndex + rowspan; p++) { + Map nowColumnList = rowList.computeIfAbsent(p, k1 -> new HashMap<>()); + for (int m = colIndex; m < colIndex + colspan; m++) { + if (p == rowIndex && m == colIndex) { + // 填充当前单元格内容 + nowColumnList.put(m, trimStr); + } else { + // 填充空字符串占位 + nowColumnList.put(m, ""); } - columnList.put(columnList.size(), ""); - } - } else { - mergeJsonArray.add(mergeJsonObj); - } - - // 合并行 - // 再填充后面行 - if (rowspan > 1) { - Map nextColumnList = null; - for (int p = j + 1; p < rowspan + j; p++) { - // 下一个行号为 p - nextColumnList = rowList.computeIfAbsent(p, k1 -> new HashMap<>()); - for (int m = k; m < colspan + k; m++) { - nextColumnList.put(m, ""); + if (isTh) { + thMap.computeIfAbsent(p, k2 -> new HashSet<>()).add(m); } } } - + mergeJsonObj = new JSONObject(); + mergeJsonObj.put("firstRow", rowIndex); + mergeJsonObj.put("lastRow", rowIndex + rowspan - 1); + mergeJsonObj.put("firstCol", colIndex); + mergeJsonObj.put("lastCol", colIndex + colspan - 1); + mergeJsonArray.add(mergeJsonObj); + } else { + columnList.put(colIndex, trimStr); + if (isTh) { + thMap.computeIfAbsent(rowIndex, k2 -> new HashSet<>()).add(colIndex); + } } } } - mergeList.add(mergeJsonArray); - sheetList.add(rowList); - } - // 生成sheet及填充数据 - CellStyle headStyle = getDefualtHeadCellStyle(workbook); - CellStyle style = getDefualtCellStyle(workbook); - if (CollectionUtils.isNotEmpty(sheetList)) { - SXSSFSheet sheet = null; - SXSSFRow row = null; - SXSSFCell cell = null; - for (int index = 0; index < sheetList.size(); index++) { - Map> table = sheetList.get(index); - - sheet = workbook.createSheet(); - for (int i = 0; i < table.size(); i++) { - Map tr = table.get(i); - row = sheet.createRow(i); - for (int j = 0; j < tr.size(); j++) { - String cellValue = tr.get(j); - - cell = row.createCell((short) j); - if (i == 0 && hasTableHead) { - cell.setCellStyle(headStyle); - } else { - cell.setCellStyle(style); - } - cell.setCellValue(cellValue); + // 生成sheet及填充数据 + SXSSFSheet sheet = workbook.createSheet(); + for (int i = 0; i < rowList.size(); i++) { + Map tr = rowList.get(i); + SXSSFRow row = sheet.createRow(i); + for (int j = 0; j < tr.size(); j++) { + if (i == 0) { + sheet.setColumnWidth(j, 30 * 256); } - } - // 合并单元格 - mergeJsonArray = mergeList.get(index); - for (int p = 0; p < mergeJsonArray.size(); p++) { - mergeJsonObj = mergeJsonArray.getJSONObject(p); - int firstRow = mergeJsonObj.getInteger("firstRow"); - int lastRow = mergeJsonObj.getInteger("lastRow"); - int firstCol = mergeJsonObj.getInteger("firstCol"); - int lastCol = mergeJsonObj.getInteger("lastCol"); - - sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol)); + String cellValue = tr.get(j); + SXSSFCell cell = row.createCell((short) j); + if (thMap.containsKey(i) && thMap.get(i).contains(j)) { + cell.setCellStyle(headStyle); + } else { + cell.setCellStyle(style); + } + cell.setCellValue(cellValue); } } + for (int p = 0; p < mergeJsonArray.size(); p++) { + mergeJsonObj = mergeJsonArray.getJSONObject(p); + int firstRow = mergeJsonObj.getInteger("firstRow"); + int lastRow = mergeJsonObj.getInteger("lastRow"); + int firstCol = mergeJsonObj.getInteger("firstCol"); + int lastCol = mergeJsonObj.getInteger("lastCol"); + sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol)); + } + } } return workbook; @@ -921,6 +904,7 @@ public class ReportServiceImpl implements ReportService { style.setLeftBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); style.setRightBorderColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex()); + style.setAlignment(HorizontalAlignment.CENTER); style.setBorderBottom(BorderStyle.THIN); style.setBorderLeft(BorderStyle.THIN); style.setBorderRight(BorderStyle.THIN); @@ -959,4 +943,22 @@ public class ReportServiceImpl implements ReportService { } return columnNum; } + + private int getBottomRowIndex(Map> rowList, int j) { + if (j == 0) { + return 0; + } + int length = rowList.get(j - 1).size(); + int row = j; + int col = 0; + while (rowList.containsKey(row) && rowList.get(row).get(col) != null) { + if (col == length - 1) { + row++; + col = 0; + } else { + col++; + } + } + return row; + } } -- Gitee