Repository: wangguanquan/eec
Pull request #379 (2024-06-28 15:41:16)

Feature/v0.5.16


Patch:
diff --git a/CHANGELOG b/CHANGELOG
index 3110b17a..33e6d75b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+Version 0.5.16 (2024-06-29)
+-------------
+- 输入转换Converter#reversion增加数据类型提升扩展性(#376)
+- 修复读取自定义theme颜色会出现偏差的问题
+
 Version 0.5.15 (2024-05-21)
 -------------
 - 修复onProgress出现越界问题
diff --git a/README.md b/README.md
index 5212ba18..62a8a333 100644
--- a/README.md
+++ b/README.md
@@ -291,6 +291,11 @@ try (ExcelReader reader = ExcelReader.read(Paths.get("d:\\abc.xlsx"))) {
 ```
 
 ## CHANGELOG
+Version 0.5.16 (2024-06-29)
+-------------
+- 输入转换Converter#reversion增加数据类型提升扩展性(#376) [兼容问题参考](./wiki/版本兼容性升级指引)
+- 修复读取自定义theme颜色会出现偏差的问题
+
 Version 0.5.15 (2024-05-21)
 -------------
 - 修复onProgress出现越界问题
@@ -318,19 +323,10 @@ Version 0.5.13 (2024-02-20)
 - 修复部分场景读取Excel发生IndexOutOfBound异常
 - 修复HeaderStyle注解设置样式时，字段样式被全局样式替换的问题
 
-Version 0.5.12 (2023-11-26)
--------------
-- 移除watch改用slf4j输出日志
-- 新增进度窗口`onProgress`
-- 优化自适应列宽算法使其支持更多字体和大小
-- 数据转换器功能增强，ExcelColumn增加converter属性以支持导出/导入时双向数据转换([#362](https://github.com/wangguanquan/eec/issues/362))
-- 支持读取xlsx格式wps的内嵌图片([#363](https://github.com/wangguanquan/eec/issues/363))
-- 部分类的注释改为中文，后续会将全部注释改为中文
-
 [更多...](./CHANGELOG)
 
 [releases]: https://github.com/wangguanquan/eec/releases
-[release-image]: http://img.shields.io/badge/release-0.5.15-blue.svg?style=flat
+[release-image]: http://img.shields.io/badge/release-0.5.16-blue.svg?style=flat
 
 [license]: http://www.apache.org/licenses/LICENSE-2.0
 [license-image]: http://img.shields.io/badge/license-Apache--2-blue.svg?style=flat
diff --git a/pom.xml b/pom.xml
index b3508b10..6d13468d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
 
   <groupId>org.ttzero</groupId>
   <artifactId>eec</artifactId>
-  <version>0.5.15</version>
+  <version>0.5.16-SNAPSHOT</version>
   <name>Excel Write/Read Framework</name>
   <packaging>jar</packaging>
   <url>https://www.ttzero.org</url>
diff --git a/src/main/java/org/ttzero/excel/entity/style/ColorIndex.java b/src/main/java/org/ttzero/excel/entity/style/ColorIndex.java
index 368584d9..0bb53de3 100644
--- a/src/main/java/org/ttzero/excel/entity/style/ColorIndex.java
+++ b/src/main/java/org/ttzero/excel/entity/style/ColorIndex.java
@@ -25,7 +25,7 @@ import java.awt.Color;
  *
  * @author guanquan.wang at 2018-02-06 14:40
  */
-public class ColorIndex {
+public final class ColorIndex {
     /** Default indexed color */
     public static final int[] indexedColors = {
         -16777216, -1,        -65536,    -16711936, -16776961, -256,      -65281,    -16711681,
diff --git a/src/main/java/org/ttzero/excel/entity/style/Font.java b/src/main/java/org/ttzero/excel/entity/style/Font.java
index 90a76f9d..afb2aa79 100644
--- a/src/main/java/org/ttzero/excel/entity/style/Font.java
+++ b/src/main/java/org/ttzero/excel/entity/style/Font.java
@@ -369,6 +369,10 @@ public class Font implements Cloneable {
      * @return 当前字体
      */
     public Font setScheme(String scheme) {
+        if (StringUtil.isNotEmpty(scheme)) {
+            scheme = scheme.toLowerCase();
+            scheme = ("minor".equals(scheme) || "major".equals(scheme)) ? scheme : null;
+        } else scheme = null;
         this.scheme = scheme;
         return this;
     }
@@ -603,7 +607,7 @@ public class Font implements Cloneable {
         if (charset > 0) {
             buf.append("<charset val=\"").append(charset).append("\"/>");
         }
-        if (StringUtil.isNotEmpty(scheme)) {
+        if (("minor".equals(scheme) || "major".equals(scheme))) {
             buf.append("<scheme val=\"").append(scheme).append("\"/>");
         }
 
@@ -612,31 +616,34 @@ public class Font implements Cloneable {
 
     @Override
     public int hashCode() {
-        int hash;
-        hash = style << 24;
-        hash += size << 16;
-        hash += name.hashCode() << 8;
+        int hash = size << 16;
         hash += color != null ? color.hashCode() : 0;
-        hash += charset;
-        hash += family;
-        if (StringUtil.isNotEmpty(scheme)) hash += scheme.hashCode();
+        hash += style << 24;
+        if (("minor".equals(scheme) || "major".equals(scheme))) {
+            hash += scheme.hashCode();
+        } else {
+            hash += name.hashCode() << 8;
+            hash += charset;
+            hash += family;
+        }
         return hash;
     }
 
     @Override
     public boolean equals(Object o) {
+        boolean r = false;
         if (o instanceof Font) {
             Font other = (Font) o;
-            return other.size == size
-                && (Objects.equals(other.name, name))
-                && (Objects.equals(other.color, color))
-                && other.charset == charset
-                && other.family == family
-                && other.style == style
-                && (Objects.equals(other.scheme, scheme))
-                ;
+            r = other.size == size
+                && Objects.equals(other.color, color)
+                && other.style == style;
+            if (r) {
+                r = (("minor".equals(scheme) || "major".equals(scheme)))
+                    ? Objects.equals(other.scheme, scheme)
+                    : Objects.equals(other.name, name) && other.charset == charset && other.family == family;
+            }
         }
-        return false;
+        return r;
     }
 
     /**
@@ -671,7 +678,7 @@ public class Font implements Cloneable {
         if (family > 0) {
             element.addElement("family").addAttribute("val", String.valueOf(family));
         }
-        if (StringUtil.isNotEmpty(scheme)) {
+        if (("minor".equals(scheme) || "major".equals(scheme))) {
             element.addElement("scheme").addAttribute("val", scheme);
         }
         if (charset > 0) {
@@ -768,7 +775,7 @@ public class Font implements Cloneable {
                 case "color"  : font.color = Styles.parseColor(e);                          break;
                 case "name"   : font.name = getAttr(e, "val");                              break;
                 case "charset": font.charset = Integer.parseInt(getAttr(e, "val"));         break;
-                case "scheme" : font.scheme = getAttr(e, "val");                            break;
+                case "scheme" : font.setScheme(getAttr(e, "val"));                          break;
                 case "family" : font.family = Integer.parseInt(getAttr(e, "val"));          break;
                 case "b"      : font.style |= Style.BOLD;                                   break;
                 case "i"      : font.style |= Style.ITALIC;                                 break;
diff --git a/src/main/java/org/ttzero/excel/entity/style/HlsColor.java b/src/main/java/org/ttzero/excel/entity/style/HlsColor.java
index 40c1a676..474120e4 100644
--- a/src/main/java/org/ttzero/excel/entity/style/HlsColor.java
+++ b/src/main/java/org/ttzero/excel/entity/style/HlsColor.java
@@ -45,10 +45,10 @@ public class HlsColor {
     public float s;
 
     /**
-     * Convert rgba color to hls Color
+     * 将RGBA颜色转换为HLS颜色
      *
-     * @param rgbColor rgb Color
-     * @return hlsColor
+     * @param rgbColor RGB颜色
+     * @return HLS颜色
      */
     public static HlsColor rgbToHls(Color rgbColor) {
         HlsColor hlsColor = new HlsColor();
@@ -76,31 +76,23 @@ public class HlsColor {
     }
 
     /**
-     * Convert hls color to rgba
+     * 将HLS颜色转换为RGBA颜色
      *
-     * @param hlsColor hls color
-     * @return rgbColor
+     * @param hlsColor HLS颜色
+     * @return RGBA颜色
      */
     public static Color hlsToRgb(HlsColor hlsColor) {
-        Color rgbColor;
+        // 灰色，没有色相和饱和度
         if (hlsColor.s == 0) {
-            rgbColor = new Color(hlsColor.l, hlsColor.l, hlsColor.l, hlsColor.a);
-            return rgbColor;
+            return new Color(hlsColor.l, hlsColor.l, hlsColor.l, hlsColor.a);
         }
-        float t1;
-        if (hlsColor.l < 0.5F) t1 = hlsColor.l * (1.0F + hlsColor.s);
-        else t1 = hlsColor.l + hlsColor.s - (hlsColor.l * hlsColor.s);
+        float t1 = hlsColor.l < 0.5F ? hlsColor.l * (1.0F + hlsColor.s) : hlsColor.l + hlsColor.s - (hlsColor.l * hlsColor.s);
         float t2 = 2.0F * hlsColor.l - t1;
         float h = hlsColor.h / 360F;
-        float tR = h + (1.0F / 3.0F);
-        float r = hueToRGB(t1, t2, tR);
-        float tG = h;
-        float g = hueToRGB(t1, t2, tG);
-        float tB = h - (1.0F / 3.0F);
-        float b = hueToRGB(t1, t2, tB);
-//        rgbColor = new Color((int) (r * 255), (int) (g * 255), (int) (b * 255), (int) (hlsColor.a * 255));
-        rgbColor = new Color(r, g, b, hlsColor.a);
-        return rgbColor;
+        float r = hueToRGB(t1, t2, h + 0.3333333333F);
+        float g = hueToRGB(t1, t2, h);
+        float b = hueToRGB(t1, t2, h - 0.3333333333F);
+        return new Color(r, g, b, hlsColor.a);
     }
 
     private static float hueToRGB(float t1, float t2, float t3) {
diff --git a/src/main/java/org/ttzero/excel/entity/style/Styles.java b/src/main/java/org/ttzero/excel/entity/style/Styles.java
index 4952bdac..9054e5f0 100644
--- a/src/main/java/org/ttzero/excel/entity/style/Styles.java
+++ b/src/main/java/org/ttzero/excel/entity/style/Styles.java
@@ -52,7 +52,7 @@ import static org.ttzero.excel.util.StringUtil.isNotEmpty;
 /**
  * Excel样式包含格式化{@link NumFmt}、字体{@link Font}、填充{@link Fill}、边框{@link Border}、
  * 垂直对齐{@link Verticals}和水平对齐{@link Horizontals}以及自动折行组成，样式存放在共享区域供多个工作表共用，
- * 每个样式值都是由这7种值进行组合而来。全局样式库统管样式读写，添加样式时会查询当前库是否已存在，如果存在则直接返回该样式在库中的索引值，
+ * 每个样式值都是由这7种值进行组合而来。全局样式库统管样式读写，添加样式时会查询当前样式是否已存在，如果存在则直接返回该样式在库中的索引值
  * 否则添加到样式库末尾
  *
  * <p>本工具简化了样式设计，基础的4种样式由数组保存，单元格样式值由一个{@code int}值保存，
diff --git a/src/main/java/org/ttzero/excel/processor/Converter.java b/src/main/java/org/ttzero/excel/processor/Converter.java
index 730def02..8fb97895 100644
--- a/src/main/java/org/ttzero/excel/processor/Converter.java
+++ b/src/main/java/org/ttzero/excel/processor/Converter.java
@@ -11,9 +11,10 @@ public interface Converter<T> extends ConversionProcessor {
      * 输入转换器，读取Excel时将单元格的值转为指定类型{@code T}
      *
      * @param v Excel原始值
+     * @param fieldClazz 导入字段类型
      * @return 转换后的值
      */
-    T reversion(String v);
+    T reversion(String v, Class<?> fieldClazz);
 
     /**
      * 无类型转换，默认
@@ -21,7 +22,7 @@ public interface Converter<T> extends ConversionProcessor {
     final class None implements Converter<Object> {
 
         @Override
-        public Object reversion(String v) {
+        public Object reversion(String v, Class<?> fieldClazz) {
             return v;
         }
 
diff --git a/src/main/java/org/ttzero/excel/reader/ExcelReader.java b/src/main/java/org/ttzero/excel/reader/ExcelReader.java
index 5304fd1b..b4c20896 100644
--- a/src/main/java/org/ttzero/excel/reader/ExcelReader.java
+++ b/src/main/java/org/ttzero/excel/reader/ExcelReader.java
@@ -490,10 +490,12 @@ public class ExcelReader implements Closeable {
         entry = getEntry("xl/styles.xml");
         if (entry != null) {
             try {
-                styles = Styles.load(zipFile.getInputStream(entry));
-                // Load Theme style
+                // Load Theme style first
                 ZipEntry themeEntry = getEntry("xl/theme/theme1.xml");
                 if (themeEntry != null) Theme.load(zipFile.getInputStream(themeEntry));
+
+                // Then load custom styles
+                styles = Styles.load(zipFile.getInputStream(entry));
             } catch (Exception ex) {
                 LOGGER.warn("Parse style failed.", ex);
             }
diff --git a/src/main/java/org/ttzero/excel/reader/HeaderRow.java b/src/main/java/org/ttzero/excel/reader/HeaderRow.java
index 4eb99b53..946d65e4 100644
--- a/src/main/java/org/ttzero/excel/reader/HeaderRow.java
+++ b/src/main/java/org/ttzero/excel/reader/HeaderRow.java
@@ -485,7 +485,7 @@ public class HeaderRow extends Row {
         int c = ec.colIndex;
         Class<?> fieldClazz = ec.clazz;
         if (ec.converter != null) {
-            ec.field.set(t, ec.converter.reversion(row.getString(c)));
+            ec.field.set(t, ec.converter.reversion(row.getString(c), fieldClazz));
         }
         else if (fieldClazz == String.class) {
             ec.field.set(t, row.getString(c));
@@ -577,7 +577,7 @@ public class HeaderRow extends Row {
         int c = ec.colIndex;
         Class<?> fieldClazz = ec.clazz;
         if (ec.converter != null) {
-            ec.method.invoke(t, ec.converter.reversion(row.getString(c)));
+            ec.method.invoke(t, ec.converter.reversion(row.getString(c), fieldClazz));
         }
         else if (fieldClazz == String.class) {
             ec.method.invoke(t, row.getString(c));


Test patch:
diff --git a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest2.java b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest2.java
index 8639bf0d..fe15a482 100644
--- a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest2.java
+++ b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest2.java
@@ -59,6 +59,7 @@ import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.HashMap;
 import java.util.Objects;
 import java.util.stream.Collectors;
 
@@ -1041,7 +1042,7 @@ public class ListObjectSheetTest2 extends WorkbookTest {
         final String[] statusDesc = { "未开始", "进行中", "完结", "中止" };
 
         @Override
-        public Integer reversion(String v) {
+        public Integer reversion(String v, Class<?> fieldClazz) {
             for (int i = 0; i < statusDesc.length; i++) {
                 if (statusDesc[i].equals(v)) {
                     return i;
@@ -1078,5 +1079,138 @@ public class ListObjectSheetTest2 extends WorkbookTest {
         }
     }
 
+    @Test  public void multipleEnumReversionTest() throws IOException {
+        List<MultipleEnumReversionModel> list = MultipleEnumReversionModel.testData();
+        new Workbook()
+                .addSheet(new ListSheet<>(list))
+                .writeTo(defaultTestPath.resolve("multipleEnumReversion.xlsx"));
+
+        try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("multipleEnumReversion.xlsx"))) {
+            List<MultipleEnumReversionModel> readList = reader.sheet(0).dataRows().map(row -> row.to(MultipleEnumReversionModel.class)).collect(Collectors.toList());
+            assertEquals(list.size(), readList.size());
+
+            MultipleEnumReversionModel multipleEnumReversionModel0 = readList.get(0);
+            assertEquals(multipleEnumReversionModel0.getOperator(), Operator.PLUS);
+            assertEquals(multipleEnumReversionModel0.getSymbol(), Symbol.PLUS);
+
+            MultipleEnumReversionModel multipleEnumReversionModel1 = readList.get(1);
+            assertEquals(multipleEnumReversionModel1.getOperator(), Operator.REDUCE);
+            assertEquals(multipleEnumReversionModel1.getSymbol(), Symbol.REDUCE);
+        }
+    }
+
+
+    public static class MultipleEnumReversionModel {
+        @ExcelColumn(value = "运算符", converter = MultipleEnumConverter.class)
+        private Operator operator;
+        @ExcelColumn(value = "符号", converter = MultipleEnumConverter.class)
+        private Symbol symbol;
+
+
+        public Operator getOperator() {
+            return this.operator;
+        }
+
+        public Symbol getSymbol() {
+            return this.symbol;
+        }
+
+        public void setOperator(Operator operator) {
+            this.operator = operator;
+        }
+
+        public void setSymbol(Symbol symbol) {
+            this.symbol = symbol;
+        }
+
+        public static List<MultipleEnumReversionModel> testData() {
+            List<MultipleEnumReversionModel> reversionModels = new ArrayList<>();
+            MultipleEnumReversionModel model0 = new MultipleEnumReversionModel();
+            model0.setOperator(Operator.PLUS);
+            model0.setSymbol(Symbol.PLUS);
+            reversionModels.add(model0);
+
+            MultipleEnumReversionModel model1 = new MultipleEnumReversionModel();
+            model1.setOperator(Operator.REDUCE);
+            model1.setSymbol(Symbol.REDUCE);
+            reversionModels.add(model1);
+            return reversionModels;
+        }
+    }
+
+    public interface IBaseEnum {
+        String caption();
+    }
+
+    public enum Operator implements IBaseEnum {
+
+        PLUS("加"),
+        REDUCE("减");
+
+
+        private final String caption;
+
+        Operator(String caption) {
+            this.caption = caption;
+        }
+
+
+        @Override
+        public String caption() {
+            return caption;
+        }
+    }
+
+    public enum Symbol implements IBaseEnum {
+
+        PLUS("加"),
+        REDUCE("减");
+
+
+        private final String caption;
+
+        Symbol(String caption) {
+            this.caption = caption;
+        }
+
+
+        @Override
+        public String caption() {
+            return caption;
+        }
+    }
+
+    public static class MultipleEnumConverter implements Converter<IBaseEnum> {
+        Map<String, Map<String, IBaseEnum>> map = new HashMap<>(4);
+
+        public MultipleEnumConverter() {
+            Map<String, IBaseEnum> operatorMap = new HashMap<>();
+            operatorMap.put(Operator.PLUS.caption(), Operator.PLUS);
+            operatorMap.put(Operator.REDUCE.caption(), Operator.REDUCE);
+            map.put(Operator.class.getSimpleName(), operatorMap);
+
+            Map<String, IBaseEnum> symbolMap = new HashMap<>();
+            symbolMap.put(Symbol.PLUS.caption(), Symbol.PLUS);
+            symbolMap.put(Symbol.REDUCE.caption(), Symbol.REDUCE);
+            map.put(Symbol.class.getSimpleName(), symbolMap);
+        }
+
+
+        @Override
+        public IBaseEnum reversion(String v, Class<?> fieldClazz) {
+
+            Map<String, IBaseEnum> clazzIBaseEnumMap = map.get(fieldClazz.getSimpleName());
+            if (clazzIBaseEnumMap == null) {
+                return null;
+            }
+            return clazzIBaseEnumMap.get(v);
+        }
+
+        @Override
+        public Object conversion(Object v) {
+            IBaseEnum v1 = (IBaseEnum)  v;
+            return v1.caption();
+        }
+    }
 
 }
diff --git a/src/test/java/org/ttzero/excel/entity/PictureTest.java b/src/test/java/org/ttzero/excel/entity/PictureTest.java
index c785d6c2..47516462 100644
--- a/src/test/java/org/ttzero/excel/entity/PictureTest.java
+++ b/src/test/java/org/ttzero/excel/entity/PictureTest.java
@@ -446,7 +446,7 @@ public class PictureTest extends WorkbookTest {
 
         int count = expectList.size(), rowLimit = worksheetWriter.getRowLimit();
         try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("test Picture auto-size paging.xlsx"))) {
-            assertEquals(reader.getSheetCount(), (count + (rowLimit >> 1)) / rowLimit);
+            assertEquals(reader.getSheetCount(), (count % rowLimit > 0 ? count / rowLimit + 1 : count / rowLimit));
 
             for (int i = 0, len = reader.getSheetCount(), a = 0; i < len; i++) {
                 List<Drawings.Picture> list = reader.sheet(i).listPictures();
diff --git a/src/test/java/org/ttzero/excel/entity/TemplateSheetTest.java b/src/test/java/org/ttzero/excel/entity/TemplateSheetTest.java
index 6037c6ea..28e444e7 100644
--- a/src/test/java/org/ttzero/excel/entity/TemplateSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/TemplateSheetTest.java
@@ -22,6 +22,7 @@ import org.ttzero.excel.entity.style.Fill;
 import org.ttzero.excel.entity.style.Font;
 import org.ttzero.excel.entity.style.Horizontals;
 import org.ttzero.excel.entity.style.Styles;
+import org.ttzero.excel.manager.ExcelType;
 import org.ttzero.excel.reader.Cell;
 import org.ttzero.excel.reader.Dimension;
 import org.ttzero.excel.reader.ExcelReader;
@@ -73,7 +74,7 @@ public class TemplateSheetTest extends WorkbookTest {
         File[] files = testResourceRoot().toFile().listFiles();
         if (files != null) {
             for (File file : files) {
-                if (file.getName().endsWith(".xlsx")) {
+                if (ExcelReader.getType(file.toPath()) == ExcelType.XLSX) {
                     try (ExcelReader reader = ExcelReader.read(file.toPath())) {
                         org.ttzero.excel.reader.Sheet[] sheets = reader.all();
                         // 这里设置占位符前缀为[#@^!]是为了全量复制数据用