View | Details | Raw Unified | Return to bug 57401
Collapse All | Expand All

(-)src/ooxml/testcases/org/apache/poi/xssf/model/TestDBMappedSharedStringsTableOption.java (+206 lines)
Line 0 Link Here
1
package org.apache.poi.xssf.model;
2
3
import org.apache.poi.ss.usermodel.Cell;
4
import org.apache.poi.ss.usermodel.Row;
5
import org.apache.poi.ss.usermodel.Workbook;
6
import org.apache.poi.xssf.SXSSFITestDataProvider;
7
import org.apache.poi.xssf.streaming.SXSSFSheet;
8
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
9
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
10
import org.junit.After;
11
import org.junit.Before;
12
import org.junit.Test;
13
14
import java.io.*;
15
import java.util.Random;
16
17
import static org.junit.Assert.assertEquals;
18
19
public class TestDBMappedSharedStringsTableOption {
20
    //Streaming version of workbook
21
    private SXSSFWorkbook workbook;
22
23
    private SXSSFSheet sheet;
24
25
    private File outputFile;
26
    public static final String TEST_OUTPUT_DIR = "poi.test.xssf.output.dir";
27
28
    @Before
29
    public void setUp() {
30
        outputFile = new File(System.getProperty(TEST_OUTPUT_DIR), "output.xlsx");
31
        setupWorkBook();
32
        setupBlankSheet();
33
    }
34
35
    private void setupWorkBook() {
36
        XSSFWorkbook wb = new XSSFWorkbook(SharedStringsTableType.LOW_FOOTPRINT_MAP_DB_SST);
37
        workbook = new SXSSFWorkbook(wb, 2, false, true);
38
    }
39
40
    private void setupBlankSheet() {
41
        sheet = (SXSSFSheet) workbook.createSheet("Employee Data");
42
    }
43
44
    @After
45
    public void cleanup() {
46
        outputFile.delete();
47
    }
48
49
    @Test
50
    public void testWrite100UniqueRecordsOf10Char() throws IOException {
51
        int recordCount = 100;
52
        addUniqueRecordsToSheet(0, 100, 10);
53
        writeAndAssertRecord(recordCount);
54
    }
55
56
    @Test
57
    public void testWrite1MUniqueRecordsOf100Char() {
58
        int recordCount = 1000000;
59
        addUniqueRecordsToSheet(0, recordCount, 100);
60
        writeAndAssertRecord(recordCount);
61
    }
62
63
    @Test
64
    public void testWriteFromTextFile() {
65
        int recordCount = 3;
66
        File textInputFile = new File(System.getProperty(TEST_OUTPUT_DIR), "temp.txt");
67
        try {
68
            FileWriter w = new FileWriter(textInputFile);
69
            for (int i = 1; i <= recordCount; i++) {
70
                w.write("Line" + i + ",FirstColumn,SecondColumn,ThirdColumn\r\n");
71
            }
72
            w.close();
73
        } catch (IOException e) {
74
        }
75
        addRecordsFromFile("temp.txt");
76
        writeAndAssertRecord(recordCount);
77
        textInputFile.delete();
78
    }
79
80
    @Test
81
    public void testWrite1MRandomRecordsOf10Char() {
82
        int recordCount = 100000;
83
        addRandomRecordsToSheet(0, recordCount, 200000, 10);
84
        writeAndAssertRecord(recordCount);
85
    }
86
87
    @Test
88
    public void test1MRecordHavingRepetitiveRecordsOf10Char() {
89
        int recordCount = 1000000;
90
        addUniqueRecordsToSheet(0, 200000, 10);
91
        addUniqueRecordsToSheet(200000, 200000, 10);
92
        addUniqueRecordsToSheet(400000, 200000, 10);
93
        addUniqueRecordsToSheet(600000, 200000, 10);
94
        addUniqueRecordsToSheet(800000, 200000, 10);
95
        writeAndAssertRecord(recordCount);
96
    }
97
98
    @Test
99
    public void testWriteAllDuplicateRecord() {
100
        int recordCount = 100000;
101
        addRepeatingRecordsToSheet(recordCount);
102
        writeAndAssertRecord(recordCount);
103
    }
104
105
    private void writeAndAssertRecord(int recordCount) {
106
        System.out.print("Started writing.....");
107
        //NOTE: all tests can be executed within -Xmx100M by commenting out out code below
108
        //----
109
        XSSFWorkbook wb = (XSSFWorkbook) SXSSFITestDataProvider.instance.writeOutAndReadBack(workbook);
110
        System.out.println("File creation done...Asserting");
111
        assertRows(wb, recordCount);
112
        //----
113
    }
114
115
    private void addUniqueRecordsToSheet(int fromRowNum, int numberOfRecords, int constantStringLength) {
116
        System.out.print("adding records to sheet.....");
117
        int i = 0;
118
        String constantString = getStringOf(constantStringLength);
119
        while (i++ < numberOfRecords) {
120
            if (i % 10000 == 0) System.out.print(i + ",");
121
            Row row = sheet.createRow(fromRowNum++);
122
            Object[] objArr = new Object[]{constantString + i};
123
            int cellNum = 0;
124
            for (Object obj : objArr) {
125
                Cell cell = row.createCell(cellNum++);
126
                if (obj instanceof String) {
127
                    cell.setCellValue((String) obj);
128
                } else if (obj instanceof Integer)
129
                    cell.setCellValue((Integer) obj);
130
            }
131
        }
132
    }
133
134
    private String getStringOf(int length) {
135
        StringBuilder str = new StringBuilder();
136
        for (int j = 0; j < length; j++) {
137
            str.append("a");
138
        }
139
        return str.toString();
140
    }
141
142
    private void addRandomRecordsToSheet(int fromRowNum, int numberOfRecords, int recordLength, int constantStringLength) {
143
        int i = 0;
144
        String constantString = getStringOf(constantStringLength);
145
        while (i++ < numberOfRecords) {
146
            if (i % 1000 == 0) System.out.print(i + ",");
147
            Row row = sheet.createRow(fromRowNum++);
148
            Object[] objArr = new Object[]{constantString + new Random().nextInt(recordLength)};
149
            int cellNum = 0;
150
            for (Object obj : objArr) {
151
                Cell cell = row.createCell(cellNum++);
152
                if (obj instanceof String)
153
                    cell.setCellValue((String) obj);
154
                else if (obj instanceof Integer)
155
                    cell.setCellValue((Integer) obj);
156
            }
157
        }
158
    }
159
160
    private void addRecordsFromFile(String fileName) {
161
        System.out.print("adding records to sheet.....");
162
        try {
163
            int fromRowNum = 0;
164
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)));
165
            String line = null;
166
            while ((line = br.readLine()) != null) {
167
                Row row = sheet.createRow(fromRowNum++);
168
                Object[] objArr = line.split(",");
169
                int cellNum = 0;
170
                for (Object obj : objArr) {
171
                    Cell cell = row.createCell(cellNum++);
172
                    if (obj instanceof String)
173
                        cell.setCellValue((String) obj);
174
                    else if (obj instanceof Integer)
175
                        cell.setCellValue((Integer) obj);
176
                }
177
            }
178
            br.close();
179
        } catch (Exception e) {
180
            e.printStackTrace();
181
        }
182
    }
183
184
    private void addRepeatingRecordsToSheet(int count) {
185
        int rownum = 0;
186
        int i = 0;
187
        String constantString = getStringOf(10);
188
        while (i++ < count) {
189
            Row row = sheet.createRow(rownum++);
190
            Object[] objArr = new Object[]{constantString};
191
            int cellnum = 0;
192
            for (Object obj : objArr) {
193
                Cell cell = row.createCell(cellnum++);
194
                if (obj instanceof String)
195
                    cell.setCellValue((String) obj);
196
                else if (obj instanceof Integer)
197
                    cell.setCellValue((Integer) obj);
198
            }
199
        }
200
    }
201
202
    public void assertRows(Workbook wb, int expectedRecordCount) {
203
        assertEquals(expectedRecordCount, wb.getSheetAt(0).getLastRowNum() + 1);
204
    }
205
206
}
(-)src/ooxml/java/org/apache/poi/xssf/model/SharedStringsTableType.java (+37 lines)
Line 0 Link Here
1
/* ====================================================================
2
   Licensed to the Apache Software Foundation (ASF) under one or more
3
   contributor license agreements.  See the NOTICE file distributed with
4
   this work for additional information regarding copyright ownership.
5
   The ASF licenses this file to You under the Apache License, Version 2.0
6
   (the "License"); you may not use this file except in compliance with
7
   the License.  You may obtain a copy of the License at
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
11
   Unless required by applicable law or agreed to in writing, software
12
   distributed under the License is distributed on an "AS IS" BASIS,
13
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
   See the License for the specific language governing permissions and
15
   limitations under the License.
16
==================================================================== */
17
package org.apache.poi.xssf.model;
18
19
/**
20
 * enum to specify shared strings table to use
21
 */
22
public enum SharedStringsTableType {
23
    DEFAULT_SST(SharedStringsTable.class),//in memory shared strings string table
24
    LOW_FOOTPRINT_MAP_DB_SST(DBMappedSharedStringsTable.class); //streaming version low foot print shared strings table
25
    /**
26
     * Defines what object is used to construct instances of this relationship
27
     */
28
    private Class<? extends SharedStringsTable> instance;
29
30
    private SharedStringsTableType(Class<? extends SharedStringsTable> sharedStringsTableInstance) {
31
        instance = sharedStringsTableInstance;
32
    }
33
34
    public Class<? extends SharedStringsTable> getInstance() {
35
        return instance;
36
    }
37
}
(-)src/ooxml/java/org/apache/poi/POIXMLDocument.java (-1 / +1 lines)
Lines 189-195 Link Here
189
     *
189
     *
190
     * @exception IOException if anything can't be written.
190
     * @exception IOException if anything can't be written.
191
     */
191
     */
192
    public final void write(OutputStream stream) throws IOException {
192
    public void write(OutputStream stream) throws IOException {
193
        //force all children to commit their changes into the underlying OOXML Package
193
        //force all children to commit their changes into the underlying OOXML Package
194
        Set<PackagePart> context = new HashSet<PackagePart>();
194
        Set<PackagePart> context = new HashSet<PackagePart>();
195
        onSave(context);
195
        onSave(context);
(-)src/ooxml/java/org/apache/poi/xssf/model/DBMappedSharedStringsTable.java (+311 lines)
Line 0 Link Here
1
/* ====================================================================
2
   Licensed to the Apache Software Foundation (ASF) under one or more
3
   contributor license agreements.  See the NOTICE file distributed with
4
   this work for additional information regarding copyright ownership.
5
   The ASF licenses this file to You under the Apache License, Version 2.0
6
   (the "License"); you may not use this file except in compliance with
7
   the License.  You may obtain a copy of the License at
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
11
   Unless required by applicable law or agreed to in writing, software
12
   distributed under the License is distributed on an "AS IS" BASIS,
13
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
   See the License for the specific language governing permissions and
15
   limitations under the License.
16
==================================================================== */
17
18
package org.apache.poi.xssf.model;
19
20
import org.apache.poi.openxml4j.opc.PackagePart;
21
import org.apache.poi.openxml4j.opc.PackageRelationship;
22
import org.apache.poi.util.TempFile;
23
import org.apache.xmlbeans.XmlException;
24
import org.apache.xmlbeans.XmlOptions;
25
import org.mapdb.DB;
26
import org.mapdb.DBMaker;
27
import org.mapdb.HTreeMap;
28
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst;
29
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSst;
30
import org.openxmlformats.schemas.spreadsheetml.x2006.main.SstDocument;
31
32
import javax.xml.stream.XMLStreamException;
33
import java.io.*;
34
import java.math.BigInteger;
35
import java.security.SecureRandom;
36
import java.util.ArrayList;
37
import java.util.Collections;
38
import java.util.List;
39
40
/**
41
 * SharedStringsTable With Map DB implementation
42
 * </p>
43
 *
44
 */
45
public class DBMappedSharedStringsTable extends SharedStringsTable implements AutoCloseable{
46
47
    /**
48
     * Maps strings and their indexes in the <code>recordVsIndexBasedSTMap</code> map db
49
     */
50
    private DB recordVsIndexMapDB;
51
    private HTreeMap<String, Integer> recordVsIndexBasedSTMap; //string vs index map to lookup existing record in stTable
52
    /**
53
     * Maps strings and their indexes in the <code>recordVsIndexBasedSTMap</code> map db
54
     */
55
    private DB indexVsRecordMapDB;
56
    private HTreeMap<Integer, String> indexVsRecordBasedSTMap; //index vs string map to retrieve record with index
57
58
    private final File temp_shared_string_file;
59
60
    /**
61
     * An integer representing the total count of strings in the workbook. This count does not
62
     * include any numbers, it counts only the total of text strings in the workbook.
63
     */
64
    private int count;
65
66
    /**
67
     * An integer representing the total count of unique strings in the Shared String Table.
68
     * A string is unique even if it is a copy of another string, but has different formatting applied
69
     * at the character level.
70
     */
71
    private int uniqueCount;
72
73
    private SstDocument _sstDoc;
74
75
    private final static XmlOptions options = new XmlOptions();
76
    private final static XmlOptions out_options = new XmlOptions();
77
78
79
    static {
80
        options.put(XmlOptions.SAVE_INNER);
81
        options.put(XmlOptions.SAVE_AGGRESSIVE_NAMESPACES);
82
        options.put(XmlOptions.SAVE_USE_DEFAULT_NAMESPACE);
83
        options.setSaveImplicitNamespaces(Collections.singletonMap("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"));
84
85
        out_options.setLoadSubstituteNamespaces(Collections.singletonMap("", "http://schemas.openxmlformats.org/spreadsheetml/2006/main"));   //TODO add options if required
86
    }
87
88
    public DBMappedSharedStringsTable() {
89
        super();
90
        temp_shared_string_file = createTempFile("poi-shared-string-table", ".xml");
91
        initMapDbBasedSharedStringTableMap();
92
    }
93
94
    private File createTempFile(String prefix, String suffix) {
95
        try {
96
            return TempFile.createTempFile(prefix, suffix);
97
        } catch (IOException e) {
98
            throw new RuntimeException("Couldn't create required temp file", e);
99
        }
100
    }
101
102
    public DBMappedSharedStringsTable(PackagePart part, PackageRelationship rel) throws IOException {
103
        super(part, rel);//TODO needs to be commented out whiler reading
104
        temp_shared_string_file = createTempFile("poi-shared-string-table", ".xml");
105
        initMapDbBasedSharedStringTableMap();
106
        readFrom(part.getInputStream());
107
    }
108
109
    public FileInputStream getSharedStringInputStream() throws IOException {
110
        return new FileInputStream(temp_shared_string_file);
111
    }
112
113
    public FileOutputStream getSharedStringsTableOutputStream() throws IOException {
114
        return new FileOutputStream(temp_shared_string_file);
115
    }
116
117
    public File getTemp_shared_string_file() {
118
        return temp_shared_string_file;
119
    }
120
121
    private void initMapDbBasedSharedStringTableMap() {
122
        initRecordVsIndexBasedMapDB();
123
        initIndexVsRecordBasedMapDB();
124
    }
125
126
    private void initRecordVsIndexBasedMapDB() {
127
        File mapDbFile = createTempFile(new BigInteger(130, new SecureRandom()).toString(32), "");//creating random name file to store map db
128
        recordVsIndexMapDB = DBMaker.newFileDB(mapDbFile)
129
                .transactionDisable()
130
                .cacheHardRefEnable()
131
                .cacheSize(65536)
132
                .deleteFilesAfterClose()
133
                .mmapFileEnablePartial()
134
                .closeOnJvmShutdown().make();
135
        recordVsIndexBasedSTMap = recordVsIndexMapDB.createHashMap(new BigInteger(130, new SecureRandom()).toString(32)).make();
136
    }
137
138
    private void initIndexVsRecordBasedMapDB() {
139
        File mapDb2File = createTempFile(new BigInteger(130, new SecureRandom()).toString(32), "");//creating random name file to store map db
140
        indexVsRecordMapDB = DBMaker.newFileDB(mapDb2File)
141
                .transactionDisable()
142
                .cacheDisable() //caching not required indexVsRecordBasedSTMap will be used to write all existing values
143
                .deleteFilesAfterClose()
144
                .mmapFileEnablePartial()
145
                .closeOnJvmShutdown().make();
146
        indexVsRecordBasedSTMap = indexVsRecordMapDB.createHashMap(new BigInteger(130, new SecureRandom()).toString(32)).make();
147
    }
148
149
    /**
150
     * Read this shared strings table from an XML file.
151
     *
152
     * @param is The input stream containing the XML document.
153
     * @throws java.io.IOException if an error occurs while reading.
154
     */
155
    @SuppressWarnings("deprecation") //YK: getXYZArray() array accessors are deprecated in xmlbeans with JDK 1.5 support
156
    public void readFrom(InputStream is) throws IOException {
157
        try {
158
            int cnt = 0;
159
            _sstDoc = SstDocument.Factory.parse(is);
160
            CTSst sst = _sstDoc.getSst();
161
            count = (int) sst.getCount();
162
            uniqueCount = (int) sst.getUniqueCount();
163
            for (CTRst st : sst.getSiArray()) {
164
                String key = getKey(st);
165
                recordVsIndexBasedSTMap.put(key, cnt);
166
                indexVsRecordBasedSTMap.put(cnt, key);
167
                cnt++;
168
            }
169
        } catch (XmlException e) {
170
            throw new IOException(e.getLocalizedMessage());
171
        }
172
    }
173
174
    private String getKey(CTRst st) {
175
        return st.xmlText(options);
176
    }
177
178
    /**
179
     * Return a string item by index
180
     *
181
     * @param idx index of item to return.
182
     * @return the item at the specified position in this Shared String table.
183
     */
184
    public CTRst getEntryAt(int idx) {
185
        try {
186
            return CTRst.Factory.parse(indexVsRecordBasedSTMap.get(idx), out_options);
187
        } catch (XmlException e) {
188
            throw new RuntimeException("Error Parsing xmlText from SSTable");
189
        }
190
    }
191
192
    /**
193
     * Return an integer representing the total count of strings in the workbook. This count does not
194
     * include any numbers, it counts only the total of text strings in the workbook.
195
     *
196
     * @return the total count of strings in the workbook
197
     */
198
    public int getCount() {
199
        return count;
200
    }
201
202
    /**
203
     * Returns an integer representing the total count of unique strings in the Shared String Table.
204
     * A string is unique even if it is a copy of another string, but has different formatting applied
205
     * at the character level.
206
     *
207
     * @return the total count of unique strings in the workbook
208
     */
209
    public int getUniqueCount() {
210
        return uniqueCount;
211
    }
212
213
    /**
214
     * Add an entry to this Shared String table (a new value is appened to the end).
215
     * <p/>
216
     * <p>
217
     * If the Shared String table already contains this <code>CTRst</code> bean, its index is returned.
218
     * Otherwise a new entry is aded.
219
     * </p>
220
     *
221
     * @param st the entry to add
222
     * @return index the index of added entry
223
     */
224
    public int addEntry(CTRst st) {
225
        String s = getKey(st);
226
        count++;
227
        if (recordVsIndexBasedSTMap.containsKey(s)) {
228
            return recordVsIndexBasedSTMap.get(s);
229
        }
230
        //new unique record
231
        recordVsIndexBasedSTMap.put(s, uniqueCount);
232
        indexVsRecordBasedSTMap.put(uniqueCount, s);
233
        return uniqueCount++;
234
    }
235
    /**
236
     * Provide low-level access to the underlying array of CTRst beans
237
     *
238
     * @return array of CTRst beans
239
     */
240
    public List<CTRst> getItems() {
241
        List<CTRst> beans = new ArrayList<CTRst>();
242
        for (int i = 0; i < uniqueCount; i++) {
243
            beans.add(getEntryAt(i));
244
        }
245
        return beans;
246
    }
247
248
    /**
249
     * Write this table out as XML.
250
     *
251
     * @param out The stream to write to.
252
     * @throws java.io.IOException if an error occurs while writing.
253
     */
254
    public void writeTo(OutputStream out) throws IOException {
255
        //re-create the sst table every time saving a workbook at the end after adding all record using map DB
256
        try {
257
            Writer writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
258
            addDefaultXmlOptions(writer);
259
            if (uniqueCount != 0) {
260
                addStringItems(writer);
261
                addEndDocument(writer);
262
            }
263
            writer.flush();
264
        } catch (XMLStreamException e) {
265
            throw new RuntimeException("Couldn't write to SharedStringsTable", e);
266
        }
267
    }
268
269
    private void addDefaultXmlOptions(Writer writer) throws XMLStreamException, IOException {
270
        writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
271
        String isNoSIElements = uniqueCount == 0 ? "/" : "";
272
        writer.write("<sst count=\"" + count + "\" uniqueCount=\"" + uniqueCount + "\" xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\"" + isNoSIElements + ">");
273
    }
274
275
    private void addStringItems(Writer writer) throws XMLStreamException, IOException {
276
        for (int i = 0; i < uniqueCount; i++) {
277
            String s = indexVsRecordBasedSTMap.get(i);
278
            writer.write("<si>");
279
            writer.write(s);
280
            writer.write("</si>");
281
        }
282
    }
283
284
    private void addEndDocument(Writer writer) throws XMLStreamException, IOException {
285
        writer.write("</sst>");
286
    }
287
288
    @Override
289
    protected void commit() throws IOException {
290
       // createDefaultSSTTableXml();
291
        FileOutputStream sharedStringOutputStream = getSharedStringsTableOutputStream();
292
        writeTo(sharedStringOutputStream);
293
        sharedStringOutputStream.close();
294
    }
295
296
    private void createDefaultSSTTableXml() throws IOException {         //Todo, check if needed to create default one
297
        _sstDoc = SstDocument.Factory.newInstance();
298
        PackagePart part = getPackagePart();
299
        OutputStream out = part.getOutputStream();
300
        _sstDoc.save(out, options);
301
        out.close();
302
    }
303
304
    @Override
305
    public void close() throws Exception {
306
        recordVsIndexBasedSTMap.clear();
307
        indexVsRecordBasedSTMap.clear();
308
        recordVsIndexMapDB.close();
309
        indexVsRecordMapDB.close();
310
    }
311
}
(-)src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java (-33 / +121 lines)
Lines 20-46 Link Here
20
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.setPassword;
20
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.setPassword;
21
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.validatePassword;
21
import static org.apache.poi.xssf.usermodel.helpers.XSSFPaswordHelper.validatePassword;
22
22
23
import java.io.ByteArrayInputStream;
23
import java.io.*;
24
import java.io.ByteArrayOutputStream;
24
import java.lang.reflect.Constructor;
25
import java.io.File;
25
import java.util.*;
26
import java.io.IOException;
27
import java.io.InputStream;
28
import java.io.OutputStream;
29
import java.util.ArrayList;
30
import java.util.Collection;
31
import java.util.HashMap;
32
import java.util.Iterator;
33
import java.util.LinkedList;
34
import java.util.List;
35
import java.util.Map;
36
import java.util.regex.Pattern;
26
import java.util.regex.Pattern;
27
import java.util.zip.ZipEntry;
28
import java.util.zip.ZipFile;
29
import java.util.zip.ZipOutputStream;
37
30
38
import javax.xml.namespace.QName;
31
import javax.xml.namespace.QName;
39
32
40
import org.apache.poi.POIXMLDocument;
33
import org.apache.poi.*;
41
import org.apache.poi.POIXMLDocumentPart;
42
import org.apache.poi.POIXMLException;
43
import org.apache.poi.POIXMLProperties;
44
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
34
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
45
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
35
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
46
import org.apache.poi.openxml4j.opc.OPCPackage;
36
import org.apache.poi.openxml4j.opc.OPCPackage;
Lines 61-79 Link Here
61
import org.apache.poi.ss.util.CellRangeAddress;
51
import org.apache.poi.ss.util.CellRangeAddress;
62
import org.apache.poi.ss.util.CellReference;
52
import org.apache.poi.ss.util.CellReference;
63
import org.apache.poi.ss.util.WorkbookUtil;
53
import org.apache.poi.ss.util.WorkbookUtil;
64
import org.apache.poi.util.Beta;
54
import org.apache.poi.util.*;
65
import org.apache.poi.util.IOUtils;
66
import org.apache.poi.util.Internal;
67
import org.apache.poi.util.POILogFactory;
68
import org.apache.poi.util.POILogger;
69
import org.apache.poi.util.PackageHelper;
70
import org.apache.poi.xssf.XLSBUnsupportedException;
55
import org.apache.poi.xssf.XLSBUnsupportedException;
71
import org.apache.poi.xssf.model.CalculationChain;
56
import org.apache.poi.xssf.model.*;
72
import org.apache.poi.xssf.model.ExternalLinksTable;
73
import org.apache.poi.xssf.model.MapInfo;
74
import org.apache.poi.xssf.model.SharedStringsTable;
75
import org.apache.poi.xssf.model.StylesTable;
76
import org.apache.poi.xssf.model.ThemesTable;
77
import org.apache.poi.xssf.usermodel.helpers.XSSFFormulaUtils;
57
import org.apache.poi.xssf.usermodel.helpers.XSSFFormulaUtils;
78
import org.apache.xmlbeans.XmlException;
58
import org.apache.xmlbeans.XmlException;
79
import org.apache.xmlbeans.XmlObject;
59
import org.apache.xmlbeans.XmlObject;
Lines 147-152 Link Here
147
    private SharedStringsTable sharedStringSource;
127
    private SharedStringsTable sharedStringSource;
148
128
149
    /**
129
    /**
130
     * shared strings table type- to specify use default or map db shared strings table source
131
     */
132
    private SharedStringsTableType sharedStringsTableType = SharedStringsTableType.DEFAULT_SST;
133
134
    /**
150
     * A collection of shared objects used for styling content,
135
     * A collection of shared objects used for styling content,
151
     * e.g. fonts, cell styles, colors, etc.
136
     * e.g. fonts, cell styles, colors, etc.
152
     */
137
     */
Lines 218-229 Link Here
218
        onWorkbookCreate();
203
        onWorkbookCreate();
219
    }
204
    }
220
205
206
    public XSSFWorkbook(SharedStringsTableType sharedStringsTableType) {
207
        super(newPackage());
208
        this.sharedStringsTableType = sharedStringsTableType;
209
        onWorkbookCreate();
210
    }
211
212
    public SharedStringsTableType getSharedStringsTableType() {
213
        return sharedStringsTableType;
214
    }
215
221
    /**
216
    /**
222
     * Constructs a XSSFWorkbook object given a OpenXML4J <code>Package</code> object,
217
     * Constructs a XSSFWorkbook object given a OpenXML4J <code>Package</code> object,
223
     *  see <a href="http://poi.apache.org/oxml4j/">http://poi.apache.org/oxml4j/</a>.
218
     *  see <a href="http://poi.apache.org/oxml4j/">http://poi.apache.org/oxml4j/</a>.
224
     * 
219
     * 
225
     * <p>Once you have finished working with the Workbook, you should close the package
220
     * <p>Once you have finished working with the Workbook, you should close the package
226
     * by calling either {@link #close()} or {@link OPCPackage#close()}, to avoid 
221
     * by calling either {@link #close()} or {@link OPCPackage#close()}, to avoid
227
     * leaving file handles open.
222
     * leaving file handles open.
228
     * 
223
     * 
229
     * <p>Creating a XSSFWorkbook from a file-backed OPC Package has a lower memory
224
     * <p>Creating a XSSFWorkbook from a file-backed OPC Package has a lower memory
Lines 338-344 Link Here
338
333
339
            if (sharedStringSource == null) {
334
            if (sharedStringSource == null) {
340
                // Create SST if it is missing
335
                // Create SST if it is missing
341
                sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, XSSFFactory.getInstance());
336
                sharedStringSource = createSSTSourceBasedOnSSTType();
342
            }
337
            }
343
            
338
            
344
            // Load individual sheets. The order of sheets is defined by the order
339
            // Load individual sheets. The order of sheets is defined by the order
Lines 376-381 Link Here
376
        }
371
        }
377
    }
372
    }
378
373
374
    @Override
375
    public void write(OutputStream stream) throws IOException {
376
        if (getSharedStringsTableType() == SharedStringsTableType.DEFAULT_SST) {
377
            super.write(stream);
378
        } else {
379
            writeWithPatchingMDBSST(stream);
380
        }
381
    }
382
383
    public void writeWithPatchingMDBSST(OutputStream stream) throws IOException {
384
        //Save the template
385
        File tmplFile = TempFile.createTempFile("poi-sxssf-template", ".xlsx");
386
        try {
387
            FileOutputStream os = new FileOutputStream(tmplFile);
388
            try {
389
                super.write(os);
390
            } finally {
391
                os.close();
392
            }
393
394
            //Substitute the template shared string xml with the temporarily generated xml data file
395
            injectSharedStringTableXml(tmplFile, stream);
396
        } finally {
397
            if (!tmplFile.delete()) {
398
                throw new IOException("Could not delete temporary file after processing: " + tmplFile);
399
            }
400
        }
401
    }
402
403
    private void injectSharedStringTableXml(File zipfile, OutputStream out) throws IOException {
404
        ZipFile zip = new ZipFile(zipfile);
405
        DBMappedSharedStringsTable _sst = (DBMappedSharedStringsTable) sharedStringSource;
406
        try {
407
            ZipOutputStream zos = new ZipOutputStream(out);
408
            try {
409
                Enumeration<? extends ZipEntry> en = zip.entries();
410
                while (en.hasMoreElements()) {
411
                    ZipEntry ze = en.nextElement();
412
                    zos.putNextEntry(new ZipEntry(ze.getName()));
413
                    InputStream is;
414
                    if (ze.getName().equals("xl/sharedStrings.xml")) {
415
                        is = _sst.getSharedStringInputStream(); //injecting shared string table in target output
416
                    } else {
417
                        is = zip.getInputStream(ze);
418
                    }
419
                    copyStream(is, zos);
420
                    is.close();
421
                }
422
            } finally {
423
                zos.close();
424
                if (!_sst.getTemp_shared_string_file().delete()) {
425
                    throw new RuntimeException("Couldn't delete temporary shared strings table file.");
426
                }
427
            }
428
        } finally {
429
            zip.close();
430
        }
431
    }
432
433
    private static void copyStream(InputStream in, OutputStream out) throws IOException {
434
        byte[] chunk = new byte[1024];
435
        int count;
436
        while ((count = in.read(chunk)) >= 0) {
437
            out.write(chunk, 0, count);
438
        }
439
    }
440
379
    /**
441
    /**
380
     * Create a new CTWorkbook with all values set to default
442
     * Create a new CTWorkbook with all values set to default
381
     */
443
     */
Lines 394-405 Link Here
394
        POIXMLProperties.ExtendedProperties expProps = getProperties().getExtendedProperties();
456
        POIXMLProperties.ExtendedProperties expProps = getProperties().getExtendedProperties();
395
        expProps.getUnderlyingProperties().setApplication(DOCUMENT_CREATOR);
457
        expProps.getUnderlyingProperties().setApplication(DOCUMENT_CREATOR);
396
458
397
        sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, XSSFFactory.getInstance());
459
        sharedStringSource = createSSTSourceBasedOnSSTType();
398
        stylesSource = (StylesTable)createRelationship(XSSFRelation.STYLES, XSSFFactory.getInstance());
460
        stylesSource = (StylesTable) createRelationship(XSSFRelation.STYLES, XSSFFactory.getInstance());
399
461
400
        namedRanges = new ArrayList<XSSFName>();
462
        namedRanges = new ArrayList<XSSFName>();
401
        sheets = new ArrayList<XSSFSheet>();
463
        sheets = new ArrayList<XSSFSheet>();
402
        pivotTables = new ArrayList<XSSFPivotTable>();
464
        pivotTables = new ArrayList<XSSFPivotTable>();
465
    }
466
467
    private SharedStringsTable createSSTSourceBasedOnSSTType() {
468
        return (SharedStringsTable) createRelationship(XSSFRelation.SHARED_STRINGS, new POIXMLFactory() {
469
            @Override
470
            public POIXMLDocumentPart createDocumentPart(POIXMLDocumentPart parent, PackageRelationship rel, PackagePart part) {
471
                try {
472
                    Class<? extends POIXMLDocumentPart> cls = sharedStringsTableType.getInstance();
473
                    Constructor<? extends POIXMLDocumentPart> constructor = cls.getDeclaredConstructor(PackagePart.class, PackageRelationship.class);
474
                    return constructor.newInstance(part, rel);
475
                } catch (Exception e) {
476
                    throw new POIXMLException(e);
477
                }
478
            }
479
480
            @Override
481
            public POIXMLDocumentPart newDocumentPart(POIXMLRelation descriptor) {
482
                try {
483
                    Class<? extends POIXMLDocumentPart> cls = sharedStringsTableType.getInstance();
484
                    Constructor<? extends POIXMLDocumentPart> constructor = cls.getDeclaredConstructor();
485
                    return constructor.newInstance();
486
                } catch (Exception e) {
487
                    throw new POIXMLException(e);
488
                }
489
            }
490
        });
403
    }
491
    }
404
492
405
    /**
493
    /**
(-)maven/poi-ooxml.pom (+5 lines)
Lines 69-73 Link Here
69
      <artifactId>poi-ooxml-schemas</artifactId>
69
      <artifactId>poi-ooxml-schemas</artifactId>
70
      <version>@VERSION@</version>
70
      <version>@VERSION@</version>
71
    </dependency>
71
    </dependency>
72
    <dependency>
73
      <groupId>org.mapdb</groupId>
74
      <artifactId>mapdb</artifactId>
75
      <version>1.0.6</version>
76
      </dependency>
72
  </dependencies>
77
  </dependencies>
73
</project>
78
</project>

Return to bug 57401