Browse Source

Patch from Javen ONeal from bug #58245 - Make Workbook support iterating over Sheets

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1703573 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_13_FINAL
Nick Burch 8 years ago
parent
commit
64c5d60d33

+ 49
- 1
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java View File

@@ -33,6 +33,7 @@ import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Pattern;

import org.apache.commons.codec.digest.DigestUtils;
@@ -82,6 +83,7 @@ import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
import org.apache.poi.ss.formula.udf.IndexedUDFFinder;
import org.apache.poi.ss.formula.udf.UDFFinder;
import org.apache.poi.ss.usermodel.Row.MissingCellPolicy;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.WorkbookUtil;
@@ -913,12 +915,58 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
sheet.setActive(isOnlySheet);
return sheet;
}
/**
* Returns an iterator of the sheets in the workbook
* in sheet order. Includes hidden and very hidden sheets.
*
* @return an iterator of the sheets.
*/
public Iterator<Sheet> sheetIterator() {
Iterator<Sheet> result = new SheetIterator<Sheet>();
return result;
}

/**
* Alias for {@link #sheetIterator()} to allow
* foreach loops
*/
public Iterator<Sheet> iterator() {
return sheetIterator();
}
private final class SheetIterator<T extends Sheet> implements Iterator<T> {
final private Iterator<T> it;
private T cursor = null;
@SuppressWarnings("unchecked")
public SheetIterator() {
it = (Iterator<T>) _sheets.iterator();
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public T next() throws NoSuchElementException {
cursor = it.next();
return cursor;
}
/**
* 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 HSSFWorkbook.iterator(). "+
"Use Sheet.removeSheetAt(int) instead.");
}
}

/**
* get the number of spreadsheets in the workbook (this will be three after serialization)
* @return number of sheets
*/

@Override
public int getNumberOfSheets()
{

+ 10
- 1
src/java/org/apache/poi/ss/usermodel/Workbook.java View File

@@ -20,6 +20,7 @@ package org.apache.poi.ss.usermodel;
import java.io.Closeable;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;

import org.apache.poi.ss.formula.udf.UDFFinder;
@@ -31,7 +32,7 @@ import org.apache.poi.ss.util.CellRangeAddress;
* will construct whether they are reading or writing a workbook. It is also the
* top level object for creating new sheets/etc.
*/
public interface Workbook extends Closeable {
public interface Workbook extends Closeable, Iterable<Sheet> {

/** Extended windows meta file */
public static final int PICTURE_TYPE_EMF = 2;
@@ -231,6 +232,14 @@ public interface Workbook extends Closeable {
*/
Sheet cloneSheet(int sheetNum);

/**
* Returns an iterator of the sheets in the workbook
* in sheet order. Includes hidden and very hidden sheets.
*
* @return an iterator of the sheets.
*/
Iterator<Sheet> sheetIterator();

/**
* Get the number of spreadsheets in the workbook

+ 51
- 0
src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java View File

@@ -26,7 +26,9 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
@@ -679,6 +681,55 @@ public class SXSSFWorkbook implements Workbook {
{
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<Sheet>();
}
private 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();
final T sxssfSheet = (T) getSXSSFSheet(xssfSheet);
return sxssfSheet;
}
/**
* 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.");
}
}
/**
* Alias for {@link #sheetIterator()} to allow
* foreach loops
*/
@Override
public Iterator<Sheet> iterator() {
return sheetIterator();
}

/**
* Get the Sheet object at the given index.

+ 125
- 9
src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java View File

@@ -33,6 +33,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.regex.Pattern;

import javax.xml.namespace.QName;
@@ -104,7 +105,7 @@ import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument;
* will construct whether they are reading or writing a workbook. It is also the
* top level object for creating new sheets/etc.
*/
public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<XSSFSheet> {
public class XSSFWorkbook extends POIXMLDocument implements Workbook {
private static final Pattern COMMA_PATTERN = Pattern.compile(",");

/**
@@ -1055,18 +1056,133 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
}

/**
* Allows foreach loops:
* <pre><code>
* XSSFWorkbook wb = new XSSFWorkbook(package);
* for(XSSFSheet sheet : wb){
* Returns an iterator of the sheets in the workbook
* in sheet order. Includes hidden and very hidden sheets.
*
* }
* </code></pre>
* Note: remove() is not supported on this iterator.
* Use {@link #removeSheetAt(int)} to remove sheets instead.
*
* @return an iterator of the sheets.
*/
public Iterator<Sheet> sheetIterator() {
return new SheetIterator<Sheet>();
}
/**
* Alias for {@link #sheetIterator()} to allow
* foreach loops
*
* <code>Iterator<XSSFSheet> iterator()<code> was replaced with <code>Iterator<Sheet> iterator()</code>
* to make iterating over a container (Workbook, Sheet, Row) consistent across POI spreadsheets.
* This breaks backwards compatibility and may affect your code.
* See {@link XSSFWorkbook#xssfSheetIterator} for how to upgrade your code to be compatible
* with the new interface.
*
* Note: remove() is not supported on this iterator.
* Use {@link #removeSheetAt(int)} to remove sheets instead.
*
* @return an iterator of the sheets.
*/
@Override
public Iterator<XSSFSheet> iterator() {
return sheets.iterator();
public Iterator<Sheet> iterator() {
return sheetIterator();
}
private final class SheetIterator<T extends Sheet> implements Iterator<T> {
final private Iterator<T> it;
@SuppressWarnings("unchecked")
public SheetIterator() {
it = (Iterator<T>) sheets.iterator();
}
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public T next() throws NoSuchElementException {
return it.next();
}
/**
* 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.");
}
}
/**
*
* xssfSheetIterator was added to make transitioning to the new Iterator<Sheet> iterator()
* interface less painful for projects currently using POI.
*
* If your code was written using a for-each loop:
* <pre><code>
* for (XSSFSheet sh : wb) {
* sh.createRow(0);
* }
* </code></pre>
*
* There are two ways to upgrade your code:
* // Option A:
* <pre><code>
* for (XSSFSheet sh : (Iterable<XSSFSheet>) (Iterable<? extends Sheet>) wb) {
* sh.createRow(0);
* }
* </code></pre>
*
* // Option B (preferred for new code):
* <pre><code>
* for (Sheet sh : wb) {
* sh.createRow(0);
* }
* </code></pre>
*
*
*
* If your code was written using an iterator variable:
* <pre><code>
* Iterator<XSSFSheet> it = wb.iterator();
* XSSFSheet sh = it.next();
* sh.createRow(0);
* </code></pre>
*
* There are three ways to upgrade your code:
* // Option A:
* <pre><code>
* Iterator<XSSFSheet> it = (Iterator<XSSFSheet>) (Iterator<? extends Sheet>) wb.iterator();
* XSSFSheet sh = it.next();
* sh.createRow(0);
* </code></pre>
*
* // Option B:
* <pre><code>
* @SuppressWarnings("deprecation")
* Iterator<XSSFSheet> it = wb.xssfSheetIterator();
* XSSFSheet sh = it.next();
* sh.createRow(0);
* </code></pre>
*
* // Option C (preferred for new code):
* <pre><code>
* Iterator<Sheet> it = wb.iterator();
* Sheet sh = it.next();
* sh.createRow(0);
* </code></pre>
*
* New projects should use the preferred options. Note: XSSFWorkbook.xssfSheetIterator
* is deprecated and will be removed in a future version.
*
* @return an iterator of the sheets.
*/
@Deprecated
public Iterator<XSSFSheet> xssfSheetIterator() {
return new SheetIterator<XSSFSheet>();
}
/**
* Are we a normal workbook (.xlsx), or a
* macro enabled workbook (.xlsm)?

+ 3
- 2
src/ooxml/java/org/apache/poi/xssf/usermodel/helpers/XSSFRowShifter.java View File

@@ -32,6 +32,7 @@ import org.apache.poi.ss.formula.ptg.AreaPtg;
import org.apache.poi.ss.formula.ptg.Ptg;
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.ss.util.CellRangeAddress;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -148,13 +149,13 @@ public final class XSSFRowShifter {

//update formulas on other sheets
XSSFWorkbook wb = sheet.getWorkbook();
for (XSSFSheet sh : wb) {
for (Sheet sh : wb) {
if (sheet == sh) continue;
updateSheetFormulas(sh, shifter);
}
}

private void updateSheetFormulas(XSSFSheet sh, FormulaShifter shifter) {
private void updateSheetFormulas(Sheet sh, FormulaShifter shifter) {
for (Row r : sh) {
XSSFRow row = (XSSFRow) r;
updateRowFormulas(row, shifter);

+ 5
- 4
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFRichTextString.java View File

@@ -22,6 +22,7 @@ import java.util.TreeMap;

import junit.framework.TestCase;

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.XSSFTestDataSamples;
@@ -441,20 +442,20 @@ public final class TestXSSFRichTextString extends TestCase {
@Test
public void testBug56511() {
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("56511.xlsx");
for (XSSFSheet sheet : wb) {
for (Sheet sheet : wb) {
int lastRow = sheet.getLastRowNum();
for (int rowIdx = sheet.getFirstRowNum(); rowIdx <= lastRow; rowIdx++) {
XSSFRow row = sheet.getRow(rowIdx);
Row row = sheet.getRow(rowIdx);
if(row != null) {
int lastCell = row.getLastCellNum();
for (int cellIdx = row.getFirstCellNum(); cellIdx <= lastCell; cellIdx++) {
XSSFCell cell = row.getCell(cellIdx);
Cell cell = row.getCell(cellIdx);
if (cell != null) {
//System.out.println("row " + rowIdx + " column " + cellIdx + ": " + cell.getCellType() + ": " + cell.toString());
XSSFRichTextString richText = cell.getRichStringCellValue();
XSSFRichTextString richText = (XSSFRichTextString) cell.getRichStringCellValue();
int anzFormattingRuns = richText.numFormattingRuns();
for (int run = 0; run < anzFormattingRuns; run++) {
/*XSSFFont font =*/ richText.getFontOfFormattingRun(run);

+ 80
- 0
src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFWorkbook.java View File

@@ -32,6 +32,7 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.zip.CRC32;

@@ -940,4 +941,83 @@ public final class TestXSSFWorkbook extends BaseTestWorkbook {
// workbook.write(fileOutputStream);
// fileOutputStream.close();
}
@SuppressWarnings("unchecked")
@Test
/**
* Iterator<XSSFSheet> XSSFWorkbook.iterator was committed in r700472 on 2008-09-30
* and has been replaced with Iterator<Sheet> XSSFWorkbook.iterator
*
* In order to make code for looping over sheets in workbooks standard, regardless
* of the type of workbook (HSSFWorkbook, XSSFWorkbook, SXSSFWorkbook), the previously
* available Iterator<XSSFSheet> iterator and Iterator<XSSFSheet> sheetIterator
* have been replaced with Iterator<Sheet> {@link #iterator} and
* Iterator<Sheet> {@link #sheetIterator}. This makes iterating over sheets in a workbook
* similar to iterating over rows in a sheet and cells in a row.
*
* Note: this breaks backwards compatibility! Existing codebases will need to
* upgrade their code with either of the following options presented in this test case.
*
*/
public void bug58245_XSSFSheetIterator() {
final XSSFWorkbook wb = new XSSFWorkbook();
try {
wb.createSheet();
// =====================================================================
// Case 1: Existing code uses XSSFSheet for-each loop
// =====================================================================
// Original code (no longer valid)
/*
for (XSSFSheet sh : wb) {
sh.createRow(0);
}
*/
// Option A:
for (XSSFSheet sh : (Iterable<XSSFSheet>) (Iterable<? extends Sheet>) wb) {
sh.createRow(0);
}
// Option B (preferred for new code):
for (Sheet sh : wb) {
sh.createRow(0);
}
// =====================================================================
// Case 2: Existing code creates an iterator variable
// =====================================================================
// Original code (no longer valid)
/*
Iterator<XSSFSheet> it = wb.iterator();
XSSFSheet sh = it.next();
sh.createRow(0);
*/
// Option A:
{
Iterator<XSSFSheet> it = (Iterator<XSSFSheet>) (Iterator<? extends Sheet>) wb.iterator();
XSSFSheet sh = it.next();
sh.createRow(0);
}
// Option B:
{
@SuppressWarnings("deprecation")
Iterator<XSSFSheet> it = wb.xssfSheetIterator();
XSSFSheet sh = it.next();
sh.createRow(0);
}
// Option C (preferred for new code):
{
Iterator<Sheet> it = wb.iterator();
Sheet sh = it.next();
sh.createRow(0);
}
}
finally {
IOUtils.closeQuietly(wb);
}
}
}

+ 75
- 0
src/testcases/org/apache/poi/ss/usermodel/BaseTestWorkbook.java View File

@@ -26,6 +26,8 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.util.ConcurrentModificationException;
import java.util.Iterator;

import junit.framework.AssertionFailedError;

@@ -43,6 +45,79 @@ public abstract class BaseTestWorkbook {
protected BaseTestWorkbook(ITestDataProvider testDataProvider) {
_testDataProvider = testDataProvider;
}
@Test
public void sheetIterator_forEach() {
final Workbook wb = _testDataProvider.createWorkbook();
wb.createSheet("Sheet0");
wb.createSheet("Sheet1");
wb.createSheet("Sheet2");
int i = 0;
for (Sheet sh : wb) {
assertEquals("Sheet"+i, sh.getSheetName());
i++;
}
}
@Test
public void sheetIterator_sheetsReordered() {
final Workbook wb = _testDataProvider.createWorkbook();
wb.createSheet("Sheet0");
wb.createSheet("Sheet1");
wb.createSheet("Sheet2");
Iterator<Sheet> it = wb.sheetIterator();
it.next();
wb.setSheetOrder("Sheet2", 1);
// Iterator order should be fixed when iterator is created
try {
assertEquals("Sheet1", it.next().getSheetName());
fail("Expected ConcurrentModificationException: "+
"should not be able to advance an iterator when the "+
"underlying data has been reordered");
} catch (final ConcurrentModificationException e) {
// expected
}
}
@Test
public void sheetIterator_sheetRemoved() {
final Workbook wb = _testDataProvider.createWorkbook();
wb.createSheet("Sheet0");
wb.createSheet("Sheet1");
wb.createSheet("Sheet2");
Iterator<Sheet> it = wb.sheetIterator();
wb.removeSheetAt(1);
// Iterator order should be fixed when iterator is created
try {
it.next();
fail("Expected ConcurrentModificationException: "+
"should not be able to advance an iterator when the "+
"underlying data has been reordered");
} catch (final ConcurrentModificationException e) {
// expected
}
}
@Test
public void sheetIterator_remove() {
final Workbook wb = _testDataProvider.createWorkbook();
wb.createSheet("Sheet0");
Iterator<Sheet> it = wb.sheetIterator();
it.next(); //Sheet0
try {
it.remove();
fail("Expected UnsupportedOperationException: "+
"should not be able to remove sheets from the sheet iterator");
} catch (final UnsupportedOperationException e) {
// expected
}
}


@Test
public void createSheet() {

Loading…
Cancel
Save