]> source.dussan.org Git - poi.git/commitdiff
[bug-65562] derive sheet dimensions when outputting SXSSFSheets
authorPJ Fanning <fanningpj@apache.org>
Tue, 26 Jul 2022 21:42:24 +0000 (21:42 +0000)
committerPJ Fanning <fanningpj@apache.org>
Tue, 26 Jul 2022 21:42:24 +0000 (21:42 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1903037 13f79535-47bb-0310-9956-ffa450edef68

poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFRow.java
poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFSheet.java
poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java
poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFWorkbook.java

index ff86b1ef30a6082378c95280934ac9165ff04c02..fb9a8846a142259d38d05828c9dde4e94ab693b1 100644 (file)
@@ -141,6 +141,7 @@ public class SXSSFRow implements Row, Comparable<SXSSFRow>
         checkBounds(column);
         SXSSFCell cell = new SXSSFCell(this, type);
         _cells.put(column, cell);
+        _sheet.trackNewCell(cell);
         return cell;
     }
 
index b5ad3dfb661e6b68f2962b8793151ef0d6a5605a..4fa4138b3f89a9ee440fccb8fe551724f4120478 100644 (file)
@@ -27,6 +27,8 @@ import java.util.Set;
 import java.util.Spliterator;
 import java.util.TreeMap;
 
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 import org.apache.poi.ss.SpreadsheetVersion;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellAddress;
@@ -47,6 +49,8 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
  * Streaming version of XSSFSheet implementing the "BigGridDemo" strategy.
  */
 public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
+    private static final Logger LOG = LogManager.getLogger(SXSSFSheet.class);
+
     /*package*/ final XSSFSheet _sh;
     protected final SXSSFWorkbook _workbook;
     private final TreeMap<Integer,SXSSFRow> _rows = new TreeMap<>();
@@ -56,14 +60,38 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
     private int outlineLevelRow;
     private int lastFlushedRowNumber = -1;
     private boolean allFlushed;
+    private int leftMostColumn = SpreadsheetVersion.EXCEL2007.getLastColumnIndex();
+    private int rightMostColumn;
 
     protected SXSSFSheet(SXSSFWorkbook workbook, XSSFSheet xSheet, int randomAccessWindowSize) {
         _workbook = workbook;
         _sh = xSheet;
+        calculateLeftAndRightMostColumns(xSheet);
         setRandomAccessWindowSize(randomAccessWindowSize);
         _autoSizeColumnTracker = new AutoSizeColumnTracker(this);
     }
 
+    private void calculateLeftAndRightMostColumns(XSSFSheet xssfSheet) {
+        if (_workbook.shouldCalculateSheetDimensions()) {
+            int rowCount = 0;
+            int leftMostColumn = Integer.MAX_VALUE;
+            int rightMostColumn = 0;
+            for (Row row : xssfSheet) {
+                rowCount++;
+                if (row.getFirstCellNum() < leftMostColumn) {
+                    final int first = row.getFirstCellNum();
+                    final int last = row.getLastCellNum() - 1;
+                    leftMostColumn = Math.min(first, leftMostColumn);
+                    rightMostColumn = Math.max(last, rightMostColumn);
+                }
+            }
+            if (rowCount > 0) {
+                this.leftMostColumn = leftMostColumn;
+                this.rightMostColumn = rightMostColumn;
+            }
+        }
+    }
+
     public SXSSFSheet(SXSSFWorkbook workbook, XSSFSheet xSheet) throws IOException {
         _workbook = workbook;
         _sh = xSheet;
@@ -2106,4 +2134,21 @@ public class SXSSFSheet implements Sheet, OoxmlSheetExtensions {
     public void shiftColumns(int startColumn, int endColumn, int n){
         throw new UnsupportedOperationException("Not Implemented");
     }
+
+    void trackNewCell(SXSSFCell cell) {
+        leftMostColumn = Math.min(cell.getColumnIndex(), leftMostColumn);
+        rightMostColumn = Math.max(cell.getColumnIndex(), rightMostColumn);
+    }
+
+    void deriveDimension() {
+        if (_workbook.shouldCalculateSheetDimensions()) {
+            try {
+                CellRangeAddress cellRangeAddress = new CellRangeAddress(
+                        getFirstRowNum(), getLastRowNum(), leftMostColumn, rightMostColumn);
+                _sh.setDimensionOverride(cellRangeAddress);
+            } catch (Exception e) {
+                LOG.atDebug().log("Failed to set dimension details on sheet", e);
+            }
+        }
+    }
 }
index bc93ebd5fdb680f9dc1fa3473e34c4d3e293fa78..54457e86ffc8c580a151360e6e0cc3e3216af25c 100644 (file)
@@ -136,6 +136,8 @@ public class SXSSFWorkbook implements Workbook {
      */
     protected Zip64Mode zip64Mode = Zip64Mode.Always;
 
+    private boolean shouldCalculateSheetDimensions = true;
+
     /**
      * Construct a new workbook with default row window size
      */
@@ -351,6 +353,24 @@ public class SXSSFWorkbook implements Workbook {
         _compressTmpFiles = compress;
     }
 
+    /**
+     * @param shouldCalculateSheetDimensions defaults to <code>true</code>, set to <code>false</code> if
+     *                                       the calculated dimensions are causing trouble
+     * @since POI 5.2.3
+     */
+    public void setShouldCalculateSheetDimensions(boolean shouldCalculateSheetDimensions) {
+        this.shouldCalculateSheetDimensions = shouldCalculateSheetDimensions;
+    }
+
+    /**
+     * @return shouldCalculateSheetDimensions defaults to <code>true</code>, set to <code>false</code> if
+     * the calculated dimensions are causing trouble
+     * @since POI 5.2.3
+     */
+    public boolean shouldCalculateSheetDimensions() {
+        return shouldCalculateSheetDimensions;
+    }
+
     @Internal
     protected SharedStringsTable getSharedStringSource() {
         return _sharedStringSource;
@@ -971,8 +991,10 @@ public class SXSSFWorkbook implements Workbook {
             }
 
             //Substitute the template entries with the generated sheet data files
-            try (ZipSecureFile zf = new ZipSecureFile(tmplFile);
-                 ZipFileZipEntrySource source = new ZipFileZipEntrySource(zf)) {
+            try (
+                    ZipSecureFile zf = new ZipSecureFile(tmplFile);
+                    ZipFileZipEntrySource source = new ZipFileZipEntrySource(zf)
+            ) {
                 injectData(source, stream);
             }
         } finally {
@@ -1012,8 +1034,8 @@ public class SXSSFWorkbook implements Workbook {
     }
 
     protected void flushSheets() throws IOException {
-        for (SXSSFSheet sheet : _xFromSxHash.values())
-        {
+        for (SXSSFSheet sheet : _xFromSxHash.values()) {
+            sheet.deriveDimension();
             sheet.flushRows();
         }
     }
index deed5b68d2e589e00613cae2faa9a99c6be7b0ad..83aa926529f85ba96b9799c92c4dcfc5216f3302 100644 (file)
@@ -106,6 +106,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet, OoxmlSheetEx
     private List<CellRangeAddress> arrayFormulas;
     private final XSSFDataValidationHelper dataValidationHelper;
     private XSSFVMLDrawing xssfvmlDrawing;
+    private CellRangeAddress dimensionOverride;
 
     /**
      * Creates new XSSFSheet   - called by XSSFWorkbook to create a sheet from scratch.
@@ -3747,29 +3748,34 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet, OoxmlSheetEx
             }*/
         }
 
-        int minCell = Integer.MAX_VALUE, maxCell = Integer.MIN_VALUE;
-        for(Map.Entry<Integer, XSSFRow> entry : _rows.entrySet()) {
-            XSSFRow row = entry.getValue();
+        CellRangeAddress cellRangeAddress = dimensionOverride;
+        if (cellRangeAddress == null) {
+            int minCell = Integer.MAX_VALUE, maxCell = Integer.MIN_VALUE;
+            for(Map.Entry<Integer, XSSFRow> entry : _rows.entrySet()) {
+                XSSFRow row = entry.getValue();
 
-            // first perform the normal write actions for the row
-            row.onDocumentWrite();
+                // first perform the normal write actions for the row
+                row.onDocumentWrite();
 
-            // then calculate min/max cell-numbers for the worksheet-dimension
-            if(row.getFirstCellNum() != -1) {
-                minCell = Math.min(minCell, row.getFirstCellNum());
+                // then calculate min/max cell-numbers for the worksheet-dimension
+                if(row.getFirstCellNum() != -1) {
+                    minCell = Math.min(minCell, row.getFirstCellNum());
+                }
+                if(row.getLastCellNum() != -1) {
+                    maxCell = Math.max(maxCell, row.getLastCellNum()-1);
+                }
             }
-            if(row.getLastCellNum() != -1) {
-                maxCell = Math.max(maxCell, row.getLastCellNum()-1);
+
+            // finally, if we had at least one cell we can populate the optional dimension-field
+            if(minCell != Integer.MAX_VALUE) {
+                cellRangeAddress = new CellRangeAddress(getFirstRowNum(), getLastRowNum(), minCell, maxCell);
             }
         }
-
-        // finally, if we had at least one cell we can populate the optional dimension-field
-        if(minCell != Integer.MAX_VALUE) {
-            String ref = new CellRangeAddress(getFirstRowNum(), getLastRowNum(), minCell, maxCell).formatAsString();
-            if(worksheet.isSetDimension()) {
-                worksheet.getDimension().setRef(ref);
+        if (cellRangeAddress != null) {
+            if (worksheet.isSetDimension()) {
+                worksheet.getDimension().setRef(cellRangeAddress.formatAsString());
             } else {
-                worksheet.addNewDimension().setRef(ref);
+                worksheet.addNewDimension().setRef(cellRangeAddress.formatAsString());
             }
         }
 
@@ -4051,6 +4057,9 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet, OoxmlSheetEx
      * @since POI 5.2.3
      */
     public CellRangeAddress getDimension() {
+        if (dimensionOverride != null) {
+            return dimensionOverride;
+        }
         CTSheetDimension ctSheetDimension = worksheet.getDimension();
         String ref = ctSheetDimension == null ? null : ctSheetDimension.getRef();
         if (ref != null) {
@@ -4845,4 +4854,14 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet, OoxmlSheetEx
     public XSSFHeaderFooterProperties getHeaderFooterProperties() {
         return new XSSFHeaderFooterProperties(getSheetTypeHeaderFooter());
     }
+
+    /**
+     * Currently, this is for internal use. Overrides the default dimensions of the sheet.
+     * @param dimension {@link CellRangeAddress}, <code>null</code> removes the existing override
+     * @since POI 5.2.3
+     */
+    @Beta
+    public void setDimensionOverride(CellRangeAddress dimension) {
+        this.dimensionOverride = dimension;
+    }
 }
index 37b17406238bcb9fba617c7fd6f9cfa38bc38b51..16449a9f8cdf74b2440cdec353f04aa89163f5e9 100644 (file)
@@ -48,6 +48,7 @@ import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.CellReference;
 import org.apache.poi.xssf.SXSSFITestDataProvider;
 import org.apache.poi.xssf.XSSFTestDataSamples;
@@ -560,6 +561,45 @@ public final class TestSXSSFWorkbook extends BaseTestXWorkbook {
         }
     }
 
+    @Test
+    void addDimension() throws IOException {
+        try (
+                SXSSFWorkbook wb = new SXSSFWorkbook();
+                UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()
+        ) {
+            SXSSFSheet sheet = wb.createSheet();
+            sheet.createRow(2).createCell(3).setCellValue("top left");
+            sheet.createRow(6).createCell(5).setCellValue("bottom right");
+            assertEquals(2, sheet.getFirstRowNum());
+            assertEquals(6, sheet.getLastRowNum());
+            wb.write(bos);
+            try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+                XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
+                assertEquals(CellRangeAddress.valueOf("D3:F7"), xssfSheet.getDimension());
+            }
+        }
+    }
+
+    @Test
+    void addDimensionDisabled() throws IOException {
+        try (
+                SXSSFWorkbook wb = new SXSSFWorkbook();
+                UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream()
+        ) {
+            wb.setShouldCalculateSheetDimensions(false);
+            SXSSFSheet sheet = wb.createSheet();
+            sheet.createRow(2).createCell(3).setCellValue("top left");
+            sheet.createRow(6).createCell(5).setCellValue("bottom right");
+            assertEquals(2, sheet.getFirstRowNum());
+            assertEquals(6, sheet.getLastRowNum());
+            wb.write(bos);
+            try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bos.toInputStream())) {
+                XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0);
+                assertEquals(CellRangeAddress.valueOf("A1:A1"), xssfSheet.getDimension());
+            }
+        }
+    }
+
     @Override
     @Disabled("not implemented")
     protected void changeSheetNameWithSharedFormulas() {