Browse Source

加入解析class缓存

2.1.x
Jiaju Zhuang 5 years ago
parent
commit
cba3a9cea9
  1. 2
      pom.xml
  2. 2
      src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java
  3. 42
      src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java
  4. 4
      src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java
  5. 2
      src/main/java/com/alibaba/excel/event/AnalysisEventListener.java
  6. 35
      src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java
  7. 7
      src/main/java/com/alibaba/excel/metadata/CellData.java
  8. 46
      src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java
  9. 3
      src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java
  10. 3
      src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java
  11. 28
      src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java
  12. 150
      src/main/java/com/alibaba/excel/util/ClassUtils.java
  13. 19
      src/main/java/com/alibaba/excel/util/ConverterUtils.java
  14. 2
      src/main/java/com/alibaba/excel/util/DateUtils.java
  15. 16
      src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java
  16. 18
      src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java
  17. 38
      src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java
  18. 9
      src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java
  19. 4
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java
  20. 4
      src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java
  21. 110
      src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java
  22. 4
      src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java
  23. 1
      src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java
  24. 2
      src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java
  25. 7
      src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java
  26. 5
      update.md

2
pom.xml

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.0-beta4</version>
<version>2.1.1</version>
<packaging>jar</packaging>
<name>easyexcel</name>

2
src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java

@ -27,6 +27,7 @@ import com.alibaba.excel.analysis.ExcelReadExecutor;
import com.alibaba.excel.analysis.v03.handlers.BlankOrErrorRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.IndexRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.LabelRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.MissingCellDummyRecordHandler;
import com.alibaba.excel.analysis.v03.handlers.NoteRecordHandler;
@ -211,6 +212,7 @@ public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor {
recordHandlers.add(new RkRecordHandler());
recordHandlers.add(new SstRecordHandler());
recordHandlers.add(new MissingCellDummyRecordHandler());
recordHandlers.add(new IndexRecordHandler(analysisContext));
Collections.sort(recordHandlers);
}

42
src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java

@ -0,0 +1,42 @@
package com.alibaba.excel.analysis.v03.handlers;
import org.apache.poi.hssf.record.IndexRecord;
import org.apache.poi.hssf.record.Record;
import com.alibaba.excel.analysis.v03.AbstractXlsRecordHandler;
import com.alibaba.excel.context.AnalysisContext;
/**
* Record handler
*
* @author Jiaju Zhuang
*/
public class IndexRecordHandler extends AbstractXlsRecordHandler {
private AnalysisContext context;
public IndexRecordHandler(AnalysisContext context) {
this.context = context;
}
@Override
public boolean support(Record record) {
return record instanceof IndexRecord;
}
@Override
public void init() {}
@Override
public void processRecord(Record record) {
if (context.readSheetHolder() == null) {
return;
}
context.readSheetHolder().setApproximateTotalRowNumber(((IndexRecord)record).getLastRowAdd1());
}
@Override
public int getOrder() {
return 1;
}
}

4
src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java

@ -10,7 +10,7 @@ import com.alibaba.excel.context.AnalysisContext;
/**
* Cell Handler
*
*
* @author jipengfei
*/
public class CountRowCellHandler implements XlsxCellHandler {
@ -31,7 +31,7 @@ public class CountRowCellHandler implements XlsxCellHandler {
String d = attributes.getValue(DIMENSION_REF);
String totalStr = d.substring(d.indexOf(":") + 1, d.length());
String c = totalStr.toUpperCase().replaceAll("[A-Z]", "");
analysisContext.readSheetHolder().setTotal(Integer.parseInt(c));
analysisContext.readSheetHolder().setApproximateTotalRowNumber(Integer.parseInt(c));
}
@Override

2
src/main/java/com/alibaba/excel/event/AnalysisEventListener.java

@ -16,7 +16,7 @@ public abstract class AnalysisEventListener<T> implements ReadListener<T> {
@Override
public void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context.currentReadHolder()), context);
invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context);
}
/**

35
src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java

@ -1,5 +1,6 @@
package com.alibaba.excel.exception;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
@ -17,6 +18,10 @@ public class ExcelDataConvertException extends RuntimeException {
* NotNull.
*/
private Integer columnIndex;
/**
* NotNull.
*/
private CellData cellData;
/**
* Nullable.Only when the header is configured and when the class header is used is not null.
*
@ -24,34 +29,24 @@ public class ExcelDataConvertException extends RuntimeException {
*/
private ExcelContentProperty excelContentProperty;
public ExcelDataConvertException(String message) {
super(message);
}
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty,
String message) {
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData,
ExcelContentProperty excelContentProperty, String message) {
super(message);
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
this.cellData = cellData;
this.excelContentProperty = excelContentProperty;
}
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, ExcelContentProperty excelContentProperty,
String message, Throwable cause) {
public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData,
ExcelContentProperty excelContentProperty, String message, Throwable cause) {
super(message, cause);
this.rowIndex = rowIndex;
this.columnIndex = columnIndex;
this.cellData = cellData;
this.excelContentProperty = excelContentProperty;
}
public ExcelDataConvertException(String message, Throwable cause) {
super(message, cause);
}
public ExcelDataConvertException(Throwable cause) {
super(cause);
}
public Integer getRowIndex() {
return rowIndex;
}
@ -75,4 +70,12 @@ public class ExcelDataConvertException extends RuntimeException {
public void setExcelContentProperty(ExcelContentProperty excelContentProperty) {
this.excelContentProperty = excelContentProperty;
}
public CellData getCellData() {
return cellData;
}
public void setCellData(CellData cellData) {
this.cellData = cellData;
}
}

7
src/main/java/com/alibaba/excel/metadata/CellData.java

@ -229,18 +229,21 @@ public class CellData<T> {
@Override
public String toString() {
if (type == null) {
return "empty";
return StringUtils.EMPTY;
}
switch (type) {
case NUMBER:
return numberValue.toString();
case BOOLEAN:
return booleanValue.toString();
case DIRECT_STRING:
case STRING:
case ERROR:
return stringValue;
case IMAGE:
return "image[" + imageValue.length + "]";
default:
return "empty";
return StringUtils.EMPTY;
}
}

46
src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java

@ -24,6 +24,7 @@ import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.exception.ExcelGenerateException;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.Holder;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder;
@ -119,51 +120,10 @@ public class ExcelHeadProperty {
if (headClazz == null) {
return;
}
List<Field> fieldList = new ArrayList<Field>();
Class tempClass = headClazz;
// When the parent class is null, it indicates that the parent class (Object class) has reached the top
// level.
while (tempClass != null) {
Collections.addAll(fieldList, tempClass.getDeclaredFields());
// Get the parent class and give it to yourself
tempClass = tempClass.getSuperclass();
}
ExcelIgnoreUnannotated excelIgnoreUnannotated =
(ExcelIgnoreUnannotated)headClazz.getAnnotation(ExcelIgnoreUnannotated.class);
// Screening of field
// Declared fields
List<Field> defaultFieldList = new ArrayList<Field>();
Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
for (Field field : fieldList) {
ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
if (excelIgnore != null) {
ignoreMap.put(field.getName(), field);
continue;
}
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
boolean noExcelProperty = excelProperty == null
&& ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null);
if (noExcelProperty) {
ignoreMap.put(field.getName(), field);
continue;
}
boolean isStaticFinalOrTransient =
(Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))
|| Modifier.isTransient(field.getModifiers());
if (excelProperty == null && isStaticFinalOrTransient) {
ignoreMap.put(field.getName(), field);
continue;
}
if (excelProperty == null || excelProperty.index() < 0) {
defaultFieldList.add(field);
continue;
}
if (customFiledMap.containsKey(excelProperty.index())) {
throw new ExcelGenerateException("The index of '" + customFiledMap.get(excelProperty.index()).getName()
+ "' and '" + field.getName() + "' must be inconsistent");
}
customFiledMap.put(excelProperty.index(), field);
}
ClassUtils.declaredFields(headClazz, defaultFieldList, customFiledMap, ignoreMap, convertAllFiled);
int index = 0;
for (Field field : defaultFieldList) {

3
src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java

@ -93,7 +93,8 @@ public class ModelBuildEventListener extends AbstractIgnoreExceptionReadListener
try {
resultModel = excelReadHeadProperty.getHeadClazz().newInstance();
} catch (Exception e) {
throw new ExcelDataConvertException(
throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), 0,
new CellData(CellDataTypeEnum.EMPTY), null,
"Can not instance class: " + excelReadHeadProperty.getHeadClazz().getName(), e);
}
Map<Integer, Head> headMap = excelReadHeadProperty.getHeadMap();

3
src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java

@ -190,8 +190,7 @@ public abstract class AbstractReadHolder extends AbstractHolder implements ReadH
if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) {
return;
}
Map<Integer, String> dataMap =
ConverterUtils.convertToStringMap(cellDataMap, analysisContext.currentReadHolder());
Map<Integer, String> dataMap = ConverterUtils.convertToStringMap(cellDataMap, analysisContext);
ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty();
Map<Integer, Head> headMapData = excelHeadPropertyData.getHeadMap();
Map<Integer, ExcelContentProperty> contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap();

28
src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java

@ -26,10 +26,9 @@ public class ReadSheetHolder extends AbstractReadHolder {
*/
private String sheetName;
/**
* get total row , Data may be inaccurate
* Gets the total number of rows , data may be inaccurate
*/
@Deprecated
private Integer total;
private Integer approximateTotalRowNumber;
public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) {
super(readSheet, readWorkbookHolder, readWorkbookHolder.getReadWorkbook().getConvertAllFiled());
@ -71,12 +70,29 @@ public class ReadSheetHolder extends AbstractReadHolder {
this.sheetName = sheetName;
}
/**
*
* Approximate total number of rows
*
* @return
* @see #getApproximateTotalRowNumber()
*/
@Deprecated
public Integer getTotal() {
return total;
return approximateTotalRowNumber;
}
/**
* Approximate total number of rows
*
* @return
*/
public Integer getApproximateTotalRowNumber() {
return approximateTotalRowNumber;
}
public void setTotal(Integer total) {
this.total = total;
public void setApproximateTotalRowNumber(Integer approximateTotalRowNumber) {
this.approximateTotalRowNumber = approximateTotalRowNumber;
}
@Override

150
src/main/java/com/alibaba/excel/util/ClassUtils.java

@ -0,0 +1,150 @@
package com.alibaba.excel.util;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.exception.ExcelCommonException;
import com.alibaba.excel.metadata.BaseRowModel;
/**
* Class utils
*
* @author Jiaju Zhuang
**/
public class ClassUtils {
private static final Map<Class, SoftReference<FieldCache>> FIELD_CACHE =
new ConcurrentHashMap<Class, SoftReference<FieldCache>>();
public static void declaredFields(Class clazz, List<Field> defaultFieldList, Map<Integer, Field> customFiledMap,
Map<String, Field> ignoreMap, Boolean convertAllFiled) {
FieldCache fieldCache = getFieldCache(clazz, convertAllFiled);
if (fieldCache != null) {
defaultFieldList.addAll(fieldCache.getDefaultFieldList());
customFiledMap.putAll(fieldCache.getCustomFiledMap());
ignoreMap.putAll(fieldCache.getIgnoreMap());
}
}
public static void declaredFields(Class clazz, List<Field> fieldList, Boolean convertAllFiled) {
FieldCache fieldCache = getFieldCache(clazz, convertAllFiled);
if (fieldCache != null) {
fieldList.addAll(fieldCache.getAllFieldList());
}
}
private static FieldCache getFieldCache(Class clazz, Boolean convertAllFiled) {
if (clazz == null) {
return null;
}
SoftReference<FieldCache> fieldCacheSoftReference = FIELD_CACHE.get(clazz);
if (fieldCacheSoftReference != null && fieldCacheSoftReference.get() != null) {
return fieldCacheSoftReference.get();
}
synchronized (clazz) {
fieldCacheSoftReference = FIELD_CACHE.get(clazz);
if (fieldCacheSoftReference != null && fieldCacheSoftReference.get() != null) {
return fieldCacheSoftReference.get();
}
declaredFields(clazz, convertAllFiled);
}
return FIELD_CACHE.get(clazz).get();
}
private static void declaredFields(Class clazz, Boolean convertAllFiled) {
List<Field> tempFieldList = new ArrayList<Field>();
Class tempClass = clazz;
// When the parent class is null, it indicates that the parent class (Object class) has reached the top
// level.
while (tempClass != null && tempClass != BaseRowModel.class) {
Collections.addAll(tempFieldList, tempClass.getDeclaredFields());
// Get the parent class and give it to yourself
tempClass = tempClass.getSuperclass();
}
// Screening of field
List<Field> defaultFieldList = new ArrayList<Field>();
Map<Integer, Field> customFiledMap = new TreeMap<Integer, Field>();
List<Field> allFieldList = new ArrayList<Field>();
Map<String, Field> ignoreMap = new HashMap<String, Field>(16);
ExcelIgnoreUnannotated excelIgnoreUnannotated =
(ExcelIgnoreUnannotated)clazz.getAnnotation(ExcelIgnoreUnannotated.class);
for (Field field : tempFieldList) {
ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class);
if (excelIgnore != null) {
ignoreMap.put(field.getName(), field);
continue;
}
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
boolean noExcelProperty = excelProperty == null
&& ((convertAllFiled != null && !convertAllFiled) || excelIgnoreUnannotated != null);
if (noExcelProperty) {
ignoreMap.put(field.getName(), field);
continue;
}
boolean isStaticFinalOrTransient =
(Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()))
|| Modifier.isTransient(field.getModifiers());
if (excelProperty == null && isStaticFinalOrTransient) {
ignoreMap.put(field.getName(), field);
continue;
}
if (excelProperty == null || excelProperty.index() < 0) {
defaultFieldList.add(field);
allFieldList.add(field);
continue;
}
if (customFiledMap.containsKey(excelProperty.index())) {
throw new ExcelCommonException("The index of '" + customFiledMap.get(excelProperty.index()).getName()
+ "' and '" + field.getName() + "' must be inconsistent");
}
customFiledMap.put(excelProperty.index(), field);
allFieldList.add(field);
}
FIELD_CACHE.put(clazz,
new SoftReference<FieldCache>(new FieldCache(defaultFieldList, customFiledMap, allFieldList, ignoreMap)));
}
private static class FieldCache {
private List<Field> defaultFieldList;
private Map<Integer, Field> customFiledMap;
private List<Field> allFieldList;
private Map<String, Field> ignoreMap;
public FieldCache(List<Field> defaultFieldList, Map<Integer, Field> customFiledMap, List<Field> allFieldList,
Map<String, Field> ignoreMap) {
this.defaultFieldList = defaultFieldList;
this.customFiledMap = customFiledMap;
this.allFieldList = allFieldList;
this.ignoreMap = ignoreMap;
}
public List<Field> getDefaultFieldList() {
return defaultFieldList;
}
public Map<Integer, Field> getCustomFiledMap() {
return customFiledMap;
}
public List<Field> getAllFieldList() {
return allFieldList;
}
public Map<String, Field> getIgnoreMap() {
return ignoreMap;
}
}
}

19
src/main/java/com/alibaba/excel/util/ConverterUtils.java

@ -6,6 +6,7 @@ import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ConverterKeyBuild;
import com.alibaba.excel.enums.CellDataTypeEnum;
@ -28,11 +29,12 @@ public class ConverterUtils {
* Convert it into a String map
*
* @param cellDataMap
* @param readHolder
* @param context
* @return
*/
public static Map<Integer, String> convertToStringMap(Map<Integer, CellData> cellDataMap, ReadHolder readHolder) {
public static Map<Integer, String> convertToStringMap(Map<Integer, CellData> cellDataMap, AnalysisContext context) {
Map<Integer, String> stringMap = new HashMap<Integer, String>(cellDataMap.size() * 4 / 3 + 1);
ReadHolder currentReadHolder = context.currentReadHolder();
int index = 0;
for (Map.Entry<Integer, CellData> entry : cellDataMap.entrySet()) {
Integer key = entry.getKey();
@ -47,16 +49,17 @@ public class ConverterUtils {
continue;
}
Converter converter =
readHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType()));
currentReadHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType()));
if (converter == null) {
throw new ExcelDataConvertException(
throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null,
"Converter not found, convert " + cellData.getType() + " to String");
}
try {
stringMap.put(key,
(String)(converter.convertToJavaData(cellData, null, readHolder.globalConfiguration())));
(String)(converter.convertToJavaData(cellData, null, currentReadHolder.globalConfiguration())));
} catch (Exception e) {
throw new ExcelDataConvertException("Convert data " + cellData + " to String error ", e);
throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null,
"Convert data " + cellData + " to String error ", e);
}
}
return stringMap;
@ -123,13 +126,13 @@ public class ConverterUtils {
converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType()));
}
if (converter == null) {
throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty,
throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty,
"Converter not found, convert " + cellData.getType() + " to " + clazz.getName());
}
try {
return converter.convertToJavaData(cellData, contentProperty, globalConfiguration);
} catch (Exception e) {
throw new ExcelDataConvertException(rowIndex, columnIndex, contentProperty,
throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty,
"Convert data " + cellData + " to " + clazz + " error ", e);
}
}

2
src/main/java/com/alibaba/excel/util/DateUtils.java

@ -69,7 +69,7 @@ public class DateUtils {
case 10:
return DATE_FORMAT_10;
default:
throw new ExcelDataConvertException("can not find date format for:" + dateString);
throw new IllegalArgumentException("can not find date format for:" + dateString);
}
}

16
src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java

@ -59,8 +59,9 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
case EMPTY:
return cellData;
default:
throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType()
+ "at row:" + cell.getRow().getRowNum());
throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(), cellData,
excelContentProperty, "Not supported data:" + value + " return type:" + cell.getCellType()
+ "at row:" + cell.getRow().getRowNum());
}
}
@ -102,7 +103,8 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
converter = currentWriteHolder.converterMap().get(ConverterKeyBuild.buildKey(clazz));
}
if (converter == null) {
throw new ExcelDataConvertException(
throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Can not find 'Converter' support class " + clazz.getSimpleName() + ".");
}
CellData cellData;
@ -110,11 +112,13 @@ public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor {
cellData =
converter.convertToExcelData(value, excelContentProperty, currentWriteHolder.globalConfiguration());
} catch (Exception e) {
throw new ExcelDataConvertException("Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(),
e);
throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Convert data:" + value + " error,at row:" + cell.getRow().getRowNum(), e);
}
if (cellData == null || cellData.getType() == null) {
throw new ExcelDataConvertException(
throw new ExcelDataConvertException(cell.getRow().getRowNum(), cell.getColumnIndex(),
new CellData(CellDataTypeEnum.EMPTY), excelContentProperty,
"Convert data:" + value + " return null,at row:" + cell.getRow().getRowNum());
}
return cellData;

18
src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java

@ -2,7 +2,6 @@ package com.alibaba.excel.write.executor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -13,10 +12,10 @@ import org.apache.poi.ss.usermodel.Row;
import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.enums.HeadKindEnum;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.WorkBookUtil;
import com.alibaba.excel.util.WriteHandlerUtils;
@ -158,13 +157,11 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
continue;
}
Object value = beanMap.get(filedName);
if (value == null) {
continue;
}
WriteHandlerUtils.beforeCellCreate(writeContext, row, null, cellIndex, relativeRowIndex, Boolean.FALSE);
Cell cell = WorkBookUtil.createCell(row, cellIndex++);
WriteHandlerUtils.afterCellCreate(writeContext, cell, null, relativeRowIndex, Boolean.FALSE);
CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null);
CellData cellData =
converterAndSet(currentWriteHolder, value == null ? null : value.getClass(), cell, value, null);
WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, null, relativeRowIndex, Boolean.FALSE);
}
}
@ -173,13 +170,8 @@ public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor {
if (!fieldList.isEmpty()) {
return;
}
Class tempClass = clazz;
while (tempClass != null) {
if (tempClass != BaseRowModel.class) {
Collections.addAll(fieldList, tempClass.getDeclaredFields());
}
tempClass = tempClass.getSuperclass();
}
ClassUtils.declaredFields(clazz, fieldList,
writeContext.writeWorkbookHolder().getWriteWorkbook().getConvertAllFiled());
}
}

38
src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java

@ -3,9 +3,11 @@ package com.alibaba.excel.write.executor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
@ -57,6 +59,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
*/
private Map<Integer, Map<AnalysisCell, CellStyle>> collectionFieldStyleCache =
new HashMap<Integer, Map<AnalysisCell, CellStyle>>(8);
/**
* Row height cache for collection
*/
private Map<Integer, Short> collectionRowHeightCache = new HashMap<Integer, Short>(8);
/**
* Last index cache for collection fields
*/
@ -121,7 +127,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (collectionLastIndexMap == null) {
number--;
}
sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number);
sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number, true, false);
for (AnalysisCell analysisCell : templateAnalysisCache.get(writeContext.writeSheetHolder().getSheetNo())) {
if (analysisCell.getRowIndex() > maxRowIndex) {
analysisCell.setRowIndex(analysisCell.getRowIndex() + number);
@ -245,7 +251,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
} else {
row = sheet.createRow(lastRowIndex);
}
checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo);
WriteHandlerUtils.afterRowCreate(writeContext, row, null, Boolean.FALSE);
} else {
checkRowHeight(analysisCell, fillConfig, isOriginalCell, row, sheetNo);
}
}
Cell cell = row.getCell(lastColumnIndex);
@ -271,6 +280,21 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
return cell;
}
private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row,
Integer sheetNo) {
if (!analysisCell.getFirstColumn() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) {
return;
}
if (isOriginalCell) {
collectionRowHeightCache.put(sheetNo, row.getHeight());
return;
}
Short rowHeight = collectionRowHeightCache.get(sheetNo);
if (rowHeight != null) {
row.setHeight(rowHeight);
}
}
private List<AnalysisCell> readTemplateData(Map<Integer, List<AnalysisCell>> analysisCache) {
Integer sheetNo = writeContext.writeSheetHolder().getSheetNo();
List<AnalysisCell> analysisCellList = analysisCache.get(sheetNo);
@ -280,6 +304,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
Sheet sheet = writeContext.writeSheetHolder().getCachedSheet();
analysisCellList = new ArrayList<AnalysisCell>();
List<AnalysisCell> collectionAnalysisCellList = new ArrayList<AnalysisCell>();
Set<Integer> firstColumnCache = new HashSet<Integer>();
for (int i = 0; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) {
@ -290,7 +315,8 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (cell == null) {
continue;
}
String preparedData = prepareData(cell, analysisCellList, collectionAnalysisCellList, i, j);
String preparedData =
prepareData(cell, analysisCellList, collectionAnalysisCellList, i, j, firstColumnCache);
// Prevent empty data from not being replaced
if (preparedData != null) {
cell.setCellValue(preparedData);
@ -310,10 +336,11 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
* @param collectionAnalysisCellList
* @param rowIndex
* @param columnIndex
* @param firstColumnCache
* @return Returns the data that the cell needs to replace
*/
private String prepareData(Cell cell, List<AnalysisCell> analysisCellList,
List<AnalysisCell> collectionAnalysisCellList, int rowIndex, int columnIndex) {
List<AnalysisCell> collectionAnalysisCellList, int rowIndex, int columnIndex, Set<Integer> firstColumnCache) {
if (!CellType.STRING.equals(cell.getCellTypeEnum())) {
return null;
}
@ -386,6 +413,10 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) {
analysisCellList.add(analysisCell);
} else {
if (!firstColumnCache.contains(rowIndex)) {
analysisCell.setFirstColumn(Boolean.TRUE);
firstColumnCache.add(rowIndex);
}
collectionAnalysisCellList.add(analysisCell);
}
return preparedData.toString();
@ -403,6 +434,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor {
List<String> prepareDataList = new ArrayList<String>();
analysisCell.setPrepareDataList(prepareDataList);
analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON);
analysisCell.setFirstColumn(Boolean.FALSE);
return analysisCell;
}

9
src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java

@ -16,6 +16,7 @@ public class AnalysisCell {
private List<String> prepareDataList;
private Boolean onlyOneVariable;
private WriteTemplateAnalysisCellTypeEnum cellType;
private Boolean firstColumn;
public int getColumnIndex() {
return columnIndex;
@ -65,6 +66,14 @@ public class AnalysisCell {
this.cellType = cellType;
}
public Boolean getFirstColumn() {
return firstColumn;
}
public void setFirstColumn(Boolean firstColumn) {
this.firstColumn = firstColumn;
}
@Override
public boolean equals(Object o) {
if (this == o) {

4
src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java

@ -39,8 +39,8 @@ public class DemoExceptionListener extends AnalysisEventListener<ExceptionDemoDa
// 如果要获取头的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
LOGGER.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
}
}

4
src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java

@ -38,8 +38,8 @@ public class DemoHeadDataListener extends AnalysisEventListener<DemoData> {
LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex());
LOGGER.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
}
}

110
src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java

@ -0,0 +1,110 @@
package com.alibaba.easyexcel.test.temp;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Ignore;
import org.junit.Test;
import com.alibaba.easyexcel.test.demo.fill.FillData;
import com.alibaba.easyexcel.test.util.TestFileUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
/**
* 写的填充写法
*
* @since 2.1.1
* @author Jiaju Zhuang
*/
@Ignore
public class FillTempTest {
/**
* 复杂的填充
*
* @since 2.1.1
*/
@Test
public void complexFill() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量
String templateFileName = "D:\\test\\complex.xlsx";
String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx";
ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
// forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
// 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
// 如果数据量大 list不是最后一行 参照下一个
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(data(), fillConfig, writeSheet);
excelWriter.fill(data(), fillConfig, writeSheet);
Map<String, Object> map = new HashMap<String, Object>();
map.put("date", "2019年10月9日13:28:28");
map.put("total", 1000);
excelWriter.fill(map, writeSheet);
excelWriter.finish();
}
/**
* 数据量大的复杂填充
* <p>
* 这里的解决方案是 确保模板list为最后一行然后再拼接table.还有03版没救只能刚正面加内存
*
* @since 2.1.1
*/
@Test
public void complexFillWithTable() {
// 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
// {} 代表普通变量 {.} 代表是list的变量
// 这里模板 删除了list以后的数据,也就是统计的这一行
String templateFileName = "D:\\test\\complex.xlsx";
String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx";
ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
// 直接写入数据
excelWriter.fill(data(), writeSheet);
excelWriter.fill(data(), writeSheet);
// 写入list之前的数据
Map<String, Object> map = new HashMap<String, Object>();
map.put("date", "2019年10月9日13:28:28");
excelWriter.fill(map, writeSheet);
// list 后面还有个统计 想办法手动写入
// 这里偷懒直接用list 也可以用对象
List<List<String>> totalListList = new ArrayList<List<String>>();
List<String> totalList = new ArrayList<String>();
totalListList.add(totalList);
totalList.add(null);
totalList.add(null);
totalList.add(null);
// 第四列
totalList.add("统计:1000");
// 这里是write 别和fill 搞错了
excelWriter.write(totalListList, writeSheet);
excelWriter.finish();
// 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以
// 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案
}
private List<FillData> data() {
List<FillData> list = new ArrayList<FillData>();
for (int i = 0; i < 10; i++) {
FillData fillData = new FillData();
list.add(fillData);
fillData.setName("张三");
fillData.setNumber(5.2);
}
return list;
}
}

4
src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java

@ -79,11 +79,11 @@ public class PoiTest {
@Test
public void lastRowNum255() throws IOException, InvalidFormatException {
String file = TestFileUtil.getPath() + "fill" + File.separator + "complex.xlsx";
String file = "D:\\test\\complex.xlsx";
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file));
SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook);
Sheet xssfSheet = xssfWorkbook.getSheetAt(0);
xssfSheet.shiftRows(2, 4, 10);
xssfSheet.shiftRows(1, 4, 10, true, true);
FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx");
sxssfWorkbook.write(fileout);

1
src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java

@ -25,6 +25,7 @@ public class HDListener extends AnalysisEventListener<HeadReadData> {
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
LOGGER.info("HEAD:{}", JSON.toJSONString(headMap));
LOGGER.info("total:{}", context.readSheetHolder().getTotal());
}

2
src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java

@ -3,6 +3,7 @@ package com.alibaba.easyexcel.test.temp.read;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 临时测试
@ -10,6 +11,7 @@ import lombok.Data;
* @author Jiaju Zhuang
**/
@Data
@Accessors(chain = true)
public class HeadReadData {
@ExcelProperty("头1")
private String h1;

7
src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java

@ -2,6 +2,7 @@ package com.alibaba.easyexcel.test.temp.read;
import java.io.File;
import org.junit.Ignore;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -13,13 +14,15 @@ import com.alibaba.excel.EasyExcel;
*
* @author Jiaju Zhuang
**/
@Ignore
public class HeadReadTest {
private static final Logger LOGGER = LoggerFactory.getLogger(HeadReadTest.class);
@Test
public void test() throws Exception {
File file = new File("D:\\test\\headt1.xlsx");
EasyExcel.read(file, HeadReadData.class, new HDListener()).sheet().doRead();
File file = new File("D:\\test\\headt1.xls");
EasyExcel.read(file, HeadReadData.class, new HDListener()).sheet(0).doRead();
}
}

5
update.md

@ -7,6 +7,11 @@
* 加入多次关闭判断,防止多次关闭异常
* 加入根据模板自动识别导出的excel类型
* 修改默认失败后,不再往文件流写入数据。通过参数`writeExcelOnException` 参数设置异常了也要写入前面的数据。
* 循环合并策略支持一次性合并多列
* `ExcelDataConvertException`返回新增具体报错的数据
* 加入解析class缓存
* 修复填充的时候行高不复制的Bug [Issue #780](https://github.com/alibaba/easyexcel/issues/780)
* 修复03版无法获取大概总行数的bug
# 2.1.0-beta4
* 修改最长匹配策略会空指针的bug [Issue #747](https://github.com/alibaba/easyexcel/issues/747)

Loading…
Cancel
Save