Browse Source

add null edit

Looly 5 years ago
parent
commit
d8fd1e16aa

+ 7 - 4
CHANGELOG.md

@@ -5,20 +5,23 @@
 
 # 5.5.0 (2020-11-14)
 
+### 大版本特性
+* 【extra  】     增加jakarta.validation-api封装:ValidationUtil(pr#207@Gitee)
+* 【extra  】     增加表达式引擎封装:ExpressionUtil(pr#1203@Github)
+* 【extra  】     新增基于Apache-FtpServer封装:SimpleFtpServer
+* 【extra  】     新增基于Commons-Compress封装:CompressUtil
+
 ### 新特性
 * 【core   】     NumberUtil.parseInt等支持123,2.00这类数字(issue#I23ORQ@Gitee)
 * 【core   】     增加ArrayUtil.isSub、indexOfSub、lastIndexOfSub方法(issue#I23O1K@Gitee)
-* 【extra  】     增加ValidationUtil(pr#207@Gitee)
 * 【core   】     反射调用支持传递参数的值为null(pr#1205@Github)
 * 【core   】     HexUtil增加format方法(issue#I245NF@Gitee)
 * 【poi    】     ExcelWriter增加setCurrentRowToEnd方法(issue#I24A2R@Gitee)
 * 【core   】     ExcelWriter增加setCurrentRowToEnd方法(issue#I24A2R@Gitee)
-* 【extra  】     增加表达式引擎封装(ExpressionUtil)(pr#1203@Github)
 * 【core   】     增加enum转数字支持(issue#I24QZY@Gitee)
 * 【core   】     NumberUtil.toBigDecimal空白符转换为0(issue#I24MRP@Gitee)
 * 【core   】     CollUtil和IterUtil增加size方法(pr#208@Gitee)
-* 【extra  】     新增SimpleFtpServer
-* 【extra  】     新增CompressUtil压缩封装
+* 【poi    】     ExcelReader的read方法读取空单元格增加CellEditor处理(issue#1213@Github)
 
 ### Bug修复
 * 【core   】     修复DateUtil.current使用System.nanoTime的问题(issue#1198@Github)

+ 9 - 0
hutool-core/src/test/java/cn/hutool/core/util/StrUtilTest.java

@@ -1,5 +1,6 @@
 package cn.hutool.core.util;
 
+import cn.hutool.core.lang.Console;
 import cn.hutool.core.lang.Dict;
 import org.junit.Assert;
 import org.junit.Test;
@@ -434,6 +435,14 @@ public class StrUtilTest {
 	}
 
 	@Test
+	public void subBetweenAllTest3() {
+		String src1 = "'abc'and'123'";
+
+		final String[] strings = StrUtil.subBetweenAll(src1, "'", "'");
+		Console.log(strings);
+	}
+
+	@Test
 	public void briefTest() {
 		String str = RandomUtil.randomString(1000);
 		int maxLength = RandomUtil.randomInt(1000);

+ 68 - 6
hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java

@@ -1,5 +1,7 @@
 package cn.hutool.extra.compress;
 
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.compress.archiver.Archiver;
 import cn.hutool.extra.compress.archiver.SevenZArchiver;
 import cn.hutool.extra.compress.archiver.StreamArchiver;
@@ -8,6 +10,10 @@ import cn.hutool.extra.compress.extractor.SenvenZExtractor;
 import cn.hutool.extra.compress.extractor.StreamExtractor;
 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
 import org.apache.commons.compress.archivers.StreamingNotSupportedException;
+import org.apache.commons.compress.compressors.CompressorException;
+import org.apache.commons.compress.compressors.CompressorInputStream;
+import org.apache.commons.compress.compressors.CompressorOutputStream;
+import org.apache.commons.compress.compressors.CompressorStreamFactory;
 
 import java.io.File;
 import java.io.InputStream;
@@ -24,6 +30,62 @@ import java.nio.charset.Charset;
 public class CompressUtil {
 
 	/**
+	 * 获取压缩输出流,用于压缩指定内容,支持的格式例如:
+	 * <ul>
+	 *     <li>{@value CompressorStreamFactory#GZIP}</li>
+	 *     <li>{@value CompressorStreamFactory#BZIP2}</li>
+	 *     <li>{@value CompressorStreamFactory#XZ}</li>
+	 *     <li>{@value CompressorStreamFactory#PACK200}</li>
+	 *     <li>{@value CompressorStreamFactory#SNAPPY_FRAMED}</li>
+	 *     <li>{@value CompressorStreamFactory#LZ4_BLOCK}</li>
+	 *     <li>{@value CompressorStreamFactory#LZ4_FRAMED}</li>
+	 *     <li>{@value CompressorStreamFactory#ZSTANDARD}</li>
+	 *     <li>{@value CompressorStreamFactory#DEFLATE}</li>
+	 * </ul>
+	 *
+	 * @param compressorName 压缩名称,见:{@link CompressorStreamFactory}
+	 * @param out            输出流,可以输出到内存、网络或文件
+	 * @return {@link CompressorOutputStream}
+	 */
+	public CompressorOutputStream getOut(String compressorName, OutputStream out) {
+		try {
+			return new CompressorStreamFactory().createCompressorOutputStream(compressorName, out);
+		} catch (CompressorException e) {
+			throw new CompressException(e);
+		}
+	}
+
+	/**
+	 * 获取压缩输入流,用于解压缩指定内容,支持的格式例如:
+	 * <ul>
+	 *     <li>{@value CompressorStreamFactory#GZIP}</li>
+	 *     <li>{@value CompressorStreamFactory#BZIP2}</li>
+	 *     <li>{@value CompressorStreamFactory#XZ}</li>
+	 *     <li>{@value CompressorStreamFactory#PACK200}</li>
+	 *     <li>{@value CompressorStreamFactory#SNAPPY_FRAMED}</li>
+	 *     <li>{@value CompressorStreamFactory#LZ4_BLOCK}</li>
+	 *     <li>{@value CompressorStreamFactory#LZ4_FRAMED}</li>
+	 *     <li>{@value CompressorStreamFactory#ZSTANDARD}</li>
+	 *     <li>{@value CompressorStreamFactory#DEFLATE}</li>
+	 * </ul>
+	 *
+	 * @param compressorName 压缩名称,见:{@link CompressorStreamFactory},null表示自动检测
+	 * @param in            输出流,可以输出到内存、网络或文件
+	 * @return {@link CompressorOutputStream}
+	 */
+	public CompressorInputStream getIn(String compressorName, InputStream in) {
+		in = IoUtil.toMarkSupportStream(in);
+		try {
+			if(StrUtil.isBlank(compressorName)){
+				compressorName = CompressorStreamFactory.detect(in);
+			}
+			return new CompressorStreamFactory().createCompressorInputStream(compressorName, in);
+		} catch (CompressorException e) {
+			throw new CompressException(e);
+		}
+	}
+
+	/**
 	 * 创建归档器,支持:
 	 * <ul>
 	 *     <li>{@link ArchiveStreamFactory#AR}</li>
@@ -108,11 +170,11 @@ public class CompressUtil {
 		if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(archiverName)) {
 			return new SenvenZExtractor(file);
 		}
-		try{
+		try {
 			return new StreamExtractor(charset, archiverName, file);
-		} catch (CompressException e){
+		} catch (CompressException e) {
 			final Throwable cause = e.getCause();
-			if(cause instanceof StreamingNotSupportedException && cause.getMessage().contains("7z")){
+			if (cause instanceof StreamingNotSupportedException && cause.getMessage().contains("7z")) {
 				return new SenvenZExtractor(file);
 			}
 			throw e;
@@ -158,11 +220,11 @@ public class CompressUtil {
 		if (ArchiveStreamFactory.SEVEN_Z.equalsIgnoreCase(archiverName)) {
 			return new SenvenZExtractor(in);
 		}
-		try{
+		try {
 			return new StreamExtractor(charset, archiverName, in);
-		} catch (CompressException e){
+		} catch (CompressException e) {
 			final Throwable cause = e.getCause();
-			if(cause instanceof StreamingNotSupportedException && cause.getMessage().contains("7z")){
+			if (cause instanceof StreamingNotSupportedException && cause.getMessage().contains("7z")) {
 				return new SenvenZExtractor(in);
 			}
 			throw e;

+ 11 - 2
hutool-extra/src/main/java/cn/hutool/extra/compress/archiver/SevenZArchiver.java

@@ -66,6 +66,15 @@ public class SevenZArchiver implements Archiver {
 		}
 	}
 
+	/**
+	 * 获取{@link SevenZOutputFile}以便自定义相关设置
+	 *
+	 * @return {@link SevenZOutputFile}
+	 */
+	public SevenZOutputFile getSevenZOutputFile() {
+		return this.sevenZOutputFile;
+	}
+
 	@Override
 	public SevenZArchiver add(File file, String path, Filter<File> filter) {
 		try {
@@ -93,9 +102,9 @@ public class SevenZArchiver implements Archiver {
 		} catch (Exception ignore) {
 			//ignore
 		}
-		if(null != out && this.channel instanceof SeekableInMemoryByteChannel){
+		if (null != out && this.channel instanceof SeekableInMemoryByteChannel) {
 			try {
-				out.write(((SeekableInMemoryByteChannel)this.channel).array());
+				out.write(((SeekableInMemoryByteChannel) this.channel).array());
 			} catch (IOException e) {
 				throw new IORuntimeException(e);
 			}

+ 1 - 1
hutool-poi/src/main/java/cn/hutool/poi/excel/RowUtil.java

@@ -73,7 +73,7 @@ public class RowUtil {
 		Object cellValue;
 		boolean isAllNull = true;
 		for (int i = startCellNumInclude; i < size; i++) {
-			cellValue = CellUtil.getCellValue(row.getCell(i), cellEditor);
+			cellValue = CellUtil.getCellValue(CellUtil.getCell(row, i), cellEditor);
 			isAllNull &= StrUtil.isEmptyIfStr(cellValue);
 			cellValues.add(cellValue);
 		}

+ 21 - 5
hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java

@@ -74,10 +74,7 @@ public class CellUtil {
 	 * @return 值,类型可能为:Date、Double、Boolean、String
 	 */
 	public static Object getCellValue(Cell cell, CellEditor cellEditor) {
-		if (null == cell) {
-			return null;
-		}
-		return getCellValue(cell, cell.getCellTypeEnum(), cellEditor);
+		return getCellValue(cell, null, cellEditor);
 	}
 
 	/**
@@ -105,6 +102,9 @@ public class CellUtil {
 		if (null == cell) {
 			return null;
 		}
+		if(cell instanceof NullCell){
+			return null == cellEditor ? null : cellEditor.edit(cell, null);
+		}
 		if (null == cellType) {
 			cellType = cell.getCellTypeEnum();
 		}
@@ -235,7 +235,23 @@ public class CellUtil {
 	}
 
 	/**
-	 * 获取已有行或创建新行
+	 *获取单元格,如果单元格不存在,返回{@link NullCell}
+	 *
+	 * @param row       Excel表的行
+	 * @param cellIndex 列号
+	 * @return {@link Row}
+	 * @since 5.5.0
+	 */
+	public static Cell getCell(Row row, int cellIndex) {
+		Cell cell = row.getCell(cellIndex);
+		if (null == cell) {
+			return new NullCell(row, cellIndex);
+		}
+		return cell;
+	}
+
+	/**
+	 * 获取已有单元格或创建新单元格
 	 *
 	 * @param row       Excel表的行
 	 * @param cellIndex 列号

+ 240 - 0
hutool-poi/src/main/java/cn/hutool/poi/excel/cell/NullCell.java

@@ -0,0 +1,240 @@
+package cn.hutool.poi.excel.cell;
+
+import org.apache.poi.ss.formula.FormulaParseException;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.Hyperlink;
+import org.apache.poi.ss.usermodel.RichTextString;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellAddress;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import java.time.LocalDateTime;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 当单元格不存在时使用此对象表示,得到的值都为null,此对象只用于标注单元格所在位置信息。
+ *
+ * @author looly
+ * @since 5.5.0
+ */
+public class NullCell implements Cell {
+
+	private final Row row;
+	private final int columnIndex;
+
+	/**
+	 * 构造
+	 *
+	 * @param row         行
+	 * @param columnIndex 列号,从0开始
+	 */
+	public NullCell(Row row, int columnIndex) {
+		this.row = row;
+		this.columnIndex = columnIndex;
+	}
+
+	@Override
+	public int getColumnIndex() {
+		return this.columnIndex;
+	}
+
+	@Override
+	public int getRowIndex() {
+		return getRow().getRowNum();
+	}
+
+	@Override
+	public Sheet getSheet() {
+		return getRow().getSheet();
+	}
+
+	@Override
+	public Row getRow() {
+		return this.row;
+	}
+
+	@Override
+	public void setCellType(CellType cellType) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void setBlank() {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public CellType getCellType() {
+		return null;
+	}
+
+	@Override
+	public CellType getCellTypeEnum() {
+		return null;
+	}
+
+	@Override
+	public CellType getCachedFormulaResultType() {
+		return null;
+	}
+
+	@Override
+	public CellType getCachedFormulaResultTypeEnum() {
+		return null;
+	}
+
+	@Override
+	public void setCellValue(double value) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void setCellValue(Date value) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void setCellValue(LocalDateTime value) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void setCellValue(Calendar value) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void setCellValue(RichTextString value) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void setCellValue(String value) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void setCellFormula(String formula) throws FormulaParseException, IllegalStateException {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void removeFormula() throws IllegalStateException {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public String getCellFormula() {
+		return null;
+	}
+
+	@Override
+	public double getNumericCellValue() {
+		throw new UnsupportedOperationException("Cell value is null!");
+	}
+
+	@Override
+	public Date getDateCellValue() {
+		return null;
+	}
+
+	@Override
+	public LocalDateTime getLocalDateTimeCellValue() {
+		return null;
+	}
+
+	@Override
+	public RichTextString getRichStringCellValue() {
+		return null;
+	}
+
+	@Override
+	public String getStringCellValue() {
+		return null;
+	}
+
+	@Override
+	public void setCellValue(boolean value) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void setCellErrorValue(byte value) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public boolean getBooleanCellValue() {
+		throw new UnsupportedOperationException("Cell value is null!");
+	}
+
+	@Override
+	public byte getErrorCellValue() {
+		throw new UnsupportedOperationException("Cell value is null!");
+	}
+
+	@Override
+	public void setCellStyle(CellStyle style) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public CellStyle getCellStyle() {
+		return null;
+	}
+
+	@Override
+	public void setAsActiveCell() {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public CellAddress getAddress() {
+		return null;
+	}
+
+	@Override
+	public void setCellComment(Comment comment) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public Comment getCellComment() {
+		return null;
+	}
+
+	@Override
+	public void removeCellComment() {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public Hyperlink getHyperlink() {
+		return null;
+	}
+
+	@Override
+	public void setHyperlink(Hyperlink link) {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public void removeHyperlink() {
+		throw new UnsupportedOperationException("Can not set any thing to null cell!");
+	}
+
+	@Override
+	public CellRangeAddress getArrayFormulaRange() {
+		return null;
+	}
+
+	@Override
+	public boolean isPartOfArrayFormulaGroup() {
+		throw new UnsupportedOperationException("Cell value is null!");
+	}
+}

+ 17 - 0
hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelReadTest.java

@@ -3,6 +3,7 @@ package cn.hutool.poi.excel.test;
 import cn.hutool.core.io.resource.ResourceUtil;
 import cn.hutool.core.lang.Console;
 import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.poi.excel.ExcelReader;
 import cn.hutool.poi.excel.ExcelUtil;
 import lombok.Data;
@@ -201,4 +202,20 @@ public class ExcelReadTest {
 			Console.log(list);
 		}
 	}
+
+	@Test
+	public void nullValueEditTest(){
+		final ExcelReader reader = ExcelUtil.getReader("null_cell_test.xlsx");
+		reader.setCellEditor((cell, value)-> ObjectUtil.defaultIfNull(value, "#"));
+		final List<List<Object>> read = reader.read();
+
+		// 对于任意一个单元格有值的情况下,之前的单元格值按照null处理
+		Assert.assertEquals(1, read.get(1).size());
+		Assert.assertEquals(2, read.get(2).size());
+		Assert.assertEquals(3, read.get(3).size());
+
+		Assert.assertEquals("#", read.get(2).get(0));
+		Assert.assertEquals("#", read.get(3).get(0));
+		Assert.assertEquals("#", read.get(3).get(1));
+	}
 }

BIN
hutool-poi/src/test/resources/null_cell_test.xlsx