mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
add SheetDataSaxHandler
This commit is contained in:
parent
30e1eb929c
commit
7d4838a0a4
@ -15,6 +15,7 @@
|
|||||||
* 【dfa 】 增加FoundWord(pr#1290@Github)
|
* 【dfa 】 增加FoundWord(pr#1290@Github)
|
||||||
* 【core 】 增加Segment(pr#1290@Github)
|
* 【core 】 增加Segment(pr#1290@Github)
|
||||||
* 【core 】 增加CharSequenceUtil
|
* 【core 】 增加CharSequenceUtil
|
||||||
|
* 【poi 】 Excel07SaxReader拆分出SheetDataSaxHandler
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【cache 】 修复Cache中get重复misCount计数问题(issue#1281@Github)
|
* 【cache 】 修复Cache中get重复misCount计数问题(issue#1281@Github)
|
||||||
|
@ -2,30 +2,19 @@ package cn.hutool.poi.excel.sax;
|
|||||||
|
|
||||||
import cn.hutool.core.io.IORuntimeException;
|
import cn.hutool.core.io.IORuntimeException;
|
||||||
import cn.hutool.core.io.IoUtil;
|
import cn.hutool.core.io.IoUtil;
|
||||||
import cn.hutool.core.text.StrBuilder;
|
|
||||||
import cn.hutool.core.util.NumberUtil;
|
import cn.hutool.core.util.NumberUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.poi.excel.cell.FormulaCellValue;
|
|
||||||
import cn.hutool.poi.excel.sax.handler.RowHandler;
|
import cn.hutool.poi.excel.sax.handler.RowHandler;
|
||||||
import cn.hutool.poi.exceptions.POIException;
|
import cn.hutool.poi.exceptions.POIException;
|
||||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||||
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
|
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
|
||||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
import org.apache.poi.ss.usermodel.BuiltinFormats;
|
|
||||||
import org.apache.poi.xssf.eventusermodel.XSSFReader;
|
import org.apache.poi.xssf.eventusermodel.XSSFReader;
|
||||||
import org.apache.poi.xssf.model.SharedStringsTable;
|
|
||||||
import org.apache.poi.xssf.model.StylesTable;
|
|
||||||
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
|
|
||||||
import org.xml.sax.Attributes;
|
|
||||||
import org.xml.sax.helpers.DefaultHandler;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sax方式读取Excel文件<br>
|
* Sax方式读取Excel文件<br>
|
||||||
@ -34,52 +23,11 @@ import java.util.List;
|
|||||||
* @author Looly
|
* @author Looly
|
||||||
* @since 3.1.2
|
* @since 3.1.2
|
||||||
*/
|
*/
|
||||||
public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<Excel07SaxReader> {
|
public class Excel07SaxReader implements ExcelSaxReader<Excel07SaxReader> {
|
||||||
|
|
||||||
// sheet r:Id前缀
|
// sheet r:Id前缀
|
||||||
private static final String RID_PREFIX = "rId";
|
private static final String RID_PREFIX = "rId";
|
||||||
|
private final SheetDataSaxHandler handler;
|
||||||
// 单元格的格式表,对应style.xml
|
|
||||||
private StylesTable stylesTable;
|
|
||||||
// excel 2007 的共享字符串表,对应sharedString.xml
|
|
||||||
private SharedStringsTable sharedStringsTable;
|
|
||||||
// sheet的索引
|
|
||||||
private int sheetIndex;
|
|
||||||
|
|
||||||
// 当前非空行
|
|
||||||
private int index;
|
|
||||||
// 当前列
|
|
||||||
private int curCell;
|
|
||||||
// 单元数据类型
|
|
||||||
private CellDataType cellDataType;
|
|
||||||
// 当前行号,从0开始
|
|
||||||
private long rowNumber;
|
|
||||||
// 当前列坐标, 如A1,B5
|
|
||||||
private String curCoordinate;
|
|
||||||
// 当前节点名称
|
|
||||||
private ElementName curElementName;
|
|
||||||
// 前一个列的坐标
|
|
||||||
private String preCoordinate;
|
|
||||||
// 行的最大列坐标
|
|
||||||
private String maxCellCoordinate;
|
|
||||||
// 单元格样式
|
|
||||||
private XSSFCellStyle xssfCellStyle;
|
|
||||||
// 单元格存储的格式化字符串,nmtFmt的formatCode属性的值
|
|
||||||
private String numFmtString;
|
|
||||||
// 是否处于sheetData标签内,sax只解析此标签内的内容,其它标签忽略
|
|
||||||
private boolean isInSheetData;
|
|
||||||
|
|
||||||
// 上一次的内容
|
|
||||||
private final StrBuilder lastContent = StrUtil.strBuilder();
|
|
||||||
// 上一次的内容
|
|
||||||
private final StrBuilder lastFormula = StrUtil.strBuilder();
|
|
||||||
// 存储每行的列元素
|
|
||||||
private List<Object> rowCellList = new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 行处理器
|
|
||||||
*/
|
|
||||||
private RowHandler rowHandler;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
@ -87,7 +35,7 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
|
|||||||
* @param rowHandler 行处理器
|
* @param rowHandler 行处理器
|
||||||
*/
|
*/
|
||||||
public Excel07SaxReader(RowHandler rowHandler) {
|
public Excel07SaxReader(RowHandler rowHandler) {
|
||||||
this.rowHandler = rowHandler;
|
this.handler = new SheetDataSaxHandler(rowHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,7 +45,7 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
|
|||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public Excel07SaxReader setRowHandler(RowHandler rowHandler) {
|
public Excel07SaxReader setRowHandler(RowHandler rowHandler) {
|
||||||
this.rowHandler = rowHandler;
|
this.handler.setRowHandler(rowHandler);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,14 +122,14 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
|
|||||||
public Excel07SaxReader read(XSSFReader xssfReader, String idOrRid) throws POIException {
|
public Excel07SaxReader read(XSSFReader xssfReader, String idOrRid) throws POIException {
|
||||||
// 获取共享样式表
|
// 获取共享样式表
|
||||||
try {
|
try {
|
||||||
stylesTable = xssfReader.getStylesTable();
|
this.handler.stylesTable = xssfReader.getStylesTable();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
//ignore
|
//ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取共享字符串表
|
// 获取共享字符串表
|
||||||
try {
|
try {
|
||||||
this.sharedStringsTable = xssfReader.getSharedStringsTable();
|
this.handler.sharedStringsTable = xssfReader.getSharedStringsTable();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
} catch (InvalidFormatException e) {
|
} catch (InvalidFormatException e) {
|
||||||
@ -192,89 +140,6 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
|
|||||||
}
|
}
|
||||||
// ------------------------------------------------------------------------------ Read end
|
// ------------------------------------------------------------------------------ Read end
|
||||||
|
|
||||||
/**
|
|
||||||
* 读到一个xml开始标签时的回调处理方法
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void startElement(String uri, String localName, String qName, Attributes attributes) {
|
|
||||||
if("sheetData".equals(qName)){
|
|
||||||
this.isInSheetData = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(false == this.isInSheetData){
|
|
||||||
// 非sheetData标签,忽略解析
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ElementName name = ElementName.of(qName);
|
|
||||||
this.curElementName = name;
|
|
||||||
|
|
||||||
if(null != name){
|
|
||||||
switch (name){
|
|
||||||
case row:
|
|
||||||
// 行开始
|
|
||||||
startRow(attributes);
|
|
||||||
break;
|
|
||||||
case c:
|
|
||||||
// 单元格元素
|
|
||||||
startCell(attributes);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 标签结束的回调处理方法
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void endElement(String uri, String localName, String qName) {
|
|
||||||
if("sheetData".equals(qName)){
|
|
||||||
// sheetData结束,不再解析别的标签
|
|
||||||
this.isInSheetData = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(false == this.isInSheetData){
|
|
||||||
// 非sheetData标签,忽略解析
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.curElementName = null;
|
|
||||||
if (ElementName.c.match(qName)) { // 单元格结束
|
|
||||||
endCell();
|
|
||||||
} else if (ElementName.row.match(qName)) {// 行结束
|
|
||||||
endRow();
|
|
||||||
}
|
|
||||||
// 其它标签忽略
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* s标签结束的回调处理方法
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void characters(char[] ch, int start, int length) {
|
|
||||||
if(false == this.isInSheetData){
|
|
||||||
// 非sheetData标签,忽略解析
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ElementName elementName = this.curElementName;
|
|
||||||
if(null != elementName){
|
|
||||||
switch (elementName){
|
|
||||||
case v:
|
|
||||||
// 得到单元格内容的值
|
|
||||||
lastContent.append(ch, start, length);
|
|
||||||
break;
|
|
||||||
case f:
|
|
||||||
// 得到单元格内容的值
|
|
||||||
lastFormula.append(ch, start, length);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 其它标签忽略
|
|
||||||
}
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------------------------- Private method start
|
// --------------------------------------------------------------------------------------- Private method start
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -295,25 +160,25 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
|
|||||||
idOrRid = rid;
|
idOrRid = rid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.sheetIndex = Integer.parseInt(StrUtil.removePrefixIgnoreCase(idOrRid, RID_PREFIX));
|
this.handler.sheetIndex = Integer.parseInt(StrUtil.removePrefixIgnoreCase(idOrRid, RID_PREFIX));
|
||||||
InputStream sheetInputStream = null;
|
InputStream sheetInputStream = null;
|
||||||
try {
|
try {
|
||||||
if (this.sheetIndex > -1) {
|
if (this.handler.sheetIndex > -1) {
|
||||||
// 根据 rId# 或 rSheet# 查找sheet
|
// 根据 rId# 或 rSheet# 查找sheet
|
||||||
sheetInputStream = xssfReader.getSheet(RID_PREFIX + (this.sheetIndex + 1));
|
sheetInputStream = xssfReader.getSheet(RID_PREFIX + (this.handler.sheetIndex + 1));
|
||||||
ExcelSaxUtil.readFrom(sheetInputStream, this);
|
ExcelSaxUtil.readFrom(sheetInputStream, this.handler);
|
||||||
rowHandler.doAfterAllAnalysed();
|
this.handler.rowHandler.doAfterAllAnalysed();
|
||||||
} else {
|
} else {
|
||||||
this.sheetIndex = -1;
|
this.handler.sheetIndex = -1;
|
||||||
// 遍历所有sheet
|
// 遍历所有sheet
|
||||||
final Iterator<InputStream> sheetInputStreams = xssfReader.getSheetsData();
|
final Iterator<InputStream> sheetInputStreams = xssfReader.getSheetsData();
|
||||||
while (sheetInputStreams.hasNext()) {
|
while (sheetInputStreams.hasNext()) {
|
||||||
// 重新读取一个sheet时行归零
|
// 重新读取一个sheet时行归零
|
||||||
index = 0;
|
this.handler.index = 0;
|
||||||
this.sheetIndex++;
|
this.handler.sheetIndex++;
|
||||||
sheetInputStream = sheetInputStreams.next();
|
sheetInputStream = sheetInputStreams.next();
|
||||||
ExcelSaxUtil.readFrom(sheetInputStream, this);
|
ExcelSaxUtil.readFrom(sheetInputStream, this.handler);
|
||||||
rowHandler.doAfterAllAnalysed();
|
this.handler.rowHandler.doAfterAllAnalysed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
@ -325,143 +190,5 @@ public class Excel07SaxReader extends DefaultHandler implements ExcelSaxReader<E
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 行开始
|
|
||||||
*
|
|
||||||
* @param attributes 属性列表
|
|
||||||
*/
|
|
||||||
private void startRow(Attributes attributes) {
|
|
||||||
final String rValue = AttributeName.r.getValue(attributes);
|
|
||||||
if(null != rValue){
|
|
||||||
this.rowNumber = Long.parseLong(rValue) - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 单元格开始
|
|
||||||
*
|
|
||||||
* @param attributes 属性列表
|
|
||||||
*/
|
|
||||||
private void startCell(Attributes attributes) {
|
|
||||||
// 获取当前列坐标
|
|
||||||
final String tempCurCoordinate = AttributeName.r.getValue(attributes);
|
|
||||||
// 前一列为null,则将其设置为"@",A为第一列,ascii码为65,前一列即为@,ascii码64
|
|
||||||
if (preCoordinate == null) {
|
|
||||||
preCoordinate = String.valueOf(ExcelSaxUtil.CELL_FILL_CHAR);
|
|
||||||
} else {
|
|
||||||
// 存在,则前一列要设置为上一列的坐标
|
|
||||||
preCoordinate = curCoordinate;
|
|
||||||
}
|
|
||||||
// 重置当前列
|
|
||||||
curCoordinate = tempCurCoordinate;
|
|
||||||
// 设置单元格类型
|
|
||||||
setCellType(attributes);
|
|
||||||
|
|
||||||
// 清空之前的数据
|
|
||||||
lastContent.reset();
|
|
||||||
lastFormula.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一个单元格结尾
|
|
||||||
*/
|
|
||||||
private void endCell() {
|
|
||||||
// 补全单元格之间的空格
|
|
||||||
fillBlankCell(preCoordinate, curCoordinate, false);
|
|
||||||
|
|
||||||
final String contentStr = StrUtil.trim(lastContent);
|
|
||||||
Object value = ExcelSaxUtil.getDataValue(this.cellDataType, contentStr, this.sharedStringsTable, this.numFmtString);
|
|
||||||
if(false == this.lastFormula.isEmpty()){
|
|
||||||
value = new FormulaCellValue(StrUtil.trim(lastFormula), value);
|
|
||||||
}
|
|
||||||
addCellValue(curCell++, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一行结尾
|
|
||||||
*/
|
|
||||||
private void endRow() {
|
|
||||||
// 最大列坐标以第一个非空行的为准
|
|
||||||
if (index == 0) {
|
|
||||||
maxCellCoordinate = curCoordinate;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 补全一行尾部可能缺失的单元格
|
|
||||||
if (maxCellCoordinate != null) {
|
|
||||||
fillBlankCell(curCoordinate, maxCellCoordinate, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
rowHandler.handle(sheetIndex, rowNumber, rowCellList);
|
|
||||||
|
|
||||||
// 一行结束
|
|
||||||
// 新建一个新列,之前的列抛弃(可能被回收或rowHandler处理)
|
|
||||||
rowCellList = new ArrayList<>(curCell + 1);
|
|
||||||
// 行数增加
|
|
||||||
index++;
|
|
||||||
// 当前列置0
|
|
||||||
curCell = 0;
|
|
||||||
// 置空当前列坐标和前一列坐标
|
|
||||||
curCoordinate = null;
|
|
||||||
preCoordinate = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 在一行中的指定列增加值
|
|
||||||
*
|
|
||||||
* @param index 位置
|
|
||||||
* @param value 值
|
|
||||||
*/
|
|
||||||
private void addCellValue(int index, Object value) {
|
|
||||||
this.rowCellList.add(index, value);
|
|
||||||
this.rowHandler.handleCell(this.sheetIndex, this.rowNumber, index, value, this.xssfCellStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 填充空白单元格,如果前一个单元格大于后一个,不需要填充<br>
|
|
||||||
*
|
|
||||||
* @param preCoordinate 前一个单元格坐标
|
|
||||||
* @param curCoordinate 当前单元格坐标
|
|
||||||
* @param isEnd 是否为最后一个单元格
|
|
||||||
*/
|
|
||||||
private void fillBlankCell(String preCoordinate, String curCoordinate, boolean isEnd) {
|
|
||||||
if (false == curCoordinate.equals(preCoordinate)) {
|
|
||||||
int len = ExcelSaxUtil.countNullCell(preCoordinate, curCoordinate);
|
|
||||||
if (isEnd) {
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
while (len-- > 0) {
|
|
||||||
addCellValue(curCell++, StrUtil.EMPTY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置单元格的类型
|
|
||||||
*
|
|
||||||
* @param attributes 属性
|
|
||||||
*/
|
|
||||||
private void setCellType(Attributes attributes) {
|
|
||||||
// numFmtString的值
|
|
||||||
numFmtString = StrUtil.EMPTY;
|
|
||||||
this.cellDataType = CellDataType.of(AttributeName.t.getValue(attributes));
|
|
||||||
|
|
||||||
// 获取单元格的xf索引,对应style.xml中cellXfs的子元素xf
|
|
||||||
if (null != this.stylesTable) {
|
|
||||||
final String xfIndexStr = AttributeName.s.getValue(attributes);
|
|
||||||
if (null != xfIndexStr) {
|
|
||||||
this.xssfCellStyle = stylesTable.getStyleAt(Integer.parseInt(xfIndexStr));
|
|
||||||
// 单元格存储格式的索引,对应style.xml中的numFmts元素的子元素索引
|
|
||||||
final int numFmtIndex = xssfCellStyle.getDataFormat();
|
|
||||||
this.numFmtString = ObjectUtil.defaultIfNull(
|
|
||||||
xssfCellStyle.getDataFormatString(),
|
|
||||||
BuiltinFormats.getBuiltinFormat(numFmtIndex));
|
|
||||||
if (CellDataType.NUMBER == this.cellDataType && ExcelSaxUtil.isDateFormat(numFmtIndex, numFmtString)) {
|
|
||||||
cellDataType = CellDataType.DATE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
// --------------------------------------------------------------------------------------- Private method end
|
// --------------------------------------------------------------------------------------- Private method end
|
||||||
}
|
}
|
@ -0,0 +1,307 @@
|
|||||||
|
package cn.hutool.poi.excel.sax;
|
||||||
|
|
||||||
|
import cn.hutool.core.text.StrBuilder;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.poi.excel.cell.FormulaCellValue;
|
||||||
|
import cn.hutool.poi.excel.sax.handler.RowHandler;
|
||||||
|
import org.apache.poi.ss.usermodel.BuiltinFormats;
|
||||||
|
import org.apache.poi.xssf.model.SharedStringsTable;
|
||||||
|
import org.apache.poi.xssf.model.StylesTable;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
|
||||||
|
import org.xml.sax.Attributes;
|
||||||
|
import org.xml.sax.helpers.DefaultHandler;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sheetData标签内容读取处理器
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* <sheetData></sheetData>
|
||||||
|
* </pre>
|
||||||
|
* @since 5.5.3
|
||||||
|
*/
|
||||||
|
public class SheetDataSaxHandler extends DefaultHandler {
|
||||||
|
|
||||||
|
// 单元格的格式表,对应style.xml
|
||||||
|
protected StylesTable stylesTable;
|
||||||
|
// excel 2007 的共享字符串表,对应sharedString.xml
|
||||||
|
protected SharedStringsTable sharedStringsTable;
|
||||||
|
// sheet的索引
|
||||||
|
protected int sheetIndex;
|
||||||
|
|
||||||
|
// 当前非空行
|
||||||
|
protected int index;
|
||||||
|
// 当前列
|
||||||
|
private int curCell;
|
||||||
|
// 单元数据类型
|
||||||
|
private CellDataType cellDataType;
|
||||||
|
// 当前行号,从0开始
|
||||||
|
private long rowNumber;
|
||||||
|
// 当前列坐标, 如A1,B5
|
||||||
|
private String curCoordinate;
|
||||||
|
// 当前节点名称
|
||||||
|
private ElementName curElementName;
|
||||||
|
// 前一个列的坐标
|
||||||
|
private String preCoordinate;
|
||||||
|
// 行的最大列坐标
|
||||||
|
private String maxCellCoordinate;
|
||||||
|
// 单元格样式
|
||||||
|
private XSSFCellStyle xssfCellStyle;
|
||||||
|
// 单元格存储的格式化字符串,nmtFmt的formatCode属性的值
|
||||||
|
private String numFmtString;
|
||||||
|
// 是否处于sheetData标签内,sax只解析此标签内的内容,其它标签忽略
|
||||||
|
private boolean isInSheetData;
|
||||||
|
|
||||||
|
// 上一次的内容
|
||||||
|
private final StrBuilder lastContent = StrUtil.strBuilder();
|
||||||
|
// 上一次的内容
|
||||||
|
private final StrBuilder lastFormula = StrUtil.strBuilder();
|
||||||
|
// 存储每行的列元素
|
||||||
|
private List<Object> rowCellList = new ArrayList<>();
|
||||||
|
|
||||||
|
public SheetDataSaxHandler(RowHandler rowHandler){
|
||||||
|
this.rowHandler = rowHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行处理器
|
||||||
|
*/
|
||||||
|
protected RowHandler rowHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置行处理器
|
||||||
|
*
|
||||||
|
* @param rowHandler 行处理器
|
||||||
|
*/
|
||||||
|
public void setRowHandler(RowHandler rowHandler) {
|
||||||
|
this.rowHandler = rowHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读到一个xml开始标签时的回调处理方法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void startElement(String uri, String localName, String qName, Attributes attributes) {
|
||||||
|
if ("sheetData".equals(qName)) {
|
||||||
|
this.isInSheetData = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false == this.isInSheetData) {
|
||||||
|
// 非sheetData标签,忽略解析
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ElementName name = ElementName.of(qName);
|
||||||
|
this.curElementName = name;
|
||||||
|
|
||||||
|
if (null != name) {
|
||||||
|
switch (name) {
|
||||||
|
case row:
|
||||||
|
// 行开始
|
||||||
|
startRow(attributes);
|
||||||
|
break;
|
||||||
|
case c:
|
||||||
|
// 单元格元素
|
||||||
|
startCell(attributes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标签结束的回调处理方法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void endElement(String uri, String localName, String qName) {
|
||||||
|
if ("sheetData".equals(qName)) {
|
||||||
|
// sheetData结束,不再解析别的标签
|
||||||
|
this.isInSheetData = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false == this.isInSheetData) {
|
||||||
|
// 非sheetData标签,忽略解析
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.curElementName = null;
|
||||||
|
if (ElementName.c.match(qName)) { // 单元格结束
|
||||||
|
endCell();
|
||||||
|
} else if (ElementName.row.match(qName)) {// 行结束
|
||||||
|
endRow();
|
||||||
|
}
|
||||||
|
// 其它标签忽略
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* s标签结束的回调处理方法
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void characters(char[] ch, int start, int length) {
|
||||||
|
if (false == this.isInSheetData) {
|
||||||
|
// 非sheetData标签,忽略解析
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ElementName elementName = this.curElementName;
|
||||||
|
if (null != elementName) {
|
||||||
|
switch (elementName) {
|
||||||
|
case v:
|
||||||
|
// 得到单元格内容的值
|
||||||
|
lastContent.append(ch, start, length);
|
||||||
|
break;
|
||||||
|
case f:
|
||||||
|
// 得到单元格内容的值
|
||||||
|
lastFormula.append(ch, start, length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 其它标签忽略
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------- Private method start
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行开始
|
||||||
|
*
|
||||||
|
* @param attributes 属性列表
|
||||||
|
*/
|
||||||
|
private void startRow(Attributes attributes) {
|
||||||
|
final String rValue = AttributeName.r.getValue(attributes);
|
||||||
|
if (null != rValue) {
|
||||||
|
this.rowNumber = Long.parseLong(rValue) - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单元格开始
|
||||||
|
*
|
||||||
|
* @param attributes 属性列表
|
||||||
|
*/
|
||||||
|
private void startCell(Attributes attributes) {
|
||||||
|
// 获取当前列坐标
|
||||||
|
final String tempCurCoordinate = AttributeName.r.getValue(attributes);
|
||||||
|
// 前一列为null,则将其设置为"@",A为第一列,ascii码为65,前一列即为@,ascii码64
|
||||||
|
if (preCoordinate == null) {
|
||||||
|
preCoordinate = String.valueOf(ExcelSaxUtil.CELL_FILL_CHAR);
|
||||||
|
} else {
|
||||||
|
// 存在,则前一列要设置为上一列的坐标
|
||||||
|
preCoordinate = curCoordinate;
|
||||||
|
}
|
||||||
|
// 重置当前列
|
||||||
|
curCoordinate = tempCurCoordinate;
|
||||||
|
// 设置单元格类型
|
||||||
|
setCellType(attributes);
|
||||||
|
|
||||||
|
// 清空之前的数据
|
||||||
|
lastContent.reset();
|
||||||
|
lastFormula.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一行结尾
|
||||||
|
*/
|
||||||
|
private void endRow() {
|
||||||
|
// 最大列坐标以第一个非空行的为准
|
||||||
|
if (index == 0) {
|
||||||
|
maxCellCoordinate = curCoordinate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补全一行尾部可能缺失的单元格
|
||||||
|
if (maxCellCoordinate != null) {
|
||||||
|
fillBlankCell(curCoordinate, maxCellCoordinate, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
rowHandler.handle(sheetIndex, rowNumber, rowCellList);
|
||||||
|
|
||||||
|
// 一行结束
|
||||||
|
// 新建一个新列,之前的列抛弃(可能被回收或rowHandler处理)
|
||||||
|
rowCellList = new ArrayList<>(curCell + 1);
|
||||||
|
// 行数增加
|
||||||
|
index++;
|
||||||
|
// 当前列置0
|
||||||
|
curCell = 0;
|
||||||
|
// 置空当前列坐标和前一列坐标
|
||||||
|
curCoordinate = null;
|
||||||
|
preCoordinate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一个单元格结尾
|
||||||
|
*/
|
||||||
|
private void endCell() {
|
||||||
|
// 补全单元格之间的空格
|
||||||
|
fillBlankCell(preCoordinate, curCoordinate, false);
|
||||||
|
|
||||||
|
final String contentStr = StrUtil.trim(lastContent);
|
||||||
|
Object value = ExcelSaxUtil.getDataValue(this.cellDataType, contentStr, this.sharedStringsTable, this.numFmtString);
|
||||||
|
if (false == this.lastFormula.isEmpty()) {
|
||||||
|
value = new FormulaCellValue(StrUtil.trim(lastFormula), value);
|
||||||
|
}
|
||||||
|
addCellValue(curCell++, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在一行中的指定列增加值
|
||||||
|
*
|
||||||
|
* @param index 位置
|
||||||
|
* @param value 值
|
||||||
|
*/
|
||||||
|
private void addCellValue(int index, Object value) {
|
||||||
|
this.rowCellList.add(index, value);
|
||||||
|
this.rowHandler.handleCell(this.sheetIndex, this.rowNumber, index, value, this.xssfCellStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 填充空白单元格,如果前一个单元格大于后一个,不需要填充<br>
|
||||||
|
*
|
||||||
|
* @param preCoordinate 前一个单元格坐标
|
||||||
|
* @param curCoordinate 当前单元格坐标
|
||||||
|
* @param isEnd 是否为最后一个单元格
|
||||||
|
*/
|
||||||
|
private void fillBlankCell(String preCoordinate, String curCoordinate, boolean isEnd) {
|
||||||
|
if (false == curCoordinate.equals(preCoordinate)) {
|
||||||
|
int len = ExcelSaxUtil.countNullCell(preCoordinate, curCoordinate);
|
||||||
|
if (isEnd) {
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
while (len-- > 0) {
|
||||||
|
addCellValue(curCell++, StrUtil.EMPTY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置单元格的类型
|
||||||
|
*
|
||||||
|
* @param attributes 属性
|
||||||
|
*/
|
||||||
|
private void setCellType(Attributes attributes) {
|
||||||
|
// numFmtString的值
|
||||||
|
numFmtString = StrUtil.EMPTY;
|
||||||
|
this.cellDataType = CellDataType.of(AttributeName.t.getValue(attributes));
|
||||||
|
|
||||||
|
// 获取单元格的xf索引,对应style.xml中cellXfs的子元素xf
|
||||||
|
if (null != this.stylesTable) {
|
||||||
|
final String xfIndexStr = AttributeName.s.getValue(attributes);
|
||||||
|
if (null != xfIndexStr) {
|
||||||
|
this.xssfCellStyle = stylesTable.getStyleAt(Integer.parseInt(xfIndexStr));
|
||||||
|
// 单元格存储格式的索引,对应style.xml中的numFmts元素的子元素索引
|
||||||
|
final int numFmtIndex = xssfCellStyle.getDataFormat();
|
||||||
|
this.numFmtString = ObjectUtil.defaultIfNull(
|
||||||
|
xssfCellStyle.getDataFormatString(),
|
||||||
|
BuiltinFormats.getBuiltinFormat(numFmtIndex));
|
||||||
|
if (CellDataType.NUMBER == this.cellDataType && ExcelSaxUtil.isDateFormat(numFmtIndex, numFmtString)) {
|
||||||
|
cellDataType = CellDataType.DATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------- Private method end
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user