]> source.dussan.org Git - poi.git/commitdiff
memory usage optimization in XSSF - avoid creating parentless xml beans
authorYegor Kozlov <yegor@apache.org>
Mon, 30 Nov 2009 14:09:03 +0000 (14:09 +0000)
committerYegor Kozlov <yegor@apache.org>
Mon, 30 Nov 2009 14:09:03 +0000 (14:09 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@885429 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/status.xml
src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTable.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/RichTextStringHelper.java [deleted file]
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java
src/ooxml/testcases/org/apache/poi/xssf/util/MemoryUsage.java [new file with mode: 0755]

index 9109c2b37e7a188cb9a3c0f1734a1713780a36ad..26d63412425849e5e208786465462b7a3422bc50 100644 (file)
@@ -34,6 +34,7 @@
 
     <changes>
         <release version="3.6-beta1" date="2009-??-??">
+          <action dev="POI-DEVELOPERS" type="add"> memory usage optimization in XSSF - avoid creating parentless xml beans</action> 
           <action dev="POI-DEVELOPERS" type="fix">47188 - avoid corruption of workbook when adding cell comments </action> 
           <action dev="POI-DEVELOPERS" type="fix">48106 - improved work with cell comments in XSSF</action> 
           <action dev="POI-DEVELOPERS" type="add">Add support for creating SummaryInformation and DocumentSummaryInformation properties 
index 8287df55b4271ec7689197e59494d8ea548d3462..ac648c1423f68aafd907f5b1c05d0fef01c4591d 100644 (file)
@@ -84,8 +84,12 @@ public class SharedStringsTable extends POIXMLDocumentPart {
      */
     private int uniqueCount;
 
+    private SstDocument _sstDoc;
+
     public SharedStringsTable() {
         super();
+        _sstDoc = SstDocument.Factory.newInstance();
+        _sstDoc.addNewSst();
     }
 
     public SharedStringsTable(PackagePart part, PackageRelationship rel) throws IOException {
@@ -102,7 +106,8 @@ public class SharedStringsTable extends POIXMLDocumentPart {
     public void readFrom(InputStream is) throws IOException {
         try {
             int cnt = 0;
-            CTSst sst = SstDocument.Factory.parse(is).getSst();
+            _sstDoc = SstDocument.Factory.parse(is);
+            CTSst sst = _sstDoc.getSst();
             count = (int)sst.getCount();
             uniqueCount = (int)sst.getUniqueCount();
             for (CTRst st : sst.getSiArray()) {
@@ -163,10 +168,14 @@ public class SharedStringsTable extends POIXMLDocumentPart {
         if (stmap.containsKey(s)) {
             return stmap.get(s);
         }
+
         uniqueCount++;
+        //create a CTRst bean attached to this SstDocument and copy the argument CTRst into it
+        CTRst newSt = _sstDoc.getSst().addNewSi();
+        newSt.set(st);
         int idx = strings.size();
         stmap.put(s, idx);
-        strings.add(st);
+        strings.add(newSt);
         return idx;
     }
     /**
@@ -188,14 +197,11 @@ public class SharedStringsTable extends POIXMLDocumentPart {
         XmlOptions options = new XmlOptions(DEFAULT_XML_OPTIONS);
 
         //re-create the sst table every time saving a workbook
-        SstDocument doc = SstDocument.Factory.newInstance();
-        CTSst sst = doc.addNewSst();
+        CTSst sst = _sstDoc.getSst();
         sst.setCount(count);
         sst.setUniqueCount(uniqueCount);
 
-        CTRst[] ctr = strings.toArray(new CTRst[strings.size()]);
-        sst.setSiArray(ctr);
-        doc.save(out, options);
+        _sstDoc.save(out, options);
     }
 
     @Override
index d81a6924081dcc715b8e82b4bbd6d7b8000b5c63..91bb3e4c8d5f145bb79965a85d5ddfc9412b4870 100644 (file)
@@ -35,7 +35,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow;
  * High level representation of a row of a spreadsheet.
  */
 public class XSSFRow implements Row, Comparable<XSSFRow> {
-    private static final POILogger logger = POILogFactory.getLogger(XSSFRow.class);
+    private static final POILogger _logger = POILogFactory.getLogger(XSSFRow.class);
 
     /**
      * the xml bean containing all cell definitions for this row
@@ -46,7 +46,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> {
      * Cells of this row keyed by their column indexes.
      * The TreeMap ensures that the cells are ordered by columnIndex in the ascending order.
      */
-    private final TreeMap<Integer, Cell> _cells;
+    private final TreeMap<Integer, XSSFCell> _cells;
 
     /**
      * the parent sheet
@@ -62,7 +62,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> {
     protected XSSFRow(CTRow row, XSSFSheet sheet) {
         _row = row;
         _sheet = sheet;
-        _cells = new TreeMap<Integer, Cell>();
+        _cells = new TreeMap<Integer, XSSFCell>();
         for (CTCell c : row.getCArray()) {
             XSSFCell cell = new XSSFCell(this, c);
             _cells.put(cell.getColumnIndex(), cell);
@@ -91,7 +91,7 @@ public class XSSFRow implements Row, Comparable<XSSFRow> {
      * @return an iterator over cells in this row.
      */
     public Iterator<Cell> cellIterator() {
-        return _cells.values().iterator();
+        return (Iterator<Cell>)(Iterator<? extends Cell>)_cells.values().iterator();
     }
 
     /**
@@ -160,8 +160,15 @@ public class XSSFRow implements Row, Comparable<XSSFRow> {
      * @see Cell#CELL_TYPE_STRING
      */
     public XSSFCell createCell(int columnIndex, int type) {
-        CTCell ctcell = CTCell.Factory.newInstance();
-        XSSFCell xcell = new XSSFCell(this, ctcell);
+        CTCell ctCell;
+        XSSFCell prev = _cells.get(columnIndex);
+        if(prev != null){
+            ctCell = prev.getCTCell();
+            ctCell.set(CTCell.Factory.newInstance());
+        } else {
+            ctCell = _row.addNewC();
+        }
+        XSSFCell xcell = new XSSFCell(this, ctCell);
         xcell.setCellNum(columnIndex);
         if (type != Cell.CELL_TYPE_BLANK) {
                xcell.setCellType(type);
index 2de3c16b0a888673174c282dd9cbdaf7803ca409..318f2f6ddb62c16d68b1cdfc8c296498da0d55bf 100644 (file)
@@ -69,8 +69,10 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
 public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
     private static final POILogger logger = POILogFactory.getLogger(XSSFSheet.class);
 
+    //TODO make the two variable below private!
     protected CTSheet sheet;
     protected CTWorksheet worksheet;
+
     private TreeMap<Integer, XSSFRow> rows;
     private List<XSSFHyperlink> hyperlinks;
     private ColumnHelper columnHelper;
@@ -422,10 +424,17 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet {
      * @see #removeRow(org.apache.poi.ss.usermodel.Row)
      */
     public XSSFRow createRow(int rownum) {
-        CTRow ctRow = CTRow.Factory.newInstance();
+        CTRow ctRow;
+        XSSFRow prev = rows.get(rownum);
+        if(prev != null){
+            ctRow = prev.getCTRow();
+            ctRow.set(CTRow.Factory.newInstance());
+        } else {
+            ctRow = worksheet.getSheetData().addNewRow();
+        }
         XSSFRow r = new XSSFRow(ctRow, this);
         r.setRowNum(rownum);
-        rows.put(r.getRowNum(), r);
+        rows.put(rownum, r);
         return r;
     }
 
index 1de055db35f8f14ee791816705998a42a101c5d3..9af7515af9f20a14d7d5bd8d4f8f89fd3a1af292 100644 (file)
@@ -148,6 +148,12 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
 
     private static POILogger logger = POILogFactory.getLogger(XSSFWorkbook.class);
 
+    /**
+     * cached instance of XSSFCreationHelper for this workbook
+     * @see {@link #getCreationHelper()}
+     */
+    private XSSFCreationHelper _creationHelper;
+
     /**
      * Create a new SpreadsheetML workbook.
      */
@@ -1191,7 +1197,8 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
      *  classes of the various instances for XSSF.
      */
     public XSSFCreationHelper getCreationHelper() {
-        return new XSSFCreationHelper(this);
+        if(_creationHelper == null) _creationHelper = new XSSFCreationHelper(this);
+        return _creationHelper;
     }
 
     /**
diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/RichTextStringHelper.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/RichTextStringHelper.java
deleted file mode 100644 (file)
index a87a722..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/* ====================================================================
-   Licensed to the Apache Software Foundation (ASF) under one or more
-   contributor license agreements.  See the NOTICE file distributed with
-   this work for additional information regarding copyright ownership.
-   The ASF licenses this file to You 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.apache.poi.xssf.usermodel.helpers;
-
-import org.apache.poi.ss.usermodel.RichTextString;
-import org.apache.poi.xssf.usermodel.XSSFRichTextString;
-import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRElt;
-import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRPrElt;
-import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst;
-
-public class RichTextStringHelper {
-
-       public static void convertToRst(RichTextString string, CTRst text) {
-               // TODO: implement RichTextString to Rst conversion
-               text.setT(string.getString());
-       }
-
-       public static RichTextString convertFromRst(CTRst ctText) {
-               if(ctText == null) {
-                       return new XSSFRichTextString("");
-               }
-               if(ctText.getT() != null) {
-                       return new XSSFRichTextString(ctText.getT());
-               }
-               
-               // Grab all the text
-               StringBuffer t = new StringBuffer();
-               for(CTRElt r : ctText.getRArray()) {
-                       t.append( r.getT() );
-               }
-               XSSFRichTextString rtxt = new XSSFRichTextString(t.toString());
-               
-               // Now get all the formatting
-               // TODO: implement Rst/RpR to RichTextString conversion
-               for(CTRElt r : ctText.getRArray()) {
-                       // Formatting info comes from rPr
-                       CTRPrElt rPr = r.getRPr();
-                       rPr.getRFontArray();
-               }
-               return rtxt;
-       }
-}
index 87ce14e9221570493b02c162dec2c0fba859f8d8..ff7764c82e9084f8dee27bfd1e93f81edc9a7379 100644 (file)
@@ -877,8 +877,23 @@ public class TestXSSFSheet extends BaseTestSheet {
         assertNotNull(comment1);
         assertEquals("/xl/comments1.xml", comment1.getPackageRelationship().getTargetURI().toString());
         assertSame(comment1, sheet1.getCommentsTable(true));
-
-
     }
 
+    public void testCreateRow(){
+        XSSFWorkbook workbook = new XSSFWorkbook();
+        XSSFSheet sheet = workbook.createSheet();
+        CTWorksheet wsh = sheet.getCTWorksheet();
+        assertEquals(0, wsh.getSheetData().sizeOfRowArray());
+        XSSFRow row1 = sheet.createRow(1);
+        row1.createCell(1);
+        row1.createCell(2);
+        assertEquals(1, wsh.getSheetData().sizeOfRowArray());
+        assertEquals(2, wsh.getSheetData().getRowArray(0).sizeOfCArray());
+
+        //re-creating a row does NOT add extra data to the parent   
+        sheet.createRow(1);
+        assertEquals(1, wsh.getSheetData().sizeOfRowArray());
+        //existing cells are invalidated
+        assertEquals(0, wsh.getSheetData().getRowArray(0).sizeOfCArray());
+    }
 }
diff --git a/src/ooxml/testcases/org/apache/poi/xssf/util/MemoryUsage.java b/src/ooxml/testcases/org/apache/poi/xssf/util/MemoryUsage.java
new file mode 100755 (executable)
index 0000000..c0df4b6
--- /dev/null
@@ -0,0 +1,197 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.apache.poi.xssf.util;
+
+import junit.framework.TestCase;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.util.CellReference;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Mixed utilities for testing memory usage in XSSF
+ *
+ * @author Yegor Kozlov
+ */
+public class MemoryUsage extends TestCase {
+    private static final int NUM_COLUMNS = 255;
+
+    /**
+     * Generate a spreadsheet until OutOfMemoryError
+     * <p>
+     *  cells in even columns are numbers, cells in odd columns are strings
+     * </p>
+     *
+     * @param wb        the workbook to write to
+     * @param numCols   the number of columns in a row
+     */
+    public static void mixedSpreadsheet(Workbook wb, int numCols){
+
+        System.out.println("Testing " + wb.getClass().getName());
+        System.out.println("Memory: " + Runtime.getRuntime().totalMemory()/(1024*1024) + "MB");
+        int i=0, cnt=0;
+        try {
+            Sheet sh = wb.createSheet();
+            for(i=0; ; i++){
+                Row row = sh.createRow(i);
+                for(int j=0; j < numCols; j++){
+                    Cell cell = row.createCell(j);
+                    if(j % 2 == 0) cell.setCellValue(j);
+                    else cell.setCellValue(new CellReference(j, i).formatAsString());
+                    cnt++;
+                }
+            }
+        } catch (OutOfMemoryError er){
+            System.out.println("Failed at row=" + i + ", objects : " + cnt);
+        }
+        System.out.println("Memory: " + Runtime.getRuntime().totalMemory()/(1024*1024) + "MB");
+    }
+
+    /**
+     * Generate a spreadsheet who's all cell values are numbers.
+     * The data is generated until OutOfMemoryError. 
+     * <p>
+     * as compared to {@link #mixedSpreadsheet(org.apache.poi.ss.usermodel.Workbook, int)},
+     * this method does not set string values and, hence, does not invole the Shared Strings Table.
+     * </p>
+     *
+     * @param wb        the workbook to write to
+     * @param numCols   the number of columns in a row
+     */
+    public static void numberSpreadsheet(Workbook wb, int numCols){
+
+        System.out.println("Testing " + wb.getClass().getName());
+        System.out.println("Memory: " + Runtime.getRuntime().totalMemory()/(1024*1024) + "MB");
+        int i=0, cnt=0;
+        try {
+            Sheet sh = wb.createSheet();
+            for(i=0; ; i++){
+                Row row = sh.createRow(i);
+                for(int j=0; j < numCols; j++){
+                    Cell cell = row.createCell(j);
+                    cell.setCellValue(j);
+                    cnt++;
+                }
+            }
+        } catch (OutOfMemoryError er){
+            System.out.println("Failed at row=" + i + ", objects : " + cnt);
+        }
+        System.out.println("Memory: " + Runtime.getRuntime().totalMemory()/(1024*1024) + "MB");
+    }
+
+    /**
+     * Generate a spreadsheet until OutOfMemoryError using low-level OOXML XmlBeans.
+     * Similar to {@link #numberSpreadsheet(org.apache.poi.ss.usermodel.Workbook, int)}
+     *
+     * <p>
+     *
+     * @param numCols  the number of columns in a row
+     */
+    public static void xmlBeans(int numCols) {
+        int i = 0, cnt = 0;
+        System.out.println("Memory: " + Runtime.getRuntime().totalMemory() / (1024 * 1024) + "MB");
+
+        CTWorksheet sh = CTWorksheet.Factory.newInstance();
+        CTSheetData data = sh.addNewSheetData();
+        try {
+            for (i = 0; ; i++) {
+                CTRow row = data.addNewRow();
+                row.setR(i);
+                for (int j = 0; j < numCols; j++) {
+                    CTCell cell = row.addNewC();
+                    cell.setT(STCellType.N);
+                    cell.setV(String.valueOf(j));
+                    cnt++;
+                }
+            }
+        } catch (OutOfMemoryError er) {
+            System.out.println("Failed at row=" + i + ", objects: " + cnt);
+        }
+        System.out.println("Memory: " + Runtime.getRuntime().totalMemory() / (1024 * 1024) + "MB");
+    }
+
+    /**
+     * Generate detached (parentless) Xml beans until OutOfMemoryError
+     *
+     * @see #testXmlAttached()
+     */
+    public void testXmlDetached(){
+        List<CTRow> rows = new ArrayList<CTRow>();
+        int i = 0;
+        try {
+            for(;;){
+                //create a standalone CTRow bean
+                CTRow r = CTRow.Factory.newInstance();
+                r.setR(++i);
+                rows.add(r);
+            }
+        } catch (OutOfMemoryError er) {
+            System.out.println("Failed at row=" + i);
+        }
+        System.out.println("Memory: " + Runtime.getRuntime().totalMemory() / (1024 * 1024) + "MB");
+    }
+
+    /**
+     * Generate atatched (having a parent bean) Xml beans until OutOfMemoryError.
+     * This is MUCH more memory-efficient than {@link #testXmlDetached()}
+     *
+     * @see #testXmlAttached()
+     */
+    public void testXmlAttached(){
+        List<CTRow> rows = new ArrayList<CTRow>();
+        int i = 0;
+        //top-level element in sheet.xml
+        CTWorksheet sh = CTWorksheet.Factory.newInstance();
+        CTSheetData data = sh.addNewSheetData();
+        try {
+            for(;;){
+                //create CTRow attached to the parent object
+                CTRow r = data.addNewRow();
+                r.setR(++i);
+                rows.add(r);
+            }
+        } catch (OutOfMemoryError er) {
+            System.out.println("Failed at row=" + i);
+        }
+        System.out.println("Memory: " + Runtime.getRuntime().totalMemory() / (1024 * 1024) + "MB");
+    }
+
+    public void testMixedHSSF(){
+        numberSpreadsheet(new HSSFWorkbook(), NUM_COLUMNS);
+    }
+
+    public void testMixedXSSF(){
+        numberSpreadsheet(new XSSFWorkbook(), NUM_COLUMNS);
+    }
+
+    public void testNumberHSSF(){
+        numberSpreadsheet(new HSSFWorkbook(), NUM_COLUMNS);
+    }
+
+    public void testNumberXSSF(){
+        numberSpreadsheet(new XSSFWorkbook(), NUM_COLUMNS);
+    }
+
+}
\ No newline at end of file