应付账单初始化
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
package com.bms.common.utils.poi;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellType;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.apache.poi.ss.usermodel.Hyperlink;
|
||||
import org.apache.poi.xssf.usermodel.XSSFHyperlink;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import com.bms.common.utils.DateUtils;
|
||||
|
||||
/**
|
||||
* 把一个工作表读成文本网格。
|
||||
* 合并单元格只保留左上角的原文,不把国家、处理费、超重费再填到每一段重量上。
|
||||
*/
|
||||
public class ExcelGrid
|
||||
{
|
||||
/** 单表最多读取的行数,避免样式撑满的空行把内存打满 */
|
||||
public static final int MAX_ROWS = 2500;
|
||||
|
||||
/** 单表最多读取的列数。价卡分区列不会超过这个宽度 */
|
||||
public static final int MAX_COLS = 60;
|
||||
|
||||
private final String[][] text;
|
||||
|
||||
private final Double[][] number;
|
||||
|
||||
private final String[][] linkSheet;
|
||||
|
||||
/** 合并区域左上角横向占几列,其余单元格为 0 */
|
||||
private final int[][] colSpan;
|
||||
|
||||
private final int rows;
|
||||
|
||||
private final int cols;
|
||||
|
||||
private ExcelGrid(String[][] text, Double[][] number, String[][] linkSheet, int[][] colSpan, int rows, int cols)
|
||||
{
|
||||
this.text = text;
|
||||
this.number = number;
|
||||
this.linkSheet = linkSheet;
|
||||
this.colSpan = colSpan;
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取工作表。日期写成 yyyy-MM-dd,不把日期序列号当成金额。
|
||||
*/
|
||||
public static ExcelGrid read(Sheet sheet)
|
||||
{
|
||||
return read(sheet, MAX_ROWS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定行数上限读取。邮编分区表会超过价卡默认的 2500 行。
|
||||
*/
|
||||
public static ExcelGrid read(Sheet sheet, int maxRows)
|
||||
{
|
||||
int limit = maxRows < 1 ? MAX_ROWS : maxRows;
|
||||
int lastRow = sheet == null || sheet.getLastRowNum() < 0 ? -1 : Math.min(sheet.getLastRowNum(), limit - 1);
|
||||
int cols = 0;
|
||||
if (sheet != null)
|
||||
{
|
||||
for (int r = 0; r <= lastRow; r++)
|
||||
{
|
||||
Row row = sheet.getRow(r);
|
||||
if (row == null || row.getLastCellNum() <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int lastCell = Math.min(row.getLastCellNum(), MAX_COLS);
|
||||
for (int c = 0; c < lastCell; c++)
|
||||
{
|
||||
Cell cell = row.getCell(c);
|
||||
if (hasValue(cell) && c + 1 > cols)
|
||||
{
|
||||
cols = c + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int rows = lastRow + 1;
|
||||
String[][] text = new String[Math.max(rows, 0)][Math.max(cols, 0)];
|
||||
Double[][] number = new Double[Math.max(rows, 0)][Math.max(cols, 0)];
|
||||
String[][] linkSheet = new String[Math.max(rows, 0)][Math.max(cols, 0)];
|
||||
int[][] colSpan = new int[Math.max(rows, 0)][Math.max(cols, 0)];
|
||||
if (sheet != null && rows > 0 && cols > 0)
|
||||
{
|
||||
for (int r = 0; r < rows; r++)
|
||||
{
|
||||
Row row = sheet.getRow(r);
|
||||
if (row == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int c = 0; c < cols; c++)
|
||||
{
|
||||
fill(row.getCell(c), text, number, linkSheet, r, c);
|
||||
}
|
||||
}
|
||||
for (CellRangeAddress region : sheet.getMergedRegions())
|
||||
{
|
||||
int r0 = Math.max(region.getFirstRow(), 0);
|
||||
int c0 = Math.max(region.getFirstColumn(), 0);
|
||||
if (r0 >= rows || c0 >= cols)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int width = region.getLastColumn() - region.getFirstColumn() + 1;
|
||||
if (width > 1)
|
||||
{
|
||||
colSpan[r0][c0] = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ExcelGrid(text, number, linkSheet, colSpan, rows, cols);
|
||||
}
|
||||
|
||||
public int rows()
|
||||
{
|
||||
return rows;
|
||||
}
|
||||
|
||||
public int cols()
|
||||
{
|
||||
return cols;
|
||||
}
|
||||
|
||||
public String text(int row, int col)
|
||||
{
|
||||
if (row < 0 || col < 0 || row >= rows || col >= cols)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return text[row][col] == null ? "" : text[row][col];
|
||||
}
|
||||
|
||||
public Double number(int row, int col)
|
||||
{
|
||||
if (row < 0 || col < 0 || row >= rows || col >= cols)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return number[row][col];
|
||||
}
|
||||
|
||||
/** 单元格内部超链接指向的工作表名,没有链接时返回空串 */
|
||||
public String linkSheet(int row, int col)
|
||||
{
|
||||
if (row < 0 || col < 0 || row >= rows || col >= cols || linkSheet[row][col] == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return linkSheet[row][col];
|
||||
}
|
||||
|
||||
/** 该格是横向合并的左上角时,返回合并了几列;普通格返回 1 */
|
||||
public int colSpan(int row, int col)
|
||||
{
|
||||
if (row < 0 || col < 0 || row >= rows || col >= cols)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return colSpan[row][col] > 1 ? colSpan[row][col] : 1;
|
||||
}
|
||||
|
||||
public boolean blankRow(int row)
|
||||
{
|
||||
if (row < 0 || row >= rows)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (int c = 0; c < cols; c++)
|
||||
{
|
||||
if (!isBlank(text[row][c]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void fill(Cell cell, String[][] text, Double[][] number, String[][] linkSheet, int r, int c)
|
||||
{
|
||||
if (cell == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
linkSheet[r][c] = sheetName(cell.getHyperlink());
|
||||
CellType type = cell.getCellType();
|
||||
if (type == CellType.FORMULA)
|
||||
{
|
||||
type = cell.getCachedFormulaResultType();
|
||||
}
|
||||
if (type == CellType.NUMERIC)
|
||||
{
|
||||
if (DateUtil.isCellDateFormatted(cell))
|
||||
{
|
||||
Date date = cell.getDateCellValue();
|
||||
text[r][c] = date == null ? "" : DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, date);
|
||||
return;
|
||||
}
|
||||
double raw = cell.getNumericCellValue();
|
||||
number[r][c] = raw;
|
||||
text[r][c] = BigDecimal.valueOf(raw).stripTrailingZeros().toPlainString();
|
||||
return;
|
||||
}
|
||||
if (type == CellType.STRING)
|
||||
{
|
||||
text[r][c] = clean(cell.getStringCellValue());
|
||||
number[r][c] = plainNumber(text[r][c]);
|
||||
return;
|
||||
}
|
||||
if (type == CellType.BOOLEAN)
|
||||
{
|
||||
text[r][c] = Boolean.toString(cell.getBooleanCellValue());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasValue(Cell cell)
|
||||
{
|
||||
if (cell == null || cell.getCellType() == CellType.BLANK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (cell.getCellType() == CellType.STRING)
|
||||
{
|
||||
return !isBlank(cell.getStringCellValue());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Double plainNumber(String text)
|
||||
{
|
||||
if (text == null || !text.matches("-?\\d+(\\.\\d+)?"))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try
|
||||
{
|
||||
return Double.valueOf(text);
|
||||
}
|
||||
catch (NumberFormatException ex)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 内部链接形如 目录!A1 或 'evri 偏远'!A1,这里只留工作表名 */
|
||||
static String sheetName(Hyperlink link)
|
||||
{
|
||||
if (link == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
String location = link instanceof XSSFHyperlink ? ((XSSFHyperlink) link).getLocation() : "";
|
||||
if (isBlank(location))
|
||||
{
|
||||
location = link.getAddress();
|
||||
}
|
||||
if (isBlank(location) || !location.contains("!"))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
String name = location.substring(0, location.lastIndexOf('!')).trim();
|
||||
if (name.startsWith("'") && name.endsWith("'") && name.length() >= 2)
|
||||
{
|
||||
name = name.substring(1, name.length() - 1).replace("''", "'");
|
||||
}
|
||||
return name.trim();
|
||||
}
|
||||
|
||||
private static String clean(String value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
return value.replace('\u00A0', ' ').replace('\r', ' ').trim();
|
||||
}
|
||||
|
||||
private static boolean isBlank(String value)
|
||||
{
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user