diff --git a/pom.xml b/pom.xml index a6183490..7dad24da 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.alibaba easyexcel - 2.1.0-beta4 + 2.1.1 jar easyexcel diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java index 76a869ae..e0c150cd 100644 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ b/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); } diff --git a/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java b/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java new file mode 100644 index 00000000..6837ebd6 --- /dev/null +++ b/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; + } +} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java index ea63409b..b94483f4 100644 --- a/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountRowCellHandler.java +++ b/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 diff --git a/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java b/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java index 89bca753..176bd798 100644 --- a/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java +++ b/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java @@ -16,7 +16,7 @@ public abstract class AnalysisEventListener implements ReadListener { @Override public void invokeHead(Map headMap, AnalysisContext context) { - invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context.currentReadHolder()), context); + invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context); } /** diff --git a/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java b/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java index 2ffdde5f..5198c20f 100644 --- a/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java +++ b/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; + } } diff --git a/src/main/java/com/alibaba/excel/metadata/CellData.java b/src/main/java/com/alibaba/excel/metadata/CellData.java index a6b7af58..7cd5496e 100644 --- a/src/main/java/com/alibaba/excel/metadata/CellData.java +++ b/src/main/java/com/alibaba/excel/metadata/CellData.java @@ -229,18 +229,21 @@ public class CellData { @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; } } diff --git a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java b/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java index 9d6a5442..782f635a 100644 --- a/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java +++ b/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 fieldList = new ArrayList(); - 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 defaultFieldList = new ArrayList(); Map customFiledMap = new TreeMap(); - 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) { diff --git a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java b/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java index 1cfe1823..f5594db0 100644 --- a/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java +++ b/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 headMap = excelReadHeadProperty.getHeadMap(); diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java index 786d4386..02b2ef5f 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java +++ b/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 dataMap = - ConverterUtils.convertToStringMap(cellDataMap, analysisContext.currentReadHolder()); + Map dataMap = ConverterUtils.convertToStringMap(cellDataMap, analysisContext); ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty(); Map headMapData = excelHeadPropertyData.getHeadMap(); Map contentPropertyMapData = excelHeadPropertyData.getContentPropertyMap(); diff --git a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java b/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java index 64d1d6cb..84dd4c4a 100644 --- a/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java +++ b/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 diff --git a/src/main/java/com/alibaba/excel/util/ClassUtils.java b/src/main/java/com/alibaba/excel/util/ClassUtils.java new file mode 100644 index 00000000..64bc29d7 --- /dev/null +++ b/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> FIELD_CACHE = + new ConcurrentHashMap>(); + + public static void declaredFields(Class clazz, List defaultFieldList, Map customFiledMap, + Map 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 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 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 tempFieldList = new ArrayList(); + 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 defaultFieldList = new ArrayList(); + Map customFiledMap = new TreeMap(); + List allFieldList = new ArrayList(); + Map ignoreMap = new HashMap(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(new FieldCache(defaultFieldList, customFiledMap, allFieldList, ignoreMap))); + } + + private static class FieldCache { + private List defaultFieldList; + private Map customFiledMap; + private List allFieldList; + private Map ignoreMap; + + public FieldCache(List defaultFieldList, Map customFiledMap, List allFieldList, + Map ignoreMap) { + this.defaultFieldList = defaultFieldList; + this.customFiledMap = customFiledMap; + this.allFieldList = allFieldList; + this.ignoreMap = ignoreMap; + } + + public List getDefaultFieldList() { + return defaultFieldList; + } + + public Map getCustomFiledMap() { + return customFiledMap; + } + + public List getAllFieldList() { + return allFieldList; + } + + public Map getIgnoreMap() { + return ignoreMap; + } + + } +} diff --git a/src/main/java/com/alibaba/excel/util/ConverterUtils.java b/src/main/java/com/alibaba/excel/util/ConverterUtils.java index 200440ec..43b12c53 100644 --- a/src/main/java/com/alibaba/excel/util/ConverterUtils.java +++ b/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 convertToStringMap(Map cellDataMap, ReadHolder readHolder) { + public static Map convertToStringMap(Map cellDataMap, AnalysisContext context) { Map stringMap = new HashMap(cellDataMap.size() * 4 / 3 + 1); + ReadHolder currentReadHolder = context.currentReadHolder(); int index = 0; for (Map.Entry 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); } } diff --git a/src/main/java/com/alibaba/excel/util/DateUtils.java b/src/main/java/com/alibaba/excel/util/DateUtils.java index 0df5cf46..a581a955 100644 --- a/src/main/java/com/alibaba/excel/util/DateUtils.java +++ b/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); } } diff --git a/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java b/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java index a1746299..1f09d47e 100644 --- a/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java +++ b/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; diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java index 0fa0cae2..5b0d8ae8 100644 --- a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java +++ b/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()); } } diff --git a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java index d7cb6307..c7b227f4 100644 --- a/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java +++ b/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> collectionFieldStyleCache = new HashMap>(8); + /** + * Row height cache for collection + */ + private Map collectionRowHeightCache = new HashMap(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 readTemplateData(Map> analysisCache) { Integer sheetNo = writeContext.writeSheetHolder().getSheetNo(); List analysisCellList = analysisCache.get(sheetNo); @@ -280,6 +304,7 @@ public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); analysisCellList = new ArrayList(); List collectionAnalysisCellList = new ArrayList(); + Set firstColumnCache = new HashSet(); 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 analysisCellList, - List collectionAnalysisCellList, int rowIndex, int columnIndex) { + List collectionAnalysisCellList, int rowIndex, int columnIndex, Set 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 prepareDataList = new ArrayList(); analysisCell.setPrepareDataList(prepareDataList); analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); + analysisCell.setFirstColumn(Boolean.FALSE); return analysisCell; } diff --git a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java index 791e2643..47a25947 100644 --- a/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java +++ b/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java @@ -16,6 +16,7 @@ public class AnalysisCell { private List 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) { diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java index 0f00719d..e4f9571a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java @@ -39,8 +39,8 @@ public class DemoExceptionListener extends AnalysisEventListener { 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()); } } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java new file mode 100644 index 00000000..6c82082d --- /dev/null +++ b/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 map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + } + + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板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 map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // list 后面还有个统计 想办法手动写入 + // 这里偷懒直接用list 也可以用对象 + List> totalListList = new ArrayList>(); + List totalList = new ArrayList(); + 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 data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java index 367ee583..f7778338 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java +++ b/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); diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java b/src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java index 196b2da9..e52dc51a 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java +++ b/src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java @@ -25,6 +25,7 @@ public class HDListener extends AnalysisEventListener { @Override public void invokeHeadMap(Map headMap, AnalysisContext context) { LOGGER.info("HEAD:{}", JSON.toJSONString(headMap)); + LOGGER.info("total:{}", context.readSheetHolder().getTotal()); } diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java b/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java index 38ee661f..538e2663 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java +++ b/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; diff --git a/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java b/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java index f356524a..5127f72f 100644 --- a/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java +++ b/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(); + } } diff --git a/update.md b/update.md index 4055d43d..9bd246e5 100644 --- a/update.md +++ b/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)