12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325 |
- /* ====================================================================
- 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.streaming;
-
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.Reader;
- import java.io.Writer;
- import java.nio.charset.StandardCharsets;
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.NoSuchElementException;
- import java.util.Spliterator;
-
- import org.apache.commons.compress.archivers.ArchiveOutputStream;
- import org.apache.commons.compress.archivers.zip.Zip64Mode;
- import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
- import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
- import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
- import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
- import org.apache.logging.log4j.LogManager;
- import org.apache.logging.log4j.Logger;
- import org.apache.poi.openxml4j.opc.OPCPackage;
- import org.apache.poi.openxml4j.util.ZipArchiveThresholdInputStream;
- import org.apache.poi.openxml4j.util.ZipEntrySource;
- import org.apache.poi.openxml4j.util.ZipFileZipEntrySource;
- import org.apache.poi.openxml4j.util.ZipInputStreamZipEntrySource;
- import org.apache.poi.openxml4j.util.ZipSecureFile;
- import org.apache.poi.ss.SpreadsheetVersion;
- import org.apache.poi.ss.formula.EvaluationWorkbook;
- import org.apache.poi.ss.formula.udf.UDFFinder;
- import org.apache.poi.ss.usermodel.CellReferenceType;
- import org.apache.poi.ss.usermodel.CellStyle;
- import org.apache.poi.ss.usermodel.CreationHelper;
- import org.apache.poi.ss.usermodel.DataFormat;
- import org.apache.poi.ss.usermodel.Font;
- import org.apache.poi.ss.usermodel.Name;
- import org.apache.poi.ss.usermodel.PictureData;
- import org.apache.poi.ss.usermodel.Row;
- import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
- import org.apache.poi.ss.usermodel.Sheet;
- import org.apache.poi.ss.usermodel.SheetVisibility;
- import org.apache.poi.ss.usermodel.Workbook;
- import org.apache.poi.util.Beta;
- import org.apache.poi.util.IOUtils;
- import org.apache.poi.util.Internal;
- import org.apache.poi.util.NotImplemented;
- import org.apache.poi.util.Removal;
- import org.apache.poi.util.TempFile;
- import org.apache.poi.xssf.model.SharedStringsTable;
- import org.apache.poi.xssf.usermodel.XSSFChartSheet;
- import org.apache.poi.xssf.usermodel.XSSFSheet;
- import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-
- /**
- * Streaming version of XSSFWorkbook implementing the "BigGridDemo" strategy.
- *
- * This allows to write very large files without running out of memory as only
- * a configurable portion of the rows are kept in memory at any one time.
- *
- * You can provide a template workbook which is used as basis for the written
- * data.
- *
- * See https://poi.apache.org/spreadsheet/how-to.html#sxssf for details.
- *
- * Please note that there are still things that still may consume a large
- * amount of memory based on which features you are using, e.g. merged regions,
- * comments, ... are still only stored in memory and thus may require a lot of
- * memory if used extensively.
- *
- * SXSSFWorkbook defaults to using inline strings instead of a shared strings
- * table. This is very efficient, since no document content needs to be kept in
- * memory, but is also known to produce documents that are incompatible with
- * some clients. With shared strings enabled all unique strings in the document
- * has to be kept in memory. Depending on your document content this could use
- * a lot more resources than with shared strings disabled.
- *
- * Carefully review your memory budget and compatibility needs before deciding
- * whether to enable shared strings or not.
- */
- public class SXSSFWorkbook implements Workbook {
- /**
- * Specifies how many rows can be accessed at most via {@link SXSSFSheet#getRow}.
- * When a new node is created via {@link SXSSFSheet#createRow} and the total number
- * of unflushed records would exceed the specified value, then the
- * row with the lowest index value is flushed and cannot be accessed
- * via {@link SXSSFSheet#getRow} anymore.
- */
- public static final int DEFAULT_WINDOW_SIZE = 100;
- private static final Logger LOG = LogManager.getLogger(SXSSFWorkbook.class);
-
- protected final XSSFWorkbook _wb;
-
- private final Map<SXSSFSheet,XSSFSheet> _sxFromXHash = new HashMap<>();
- private final Map<XSSFSheet,SXSSFSheet> _xFromSxHash = new HashMap<>();
-
- private int _randomAccessWindowSize = DEFAULT_WINDOW_SIZE;
-
- protected interface ISheetInjector {
- void writeSheetData(OutputStream out) throws IOException;
- }
-
- /**
- * whether temp files should be compressed.
- */
- private boolean _compressTmpFiles;
-
- /**
- * shared string table - a cache of strings in this workbook
- */
- protected final SharedStringsTable _sharedStringSource;
-
- /**
- * controls whether Zip64 mode is used - Always became the default in POI 5.0.0
- */
- protected Zip64Mode zip64Mode = Zip64Mode.Always;
-
- private boolean shouldCalculateSheetDimensions = true;
-
- /**
- * Construct a new workbook with default row window size
- */
- public SXSSFWorkbook()
- {
- this(null /*workbook*/);
- }
-
- /**
- * <p>Construct a workbook from a template.</p>
- *
- * There are three use-cases to use SXSSFWorkbook(XSSFWorkbook) :
- * <ol>
- * <li>
- * Append new sheets to existing workbooks. You can open existing
- * workbook from a file or create on the fly with XSSF.
- * </li>
- * <li>
- * Append rows to existing sheets. The row number MUST be greater
- * than {@code max(rownum)} in the template sheet.
- * </li>
- * <li>
- * Use existing workbook as a template and re-use global objects such
- * as cell styles, formats, images, etc.
- * </li>
- * </ol>
- * All three use cases can work in a combination.
- *
- * What is not supported:
- * <ul>
- * <li>
- * Access initial cells and rows in the template. After constructing
- * all internal windows are empty and {@link SXSSFSheet#getRow} and
- * {@link SXSSFRow#getCell} return <code>null</code>.
- * </li>
- * <li>
- * Override existing cells and rows. The API silently allows that but
- * the output file is invalid and Excel cannot read it.
- * </li>
- * </ul>
- *
- * @param workbook the template workbook
- */
- public SXSSFWorkbook(XSSFWorkbook workbook) {
- this(workbook, DEFAULT_WINDOW_SIZE);
- }
-
-
- /**
- * Constructs an workbook from an existing workbook.
- * <p>
- * When a new node is created via {@link SXSSFSheet#createRow} and the total number
- * of unflushed records would exceed the specified value, then the
- * row with the lowest index value is flushed and cannot be accessed
- * via {@link SXSSFSheet#getRow} anymore.
- * </p>
- * <p>
- * A value of <code>-1</code> indicates unlimited access. In this case all
- * records that have not been flushed by a call to <code>flush()</code> are available
- * for random access.
- * </p>
- * <p>
- * A value of <code>0</code> is not allowed because it would flush any newly created row
- * without having a chance to specify any cells.
- * </p>
- *
- * @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see above.
- */
- public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize) {
- this(workbook, rowAccessWindowSize, false);
- }
-
- /**
- * Constructs an workbook from an existing workbook.
- * <p>
- * When a new node is created via {@link SXSSFSheet#createRow} and the total number
- * of unflushed records would exceed the specified value, then the
- * row with the lowest index value is flushed and cannot be accessed
- * via {@link SXSSFSheet#getRow} anymore.
- * </p>
- * <p>
- * A value of <code>-1</code> indicates unlimited access. In this case all
- * records that have not been flushed by a call to <code>flush()</code> are available
- * for random access.
- * </p>
- * <p>
- * A value of <code>0</code> is not allowed because it would flush any newly created row
- * without having a chance to specify any cells.
- * </p>
- *
- * @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see above.
- * @param compressTmpFiles whether to use gzip compression for temporary files
- */
- public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize, boolean compressTmpFiles) {
- this(workbook, rowAccessWindowSize, compressTmpFiles, false);
- }
-
- /**
- * Constructs an workbook from an existing workbook.
- * <p>
- * When a new node is created via {@link SXSSFSheet#createRow} and the total number
- * of unflushed records would exceed the specified value, then the
- * row with the lowest index value is flushed and cannot be accessed
- * via {@link SXSSFSheet#getRow} anymore.
- * </p>
- * <p>
- * A value of <code>-1</code> indicates unlimited access. In this case all
- * records that have not been flushed by a call to <code>flush()</code> are available
- * for random access.
- * </p>
- * <p>
- * A value of <code>0</code> is not allowed because it would flush any newly created row
- * without having a chance to specify any cells.
- * </p>
- *
- * @param workbook the template workbook
- * @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see above.
- * @param compressTmpFiles whether to use gzip compression for temporary files
- * @param useSharedStringsTable whether to use a shared strings table
- */
- public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize, boolean compressTmpFiles, boolean useSharedStringsTable) {
- setRandomAccessWindowSize(rowAccessWindowSize);
- setCompressTempFiles(compressTmpFiles);
- if (workbook == null) {
- _wb = new XSSFWorkbook();
- _sharedStringSource = useSharedStringsTable ? _wb.getSharedStringSource() : null;
- } else {
- _wb=workbook;
- _sharedStringSource = useSharedStringsTable ? _wb.getSharedStringSource() : null;
- for ( Sheet sheet : _wb ) {
- createAndRegisterSXSSFSheet( (XSSFSheet)sheet );
- }
- }
- }
-
- /**
- * Construct an empty workbook and specify the window for row access.
- * <p>
- * When a new node is created via {@link SXSSFSheet#createRow} and the total number
- * of unflushed records would exceed the specified value, then the
- * row with the lowest index value is flushed and cannot be accessed
- * via {@link SXSSFSheet#getRow} anymore.
- * </p>
- * <p>
- * A value of <code>-1</code> indicates unlimited access. In this case all
- * records that have not been flushed by a call to <code>flush()</code> are available
- * for random access.
- * </p>
- * <p>
- * A value of <code>0</code> is not allowed because it would flush any newly created row
- * without having a chance to specify any cells.
- * </p>
- *
- * @param rowAccessWindowSize the number of rows that are kept in memory until flushed out, see above.
- */
- public SXSSFWorkbook(int rowAccessWindowSize) {
- this(null /*workbook*/, rowAccessWindowSize);
- }
-
- /**
- * See the constructors for a more detailed description of the sliding window of rows.
- *
- * @return The number of rows that are kept in memory at once before flushing them out.
- */
- public int getRandomAccessWindowSize() {
- return _randomAccessWindowSize;
- }
-
- protected void setRandomAccessWindowSize(int rowAccessWindowSize) {
- if(rowAccessWindowSize == 0 || rowAccessWindowSize < -1) {
- throw new IllegalArgumentException("rowAccessWindowSize must be greater than 0 or -1");
- }
- _randomAccessWindowSize = rowAccessWindowSize;
- }
-
- /**
- * Sets the <a href="https://commons.apache.org/proper/commons-compress/apidocs/org/apache/commons/compress/archivers/zip/Zip64Mode.html">Zip64 Mode</a>
- * @param zip64Mode {@link Zip64Mode}
- *
- * @since 4.1.0
- */
- @Beta
- public void setZip64Mode(Zip64Mode zip64Mode) {
- this.zip64Mode = zip64Mode;
- }
-
- /**
- * Get whether temp files should be compressed.
- *
- * @return whether to compress temp files
- */
- public boolean isCompressTempFiles() {
- return _compressTmpFiles;
- }
-
- /**
- * Set whether temp files should be compressed.
- * <p>
- * SXSSF writes sheet data in temporary files (a temp file per-sheet)
- * and the size of these temp files can grow to to a very large size,
- * e.g. for a 20 MB csv data the size of the temp xml file become few GB large.
- * If the "compress" flag is set to <code>true</code> then the temporary XML is gzipped.
- * </p>
- * <p>
- * Please note the "compress" option may cause performance penalty.
- * </p>
- * <p>
- * Setting this option only affects compression for subsequent <code>createSheet()</code>
- * calls.
- * </p>
- * @param compress whether to compress temp files
- */
- public void setCompressTempFiles(boolean compress) {
- _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;
- }
-
- protected SheetDataWriter createSheetDataWriter() throws IOException {
- if(_compressTmpFiles) {
- return new GZIPSheetDataWriter(_sharedStringSource);
- }
-
- return new SheetDataWriter(_sharedStringSource);
- }
-
- XSSFSheet getXSSFSheet(SXSSFSheet sheet) {
- return _sxFromXHash.get(sheet);
- }
-
- SXSSFSheet getSXSSFSheet(XSSFSheet sheet) {
- return _xFromSxHash.get(sheet);
- }
-
- void registerSheetMapping(SXSSFSheet sxSheet,XSSFSheet xSheet) {
- _sxFromXHash.put(sxSheet,xSheet);
- _xFromSxHash.put(xSheet,sxSheet);
- }
-
- void deregisterSheetMapping(XSSFSheet xSheet) {
- SXSSFSheet sxSheet = getSXSSFSheet(xSheet);
- if (sxSheet != null) {
- // ensure that the writer is closed in all cases to not have lingering writers
- IOUtils.closeQuietly(sxSheet.getSheetDataWriter());
- _sxFromXHash.remove(sxSheet);
- _xFromSxHash.remove(xSheet);
- }
- }
-
- protected XSSFSheet getSheetFromZipEntryName(String sheetRef) {
- for(XSSFSheet sheet : _sxFromXHash.values()) {
- if(sheetRef.equals(sheet.getPackagePart().getPartName().getName().substring(1))) {
- return sheet;
- }
- }
- return null;
- }
-
- protected void injectData(ZipEntrySource zipEntrySource, OutputStream out) throws IOException {
- ArchiveOutputStream zos = createArchiveOutputStream(out);
- try {
- Enumeration<? extends ZipArchiveEntry> en = zipEntrySource.getEntries();
- while (en.hasMoreElements()) {
- ZipArchiveEntry ze = en.nextElement();
- ZipArchiveEntry zeOut = new ZipArchiveEntry(ze.getName());
- if (ze.getSize() >= 0) zeOut.setSize(ze.getSize());
- if (ze.getTime() >= 0) zeOut.setTime(ze.getTime());
- zos.putArchiveEntry(zeOut);
- try (final InputStream is = zipEntrySource.getInputStream(ze)) {
- if (is instanceof ZipArchiveThresholdInputStream) {
- // #59743 - disable Threshold handling for SXSSF copy
- // as users tend to put too much repetitive data in when using SXSSF :)
- ((ZipArchiveThresholdInputStream)is).setGuardState(false);
- }
- XSSFSheet xSheet = getSheetFromZipEntryName(ze.getName());
- // See bug 56557, we should not inject data into the special ChartSheets
- if (xSheet != null && !(xSheet instanceof XSSFChartSheet)) {
- SXSSFSheet sxSheet = getSXSSFSheet(xSheet);
- copyStreamAndInjectWorksheet(is, zos, createSheetInjector(sxSheet));
- } else {
- IOUtils.copy(is, zos);
- }
- } finally {
- zos.closeArchiveEntry();
- }
- }
- } finally {
- zos.finish();
- zipEntrySource.close();
- }
- }
-
- protected ZipArchiveOutputStream createArchiveOutputStream(OutputStream out) {
- if (Zip64Mode.Always.equals(zip64Mode)) {
- return new OpcZipArchiveOutputStream(out);
- } else {
- ZipArchiveOutputStream zos = new ZipArchiveOutputStream(out);
- zos.setUseZip64(zip64Mode);
- return zos;
- }
- }
-
- protected ISheetInjector createSheetInjector(SXSSFSheet sxSheet) throws IOException {
- return (output) -> {
- try (InputStream xis = sxSheet.getWorksheetXMLInputStream()) {
- // Copy the worksheet data to "output".
- IOUtils.copy(xis, output);
- }
- };
- }
-
- // private static void copyStreamAndInjectWorksheet(InputStream in, OutputStream out, InputStream worksheetData) throws IOException {
- private static void copyStreamAndInjectWorksheet(InputStream in, OutputStream out, ISheetInjector sheetInjector) throws IOException {
- Reader inReader = new InputStreamReader(in, StandardCharsets.UTF_8);
- Writer outWriter = new OutputStreamWriter(out, StandardCharsets.UTF_8);
- boolean needsStartTag = true;
- int c;
- int pos=0;
- String s="<sheetData";
- int n=s.length();
- //Copy from "in" to "out" up to the string "<sheetData/>" or "</sheetData>" (excluding).
- while(((c=inReader.read())!=-1)) {
- if(c==s.charAt(pos)) {
- pos++;
- if(pos==n) {
- if ("<sheetData".equals(s)) {
- c = inReader.read();
- if (c == -1) {
- outWriter.write(s);
- break;
- }
- if (c == '>') {
- // Found <sheetData>
- outWriter.write(s);
- outWriter.write(c);
- s = "</sheetData>";
- n = s.length();
- pos = 0;
- needsStartTag = false;
- continue;
- }
- if (c == '/') {
- // Found <sheetData/
- c = inReader.read();
- if (c == -1) {
- outWriter.write(s);
- break;
- }
- if (c == '>') {
- // Found <sheetData/>
- break;
- }
-
- outWriter.write(s);
- outWriter.write('/');
- outWriter.write(c);
- pos = 0;
- continue;
- }
-
- outWriter.write(s);
- outWriter.write('/');
- outWriter.write(c);
- pos = 0;
- continue;
- } else {
- // Found </sheetData>
- break;
- }
- }
- } else {
- if(pos>0) {
- outWriter.write(s,0,pos);
- }
- if(c==s.charAt(0)) {
- pos=1;
- } else {
- outWriter.write(c);
- pos=0;
- }
- }
- }
- outWriter.flush();
- if (needsStartTag) {
- outWriter.write("<sheetData>\n");
- outWriter.flush();
- }
- sheetInjector.writeSheetData(out);
- outWriter.write("</sheetData>");
- outWriter.flush();
- //Copy the rest of "in" to "out".
- while(((c=inReader.read())!=-1)) {
- outWriter.write(c);
- }
- outWriter.flush();
- }
-
- public XSSFWorkbook getXSSFWorkbook() {
- return _wb;
- }
-
- //start of interface implementation
-
- /**
- * Convenience method to get the active sheet. The active sheet is is the sheet
- * which is currently displayed when the workbook is viewed in Excel.
- * 'Selected' sheet(s) is a distinct concept.
- *
- * @return the index of the active sheet (0-based)
- */
- @Override
- public int getActiveSheetIndex() {
- return _wb.getActiveSheetIndex();
- }
-
- /**
- * Convenience method to set the active sheet. The active sheet is is the sheet
- * which is currently displayed when the workbook is viewed in Excel.
- * 'Selected' sheet(s) is a distinct concept.
- *
- * @param sheetIndex index of the active sheet (0-based)
- */
- @Override
- public void setActiveSheet(int sheetIndex) {
- _wb.setActiveSheet(sheetIndex);
- }
-
- /**
- * Gets the first tab that is displayed in the list of tabs in excel.
- *
- * @return the first tab that to display in the list of tabs (0-based).
- */
- @Override
- public int getFirstVisibleTab() {
- return _wb.getFirstVisibleTab();
- }
-
- /**
- * Sets the first tab that is displayed in the list of tabs in excel.
- *
- * @param sheetIndex the first tab that to display in the list of tabs (0-based)
- */
- @Override
- public void setFirstVisibleTab(int sheetIndex) {
- _wb.setFirstVisibleTab(sheetIndex);
- }
-
- /**
- * Sets the order of appearance for a given sheet.
- *
- * @param sheetname the name of the sheet to reorder
- * @param pos the position that we want to insert the sheet into (0 based)
- */
- @Override
- public void setSheetOrder(String sheetname, int pos) {
- _wb.setSheetOrder(sheetname,pos);
- }
-
- /**
- * Sets the tab whose data is actually seen when the sheet is opened.
- * This may be different from the "selected sheet" since excel seems to
- * allow you to show the data of one sheet when another is seen "selected"
- * in the tabs (at the bottom).
- *
- * @see Sheet#setSelected(boolean)
- * @param index the index of the sheet to select (0 based)
- */
- @Override
- public void setSelectedTab(int index) {
- _wb.setSelectedTab(index);
- }
-
- /**
- * Set the sheet name.
- *
- * @param sheet number (0 based)
- * @throws IllegalArgumentException if the name is greater than 31 chars or contains <code>/\?*[]</code>
- */
- @Override
- public void setSheetName(int sheet, String name) {
- _wb.setSheetName(sheet,name);
- }
-
- /**
- * Set the sheet name
- *
- * @param sheet sheet number (0 based)
- * @return Sheet name
- */
- @Override
- public String getSheetName(int sheet) {
- return _wb.getSheetName(sheet);
- }
-
- /**
- * Returns the index of the sheet by his name
- *
- * @param name the sheet name
- * @return index of the sheet (0 based)
- */
- @Override
- public int getSheetIndex(String name) {
- return _wb.getSheetIndex(name);
- }
-
- /**
- * Returns the index of the given sheet
- *
- * @param sheet the sheet to look up
- * @return index of the sheet (0 based)
- */
- @Override
- public int getSheetIndex(Sheet sheet) {
- return _wb.getSheetIndex(getXSSFSheet((SXSSFSheet)sheet));
- }
-
- /**
- * Create a Sheet for this Workbook, adds it to the sheets and returns
- * the high level representation. Use this to create new sheets.
- *
- * @return Sheet representing the new sheet.
- */
- @Override
- public SXSSFSheet createSheet() {
- return createAndRegisterSXSSFSheet(_wb.createSheet());
- }
-
- SXSSFSheet createAndRegisterSXSSFSheet(XSSFSheet xSheet) {
- final SXSSFSheet sxSheet;
- try {
- sxSheet = new SXSSFSheet(this,xSheet);
- } catch (IOException ioe) {
- throw new IllegalStateException(ioe);
- }
- registerSheetMapping(sxSheet,xSheet);
- return sxSheet;
- }
-
- /**
- * Create a Sheet for this Workbook, adds it to the sheets and returns
- * the high level representation. Use this to create new sheets.
- *
- * @param sheetname sheetname to set for the sheet.
- * @return Sheet representing the new sheet.
- * @throws IllegalArgumentException if the name is greater than 31 chars or contains <code>/\?*[]</code>
- */
- @Override
- public SXSSFSheet createSheet(String sheetname) {
- return createAndRegisterSXSSFSheet(_wb.createSheet(sheetname));
- }
-
- /**
- * <i>Not implemented for SXSSFWorkbook</i>
- *
- * Create a Sheet from an existing sheet in the Workbook.
- *
- * @return Sheet representing the cloned sheet.
- */
- @Override
- @NotImplemented
- public Sheet cloneSheet(int sheetNum) {
- throw new IllegalStateException("Not Implemented");
- }
-
-
- /**
- * Get the number of spreadsheets in the workbook
- *
- * @return the number of sheets
- */
- @Override
- public int getNumberOfSheets() {
- return _wb.getNumberOfSheets();
- }
-
- /**
- * Returns an iterator of the sheets in the workbook
- * in sheet order. Includes hidden and very hidden sheets.
- *
- * @return an iterator of the sheets.
- */
- @Override
- public Iterator<Sheet> sheetIterator() {
- return new SheetIterator<>();
- }
-
- /**
- * Returns a spliterator of the sheets in the workbook
- * in sheet order. Includes hidden and very hidden sheets.
- *
- * @return a spliterator of the sheets.
- *
- * @since POI 5.2.0
- */
- @Override
- public Spliterator<Sheet> spliterator() {
- return _wb.spliterator();
- }
-
- protected final class SheetIterator<T extends Sheet> implements Iterator<T> {
- final private Iterator<XSSFSheet> it;
- @SuppressWarnings("unchecked")
- public SheetIterator() {
- it = (Iterator<XSSFSheet>)(Iterator<? extends Sheet>) _wb.iterator();
- }
- @Override
- public boolean hasNext() {
- return it.hasNext();
- }
- @Override
- @SuppressWarnings("unchecked")
- public T next() throws NoSuchElementException {
- final XSSFSheet xssfSheet = it.next();
- return (T) getSXSSFSheet(xssfSheet);
- }
- /**
- * Unexpected behavior may occur if sheets are reordered after iterator
- * has been created. Support for the remove method may be added in the future
- * if someone can figure out a reliable implementation.
- */
- @Override
- public void remove() throws IllegalStateException {
- throw new UnsupportedOperationException("remove method not supported on XSSFWorkbook.iterator(). "+
- "Use Sheet.removeSheetAt(int) instead.");
- }
- }
-
- /**
- * Get the Sheet object at the given index.
- *
- * @param index of the sheet number (0-based physical and logical)
- * @return Sheet at the provided index
- */
- @Override
- public SXSSFSheet getSheetAt(int index) {
- return getSXSSFSheet(_wb.getSheetAt(index));
- }
-
- /**
- * Get sheet with the given name
- *
- * @param name of the sheet
- * @return Sheet with the name provided or <code>null</code> if it does not exist
- */
- @Override
- public SXSSFSheet getSheet(String name) {
- return getSXSSFSheet(_wb.getSheet(name));
- }
-
- /**
- * Removes sheet at the given index
- *
- * @param index of the sheet to remove (0-based)
- */
- @Override
- public void removeSheetAt(int index) {
- // Get the sheet to be removed
- XSSFSheet xSheet = _wb.getSheetAt(index);
- SXSSFSheet sxSheet = getSXSSFSheet(xSheet);
-
- // De-register it
- _wb.removeSheetAt(index);
- deregisterSheetMapping(xSheet);
-
- // Clean up temporary resources
- try {
- sxSheet.dispose();
- } catch (IOException e) {
- LOG.atWarn().withThrowable(e).log("Failed to dispose old sheet");
- }
- }
-
- /**
- * Create a new Font and add it to the workbook's font table
- *
- * @return new font object
- */
- @Override
- public Font createFont() {
- return _wb.createFont();
- }
-
- /**
- * Finds a font that matches the one with the supplied attributes
- *
- * @return the font with the matched attributes or <code>null</code>
- */
- @Override
- public Font findFont(boolean bold, short color, short fontHeight, String name, boolean italic, boolean strikeout, short typeOffset, byte underline) {
- return _wb.findFont(bold, color, fontHeight, name, italic, strikeout, typeOffset, underline);
- }
-
- @Override
- public int getNumberOfFonts() {
- return _wb.getNumberOfFonts();
- }
-
- @Override
- @Deprecated
- @Removal(version = "6.0.0")
- public int getNumberOfFontsAsInt() {
- return getNumberOfFonts();
- }
-
- @Override
- public Font getFontAt(int idx) {
- return _wb.getFontAt(idx);
- }
-
- /**
- * Create a new Cell style and add it to the workbook's style table
- *
- * @return the new Cell Style object
- */
- @Override
- public CellStyle createCellStyle() {
- return _wb.createCellStyle();
- }
-
- /**
- * Get the number of styles the workbook contains
- *
- * @return count of cell styles
- */
- @Override
- public int getNumCellStyles() {
- return _wb.getNumCellStyles();
- }
-
- /**
- * Get the cell style object at the given index
- *
- * @param idx index within the set of styles (0-based)
- * @return CellStyle object at the index
- */
- @Override
- public CellStyle getCellStyleAt(int idx) {
- return _wb.getCellStyleAt(idx);
- }
-
- /**
- * Closes the underlying {@link XSSFWorkbook} and {@link OPCPackage}
- * on which this Workbook is based, if any.
- *
- * <p>Once this has been called, no further
- * operations, updates or reads should be performed on the
- * Workbook.
- */
- @Override
- public void close() throws IOException {
- // ensure that any lingering writer is closed
- for (SXSSFSheet sheet : _xFromSxHash.values()) {
- try {
- SheetDataWriter _writer = sheet.getSheetDataWriter();
- if (_writer != null) _writer.close();
- } catch (IOException e) {
- LOG.atWarn().withThrowable(e).log("An exception occurred while closing sheet data writer for sheet {}.", sheet.getSheetName());
- }
- }
-
-
- // Tell the base workbook to close, does nothing if
- // it's a newly created one
- _wb.close();
- }
-
- /**
- * Write out this workbook to an OutputStream.
- *
- * @param stream - the java OutputStream you wish to write to
- * @throws IOException if anything can't be written.
- */
- @Override
- public void write(OutputStream stream) throws IOException {
- flushSheets();
-
- //Save the template
- File tmplFile = TempFile.createTempFile("poi-sxssf-template", ".xlsx");
- boolean deleted;
- try {
- try (FileOutputStream os = new FileOutputStream(tmplFile)) {
- _wb.write(os);
- }
-
- //Substitute the template entries with the generated sheet data files
- try (
- ZipSecureFile zf = new ZipSecureFile(tmplFile);
- ZipFileZipEntrySource source = new ZipFileZipEntrySource(zf)
- ) {
- injectData(source, stream);
- }
- } finally {
- deleted = tmplFile.delete();
- }
- if (!deleted) {
- throw new IOException("Could not delete temporary file after processing: " + tmplFile);
- }
- }
-
- /**
- * Write out this workbook to an OutputStream. This (experimental) method avoids the temp file that
- * {@link #write} creates but will use more memory as a result. Other SXSSF code can create temp files,
- * so using this does not guarantee that there will be no temp file usage.
- *
- * @param stream - the java OutputStream you wish to write to
- * @throws IOException if anything can't be written.
- * @since POI 5.1.0 (experimental and still liable to change or be removed)
- */
- @Beta
- public void writeAvoidingTempFiles(OutputStream stream) throws IOException {
- flushSheets();
-
- //Save the template
- try (UnsynchronizedByteArrayOutputStream bos = UnsynchronizedByteArrayOutputStream.builder().get()) {
- _wb.write(bos);
-
- //Substitute the template entries with the generated sheet data files
- try (
- InputStream is = bos.toInputStream();
- ZipArchiveInputStream zis = new ZipArchiveInputStream(is);
- ZipInputStreamZipEntrySource source = new ZipInputStreamZipEntrySource(
- new ZipArchiveThresholdInputStream(zis))
- ) {
- injectData(source, stream);
- }
- }
- }
-
- protected void flushSheets() throws IOException {
- for (SXSSFSheet sheet : _xFromSxHash.values()) {
- sheet.deriveDimension();
- sheet.flushRows();
- }
- }
-
- /**
- * Dispose of temporary files backing this workbook on disk.
- * Calling this method will render the workbook unusable.
- * @return true if all temporary files were deleted successfully.
- */
- public boolean dispose() {
- boolean success = true;
- for (SXSSFSheet sheet : _sxFromXHash.keySet()) {
- try {
- success = sheet.dispose() && success;
- } catch (IOException e) {
- LOG.atWarn().withThrowable(e).log("Failed to dispose sheet");
- success = false;
- }
- }
- return success;
- }
-
- /**
- * @return the total number of defined names in this workbook
- */
- @Override
- public int getNumberOfNames() {
- return _wb.getNumberOfNames();
- }
-
- /**
- * @param name the name of the defined name
- * @return the defined name with the specified name. <code>null</code> if not found.
- */
- @Override
- public Name getName(String name) {
- return _wb.getName(name);
- }
-
- /**
- * Returns all defined names with the given name.
- *
- * @param name the name of the defined name
- * @return a list of the defined names with the specified name. An empty list is returned if none is found.
- */
- @Override
- public List<? extends Name> getNames(String name) {
- return _wb.getNames(name);
- }
-
- /**
- * Returns all defined names
- *
- * @return all defined names
- */
- @Override
- public List<? extends Name> getAllNames() {
- return _wb.getAllNames();
- }
-
- /**
- * Creates a new (uninitialised) defined name in this workbook
- *
- * @return new defined name object
- */
- @Override
- public Name createName() {
- return _wb.createName();
- }
-
- /**
- * Remove the given defined name
- *
- * @param name the name to remove
- */
- @Override
- public void removeName(Name name) {
- _wb.removeName(name);
- }
-
- /**
- * Sets the printarea for the sheet provided
- * <p>
- * i.e. Reference = $A$1:$B$2
- * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
- * @param reference Valid name Reference for the Print Area
- */
- @Override
- public void setPrintArea(int sheetIndex, String reference) {
- _wb.setPrintArea(sheetIndex,reference);
- }
-
- /**
- * For the Convenience of Java Programmers maintaining pointers.
- * @see #setPrintArea(int, String)
- * @param sheetIndex Zero-based sheet index (0 = First Sheet)
- * @param startColumn Column to begin printarea
- * @param endColumn Column to end the printarea
- * @param startRow Row to begin the printarea
- * @param endRow Row to end the printarea
- */
- @Override
- public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) {
- _wb.setPrintArea(sheetIndex, startColumn, endColumn, startRow, endRow);
- }
-
- /**
- * Retrieves the reference for the printarea of the specified sheet,
- * the sheet name is appended to the reference even if it was not specified.
- *
- * @param sheetIndex Zero-based sheet index (0 Represents the first sheet to keep consistent with java)
- * @return String Null if no print area has been defined
- */
- @Override
- public String getPrintArea(int sheetIndex) {
- return _wb.getPrintArea(sheetIndex);
- }
-
- /**
- * Delete the printarea for the sheet specified
- *
- * @param sheetIndex Zero-based sheet index (0 = First Sheet)
- */
- @Override
- public void removePrintArea(int sheetIndex) {
- _wb.removePrintArea(sheetIndex);
- }
-
- /**
- * Retrieves the current policy on what to do when
- * getting missing or blank cells from a row.
- * <p>
- * The default is to return blank and null cells.
- * {@link MissingCellPolicy}
- * </p>
- */
- @Override
- public MissingCellPolicy getMissingCellPolicy() {
- return _wb.getMissingCellPolicy();
- }
-
- /**
- * Sets the policy on what to do when
- * getting missing or blank cells from a row.
- *
- * This will then apply to all calls to
- * {@link Row#getCell(int)}. See
- * {@link MissingCellPolicy}
- */
- @Override
- public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) {
- _wb.setMissingCellPolicy(missingCellPolicy);
- }
-
- /**
- * Returns the instance of DataFormat for this workbook.
- *
- * @return the DataFormat object
- */
- @Override
- public DataFormat createDataFormat() {
- return _wb.createDataFormat();
- }
-
- /**
- * Adds a picture to the workbook.
- *
- * @param pictureData The bytes of the picture
- * @param format The format of the picture.
- *
- * @return the index to this picture (1 based).
- * @see #PICTURE_TYPE_EMF
- * @see #PICTURE_TYPE_WMF
- * @see #PICTURE_TYPE_PICT
- * @see #PICTURE_TYPE_JPEG
- * @see #PICTURE_TYPE_PNG
- * @see #PICTURE_TYPE_DIB
- */
- @Override
- public int addPicture(byte[] pictureData, int format) {
- return _wb.addPicture(pictureData,format);
- }
-
- /**
- * Gets all pictures from the Workbook.
- *
- * @return the list of pictures (a list of {@link PictureData} objects.)
- */
- @Override
- public List<? extends PictureData> getAllPictures() {
- return _wb.getAllPictures();
- }
-
- /**
- * Returns an object that handles instantiating concrete
- * classes of the various instances one needs for HSSF, XSSF
- * and SXSSF.
- */
- @Override
- public CreationHelper getCreationHelper() {
- return new SXSSFCreationHelper(this);
- }
-
- protected boolean isDate1904() {
- return _wb.isDate1904();
- }
-
- @Override
- @NotImplemented("XSSFWorkbook#isHidden is not implemented")
- public boolean isHidden() {
- return _wb.isHidden();
- }
-
- @Override
- @NotImplemented("XSSFWorkbook#setHidden is not implemented")
- public void setHidden(boolean hiddenFlag) {
- _wb.setHidden(hiddenFlag);
- }
-
- @Override
- public boolean isSheetHidden(int sheetIx) {
- return _wb.isSheetHidden(sheetIx);
- }
-
- @Override
- public boolean isSheetVeryHidden(int sheetIx) {
- return _wb.isSheetVeryHidden(sheetIx);
- }
-
- @Override
- public SheetVisibility getSheetVisibility(int sheetIx) {
- return _wb.getSheetVisibility(sheetIx);
- }
-
- @Override
- public void setSheetHidden(int sheetIx, boolean hidden) {
- _wb.setSheetHidden(sheetIx,hidden);
- }
-
- @Override
- public void setSheetVisibility(int sheetIx, SheetVisibility visibility) {
- _wb.setSheetVisibility(sheetIx, visibility);
- }
-
- /**
- * <i>Not implemented for SXSSFWorkbook</i>
- *
- * Adds the LinkTable records required to allow formulas referencing
- * the specified external workbook to be added to this one. Allows
- * formulas such as "[MyOtherWorkbook]Sheet3!$A$5" to be added to the
- * file, for workbooks not already referenced.
- *
- * Note: this is not implemented and thus currently throws an Exception stating this.
- *
- * @param name The name the workbook will be referenced as in formulas
- * @param workbook The open workbook to fetch the link required information from
- *
- * @throws IllegalStateException stating that this method is not implemented yet.
- */
- @Override
- @NotImplemented
- public int linkExternalWorkbook(String name, Workbook workbook) {
- throw new IllegalStateException("Not Implemented");
- }
-
- /**
- * Register a new toolpack in this workbook.
- *
- * @param toolpack the toolpack to register
- */
- @Override
- public void addToolPack(UDFFinder toolpack) {
- _wb.addToolPack(toolpack);
- }
-
- /**
- * Whether the application shall perform a full recalculation when the workbook is opened.
- * <p>
- * Typically you want to force formula recalculation when you modify cell formulas or values
- * of a workbook previously created by Excel. When set to 0, this flag will tell Excel
- * that it needs to recalculate all formulas in the workbook the next time the file is opened.
- * </p>
- *
- * @param value true if the application will perform a full recalculation of
- * workbook values when the workbook is opened
- * @since 3.8
- */
- @Override
- public void setForceFormulaRecalculation(boolean value) {
- _wb.setForceFormulaRecalculation(value);
- }
-
- /**
- * Whether Excel will be asked to recalculate all formulas when the workbook is opened.
- */
- @Override
- public boolean getForceFormulaRecalculation() {
- return _wb.getForceFormulaRecalculation();
- }
-
- /**
- * Returns the spreadsheet version (EXCEL2007) of this workbook
- *
- * @return EXCEL2007 SpreadsheetVersion enum
- * @since 3.14 beta 2
- */
- @Override
- public SpreadsheetVersion getSpreadsheetVersion() {
- return SpreadsheetVersion.EXCEL2007;
- }
-
- @Override
- public int addOlePackage(byte[] oleData, String label, String fileName, String command) throws IOException {
- return _wb.addOlePackage(oleData, label, fileName, command);
- }
-
- @Override
- public EvaluationWorkbook createEvaluationWorkbook() {
- return SXSSFEvaluationWorkbook.create(this);
- }
-
- @Override
- public CellReferenceType getCellReferenceType() {
- return getXSSFWorkbook().getCellReferenceType();
- }
-
- @Override
- public void setCellReferenceType(CellReferenceType cellReferenceType) {
- getXSSFWorkbook().setCellReferenceType(cellReferenceType);
- }
- }
|