Patch 51634 - support SXSSF streaming from templates

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1156544 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2011-08-11 08:54:11 +00:00
parent bde41cbbe6
commit 6663b6ea9f
5 changed files with 214 additions and 16 deletions

View File

@ -34,6 +34,7 @@
<changes> <changes>
<release version="3.8-beta4" date="2011-??-??"> <release version="3.8-beta4" date="2011-??-??">
<action dev="poi-developers" type="add">51634 - support SXSSF streaming from templates</action>
<action dev="poi-developers" type="add">initial support for XSLF usermodel API</action> <action dev="poi-developers" type="add">initial support for XSLF usermodel API</action>
<action dev="poi-developers" type="fix">51187 - fixed OPCPackage to correctly handle self references</action> <action dev="poi-developers" type="fix">51187 - fixed OPCPackage to correctly handle self references</action>
<action dev="poi-developers" type="fix">51635 - Improved performance of XSSFSheet#write</action> <action dev="poi-developers" type="fix">51635 - Improved performance of XSSFSheet#write</action>

View File

@ -44,11 +44,12 @@ public class SXSSFSheet implements Sheet, Cloneable
SheetDataWriter _writer; SheetDataWriter _writer;
int _randomAccessWindowSize = SXSSFWorkbook.DEFAULT_WINDOW_SIZE; int _randomAccessWindowSize = SXSSFWorkbook.DEFAULT_WINDOW_SIZE;
public SXSSFSheet(SXSSFWorkbook workbook,XSSFSheet xSheet) throws IOException public SXSSFSheet(SXSSFWorkbook workbook, XSSFSheet xSheet) throws IOException
{ {
_workbook=workbook; _workbook=workbook;
_sh=xSheet; _sh=xSheet;
_writer=new SheetDataWriter(); _writer=new SheetDataWriter();
setRandomAccessWindowSize(_workbook.getRandomAccessWindowSize());
} }
/* Gets "<sheetData>" document fragment*/ /* Gets "<sheetData>" document fragment*/
@ -1286,7 +1287,6 @@ public class SXSSFSheet implements Sheet, Cloneable
_fd = File.createTempFile("poi-sxxsf-sheet", ".xml"); _fd = File.createTempFile("poi-sxxsf-sheet", ".xml");
_fd.deleteOnExit(); _fd.deleteOnExit();
_out = new BufferedWriter(new FileWriter(_fd)); _out = new BufferedWriter(new FileWriter(_fd));
_out.write("<sheetData>\n");
} }
public int getNumberOfFlushedRows() public int getNumberOfFlushedRows()
{ {
@ -1306,7 +1306,6 @@ public class SXSSFSheet implements Sheet, Cloneable
} }
public InputStream getWorksheetXMLInputStream() throws IOException public InputStream getWorksheetXMLInputStream() throws IOException
{ {
_out.write("</sheetData>");
_out.flush(); _out.flush();
_out.close(); _out.close();
return new FileInputStream(_fd); return new FileInputStream(_fd);

View File

@ -59,25 +59,67 @@ public class SXSSFWorkbook implements Workbook
*/ */
public static final int DEFAULT_WINDOW_SIZE = 100; public static final int DEFAULT_WINDOW_SIZE = 100;
XSSFWorkbook _wb=new XSSFWorkbook(); XSSFWorkbook _wb;
HashMap<SXSSFSheet,XSSFSheet> _sxFromXHash=new HashMap<SXSSFSheet,XSSFSheet>(); HashMap<SXSSFSheet,XSSFSheet> _sxFromXHash=new HashMap<SXSSFSheet,XSSFSheet>();
HashMap<XSSFSheet,SXSSFSheet> _xFromSxHash=new HashMap<XSSFSheet,SXSSFSheet>(); HashMap<XSSFSheet,SXSSFSheet> _xFromSxHash=new HashMap<XSSFSheet,SXSSFSheet>();
int _randomAccessWindowSize = DEFAULT_WINDOW_SIZE; private int _randomAccessWindowSize = DEFAULT_WINDOW_SIZE;
/** /**
* Construct a new workbook * Construct a new workbook
*/ */
public SXSSFWorkbook(){ public SXSSFWorkbook(){
this(null /*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 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 getRow() anymore.
* </p>
* <p>
* A value of -1 indicates unlimited access. In this case all
* records that have not been flushed by a call to flush() are available
* for random access.
* <p>
* <p></p>
* A value of 0 is not allowed because it would flush any newly created row
* without having a chance to specify any cells.
* </p>
*
* @param rowAccessWindowSize
*/
public SXSSFWorkbook(XSSFWorkbook workbook, int rowAccessWindowSize){
setRandomAccessWindowSize(rowAccessWindowSize);
if (workbook == null)
{
_wb=new XSSFWorkbook();
}
else
{
_wb=workbook;
for ( int i = 0; i < _wb.getNumberOfSheets(); i++ )
{
XSSFSheet sheet = _wb.getSheetAt( i );
createAndRegisterSXSSFSheet( sheet );
}
}
} }
/** /**
* Construct an empty workbook and specify the window for row access. * Construct an empty workbook and specify the window for row access.
* <p> * <p>
* When a new node is created via createRow() and the total number * When a new node is created via createRow() and the total number
* of unflushed records would exeed the specified value, then the * of unflushed records would exceed the specified value, then the
* row with the lowest index value is flushed and cannot be accessed * row with the lowest index value is flushed and cannot be accessed
* via getRow() anymore. * via getRow() anymore.
* </p> * </p>
@ -94,6 +136,15 @@ public class SXSSFWorkbook implements Workbook
* @param rowAccessWindowSize * @param rowAccessWindowSize
*/ */
public SXSSFWorkbook(int rowAccessWindowSize){ public SXSSFWorkbook(int rowAccessWindowSize){
this(null /*workbook*/, rowAccessWindowSize);
}
public int getRandomAccessWindowSize()
{
return _randomAccessWindowSize;
}
private void setRandomAccessWindowSize(int rowAccessWindowSize)
{
if(rowAccessWindowSize == 0 || rowAccessWindowSize < -1) { if(rowAccessWindowSize == 0 || rowAccessWindowSize < -1) {
throw new IllegalArgumentException("rowAccessWindowSize must be greater than 0 or -1"); throw new IllegalArgumentException("rowAccessWindowSize must be greater than 0 or -1");
} }
@ -168,20 +219,75 @@ public class SXSSFWorkbook implements Workbook
out.write(chunk,0,count); out.write(chunk,0,count);
} }
} }
private static void copyStreamAndInjectWorksheet(InputStream in, OutputStream out,InputStream worksheetData) throws IOException { private static void copyStreamAndInjectWorksheet(InputStream in, OutputStream out, InputStream worksheetData) throws IOException {
InputStreamReader inReader=new InputStreamReader(in,"UTF-8"); //TODO: Is it always UTF-8 or do we need to read the xml encoding declaration in the file? If not, we should perhaps use a SAX reader instead. InputStreamReader inReader=new InputStreamReader(in,"UTF-8"); //TODO: Is it always UTF-8 or do we need to read the xml encoding declaration in the file? If not, we should perhaps use a SAX reader instead.
OutputStreamWriter outWriter=new OutputStreamWriter(out,"UTF-8"); OutputStreamWriter outWriter=new OutputStreamWriter(out,"UTF-8");
boolean needsStartTag = true;
int c; int c;
int pos=0; int pos=0;
String s="<sheetData/>"; String s="<sheetData";
int n=s.length(); int n=s.length();
//Copy from "in" to "out" up to the string "<sheetData/>" (excluding). //Copy from "in" to "out" up to the string "<sheetData/>" or "</sheetData>" (excluding).
while(((c=inReader.read())!=-1)) while(((c=inReader.read())!=-1))
{ {
if(c==s.charAt(pos)) if(c==s.charAt(pos))
{ {
pos++; pos++;
if(pos==n) break; 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 else
{ {
@ -198,8 +304,15 @@ public class SXSSFWorkbook implements Workbook
} }
} }
outWriter.flush(); outWriter.flush();
if (needsStartTag)
{
outWriter.write("<sheetData>\n");
outWriter.flush();
}
//Copy the worksheet data to "out". //Copy the worksheet data to "out".
copyStream(worksheetData,out); copyStream(worksheetData,out);
outWriter.write("</sheetData>");
outWriter.flush();
//Copy the rest of "in" to "out". //Copy the rest of "in" to "out".
while(((c=inReader.read())!=-1)) while(((c=inReader.read())!=-1))
outWriter.write(c); outWriter.write(c);
@ -348,7 +461,6 @@ public class SXSSFWorkbook implements Workbook
{ {
throw new RuntimeException(ioe); throw new RuntimeException(ioe);
} }
sxSheet.setRandomAccessWindowSize(_randomAccessWindowSize);
registerSheetMapping(sxSheet,xSheet); registerSheetMapping(sxSheet,xSheet);
return sxSheet; return sxSheet;
} }
@ -532,6 +644,11 @@ public class SXSSFWorkbook implements Workbook
*/ */
public void write(OutputStream stream) throws IOException public void write(OutputStream stream) throws IOException
{ {
for (SXSSFSheet sheet : _xFromSxHash.values())
{
sheet.flushRows();
}
//Save the template //Save the template
File tmplFile = File.createTempFile("poi-sxxsf-template", ".xlsx"); File tmplFile = File.createTempFile("poi-sxxsf-template", ".xlsx");
tmplFile.deleteOnExit(); tmplFile.deleteOnExit();

View File

@ -20,7 +20,6 @@
package org.apache.poi.xssf; package org.apache.poi.xssf;
import org.apache.poi.POIDataSamples; import org.apache.poi.POIDataSamples;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.ITestDataProvider; import org.apache.poi.ss.ITestDataProvider;
import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.Workbook;
@ -42,7 +41,8 @@ public final class SXSSFITestDataProvider implements ITestDataProvider {
// enforce singleton // enforce singleton
} }
public Workbook openSampleWorkbook(String sampleFileName) { public Workbook openSampleWorkbook(String sampleFileName) {
throw new IllegalArgumentException("SXSSF cannot read files"); XSSFWorkbook xssfWorkbook = XSSFITestDataProvider.instance.openSampleWorkbook(sampleFileName);
return new SXSSFWorkbook(xssfWorkbook);
} }
public Workbook writeOutAndReadBack(Workbook wb) { public Workbook writeOutAndReadBack(Workbook wb) {

View File

@ -20,7 +20,12 @@
package org.apache.poi.xssf.usermodel.streaming; package org.apache.poi.xssf.usermodel.streaming;
import org.apache.poi.ss.usermodel.BaseTestWorkbook; import org.apache.poi.ss.usermodel.BaseTestWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.SXSSFITestDataProvider; import org.apache.poi.xssf.SXSSFITestDataProvider;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public final class TestSXSSFWorkbook extends BaseTestWorkbook { public final class TestSXSSFWorkbook extends BaseTestWorkbook {
@ -55,5 +60,81 @@ public final class TestSXSSFWorkbook extends BaseTestWorkbook {
"Only XSSFCells can be evaluated.", e.getMessage()); "Only XSSFCells can be evaluated.", e.getMessage());
} }
} }
public void testExistingWorkbook() {
XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
xssfWorkbook.createSheet("S1");
SXSSFWorkbook wb = new SXSSFWorkbook(xssfWorkbook);
xssfWorkbook = (XSSFWorkbook) SXSSFITestDataProvider.instance.writeOutAndReadBack(wb);
wb = new SXSSFWorkbook(xssfWorkbook);
assertEquals(1, wb.getNumberOfSheets());
Sheet sheet = wb.getSheetAt(0);
assertNotNull(sheet);
assertEquals("S1", sheet.getSheetName());
}
public void testAddToExistingWorkbook() {
XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
xssfWorkbook.createSheet("S1");
Sheet sheet = xssfWorkbook.createSheet("S2");
Row row = sheet.createRow(1);
Cell cell = row.createCell(1);
cell.setCellValue("value 2_1_1");
SXSSFWorkbook wb = new SXSSFWorkbook(xssfWorkbook);
xssfWorkbook = (XSSFWorkbook) SXSSFITestDataProvider.instance.writeOutAndReadBack(wb);
wb = new SXSSFWorkbook(xssfWorkbook);
// Add a row to the existing empty sheet
Sheet sheet1 = wb.getSheetAt(0);
Row row1_1 = sheet1.createRow(1);
Cell cell1_1_1 = row1_1.createCell(1);
cell1_1_1.setCellValue("value 1_1_1");
// Add a row to the existing non-empty sheet
Sheet sheet2 = wb.getSheetAt(1);
Row row2_2 = sheet2.createRow(2);
Cell cell2_2_1 = row2_2.createCell(1);
cell2_2_1.setCellValue("value 2_2_1");
// Add a sheet with one row
Sheet sheet3 = wb.createSheet("S3");
Row row3_1 = sheet3.createRow(1);
Cell cell3_1_1 = row3_1.createCell(1);
cell3_1_1.setCellValue("value 3_1_1");
xssfWorkbook = (XSSFWorkbook) SXSSFITestDataProvider.instance.writeOutAndReadBack(wb);
assertEquals(3, xssfWorkbook.getNumberOfSheets());
// Verify sheet 1
sheet1 = xssfWorkbook.getSheetAt(0);
assertEquals("S1", sheet1.getSheetName());
assertEquals(1, sheet1.getPhysicalNumberOfRows());
row1_1 = sheet1.getRow(1);
assertNotNull(row1_1);
cell1_1_1 = row1_1.getCell(1);
assertNotNull(cell1_1_1);
assertEquals("value 1_1_1", cell1_1_1.getStringCellValue());
// Verify sheet 2
sheet2 = xssfWorkbook.getSheetAt(1);
assertEquals("S2", sheet2.getSheetName());
assertEquals(2, sheet2.getPhysicalNumberOfRows());
Row row2_1 = sheet2.getRow(1);
assertNotNull(row2_1);
Cell cell2_1_1 = row2_1.getCell(1);
assertNotNull(cell2_1_1);
assertEquals("value 2_1_1", cell2_1_1.getStringCellValue());
row2_2 = sheet2.getRow(2);
assertNotNull(row2_2);
cell2_2_1 = row2_2.getCell(1);
assertNotNull(cell2_2_1);
assertEquals("value 2_2_1", cell2_2_1.getStringCellValue());
// Verify sheet 3
sheet3 = xssfWorkbook.getSheetAt(2);
assertEquals("S3", sheet3.getSheetName());
assertEquals(1, sheet3.getPhysicalNumberOfRows());
row3_1 = sheet3.getRow(1);
assertNotNull(row3_1);
cell3_1_1 = row3_1.getCell(1);
assertNotNull(cell3_1_1);
assertEquals("value 3_1_1", cell3_1_1.getStringCellValue());
}
} }