Repository: wangguanquan/eec
Pull request #195 (2021-08-07 15:08:20)

Fix#188


Patch:
diff --git a/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java b/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
index bdbcc3e0..424110c4 100644
--- a/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
+++ b/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
@@ -85,4 +85,15 @@ public @interface ExcelColumn {
      * @return true if
      */
     boolean wrapText() default false;
+
+    /**
+     * Specify the column index(zero base), Range from {@code 0} to {@code 16383} include {@code 16383}
+     * <p>
+     * The column set by colIndex is an absolute position. For example,
+     * if {@code colIndex=100}, this column must be placed at the {@code "CV"} position
+     *
+     * @return -1 means unset
+     */
+    int colIndex() default -1;
+
 }
diff --git a/src/main/java/org/ttzero/excel/annotation/HeaderStyle.java b/src/main/java/org/ttzero/excel/annotation/HeaderStyle.java
index 75724af5..562b897a 100644
--- a/src/main/java/org/ttzero/excel/annotation/HeaderStyle.java
+++ b/src/main/java/org/ttzero/excel/annotation/HeaderStyle.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2017-2020, guanquan.wang@yandex.com All Rights Reserved.
+ * Copyright (c) 2017-2021, carl.jia@qq.com All Rights Reserved.
+ * 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.
@@ -28,9 +29,10 @@ import java.lang.annotation.Target;
 /**
  * Custom header styles
  *
- * @author jialei2 at 021-05-10 17:38
+ * @author jialei2 at 2021-05-10 17:38
+ * @author guanquan.wang at 2021-08-06 18:19
  */
-@Target({ElementType.FIELD, ElementType.METHOD})
+@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface HeaderStyle {
diff --git a/src/main/java/org/ttzero/excel/entity/ListMapSheet.java b/src/main/java/org/ttzero/excel/entity/ListMapSheet.java
index 80889440..8eeaced6 100644
--- a/src/main/java/org/ttzero/excel/entity/ListMapSheet.java
+++ b/src/main/java/org/ttzero/excel/entity/ListMapSheet.java
@@ -199,8 +199,6 @@ public class ListMapSheet extends ListSheet<Map<String, ?>> {
                 }
             }
         }
-        // Check the header column limit
-        checkColumnLimit();
 
         for (Column hc : columns) {
             hc.styles = workbook.getStyles();
diff --git a/src/main/java/org/ttzero/excel/entity/ListSheet.java b/src/main/java/org/ttzero/excel/entity/ListSheet.java
index c349ce1d..a926bf4e 100644
--- a/src/main/java/org/ttzero/excel/entity/ListSheet.java
+++ b/src/main/java/org/ttzero/excel/entity/ListSheet.java
@@ -19,6 +19,7 @@ package org.ttzero.excel.entity;
 import org.ttzero.excel.annotation.ExcelColumn;
 import org.ttzero.excel.annotation.HeaderComment;
 import org.ttzero.excel.annotation.HeaderStyle;
+import org.ttzero.excel.processor.IntConversionProcessor;
 import org.ttzero.excel.reader.Cell;
 import org.ttzero.excel.annotation.IgnoreExport;
 
@@ -32,7 +33,6 @@ 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;
@@ -61,8 +61,6 @@ import static org.ttzero.excel.util.StringUtil.isEmpty;
  */
 public class ListSheet<T> extends Sheet {
     protected List<T> data;
-    private Field[] fields;
-    private Method[] methods;
     protected int start, end;
     protected boolean eof;
     private int size;
@@ -258,12 +256,13 @@ public class ListSheet<T> extends Sheet {
                     cell.clear();
 
                     Object e;
-                    if (columns[i].isIgnoreValue())
+                    EntryColumn column = (EntryColumn) columns[i];
+                    if (column.isIgnoreValue())
                         e = null;
-                    else if (methods[i] != null)
-                        e = methods[i].invoke(o);
+                    else if (column.getMethod() != null)
+                        e = column.getMethod().invoke(o);
                     else
-                        e = fields[i].get(o);
+                        e = column.getField().get(o);
 
                     cellValueAndStyle.reset(rows, cell, e, columns[i]);
                 }
@@ -319,7 +318,7 @@ public class ListSheet<T> extends Sheet {
     }
 
     // Returns the reflect <T> type
-    private Class<?> getTClass() {
+    protected Class<?> getTClass() {
         Class<?> clazz;
         if (getClass().getGenericSuperclass() instanceof ParameterizedType) {
             @SuppressWarnings({"unchecked", "retype"})
@@ -354,7 +353,7 @@ public class ListSheet<T> extends Sheet {
      *
      * @return the column array length
      */
-    private int init() {
+    protected int init() {
         Class<?> clazz = getTClass();
         if (clazz == null) return 0;
 
@@ -370,17 +369,15 @@ public class ListSheet<T> extends Sheet {
             LOGGER.warn("Get class {} methods failed.", clazz);
         }
 
-        Field[] declaredFields = listDeclaredFields(clazz, c -> c.getAnnotation(IgnoreExport.class) == null);
+        Field[] declaredFields = listDeclaredFields(clazz, c -> !ignoreColumn(c));
 
         boolean forceExport = this.forceExport == 1;
 
         if (!hasHeaderColumns()) {
             // Get ExcelColumn annotation method
-            methods = new Method[declaredFields.length + tmp.size()];
             List<Column> list = new ArrayList<>(declaredFields.length);
 
-            int i = 0;
-            for (; i < declaredFields.length; i++) {
+            for (int i = 0; i < declaredFields.length; i++) {
                 Field field = declaredFields[i];
                 field.setAccessible(true);
                 String gs = field.getName();
@@ -388,18 +385,20 @@ public class ListSheet<T> extends Sheet {
                 // Ignore annotation on read method
                 Method method = tmp.get(gs);
                 if (method != null) {
-                    if (method.getAnnotation(IgnoreExport.class) != null) {
+                    // Filter all ignore column
+                    if (ignoreColumn(method)) {
                         declaredFields[i] = null;
                         continue;
                     }
 
-                    Column column = createColumn(method);
+                    EntryColumn column = createColumn(method);
                     // Force export
                     if (column == null && forceExport) {
-                        column = new Column(gs, EMPTY, false);
+                        column = new EntryColumn(gs, EMPTY, false);
                     }
                     if (column != null) {
-                        methods[i] = method;
+                        column.method = method;
+                        column.field = field;
                         column.clazz = method.getReturnType();
                         column.key = gs;
                         column.styles = workbook.getStyles();
@@ -416,13 +415,14 @@ public class ListSheet<T> extends Sheet {
                     }
                 }
 
-                Column column = createColumn(field);
+                EntryColumn column = createColumn(field);
                 // Force export
                 if (column == null && forceExport) {
-                    column = new Column(gs, EMPTY, false);
+                    column = new EntryColumn(gs, EMPTY, false);
                 }
                 if (column != null) {
                     list.add(column);
+                    column.field = field;
                     column.key = gs;
                     column.styles = workbook.getStyles();
                     if (isEmpty(column.name)) {
@@ -430,56 +430,19 @@ public class ListSheet<T> extends Sheet {
                     }
                     if (method != null) {
                         column.clazz = method.getReturnType();
-                        methods[i] = method;
+                        column.method = method;
                     } else column.clazz = field.getType();
 
                     // Attach header style
                     buildHeaderStyle(method, field, column);
                     // Attach header comment
                     buildHeaderComment(method, field, column);
-                    continue;
                 }
-
-                // Ignore others
-                declaredFields[i] = null;
-            }
-
-            // Collect the method which has ExcelColumn annotation
-            Method[] readMethods = null;
-            try {
-                Collection<Method> values = tmp.values();
-                readMethods = listReadMethods(clazz, method -> method.getAnnotation(ExcelColumn.class) != null
-                        && 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();
-                        column.key = method.getName();
-                        column.styles = workbook.getStyles();
-                        if (isEmpty(column.name)) {
-                            column.name = method.getName();
-                        }
-
-                        // Attach header style
-                        buildHeaderStyle(method, null, column);
-                        // Attach header comment
-                        buildHeaderComment(method, null, column);
-                    }
-                }
-            }
+            // Attach some custom column
+            List<Column> attachList = attachOtherColumn(tmp, clazz);
+            if (attachList != null) list.addAll(attachList);
 
             // No column to write
             if (list.isEmpty()) {
@@ -490,82 +453,93 @@ public class ListSheet<T> extends Sheet {
             }
             columns = new Column[list.size()];
             list.toArray(columns);
-
-            // Merge Header Style
-            HeaderStyle headerStyle = clazz.getDeclaredAnnotation(HeaderStyle.class);
-            int style = 0;
-            if (headerStyle != null) {
-                style = buildHeadStyle(headerStyle.fontColor(), headerStyle.fillFgColor());
-            }
-            for (i = 0; i < columns.length; i++) {
-                if (style > 0 && columns[i].headerStyleIndex == -1)
-                    columns[i].setHeaderStyle(style);
-            }
-
-            // Clean
-            i = 0;
-            fields = new Field[columns.length];
-            for (int j = 0; j < declaredFields.length; j++) {
-                if (declaredFields[j] != null) {
-                    declaredFields[j].setAccessible(true);
-                    fields[i] = declaredFields[j];
-                    methods[i] = methods[j];
-                    i++;
-                }
-            }
-            if (declaredFields.length < methods.length) {
-                System.arraycopy(methods, declaredFields.length, methods, i, methods.length - declaredFields.length);
-                i += methods.length - declaredFields.length;
-            }
-            return i;
         } else {
-            fields = new Field[columns.length];
-            methods = new Method[columns.length];
             for (int i = 0; i < columns.length; i++) {
                 Column hc = columns[i];
-                methods[i] = tmp.get(hc.key);
-                if (methods[i] != null) methods[i].setAccessible(true);
+                if (!(hc instanceof EntryColumn)) {
+                    hc = new EntryColumn(hc);
+                    columns[i] = hc;
+                }
+                EntryColumn ec = (EntryColumn) hc;
+                Method method = tmp.get(hc.key);
+                if (method != null) {
+                    method.setAccessible(true);
+                }
+                ec.method = method;
 
                 for (Field field : declaredFields) {
                     if (hc.key.equals(field.getName())) {
                         field.setAccessible(true);
-                        fields[i] = field;
+                        ec.field = field;
                         break;
                     }
                 }
 
-                if (methods[i] == null && fields[i] == null) {
+                if (method == null && ec.field == null) {
                     LOGGER.warn("Column [" + hc.getName() + "(" + hc.key + ")"
                             + "] not declare in class " + clazz);
                     hc.ignoreValue();
                 } else if (hc.getClazz() == null) {
-                    hc.setClazz(methods[i] != null ? methods[i].getReturnType() : fields[i].getType());
+                    hc.setClazz(ec.method != null ? ec.method.getReturnType() : ec.field.getType());
+                }
+
+                // Attach header style
+                if (hc.getHeaderStyleIndex() == -1) {
+                    buildHeaderStyle(method, ec.field, hc);
+                }
+                // Attach header comment
+                if (hc.headerComment == null) {
+                    buildHeaderComment(method, ec.field, hc);
                 }
             }
-            return columns.length;
         }
+
+        // Merge Header Style defined on Entry Class
+        HeaderStyle headerStyle = clazz.getDeclaredAnnotation(HeaderStyle.class);
+        int style = 0;
+        if (headerStyle != null) {
+            style = buildHeadStyle(headerStyle.fontColor(), headerStyle.fillFgColor());
+        }
+        for (Column column : columns) {
+            if (style > 0 && column.getHeaderStyleIndex() == -1)
+                column.setHeaderStyle(style);
+        }
+
+        return columns.length;
     }
 
-    private Column createColumn(AccessibleObject ao) {
-        if (ao.getAnnotation(IgnoreExport.class) != null) return null;
+    /**
+     * Create column from {@link ExcelColumn} annotation
+     * <p>
+     * Override the method to extend custom comments
+     *
+     * @param ao {@link AccessibleObject} witch defined the {@code ExcelColumn} annotation
+     * @return the Worksheet's {@link Column} information
+     */
+    protected EntryColumn createColumn(AccessibleObject ao) {
+        // Filter all ignore column
+        if (ignoreColumn(ao)) return null;
+
         ao.setAccessible(true);
         ExcelColumn ec = ao.getAnnotation(ExcelColumn.class);
         if (ec != null) {
-            Column column = new Column(ec.value(), EMPTY, ec.share());
-            // Comment
-//            column.headerComment = createComment(ao.getAnnotation(HeaderComment.class), ec.comment());
+            EntryColumn column = new EntryColumn(ec.value(), EMPTY, ec.share());
             // Number format
             if (isNotEmpty(ec.format())) {
                 column.setNumFmt(ec.format());
             }
             // Wrap
             column.setWrapText(ec.wrapText());
+            // Column index
+            if (ec.colIndex() > -1) {
+                column.colIndex = ec.colIndex();
+            }
             return column;
         }
         return null;
     }
 
-    private void buildHeaderStyle(AccessibleObject main, AccessibleObject sub, Column column) {
+    protected void buildHeaderStyle(AccessibleObject main, AccessibleObject sub, Column column) {
         HeaderStyle hs = null;
         if (main != null) {
             hs = main.getAnnotation(HeaderStyle.class);
@@ -578,7 +552,7 @@ public class ListSheet<T> extends Sheet {
         }
     }
 
-    private void buildHeaderComment(AccessibleObject main, AccessibleObject sub, Column column) {
+    protected void buildHeaderComment(AccessibleObject main, AccessibleObject sub, Column column) {
         HeaderComment comment = null;
         if (main != null) {
             comment = main.getAnnotation(HeaderComment.class);
@@ -599,6 +573,62 @@ public class ListSheet<T> extends Sheet {
         }
     }
 
+    /**
+     * Ignore some columns, override this method to add custom filtering
+     *
+     * @param ao {@code Method} or {@code Field}
+     * @return true if ignore current column
+     */
+    protected boolean ignoreColumn(AccessibleObject ao) {
+        return ao.getAnnotation(IgnoreExport.class) != null;
+    }
+
+    /**
+     * Attach some custom columns
+     *
+     * @param existsMethodMapper all exists method collection by default
+     * @param clazz Class of &ltT&gt
+     * @return list of {@link Column} or null if no more columns to attach
+     */
+    protected List<Column> attachOtherColumn(Map<String, Method> existsMethodMapper, Class<?> clazz) {
+        // Collect the method which has ExcelColumn annotation
+        Method[] readMethods = null;
+        try {
+            Collection<Method> values = existsMethodMapper.values();
+            readMethods = listReadMethods(clazz, method -> method.getAnnotation(ExcelColumn.class) != null
+                    && method.getAnnotation(IgnoreExport.class) == null && !values.contains(method));
+        } catch (IntrospectionException e) {
+            // Ignore
+        }
+
+        if (readMethods != null) {
+            Set<Method> existsMethods = new HashSet<>(existsMethodMapper.values());
+            List<Column> list = new ArrayList<>();
+            for (Method method : readMethods) {
+                // Exclusions exists
+                if (existsMethods.contains(method)) continue;
+                EntryColumn column = createColumn(method);
+                if (column != null) {
+                    list.add(column);
+                    column.method = method;
+                    column.clazz = method.getReturnType();
+                    column.key = method.getName();
+                    column.styles = workbook.getStyles();
+                    if (isEmpty(column.name)) {
+                        column.name = method.getName();
+                    }
+
+                    // Attach header style
+                    buildHeaderStyle(method, null, column);
+                    // Attach header comment
+                    buildHeaderComment(method, null, column);
+                }
+            }
+            return list;
+        }
+        return null; // No more columns
+    }
+
     /**
      * Returns the header column info
      *
@@ -607,16 +637,11 @@ public class ListSheet<T> extends Sheet {
     @Override
     public Column[] getHeaderColumns() {
         if (!headerReady) {
-//            if (!hasHeaderColumns()) {
-//                columns = new Column[0];
-//            }
             // create header columns
             int size = init();
             if (size <= 0) {
                 columns = new Column[0];
             } else {
-                // Check the header column limit
-                checkColumnLimit();
                 headerReady = true;
             }
         }
@@ -716,4 +741,95 @@ public class ListSheet<T> extends Sheet {
     protected List<T> more() {
         return null;
     }
+
+    static class EntryColumn extends Column {
+        Method method;
+        Field field;
+
+        public EntryColumn() { }
+
+        public EntryColumn(String name, Class<?> clazz) {
+            super(name, clazz);
+        }
+
+        public EntryColumn(String name, String key) {
+            super(name, key);
+        }
+
+        public EntryColumn(String name, String key, Class<?> clazz) {
+            super(name, key, clazz);
+        }
+
+        public EntryColumn(String name, Class<?> clazz, IntConversionProcessor processor) {
+            super(name, clazz, processor);
+        }
+
+        public EntryColumn(String name, String key, IntConversionProcessor processor) {
+            super(name, key, processor);
+        }
+
+        public EntryColumn(String name, Class<?> clazz, boolean share) {
+            super(name, clazz, share);
+        }
+
+        public EntryColumn(String name, String key, boolean share) {
+            super(name, key, share);
+        }
+
+        public EntryColumn(String name, Class<?> clazz, IntConversionProcessor processor, boolean share) {
+            super(name, clazz, processor, share);
+        }
+
+        public EntryColumn(String name, String key, Class<?> clazz, IntConversionProcessor processor) {
+            super(name, key, clazz, processor);
+        }
+
+        public EntryColumn(String name, String key, IntConversionProcessor processor, boolean share) {
+            super(name, key, processor, share);
+        }
+
+        public EntryColumn(String name, Class<?> clazz, int cellStyle) {
+            super(name, clazz, cellStyle);
+        }
+
+        public EntryColumn(String name, String key, int cellStyle) {
+            super(name, key, cellStyle);
+        }
+
+        public EntryColumn(String name, Class<?> clazz, int cellStyle, boolean share) {
+            super(name, clazz, cellStyle, share);
+        }
+
+        public EntryColumn(String name, String key, int cellStyle, boolean share) {
+            super(name, key, cellStyle, share);
+        }
+
+        public EntryColumn(Column other) {
+            this.key = other.key;
+            this.name = other.name;
+            this.clazz = other.clazz;
+            this.share = other.share;
+            this.type = other.type;
+            this.processor = other.processor;
+            this.styleProcessor = other.styleProcessor;
+            this.width = other.width;
+            this.o = other.o;
+            this.styles = other.styles;
+            this.headerComment = other.headerComment;
+            this.numFmt = other.numFmt;
+            this.ignoreValue = other.ignoreValue;
+            this.wrapText = other.wrapText;
+            this.colIndex = other.colIndex;
+            if (other.cellStyle > 0) setCellStyle(other.cellStyle);
+            if (other.headerStyle > 0) setHeaderStyle(other.headerStyle);
+        }
+
+        public Method getMethod() {
+            return method;
+        }
+
+        public Field getField() {
+            return field;
+        }
+    }
 }
diff --git a/src/main/java/org/ttzero/excel/entity/ResultSetSheet.java b/src/main/java/org/ttzero/excel/entity/ResultSetSheet.java
index 0f97255b..168199bc 100644
--- a/src/main/java/org/ttzero/excel/entity/ResultSetSheet.java
+++ b/src/main/java/org/ttzero/excel/entity/ResultSetSheet.java
@@ -291,8 +291,6 @@ public class ResultSetSheet extends Sheet {
         }
 
         if (hasHeaderColumns()) {
-            // Check the limit of columns
-            checkColumnLimit();
 
             for (i = 0; i < columns.length; i++) {
                 if (StringUtil.isEmpty(columns[i].getName())) {
diff --git a/src/main/java/org/ttzero/excel/entity/Sheet.java b/src/main/java/org/ttzero/excel/entity/Sheet.java
index a64ab53f..765d9c91 100644
--- a/src/main/java/org/ttzero/excel/entity/Sheet.java
+++ b/src/main/java/org/ttzero/excel/entity/Sheet.java
@@ -268,11 +268,11 @@ public abstract class Sheet implements Cloneable, Storable {
         /**
          * The style index of cell, -1 if not be setting
          */
-        public int cellStyleIndex = -1;
+        private int cellStyleIndex = -1;
         /**
          * The style index of header, -1 if not be setting
          */
-        public int headerStyleIndex = -1;
+        private int headerStyleIndex = -1;
         /**
          * The cell width
          */
@@ -292,6 +292,15 @@ public abstract class Sheet implements Cloneable, Storable {
          * Wrap text in a cell
          */
         public int wrapText;
+        /**
+         * Specify the column index
+         */
+        public int colIndex = -1;
+
+        /**
+         * Constructor Column
+         */
+        public Column() { }
 
         /**
          * Constructor Column
@@ -591,6 +600,34 @@ public abstract class Sheet implements Cloneable, Storable {
             return this;
         }
 
+        /**
+         * Settings the x-axis of column in row
+         *
+         * @param colIndex column index (zero base)
+         */
+        public Column setColIndex(int colIndex) {
+            this.colIndex = colIndex;
+            return this;
+        }
+
+        /**
+         * Returns the style index of cell, -1 if not be setting
+         *
+         * @return index of style
+         */
+        public int getCellStyleIndex() {
+            return cellStyleIndex;
+        }
+
+        /**
+         * Returns the header style index of cell, -1 if not be setting
+         *
+         * @return index of style
+         */
+        public int getHeaderStyleIndex() {
+            return headerStyleIndex;
+        }
+
         /**
          * Returns the default horizontal style
          * the Date, Character, Bool has center value, the
@@ -799,10 +836,10 @@ public abstract class Sheet implements Cloneable, Storable {
             int style;
             if (isString(clazz)) {
                 style = Styles.defaultStringBorderStyle() | wrapText;
-            } else if (isDate(clazz) || isLocalDate(clazz)) {
-                style = styles.addNumFmt(new NumFmt("yyyy\\-mm\\-dd")) | (1 << INDEX_BORDER) | Horizontals.CENTER;
             } else if (isDateTime(clazz) || isLocalDateTime(clazz)) {
                 style = styles.addNumFmt(new NumFmt("yyyy\\-mm\\-dd\\ hh:mm:ss")) | (1 << INDEX_BORDER) | Horizontals.CENTER;
+            } else if (isDate(clazz) || isLocalDate(clazz)) {
+                style = styles.addNumFmt(new NumFmt("yyyy\\-mm\\-dd")) | (1 << INDEX_BORDER) | Horizontals.CENTER;
             } else if (isBool(clazz) || isChar(clazz)) {
                 style = Styles.clearHorizontal(Styles.defaultStringBorderStyle()) | Horizontals.CENTER;
             } else if (isInt(clazz) || isLong(clazz)) {
@@ -1105,6 +1142,63 @@ public abstract class Sheet implements Cloneable, Storable {
         return columns;
     }
 
+    /**
+     * Sort column by {@code colIndex}
+     *
+     * @return header columns
+     */
+    public Column[] getAndSortHeaderColumns() {
+        if (!headerReady) {
+            getHeaderColumns();
+            // Sort column index
+            sortColumns(columns);
+            // Turn to one-base
+            for (int i = 0; i < columns.length; i++) {
+                if (i > 0 && columns[i - 1].colIndex >= columns[i].colIndex) columns[i].colIndex = columns[i - 1].colIndex + 1;
+                else if (columns[i].colIndex <= i) columns[i].colIndex = i + 1;
+                else columns[i].colIndex++;
+            }
+
+            // Check the limit of columns
+            checkColumnLimit();
+        }
+        return columns;
+    }
+
+    protected void sortColumns(Column[] columns) {
+        if (columns.length <= 1) return;
+        int j = 0;
+        for (int i = 0; i < columns.length; i++) {
+            if (columns[i].colIndex >= 0) {
+                int n = search(columns, j, columns[i].colIndex);
+                if (n < i) insert(columns, n, i);
+                j++;
+            }
+        }
+        // Finished
+        if (j == columns.length) return;
+        int n = columns[0].colIndex;
+        for (int i = 0; i < columns.length && j < columns.length; ) {
+            if (n > i) {
+                for (int k = Math.min(n - i, columns.length - j); k > 0; k--, j++)
+                    insert(columns, i++, j);
+            } else i++;
+            if (i < columns.length) n = columns[i].colIndex;
+        }
+    }
+
+    protected int search(Column[] columns, int n, int k) {
+        int i = 0;
+        for (; i < n && columns[i].colIndex <= k; i++) ;
+        return i;
+    }
+
+    private void insert(Column[] columns, int n, int k) {
+        Column t = columns[k];
+        System.arraycopy(columns, n, columns, n + 1, k - n);
+        columns[n] = t;
+    }
+
     /**
      * Setting the header rows's columns
      *
@@ -1214,7 +1308,7 @@ public abstract class Sheet implements Cloneable, Storable {
             paging();
         }
         if (!headerReady) {
-            getHeaderColumns();
+            getAndSortHeaderColumns();
         }
         if (rowBlock == null) {
             rowBlock = new RowBlock(getRowBlockSize());
@@ -1498,7 +1592,7 @@ public abstract class Sheet implements Cloneable, Storable {
      * Check the limit of columns
      */
     public void checkColumnLimit() {
-        int a = columns.length
+        int a = columns.length > 0 ? columns[columns.length - 1].colIndex : 0
             , b = sheetWriter.getColumnLimit();
         if (a > b) {
             throw new TooManyColumnsException(a, b);
@@ -1516,7 +1610,7 @@ public abstract class Sheet implements Cloneable, Storable {
 
     /**
      * Int conversion to column string number.
-     * The max column on sheet is 16_384 after office 2007 and 256 in office 2003
+     * The max column on sheet is {@code 16_384} after office 2007 and {@code 256} in office 2003
      * <blockquote><pre>
      * int    | column number
      * -------|---------
diff --git a/src/main/java/org/ttzero/excel/entity/StatementSheet.java b/src/main/java/org/ttzero/excel/entity/StatementSheet.java
index 3a75ebc9..db044960 100644
--- a/src/main/java/org/ttzero/excel/entity/StatementSheet.java
+++ b/src/main/java/org/ttzero/excel/entity/StatementSheet.java
@@ -271,7 +271,7 @@ public class StatementSheet extends ResultSetSheet {
                 }
 
                 // Check the header information is exists
-                getHeaderColumns();
+                getAndSortHeaderColumns();
             }
 
             if (rowBlock == null) {
diff --git a/src/main/java/org/ttzero/excel/entity/TooManyColumnsException.java b/src/main/java/org/ttzero/excel/entity/TooManyColumnsException.java
index ad19ef1a..5ccc6d01 100644
--- a/src/main/java/org/ttzero/excel/entity/TooManyColumnsException.java
+++ b/src/main/java/org/ttzero/excel/entity/TooManyColumnsException.java
@@ -31,7 +31,7 @@ public class TooManyColumnsException extends ExcelWriteException {
     }
 
     public TooManyColumnsException(int n, int m) {
-        super(n + " out of Total number of columns on a worksheet " + m);
+        super("The max columns on sheet is " + m + ", current is " + n);
     }
 
     public TooManyColumnsException(String s) {
diff --git a/src/main/java/org/ttzero/excel/entity/csv/CSVWorksheetWriter.java b/src/main/java/org/ttzero/excel/entity/csv/CSVWorksheetWriter.java
index c2feb908..c32e3fdd 100644
--- a/src/main/java/org/ttzero/excel/entity/csv/CSVWorksheetWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/csv/CSVWorksheetWriter.java
@@ -148,7 +148,7 @@ public class CSVWorksheetWriter implements IWorksheetWriter {
      */
     protected void writeBefore() throws IOException {
         // The header columns
-        Sheet.Column[] columns = sheet.getHeaderColumns();
+        Sheet.Column[] columns = sheet.getAndSortHeaderColumns();
         boolean noneHeader = columns == null || columns.length == 0;
 
         if (!noneHeader) {
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 98dc94e4..47c332bb 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
@@ -287,7 +287,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      */
     protected void writeBefore() throws IOException {
         // The header columns
-        columns = sheet.getHeaderColumns();
+        columns = sheet.getAndSortHeaderColumns();
         boolean noneHeader = sheet.hasNonHeader();
 
         bw.write(Const.EXCEL_XML_DECLARATION);
@@ -323,7 +323,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         if (size > 0) {
             bw.write(':');
             n--;
-            char[] col = int2Col(columns.length);
+            char[] col = int2Col(columns[columns.length - 1].colIndex);
             bw.write(col);
             n -= col.length;
             bw.writeInt(size + 1);
@@ -360,9 +360,9 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
                 bw.write('"');
                 for (int j = n; j-- > 0; ) bw.write(32); // Fill space
                 bw.write(" max=\"");
-                bw.writeInt(i + 1);
+                bw.writeInt(columns[i].colIndex + 1);
                 bw.write("\" min=\"");
-                bw.writeInt(i + 1);
+                bw.writeInt(columns[i].colIndex);
                 bw.write("\" bestFit=\"1\"/>");
             }
             bw.write("</cols>");
@@ -387,28 +387,26 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         bw.write("<row r=\"");
         bw.writeInt(row);
         bw.write("\" customHeight=\"1\" ht=\"20.5\" spans=\"1:");
-        bw.writeInt(columns.length);
+        bw.writeInt(columns[columns.length - 1].colIndex);
         bw.write("\">");
 
         int c = 0, defaultStyleIndex = sheet.defaultHeadStyleIndex();
 
         if (sheet.isAutoSize()) {
             for (Sheet.Column hc : columns) {
-                writeStringAutoSize(isNotEmpty(hc.getName()) ? hc.getName() : hc.key, row, c++, hc.headerStyleIndex == -1 ? defaultStyleIndex : hc.headerStyleIndex);
+                writeStringAutoSize(isNotEmpty(hc.getName()) ? hc.getName() : hc.key, row, c++, hc.getHeaderStyleIndex() == -1 ? defaultStyleIndex : hc.getHeaderStyleIndex());
             }
         } else {
             for (Sheet.Column hc : columns) {
-                writeString(isNotEmpty(hc.getName()) ? hc.getName() : hc.key, row, c++, hc.headerStyleIndex == -1 ? defaultStyleIndex : hc.headerStyleIndex);
+                writeString(isNotEmpty(hc.getName()) ? hc.getName() : hc.key, row, c++, hc.getHeaderStyleIndex() == -1 ? defaultStyleIndex : hc.getHeaderStyleIndex());
             }
         }
 
         // Write header comments
-        c = 0;
         for (Sheet.Column hc : columns) {
-            c++;
             if (hc.headerComment != null) {
                 if (comments == null) comments = sheet.createComments();
-                comments.addComment(new String(int2Col(c)) + row
+                comments.addComment(new String(int2Col(hc.colIndex)) + row
                     , hc.headerComment.getTitle(), hc.headerComment.getValue());
             }
         }
@@ -611,7 +609,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         }
         Sheet.Column hc = columns[column];
         bw.write("<c r=\"");
-        bw.write(int2Col(column + 1));
+        bw.write(int2Col(hc.colIndex));
         bw.writeInt(row);
         int i;
         if (StringUtil.isEmpty(s)) {
@@ -663,7 +661,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      */
     protected void writeDouble(double d, int row, int column, int xf) throws IOException {
         bw.write("<c r=\"");
-        bw.write(int2Col(column + 1));
+        bw.write(int2Col(columns[column].colIndex));
         bw.writeInt(row);
         bw.write("\" s=\"");
         bw.writeInt(xf);
@@ -701,7 +699,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      */
     protected void writeDecimal(BigDecimal bd, int row, int column, int xf) throws IOException {
         bw.write("<c r=\"");
-        bw.write(int2Col(column + 1));
+        bw.write(int2Col(columns[column].colIndex));
         bw.writeInt(row);
         bw.write("\" s=\"");
         bw.writeInt(xf);
@@ -739,7 +737,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      */
     protected void writeChar(char c, int row, int column, int xf) throws IOException {
         bw.write("<c r=\"");
-        bw.write(int2Col(column + 1));
+        bw.write(int2Col(columns[column].colIndex));
         bw.writeInt(row);
         bw.write("\" t=\"s\" s=\"");
         bw.writeInt(xf);
@@ -759,7 +757,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      */
     protected void writeNumeric(long l, int row, int column, int xf) throws IOException {
         bw.write("<c r=\"");
-        bw.write(int2Col(column + 1));
+        bw.write(int2Col(columns[column].colIndex));
         bw.writeInt(row);
         bw.write("\" s=\"");
         bw.writeInt(xf);
@@ -797,7 +795,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      */
     protected void writeBool(boolean bool, int row, int column, int xf) throws IOException {
         bw.write("<c r=\"");
-        bw.write(int2Col(column + 1));
+        bw.write(int2Col(columns[column].colIndex));
         bw.writeInt(row);
         bw.write("\" t=\"b\" s=\"");
         bw.writeInt(xf);
@@ -816,7 +814,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      */
     protected void writeNull(int row, int column, int xf) throws IOException {
         bw.write("<c r=\"");
-        bw.write(int2Col(column + 1));
+        bw.write(int2Col(columns[column].colIndex));
         bw.writeInt(row);
         bw.write("\" s=\"");
         bw.writeInt(xf);
@@ -910,7 +908,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
                 int fill = 11; // fill 11 space
                 buffer.put((byte) ':');
                 fill--;
-                char[] col = int2Col(columns.length);
+                char[] col = int2Col(columns[columns.length - 1].colIndex);
                 buffer.put((new String(col) + (rows + 1)).getBytes(StandardCharsets.US_ASCII));
                 fill -= col.length;
                 fill -= stringSize(rows + 1);
diff --git a/src/main/java/org/ttzero/excel/manager/docProps/App.java b/src/main/java/org/ttzero/excel/manager/docProps/App.java
index f6cf00bd..b4961851 100644
--- a/src/main/java/org/ttzero/excel/manager/docProps/App.java
+++ b/src/main/java/org/ttzero/excel/manager/docProps/App.java
@@ -117,15 +117,10 @@ public class App extends XmlEntity {
         } else {
             // Filter other character but number and `.`
             char[] chars = appVersion.toCharArray();
-            int i = 0, n = 0;
+            int i = 0;
             for (int j = 0; j < chars.length; j++) {
-                if (chars[j] >= '0' && chars[j] <= '9')
+                if (chars[j] >= '0' && chars[j] <= '9' || chars[j] == '.' && i > 0 && chars[i - 1] != '.')
                     chars[i++] = chars[j];
-                else if (chars[j] == '.' && i > 0 && chars[i - 1] != '.' && n < 2) {
-                    chars[i++] = chars[j];
-                    n++;
-                }
-                else break;
             }
             this.appVersion = i > 0 ? new String(chars, 0, chars[i - 1] != '.' ? i : i - 1) : "1.0.0";
         }
diff --git a/src/main/java/org/ttzero/excel/reader/Cell.java b/src/main/java/org/ttzero/excel/reader/Cell.java
index 6e7952a6..af4fe718 100644
--- a/src/main/java/org/ttzero/excel/reader/Cell.java
+++ b/src/main/java/org/ttzero/excel/reader/Cell.java
@@ -95,7 +95,7 @@ public class Cell {
      */
     public boolean f;
     /**
-     * y-axis of cell in row
+     * x-axis of cell in row
      */
     public transient short i;
 
diff --git a/src/main/java/org/ttzero/excel/reader/Drawings.java b/src/main/java/org/ttzero/excel/reader/Drawings.java
index ac97b11d..2079f1e9 100644
--- a/src/main/java/org/ttzero/excel/reader/Drawings.java
+++ b/src/main/java/org/ttzero/excel/reader/Drawings.java
@@ -45,7 +45,7 @@ public interface Drawings {
     default List<Picture> listPictures(final Sheet sheet) {
         List<Picture> pictures = listPictures();
         return pictures == null ? null :
-            pictures.stream().filter(p -> p.sheet.getId() == sheet.getId()).collect(Collectors.toList());
+            pictures.stream().filter(p -> p.sheet.getIndex() == sheet.getIndex()).collect(Collectors.toList());
     }
 
     class Picture {
diff --git a/src/main/java/org/ttzero/excel/reader/ExcelReader.java b/src/main/java/org/ttzero/excel/reader/ExcelReader.java
index 78337a74..db2eb029 100644
--- a/src/main/java/org/ttzero/excel/reader/ExcelReader.java
+++ b/src/main/java/org/ttzero/excel/reader/ExcelReader.java
@@ -393,12 +393,13 @@ public class ExcelReader implements Closeable {
     public ExcelReader parseFormula() {
         if (hasFormula) {
             // Formula string if exists
-            long[][] calcArray = parseCalcChain();
+            Path calcPath = self.resolve("xl/calcChain.xml");
+            long[][] calcArray = parseCalcChain(calcPath, sheets[sheets.length - 1].getIndex());
 
             if (calcArray == null) return this;
             int i = 0;
             for (int n; i < sheets.length; i++) {
-                n = sheets[i].getId();
+                n = sheets[i].getIndex();
                 if (calcArray[n - 1] == null) continue;
                 if (!(sheets[i] instanceof CalcSheet)) {
                     sheets[i] = sheets[i].asCalcSheet();
@@ -540,12 +541,11 @@ public class ExcelReader implements Closeable {
 
         List<Sheet> sheets = new ArrayList<>();
         Iterator<Element> sheetIter = root.element("sheets").elementIterator();
-        int index = 0;
         for (; sheetIter.hasNext(); ) {
             Element e = sheetIter.next();
             XMLSheet sheet = sheetFactory(option);
             sheet.setName(e.attributeValue("name"));
-            sheet.setId(Integer.parseInt(e.attributeValue("sheetId")));
+            sheet.setIndex(Integer.parseInt(e.attributeValue("sheetId")));
             String state = e.attributeValue("state");
             sheet.setHidden("hidden".equals(state));
             Relationship r = relManager.getById(e.attributeValue(QName.get("id", ns)));
@@ -561,7 +561,6 @@ public class ExcelReader implements Closeable {
             sheet.setStyles(styles);
             // Drawings
             sheet.setDrawings(drawings);
-            sheet.setIndex(index++);
             sheets.add(sheet);
         }
 
@@ -571,7 +570,7 @@ public class ExcelReader implements Closeable {
         }
 
         // sort by sheet index
-//        sheets.sort(Comparator.comparingInt(Sheet::getIndex));
+        sheets.sort(Comparator.comparingInt(Sheet::getIndex));
 
         Sheet[] sheets1 = new Sheet[sheets.size()];
         sheets.toArray(sheets1);
@@ -757,20 +756,18 @@ public class ExcelReader implements Closeable {
     }
 
     /* Parse `calcChain` */
-    private long[][] parseCalcChain() {
-        Path calcPath = self.resolve("xl/calcChain.xml");
-        if (!FileUtil.exists(calcPath)) return null;
+    private long[][] parseCalcChain(Path path, int n) {
         Element calcChain;
         try {
             SAXReader reader = new SAXReader();
-            calcChain = reader.read(Files.newInputStream(calcPath)).getRootElement();
+            calcChain = reader.read(Files.newInputStream(path)).getRootElement();
         } catch (DocumentException | IOException e) {
             LOGGER.warn("Part of `calcChain` has be damaged, It will be ignore all formulas.");
             return null;
         }
 
         Iterator<Element> ite = calcChain.elementIterator();
-        int i = 1, n = sheets().map(Sheet::getId).max(Integer::compareTo).orElse(0);
+        int i = 1;
         long[][] array = new long[n][];
         int[] indices = new int[n];
         for (; ite.hasNext(); ) {
diff --git a/src/main/java/org/ttzero/excel/reader/Sheet.java b/src/main/java/org/ttzero/excel/reader/Sheet.java
index 2e492fa9..d619d7b5 100644
--- a/src/main/java/org/ttzero/excel/reader/Sheet.java
+++ b/src/main/java/org/ttzero/excel/reader/Sheet.java
@@ -63,14 +63,6 @@ public interface Sheet extends Closeable {
      */
     int getIndex();
 
-    /**
-     * The worksheet id, it difference with index is that the id will not change
-     * because of moving or deleting worksheet.
-     *
-     * @return id of worksheet
-     */
-    int getId();
-
     /**
      * size of rows.
      *
diff --git a/src/main/java/org/ttzero/excel/reader/XMLSheet.java b/src/main/java/org/ttzero/excel/reader/XMLSheet.java
index 989fe007..7675d078 100644
--- a/src/main/java/org/ttzero/excel/reader/XMLSheet.java
+++ b/src/main/java/org/ttzero/excel/reader/XMLSheet.java
@@ -50,7 +50,6 @@ class XMLSheet implements Sheet {
     int index; // per sheet index of workbook
 //        , size = -1; // size of rows per sheet
     Path path;
-    int id;
     /**
      * The Shared String Table
      */
@@ -130,30 +129,6 @@ class XMLSheet implements Sheet {
         this.index = index;
     }
 
-    /**
-     * Returns the worksheet id
-     *
-     * @return int value
-     */
-    @Override
-    public int getId() {
-        return id;
-    }
-
-    /**
-     * Setting id of worksheet
-     *
-     * @param id the id of worksheet
-     */
-    void setId(int id) {
-        this.id = id;
-    }
-
-    /**
-     * Setting the {@link Drawings} info
-     *
-     * @param drawings resource info
-     */
     void setDrawings(Drawings drawings) {
         this.drawings = drawings;
     }
@@ -247,7 +222,7 @@ class XMLSheet implements Sheet {
 
     @Override
     public String toString() {
-        return "Sheet id: " + getId() + ", name: " + getName() + ", dimension: " + getDimension();
+        return "Sheet name: " + name + " has " + getDimension();
     }
 
     /////////////////////////////////Read sheet file/////////////////////////////////


Test patch:
diff --git a/src/test/java/org/ttzero/excel/annotation/HeaderStyleTest.java b/src/test/java/org/ttzero/excel/annotation/HeaderStyleTest.java
index 5c24dc23..2dc13792 100644
--- a/src/test/java/org/ttzero/excel/annotation/HeaderStyleTest.java
+++ b/src/test/java/org/ttzero/excel/annotation/HeaderStyleTest.java
@@ -14,26 +14,36 @@ import java.util.Collections;
 public class HeaderStyleTest extends WorkbookTest {
 
     @Test
-    public void originalTest() throws IOException {
+    public void testOriginal() throws IOException {
         Head itemFull = new Head();
         new Workbook("customize_header_style_original").setAutoSize(true).addSheet(new ListSheet<>(Collections.singletonList(itemFull))).writeTo(defaultTestPath);
 
     }
 
     @Test
-    public void fillBgColorTest() throws IOException {
+    public void testFillBgColor() throws IOException {
         Head1 itemFull = new Head1();
         new Workbook("customize_header_style_bgc").setAutoSize(true).addSheet(new ListSheet<>(Collections.singletonList(itemFull))).writeTo(defaultTestPath);
 
     }
 
     @Test
-    public void fontColorTest() throws IOException {
+    public void testFontColor() throws IOException {
         Head2 itemFull = new Head2();
         new Workbook("customize_header_style_fc").setAutoSize(true).addSheet(new ListSheet<>(Collections.singletonList(itemFull))).writeTo(defaultTestPath);
 
     }
 
+    @Test public void testAnnoOnClassTest() throws IOException {
+        Head3 itemFull = new Head3();
+        new Workbook("annotation on class").setAutoSize(true).addSheet(new ListSheet<>(Collections.singletonList(itemFull))).writeTo(defaultTestPath);
+    }
+
+    @Test public void testAnnoOnClassAndMethodTest() throws IOException {
+        Head4 itemFull = new Head4();
+        new Workbook("annotation on class and method").setAutoSize(true).addSheet(new ListSheet<>(Collections.singletonList(itemFull))).writeTo(defaultTestPath);
+    }
+
     private static class Head {
         @ExcelColumn
         private String column1;
@@ -49,6 +59,25 @@ public class HeaderStyleTest extends WorkbookTest {
         @ExcelColumn(value = "错误信息")
         private String errorMsg;
 
+        public String getColumn1() {
+            return column1;
+        }
+
+        public String getColumn2() {
+            return column2;
+        }
+
+        public String getColumn3() {
+            return column3;
+        }
+
+        public String getCode() {
+            return code;
+        }
+
+        public String getErrorMsg() {
+            return errorMsg;
+        }
     }
 
     private static class Head1 extends Head {
@@ -78,4 +107,16 @@ public class HeaderStyleTest extends WorkbookTest {
         @HeaderStyle(fillFgColor = "#cccccc")
         private String column5;
     }
+
+    @HeaderStyle(fontColor = "red", fillFgColor = "#ffff00")
+    private static class Head3 extends Head { }
+
+    @HeaderStyle(fontColor = "red", fillFgColor = "#ffff00")
+    private static class Head4 extends Head {
+        @Override
+        @HeaderStyle(fontColor = "blue")
+        public String getErrorMsg() {
+            return super.getErrorMsg();
+        }
+    }
 }
diff --git a/src/test/java/org/ttzero/excel/entity/CustomerNumFmtTest.java b/src/test/java/org/ttzero/excel/entity/CustomerNumFmtTest.java
index 3bc7ca92..44055bcd 100644
--- a/src/test/java/org/ttzero/excel/entity/CustomerNumFmtTest.java
+++ b/src/test/java/org/ttzero/excel/entity/CustomerNumFmtTest.java
@@ -21,7 +21,7 @@ public class CustomerNumFmtTest extends WorkbookTest {
     }
 
     @Test public void testDateFmt() throws IOException {
-        new Workbook("customize_numfmt_full")
+        new Workbook("customize_data_format")
                 .setAutoSize(true)
                 .addSheet(new ListSheet<>(ItemFull.randomFull()
                 , new Sheet.Column("编码", "code")
diff --git a/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java b/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
index f9c8e115..b74de2ee 100644
--- a/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
@@ -304,6 +304,21 @@ public class ListMapSheetTest extends WorkbookTest {
                 .writeTo(defaultTestPath);
     }
 
+    @Test public void testOverLargeOrderColumn() throws IOException {
+        try {
+            new Workbook("test list map sheet Constructor8", author)
+                    .watch(Print::println)
+                    .setAutoSize(true)
+                    .addSheet(new ListMapSheet("MAP", createTestData(10)
+                            , new Sheet.Column("ID", "id", int.class).setColIndex(9999999)
+                            , new Sheet.Column("NAME", "name", String.class)))
+                    .writeTo(defaultTestPath);
+            assert false;
+        } catch (TooManyColumnsException e) {
+            assert true;
+        }
+    }
+
     public static List<Map<String, ?>> createTestData() {
         int size = random.nextInt(100) + 1;
         return createTestData(size);
diff --git a/src/test/java/org/ttzero/excel/entity/ListObjectPagingTest.java b/src/test/java/org/ttzero/excel/entity/ListObjectPagingTest.java
index 4587ac34..0b06adf8 100644
--- a/src/test/java/org/ttzero/excel/entity/ListObjectPagingTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListObjectPagingTest.java
@@ -32,7 +32,7 @@ public class ListObjectPagingTest extends WorkbookTest {
     public void testPaging() throws IOException {
         new Workbook("test paging", author)
             .watch(Print::println)
-            .addSheet(ListObjectSheetTest.Item.randomTestData(202))
+            .addSheet(ListObjectSheetTest.Item.randomTestData(1024))
             .setWorkbookWriter(new ReLimitXMLWorkbookWriter())
             .writeTo(defaultTestPath);
     }
@@ -81,4 +81,20 @@ public class ListObjectPagingTest extends WorkbookTest {
             .setWorkbookWriter(new ReLimitXMLWorkbookWriter())
             .writeTo(defaultTestPath);
     }
+
+    @Test public void testOrderPaging() throws IOException {
+        new Workbook("test fracture order paging", author)
+                .watch(Print::println)
+                .addSheet(ListObjectSheetTest.FractureOrderEntry.randomTestData(1024))
+                .setWorkbookWriter(new ReLimitXMLWorkbookWriter())
+                .writeTo(defaultTestPath);
+    }
+
+    @Test public void testLargeOrderPaging() throws IOException {
+        new Workbook("test large order paging", author)
+                .watch(Print::println)
+                .addSheet(ListObjectSheetTest.LargeOrderEntry.randomTestData(1024))
+                .setWorkbookWriter(new ReLimitXMLWorkbookWriter())
+                .writeTo(defaultTestPath);
+    }
 }
diff --git a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
index 3bc9fa86..81703077 100644
--- a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
@@ -24,6 +24,7 @@ import org.ttzero.excel.entity.style.Fill;
 import org.ttzero.excel.entity.style.Font;
 import org.ttzero.excel.entity.style.PatternType;
 import org.ttzero.excel.entity.style.Styles;
+import org.ttzero.excel.manager.Const;
 import org.ttzero.excel.manager.docProps.Core;
 import org.ttzero.excel.processor.IntConversionProcessor;
 import org.ttzero.excel.processor.StyleProcessor;
@@ -360,18 +361,14 @@ public class ListObjectSheetTest extends WorkbookTest {
     }
 
     @Test public void testFieldUnDeclare() throws IOException {
-        try {
-            new Workbook("field un-declare", author)
-                .addSheet(new ListSheet<>("期末成绩", Student.randomTestData()
-                        , new Sheet.Column("学号", "id")
-                        , new Sheet.Column("姓名", "name")
-                        , new Sheet.Column("成绩", "sore") // un-declare field
-                    )
+        new Workbook("field un-declare", author)
+            .addSheet(new ListSheet<>("期末成绩", Student.randomTestData()
+                    , new Sheet.Column("学号", "id")
+                    , new Sheet.Column("姓名", "name")
+                    , new Sheet.Column("成绩", "sore") // un-declare field
                 )
-                .writeTo(defaultTestPath);
-        } catch (ExcelWriteException e) {
-            assert true;
-        }
+            )
+            .writeTo(defaultTestPath);
     }
 
     @Test public void testResetMethod() throws IOException {
@@ -466,7 +463,7 @@ public class ListObjectSheetTest extends WorkbookTest {
         core.setDescription("为了艾尔");
         core.setKeywords("机枪兵;光头");
         core.setVersion("1.0");
-        core.setRevision("1.2");
+//        core.setRevision("1.2");
         core.setLastModifiedBy("TTT");
         new Workbook("Specify Core")
             .setCore(core)
@@ -610,7 +607,41 @@ public class ListObjectSheetTest extends WorkbookTest {
 
         workbook.writeTo(defaultTestPath);
     }
-    
+
+    @Test public void testOrderColumn() throws IOException {
+        new Workbook(("Order column")).addSheet(new ListSheet<>(OrderEntry.randomTestData())).writeTo(defaultTestPath);
+    }
+
+    @Test public void testSameOrderColumn() throws IOException {
+        new Workbook(("Same order column")).addSheet(new ListSheet<>(SameOrderEntry.randomTestData())).writeTo(defaultTestPath);
+    }
+
+    @Test public void testFractureOrderColumn() throws IOException {
+        new Workbook(("Fracture order column")).addSheet(new ListSheet<>(FractureOrderEntry.randomTestData())).writeTo(defaultTestPath);
+    }
+
+    @Test public void testLargeOrderColumn() throws IOException {
+        new Workbook(("Large order column")).addSheet(new ListSheet<>(LargeOrderEntry.randomTestData())).writeTo(defaultTestPath);
+    }
+
+    @Test public void testOverLargeOrderColumn() throws IOException {
+        try {
+            new Workbook(("Over Large order column")).addSheet(new ListSheet<>(OverLargeOrderEntry.randomTestData())).writeTo(defaultTestPath);
+            assert false;
+        } catch (TooManyColumnsException e) {
+            assert true;
+        }
+    }
+
+    @Test public void testOrderColumnSpecifyOnColumn() throws IOException {
+        new Workbook("Order column 2")
+            .addSheet(new ListSheet<>("期末成绩", Student.randomTestData()
+                , new Sheet.Column("学号", "id").setColIndex(3)
+                , new Sheet.Column("姓名", "name")
+                , new Sheet.Column("成绩", "sore").setColIndex(5) // un-declare field
+            )).writeTo(defaultTestPath);
+    }
+
     public static class Item {
         @ExcelColumn
         private int id;
@@ -1226,4 +1257,215 @@ public class ListObjectSheetTest extends WorkbookTest {
             return randomTestData(n);
         }
     }
+
+    public static class OrderEntry {
+        @ExcelColumn(colIndex = 0)
+        private String s;
+        @ExcelColumn( colIndex = 1)
+        private Date date;
+        @ExcelColumn(colIndex = 2)
+        private Double d;
+        @ExcelColumn(colIndex = 3)
+        private String s2 = "a";
+        @ExcelColumn(colIndex = 4)
+        private String s3 = "b";
+        @ExcelColumn(colIndex = 5)
+        private String s4 = "c";
+
+        public OrderEntry() {}
+        public OrderEntry(String s, Date date, Double d) {
+            this.s = s;
+            this.date = date;
+            this.d = d;
+        }
+
+        public static List<OrderEntry> randomTestData(int n) {
+            List<OrderEntry> list = new ArrayList<>(n);
+            for (int i = 0; i < n; i++) {
+                list.add(new OrderEntry(getRandomString(), new Timestamp(System.currentTimeMillis() - random.nextInt(9999999)), (double) i));
+            }
+            return list;
+        }
+
+        public static List<OrderEntry> randomTestData() {
+            int n = random.nextInt(100) + 1;
+            return randomTestData(n);
+        }
+
+        public String getS() {
+            return s;
+        }
+
+        public Date getDate() {
+            return date;
+        }
+
+        public Double getD() {
+            return d;
+        }
+
+        public String getS2() {
+            return s2;
+        }
+
+        public String getS3() {
+            return s3;
+        }
+
+        public String getS4() {
+            return s4;
+        }
+    }
+
+    public static class SameOrderEntry extends OrderEntry {
+        public SameOrderEntry() {}
+        public SameOrderEntry(String s, Date date, Double d) {
+            super(s, date, d);
+        }
+
+        @Override
+        @ExcelColumn(colIndex = 5)
+        public Double getD() {
+            return super.getD();
+        }
+
+        @Override
+        @ExcelColumn(colIndex = 5)
+        public String getS2() {
+            return super.getS2();
+        }
+
+        public static List<OrderEntry> randomTestData(int n) {
+            List<OrderEntry> list = new ArrayList<>(n);
+            for (int i = 0; i < n; i++) {
+                list.add(new SameOrderEntry(getRandomString(), new Timestamp(System.currentTimeMillis() - random.nextInt(9999999)), (double) i));
+            }
+            return list;
+        }
+
+        public static List<OrderEntry> randomTestData() {
+            int n = random.nextInt(100) + 1;
+            return randomTestData(n);
+        }
+    }
+
+    public static class FractureOrderEntry extends OrderEntry {
+        public FractureOrderEntry() {}
+        public FractureOrderEntry(String s, Date date, Double d) {
+            super(s, date, d);
+        }
+
+        @Override
+        @ExcelColumn
+        public String getS() {
+            return super.getS();
+        }
+
+        @Override
+        @ExcelColumn
+        public Date getDate() {
+            return super.getDate();
+        }
+
+        @Override
+        @ExcelColumn(colIndex = 2)
+        public Double getD() {
+            return super.getD();
+        }
+
+        @Override
+        @ExcelColumn(colIndex = 0)
+        public String getS2() {
+            return super.getS2();
+        }
+
+        @Override
+        @ExcelColumn
+        public String getS3() {
+            return super.getS3();
+        }
+
+        @Override
+        @ExcelColumn(colIndex = 4)
+        public String getS4() {
+            return super.getS4();
+        }
+
+        public static List<OrderEntry> randomTestData(int n) {
+            List<OrderEntry> list = new ArrayList<>(n);
+            for (int i = 0; i < n; i++) {
+                list.add(new FractureOrderEntry(getRandomString(), new Timestamp(System.currentTimeMillis() - random.nextInt(9999999)), (double) i));
+            }
+            return list;
+        }
+
+        public static List<OrderEntry> randomTestData() {
+            int n = random.nextInt(100) + 1;
+            return randomTestData(n);
+        }
+    }
+
+    public static class LargeOrderEntry extends OrderEntry {
+        public LargeOrderEntry() {}
+        public LargeOrderEntry(String s, Date date, Double d) {
+            super(s, date, d);
+        }
+
+        @Override
+        @ExcelColumn(colIndex = 1)
+        public String getS() {
+            return super.getS();
+        }
+
+        @Override
+        @ExcelColumn(colIndex = Const.Limit.MAX_COLUMNS_ON_SHEET - 1)
+        public Date getDate() {
+            return super.getDate();
+        }
+
+        @Override
+        @ExcelColumn(colIndex = 189)
+        public String getS2() {
+            return super.getS2();
+        }
+
+        public static List<OrderEntry> randomTestData(int n) {
+            List<OrderEntry> list = new ArrayList<>(n);
+            for (int i = 0; i < n; i++) {
+                list.add(new LargeOrderEntry(getRandomString(), new Timestamp(System.currentTimeMillis() - random.nextInt(9999999)), (double) i));
+            }
+            return list;
+        }
+
+        public static List<OrderEntry> randomTestData() {
+            int n = random.nextInt(100) + 1;
+            return randomTestData(n);
+        }
+    }
+
+    public static class OverLargeOrderEntry extends OrderEntry {
+        public OverLargeOrderEntry() {}
+        public OverLargeOrderEntry(String s, Date date, Double d) {
+            super(s, date, d);
+        }
+
+        @Override
+        @ExcelColumn(colIndex = 16_384)
+        public String getS() {
+            return super.getS();
+        }
+
+        public static List<OrderEntry> randomTestData(int n) {
+            List<OrderEntry> list = new ArrayList<>(n);
+            for (int i = 0; i < n; i++) {
+                list.add(new OverLargeOrderEntry(getRandomString(), new Timestamp(System.currentTimeMillis() - random.nextInt(9999999)), (double) i));
+            }
+            return list;
+        }
+
+        public static List<OrderEntry> randomTestData() {
+            int n = random.nextInt(100) + 1;
+            return randomTestData(n);
+        }
+    }
 }
diff --git a/src/test/java/org/ttzero/excel/entity/csv/ListObjectSheetTest.java b/src/test/java/org/ttzero/excel/entity/csv/ListObjectSheetTest.java
index 05a99cfb..d0f936dd 100644
--- a/src/test/java/org/ttzero/excel/entity/csv/ListObjectSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/csv/ListObjectSheetTest.java
@@ -23,6 +23,7 @@ import org.ttzero.excel.entity.CustomizeDataSourceSheet;
 import org.ttzero.excel.entity.ExcelWriteException;
 import org.ttzero.excel.entity.ListSheet;
 import org.ttzero.excel.entity.Sheet;
+import org.ttzero.excel.entity.TooManyColumnsException;
 import org.ttzero.excel.entity.Workbook;
 import org.ttzero.excel.entity.WorkbookTest;
 import org.ttzero.excel.entity.style.Fill;
@@ -311,4 +312,43 @@ public class ListObjectSheetTest extends WorkbookTest{
                 .saveAsCSV()
                 .writeTo(getOutputTestPath());
     }
+
+    @Test public void testOrderColumn() throws IOException {
+        new Workbook(("Order column"))
+                .addSheet(new ListSheet<>(org.ttzero.excel.entity.ListObjectSheetTest.OrderEntry.randomTestData()))
+                .saveAsCSV()
+                .writeTo(defaultTestPath);
+    }
+
+    @Test public void testSameOrderColumn() throws IOException {
+        new Workbook(("Same order column"))
+                .addSheet(new ListSheet<>(org.ttzero.excel.entity.ListObjectSheetTest.SameOrderEntry.randomTestData()))
+                .saveAsCSV()
+                .writeTo(defaultTestPath);
+    }
+
+    @Test public void testFractureOrderColumn() throws IOException {
+        new Workbook(("Fracture order column"))
+                .addSheet(new ListSheet<>(org.ttzero.excel.entity.ListObjectSheetTest.FractureOrderEntry.randomTestData()))
+                .saveAsCSV()
+                .writeTo(defaultTestPath);
+    }
+
+    @Test public void testLargeOrderColumn() throws IOException {
+        new Workbook(("Large order column"))
+                .addSheet(new ListSheet<>(org.ttzero.excel.entity.ListObjectSheetTest.LargeOrderEntry.randomTestData()))
+                .saveAsCSV()
+                .writeTo(defaultTestPath);
+    }
+
+    @Test public void testOverLargeOrderColumn() throws IOException {
+        try {
+            new Workbook(("Over Large order column"))
+                    .addSheet(new ListSheet<>(org.ttzero.excel.entity.ListObjectSheetTest.OverLargeOrderEntry.randomTestData()))
+                    .saveAsCSV()
+                    .writeTo(defaultTestPath);
+        } catch (TooManyColumnsException e) {
+            assert true;
+        }
+    }
 }