Repository: wangguanquan/eec
Pull request #164 (2021-03-27 04:52:26)

Fix#163
增加forceExport属性来强制没有@ExcelColumn注解的属性


Patch:
diff --git a/src/main/java/org/ttzero/excel/entity/ListSheet.java b/src/main/java/org/ttzero/excel/entity/ListSheet.java
index 0baa2a2e..60527d64 100644
--- a/src/main/java/org/ttzero/excel/entity/ListSheet.java
+++ b/src/main/java/org/ttzero/excel/entity/ListSheet.java
@@ -31,10 +31,13 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 
 import static org.ttzero.excel.util.ReflectUtil.listDeclaredFields;
@@ -356,18 +359,19 @@ public class ListSheet<T> extends Sheet {
 
         Map<String, Method> tmp = new HashMap<>();
         try {
-            PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz)
+            PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz, Object.class)
                     .getPropertyDescriptors();
             for (PropertyDescriptor pd : propertyDescriptors) {
                 Method method = pd.getReadMethod();
-                if (method == null) continue;
-                tmp.put(pd.getName(), method);
+                if (method != null) tmp.put(pd.getName(), method);
             }
         } catch (IntrospectionException e) {
             LOGGER.warn("Get class {} methods failed.", clazz);
         }
 
-        Field[] declaredFields = listDeclaredFields(clazz);
+        Field[] declaredFields = listDeclaredFields(clazz, c -> c.getAnnotation(IgnoreExport.class) == null);
+
+        boolean forceExport = this.forceExport == 1;
 
         if (!hasHeaderColumns()) {
             // Get ExcelColumn annotation method
@@ -389,6 +393,10 @@ public class ListSheet<T> extends Sheet {
                     }
 
                     Column column = createColumn(method);
+                    // Force export
+                    if (column == null && forceExport) {
+                        column = new Column(gs, EMPTY, false);
+                    }
                     if (column != null) {
                         methods[i] = method;
                         column.clazz = method.getReturnType();
@@ -402,6 +410,10 @@ public class ListSheet<T> extends Sheet {
                 }
 
                 Column column = createColumn(field);
+                // Force export
+                if (column == null && forceExport) {
+                    column = new Column(gs, EMPTY, false);
+                }
                 if (column != null) {
                     list.add(column);
                     column.key = gs;
@@ -424,15 +436,21 @@ public class ListSheet<T> extends Sheet {
             try {
                 Collection<Method> values = tmp.values();
                 readMethods = listReadMethods(clazz, method -> method.getAnnotation(ExcelColumn.class) != null
-                        && !values.contains(method));
+                        && method.getAnnotation(IgnoreExport.class) == null && !values.contains(method));
             } catch (IntrospectionException e) {
                 // Ignore
             }
 
             if (readMethods != null) {
+                Set<Method> existsMethods = new HashSet<>(tmp.values());
                 for (Method method : readMethods) {
+                    // Exclusions exists
+                    if (existsMethods.contains(method)) continue;
                     Column column = createColumn(method);
                     if (column != null) {
+                        if (i >= methods.length) {
+                            methods = Arrays.copyOf(methods, i + 1);
+                        }
                         methods[i++] = method;
                         list.add(column);
                         column.clazz = method.getReturnType();
diff --git a/src/main/java/org/ttzero/excel/entity/Sheet.java b/src/main/java/org/ttzero/excel/entity/Sheet.java
index 9f24fb9d..cb604e79 100644
--- a/src/main/java/org/ttzero/excel/entity/Sheet.java
+++ b/src/main/java/org/ttzero/excel/entity/Sheet.java
@@ -155,6 +155,11 @@ public abstract class Sheet implements Cloneable, Storable {
 
     protected ICellValueAndStyle cellValueAndStyle;
 
+    /**
+     * Force export all attributes
+     */
+    protected int forceExport;
+
     public int getId() {
         return id;
     }
@@ -1098,6 +1103,35 @@ public abstract class Sheet implements Cloneable, Storable {
         return this;
     }
 
+    /**
+     * Force export of attributes without {@link org.ttzero.excel.annotation.ExcelColumn} annotations
+     *
+     * @return the {@link Sheet}
+     */
+    public Sheet forceExport() {
+        this.forceExport = 1;
+        return this;
+    }
+
+    /**
+     * Cancel force export
+     *
+     * @return the {@link Sheet}
+     */
+    public Sheet cancelForceExport() {
+        this.forceExport = 2;
+        return this;
+    }
+
+    /**
+     * Returns the force export
+     *
+     * @return 1 if force, otherwise returns 0
+     */
+    public int getForceExport() {
+        return forceExport;
+    }
+
     /**
      * abstract method close
      *
@@ -1226,7 +1260,7 @@ public abstract class Sheet implements Cloneable, Storable {
         if (headStyle == 0) {
             Styles styles = workbook.getStyles();
             Font font = new Font(workbook.getI18N().getOrElse("local-font-family", "Arial")
-                , 11, Font.Style.BOLD, Color.white);
+                , 12, Font.Style.BOLD, Color.white);
             headStyle = styles.of(styles.addFont(font)
                 | styles.addFill(Fill.parse("solid #666699"))
                 | styles.addBorder(Border.parse("thin black"))
diff --git a/src/main/java/org/ttzero/excel/entity/Workbook.java b/src/main/java/org/ttzero/excel/entity/Workbook.java
index e25e3d22..2da92269 100644
--- a/src/main/java/org/ttzero/excel/entity/Workbook.java
+++ b/src/main/java/org/ttzero/excel/entity/Workbook.java
@@ -126,6 +126,11 @@ public class Workbook implements Storable {
 
     private IWorkbookWriter workbookWriter;
 
+    /**
+     * Force export all attributes
+     */
+    private int forceExport;
+
     /**
      * Create a unnamed workbook
      *
@@ -333,6 +338,24 @@ public class Workbook implements Storable {
         return autoSize;
     }
 
+    /**
+     * Force export of attributes without {@link org.ttzero.excel.annotation.ExcelColumn} annotations
+     *
+     * @return the {@link Workbook}
+     */
+    public Workbook forceExport() {
+        this.forceExport = 1;
+        return this;
+    }
+
+    /**
+     * Returns the force export
+     *
+     * @return 1 if force, otherwise returns 0
+     */
+    public int getForceExport() {
+        return forceExport;
+    }
 
     /**
      * Returns the global {@link Styles}
diff --git a/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java b/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
index 5521a1d4..3f017dff 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
@@ -381,7 +381,7 @@ public class XMLWorkbookWriter implements IWorkbookWriter {
             // Interlace changes color as default
             if (sheet.getAutoOdd() == 0) {
                 sheet.setOddFill(workbook.getOddFill() == null
-                    ? new Fill(PatternType.solid, new Color(226, 237, 218)) : workbook.getOddFill());
+                    ? new Fill(PatternType.solid, new Color(239, 245, 235)) : workbook.getOddFill());
             }
             sheet.setId(i + 1);
             // default worksheet name
@@ -390,6 +390,11 @@ public class XMLWorkbookWriter implements IWorkbookWriter {
             }
             // Set cell value and style processor
             sheet.setCellValueAndStyle(new XMLCellValueAndStyle(sheet.getAutoOdd(), sheet.getOddFill()));
+
+            // Force export all fields
+            if (workbook.getForceExport() > sheet.getForceExport()) {
+                sheet.forceExport();
+            }
         }
         workbook.what("0001");
 
diff --git a/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java b/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
index 870f908f..41705164 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
@@ -228,7 +228,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
 
     @Override
     public IWorksheetWriter clone() {
-        IWorksheetWriter copy = null;
+        IWorksheetWriter copy;
         try {
             copy = (IWorksheetWriter) super.clone();
         } catch (CloneNotSupportedException e) {
@@ -246,7 +246,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
                     copy = getClass().getConstructor(Sheet.class).newInstance(sheet);
                 } catch (NoSuchMethodException | IllegalAccessException
                     | InstantiationException | InvocationTargetException e2) {
-                    e2.printStackTrace();
+                    throw new ExcelWriteException(e2);
                 }
             } finally {
                 FileUtil.close(oos);
@@ -386,7 +386,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         int row = 1;
         bw.write("<row r=\"");
         bw.writeInt(row);
-        bw.write("\" customHeight=\"1\" ht=\"18.6\" spans=\"1:");
+        bw.write("\" customHeight=\"1\" ht=\"20.5\" spans=\"1:");
         bw.writeInt(columns.length);
         bw.write("\">");
 
diff --git a/src/main/java/org/ttzero/excel/reader/Dimension.java b/src/main/java/org/ttzero/excel/reader/Dimension.java
index c89a4f81..c4761fb4 100644
--- a/src/main/java/org/ttzero/excel/reader/Dimension.java
+++ b/src/main/java/org/ttzero/excel/reader/Dimension.java
@@ -51,11 +51,16 @@ public class Dimension {
      */
     public static Dimension of(String range) {
         int i = range.indexOf(':');
-        if (i < 0 || i == range.length() - 1)
-            throw new IllegalArgumentException(range + " can't convert to Dimension.");
 
-        long f = cellRangeToLong(range.substring(0, i))
-            , t = cellRangeToLong(range.substring(i + 1));
+        long f = 0L, t = 0L;
+        if (i < 0) {
+            f = cellRangeToLong(range);
+        } else if (i == 0) {
+            t = cellRangeToLong(range.substring(i + 1));
+        } else {
+            f = cellRangeToLong(range.substring(0, i));
+            t = cellRangeToLong(range.substring(i + 1));
+        }
         return new Dimension((int) (f >> 16), (short) f, (int) (t >> 16), (short) t);
     }
 
@@ -114,4 +119,14 @@ public class Dimension {
     public boolean checkRange(int r, int c) {
         return r >= firstRow && r <= lastRow && c >= firstColumn && c <= lastColumn;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof Dimension) {
+            Dimension other = (Dimension) o;
+            return other.firstRow == firstRow && other.firstColumn == firstColumn
+                    && other.lastRow == lastRow && other.lastColumn == lastColumn;
+        }
+        return false;
+    }
 }


Test patch:
diff --git a/src/test/java/org/ttzero/excel/entity/EmptySheetTest.java b/src/test/java/org/ttzero/excel/entity/EmptySheetTest.java
index 34727b42..4e6617b1 100644
--- a/src/test/java/org/ttzero/excel/entity/EmptySheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/EmptySheetTest.java
@@ -76,5 +76,7 @@ public class EmptySheetTest extends WorkbookTest {
             long count = reader.sheets().flatMap(org.ttzero.excel.reader.Sheet::dataRows).count();
             assert count == 0L;
         }
+
+        ExcelReader.read(defaultTestPath.resolve("test empty header.xlsx")).sheets().flatMap(org.ttzero.excel.reader.Sheet::rows).forEach(System.out::println);
     }
 }
diff --git a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
index 678b79a0..6784aa7c 100644
--- a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
@@ -26,6 +26,7 @@ import org.ttzero.excel.entity.style.Styles;
 import org.ttzero.excel.manager.docProps.Core;
 import org.ttzero.excel.processor.IntConversionProcessor;
 import org.ttzero.excel.processor.StyleProcessor;
+import org.ttzero.excel.reader.Dimension;
 import org.ttzero.excel.reader.ExcelReader;
 import org.ttzero.excel.reader.ExcelReaderTest;
 
@@ -394,7 +395,7 @@ public class ListObjectSheetTest extends WorkbookTest {
                 }
 
                 @Override
-                @ExcelColumn("SCORE")
+                @ExcelColumn("成绩")
                 public int getScore() {
                     return 97;
                 }
@@ -408,7 +409,7 @@ public class ListObjectSheetTest extends WorkbookTest {
             assert opt.isPresent();
             ExtStudent student = opt.get();
             assert student.getId() == 9527;
-            assert student.getScore() == 97;
+            assert student.getScore() == 0; // The setter column name is 'score'
         } catch (IOException e) {
             e.printStackTrace();
         }
@@ -477,7 +478,7 @@ public class ListObjectSheetTest extends WorkbookTest {
     }
 
     @Test public void testLarge() throws IOException {
-        new Workbook("large07").addSheet(new ListSheet<ExcelReaderTest.LargeData>() {
+        new Workbook("large07").forceExport().addSheet(new ListSheet<ExcelReaderTest.LargeData>() {
             private int i = 0, n;
 
             @Override
@@ -517,13 +518,82 @@ public class ListObjectSheetTest extends WorkbookTest {
                 return list;
             }
         }).writeTo(defaultTestPath);
+
+        try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("large07.xlsx"))) {
+            assert Dimension.of("A1:Y50001").equals(reader.sheet(0).getDimension());
+        }
     }
 
     // #132
     @Test public void testEmptyList() throws IOException {
         new Workbook().addSheet(new ListSheet<>(new ArrayList<>())).writeTo(defaultTestPath);
     }
+    
+    @Test public void testNoForceExport() throws IOException {
+        new Workbook("testNoForceExport").addSheet(new ListSheet<>(NoColumnAnnotation.randomTestData())).writeTo(defaultTestPath);
 
+        try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("testNoForceExport.xlsx"))) {
+            assert Dimension.of("A1").equals(reader.sheet(0).getDimension());
+        }
+    }
+    
+    @Test public void testForceExportOnWorkbook() throws IOException {
+        int lines = random.nextInt(100) + 3;
+        new Workbook("testForceExportOnWorkbook").forceExport().addSheet(new ListSheet<>(NoColumnAnnotation.randomTestData(lines))).writeTo(defaultTestPath);
+        try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("testForceExportOnWorkbook.xlsx"))) {
+            assert reader.sheet(0).dataRows().count() == lines;
+        }
+    }
+
+    @Test public void testForceExportOnWorkSheet() throws IOException {
+        int lines = random.nextInt(100) + 3;
+        new Workbook("testForceExportOnWorkSheet").addSheet(new ListSheet<>(NoColumnAnnotation.randomTestData(lines)).forceExport()).writeTo(defaultTestPath);
+        try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("testForceExportOnWorkSheet.xlsx"))) {
+            assert reader.sheet(0).dataRows().count() == lines;
+        }
+    }
+
+    @Test public void testForceExportOnWorkbook2() throws IOException {
+        int lines = random.nextInt(100) + 3, lines2 = random.nextInt(100) + 4;
+        new Workbook("testForceExportOnWorkbook2")
+                .forceExport()
+                .addSheet(new ListSheet<>(NoColumnAnnotation.randomTestData(lines)))
+                .addSheet(new ListSheet<>(NoColumnAnnotation2.randomTestData(lines2)))
+                .writeTo(defaultTestPath);
+        try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("testForceExportOnWorkbook2.xlsx"))) {
+            assert reader.sheet(0).dataRows().count() == lines;
+            assert reader.sheet(1).dataRows().count() == lines2;
+        }
+    }
+
+    @Test public void testForceExportOnWorkbook2Cancel1() throws IOException {
+        int lines = random.nextInt(100) + 3, lines2 = random.nextInt(100) + 4;
+        new Workbook("testForceExportOnWorkbook2Cancel1")
+                .forceExport()
+                .addSheet(new ListSheet<>(NoColumnAnnotation.randomTestData(lines)).cancelForceExport())
+                .addSheet(new ListSheet<>(NoColumnAnnotation2.randomTestData(lines2)))
+                .writeTo(defaultTestPath);
+        try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("testForceExportOnWorkbook2Cancel1.xlsx"))) {
+            assert reader.sheets().count() == 2L;
+            assert reader.sheet(0).dataRows().count() == 0L;
+            assert reader.sheet(1).dataRows().count() == lines2;
+        }
+    }
+
+    @Test public void testForceExportOnWorkbook2Cancel2() throws IOException {
+        int lines = random.nextInt(100) + 3, lines2 = random.nextInt(100) + 4;
+        new Workbook("testForceExportOnWorkbook2Cancel2")
+                .forceExport()
+                .addSheet(new ListSheet<>(NoColumnAnnotation.randomTestData(lines)).cancelForceExport())
+                .addSheet(new ListSheet<>(NoColumnAnnotation2.randomTestData(lines2)).cancelForceExport())
+                .writeTo(defaultTestPath);
+        try (ExcelReader reader = ExcelReader.read(defaultTestPath.resolve("testForceExportOnWorkbook2Cancel2.xlsx"))) {
+            assert reader.sheets().count() == 2L;
+            assert reader.sheet(0).dataRows().count() == 0L;
+            assert reader.sheet(1).dataRows().count() == 0L;
+        }
+    }
+    
     public static class Item {
         @ExcelColumn
         private int id;
@@ -1011,13 +1081,13 @@ public class ListObjectSheetTest extends WorkbookTest {
             super(id, name);
         }
 
-        public String getNice() {
-            return nice;
-        }
-
-        public void setNice(String nice) {
-            this.nice = nice;
-        }
+//        public String getNice() {
+//            return nice;
+//        }
+//
+//        public void setNice(String nice) {
+//            this.nice = nice;
+//        }
 
         public static List<Item> randomTestData(int n) {
             List<Item> list = new ArrayList<>(n);
@@ -1077,4 +1147,66 @@ public class ListObjectSheetTest extends WorkbookTest {
             super.setScore(score);
         }
     }
+    
+    public static class NoColumnAnnotation {
+        private int id;
+        private String name;
+
+        public int getId() {
+            return id;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public NoColumnAnnotation(int id, String name) {
+            this.id = id;
+            this.name = name;
+        }
+
+        public static List<NoColumnAnnotation> randomTestData(int n) {
+            List<NoColumnAnnotation> list = new ArrayList<>(n);
+            for (int i = 0; i < n; i++) {
+                list.add(new NoColumnAnnotation(i, getRandomString()));
+            }
+            return list;
+        }
+
+        public static List<NoColumnAnnotation> randomTestData() {
+            int n = random.nextInt(100) + 1;
+            return randomTestData(n);
+        }
+    }
+
+    public static class NoColumnAnnotation2 {
+        private int age;
+        private String abc;
+
+        public int getAge() {
+            return age;
+        }
+
+        public String getAbc() {
+            return abc;
+        }
+
+        public NoColumnAnnotation2(int age, String abc) {
+            this.age = age;
+            this.abc = abc;
+        }
+
+        public static List<NoColumnAnnotation2> randomTestData(int n) {
+            List<NoColumnAnnotation2> list = new ArrayList<>(n);
+            for (int i = 0; i < n; i++) {
+                list.add(new NoColumnAnnotation2(i, getRandomString()));
+            }
+            return list;
+        }
+
+        public static List<NoColumnAnnotation2> randomTestData() {
+            int n = random.nextInt(100) + 1;
+            return randomTestData(n);
+        }
+    }
 }
diff --git a/src/test/java/org/ttzero/excel/reader/DimensionTest.java b/src/test/java/org/ttzero/excel/reader/DimensionTest.java
new file mode 100644
index 00000000..c02b1d8d
--- /dev/null
+++ b/src/test/java/org/ttzero/excel/reader/DimensionTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017-2021, guanquan.wang@yandex.com All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.ttzero.excel.reader;
+
+import org.junit.Test;
+
+/**
+ * @author guanquan.wang at 2021-03-25 00:54
+ */
+public class DimensionTest {
+
+    @Test public void testFirstDim() {
+        Dimension d = Dimension.of("A1");
+        assert d.firstColumn == 1;
+        assert d.firstRow == 1;
+        assert d.lastColumn == 0;
+        assert d.lastRow == 0;
+    }
+
+    @Test public void testFirstDim2() {
+        Dimension d = Dimension.of("B3:");
+        assert d.firstColumn == 2;
+        assert d.firstRow == 3;
+        assert d.lastColumn == 0;
+        assert d.lastRow == 0;
+    }
+
+    @Test public void testLastDim() {
+        Dimension d = Dimension.of(":C2");
+        assert d.firstColumn == 0;
+        assert d.firstRow == 0;
+        assert d.lastColumn == 3;
+        assert d.lastRow == 2;
+    }
+
+    @Test public void testFullDim() {
+        Dimension d = Dimension.of("A1:C2");
+        assert d.firstColumn == 1;
+        assert d.firstRow == 1;
+        assert d.lastColumn == 3;
+        assert d.lastRow == 2;
+    }
+}