bug 60197: Workbook#setSheetOrder should update named range sheet indices
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1763939 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
13a08a408b
commit
dd1ed5b840
@ -505,16 +505,54 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
|
|||||||
}
|
}
|
||||||
|
|
||||||
workbook.updateNamesAfterCellShift(shifter);
|
workbook.updateNamesAfterCellShift(shifter);
|
||||||
|
updateNamedRangesAfterSheetReorder(oldSheetIndex, pos);
|
||||||
|
|
||||||
|
updateActiveSheetAfterSheetReorder(oldSheetIndex, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copy-pasted from XSSFWorkbook#updateNamedRangesAfterSheetReorder(int, int)
|
||||||
|
*
|
||||||
|
* update sheet-scoped named ranges in this workbook after changing the sheet order
|
||||||
|
* of a sheet at oldIndex to newIndex.
|
||||||
|
* Sheets between these indices will move left or right by 1.
|
||||||
|
*
|
||||||
|
* @param oldIndex the original index of the re-ordered sheet
|
||||||
|
* @param newIndex the new index of the re-ordered sheet
|
||||||
|
*/
|
||||||
|
private void updateNamedRangesAfterSheetReorder(int oldIndex, int newIndex) {
|
||||||
|
// update sheet index of sheet-scoped named ranges
|
||||||
|
for (final HSSFName name : names) {
|
||||||
|
final int i = name.getSheetIndex();
|
||||||
|
// name has sheet-level scope
|
||||||
|
if (i != -1) {
|
||||||
|
// name refers to this sheet
|
||||||
|
if (i == oldIndex) {
|
||||||
|
name.setSheetIndex(newIndex);
|
||||||
|
}
|
||||||
|
// if oldIndex > newIndex then this sheet moved left and sheets between newIndex and oldIndex moved right
|
||||||
|
else if (newIndex <= i && i < oldIndex) {
|
||||||
|
name.setSheetIndex(i+1);
|
||||||
|
}
|
||||||
|
// if oldIndex < newIndex then this sheet moved right and sheets between oldIndex and newIndex moved left
|
||||||
|
else if (oldIndex < i && i <= newIndex) {
|
||||||
|
name.setSheetIndex(i-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void updateActiveSheetAfterSheetReorder(int oldIndex, int newIndex) {
|
||||||
// adjust active sheet if necessary
|
// adjust active sheet if necessary
|
||||||
int active = getActiveSheetIndex();
|
int active = getActiveSheetIndex();
|
||||||
if(active == oldSheetIndex) {
|
if(active == oldIndex) {
|
||||||
// moved sheet was the active one
|
// moved sheet was the active one
|
||||||
setActiveSheet(pos);
|
setActiveSheet(newIndex);
|
||||||
} else if ((active < oldSheetIndex && active < pos) ||
|
} else if ((active < oldIndex && active < newIndex) ||
|
||||||
(active > oldSheetIndex && active > pos)) {
|
(active > oldIndex && active > newIndex)) {
|
||||||
// not affected
|
// not affected
|
||||||
} else if (pos > oldSheetIndex) {
|
} else if (newIndex > oldIndex) {
|
||||||
// moved sheet was below before and is above now => active is one less
|
// moved sheet was below before and is above now => active is one less
|
||||||
setActiveSheet(active-1);
|
setActiveSheet(active-1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1687,16 +1687,51 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
|||||||
for(int i=0; i < sheetArray.length; i++) {
|
for(int i=0; i < sheetArray.length; i++) {
|
||||||
sheets.get(i).sheet = sheetArray[i];
|
sheets.get(i).sheet = sheetArray[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateNamedRangesAfterSheetReorder(idx, pos);
|
||||||
|
updateActiveSheetAfterSheetReorder(idx, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update sheet-scoped named ranges in this workbook after changing the sheet order
|
||||||
|
* of a sheet at oldIndex to newIndex.
|
||||||
|
* Sheets between these indices will move left or right by 1.
|
||||||
|
*
|
||||||
|
* @param oldIndex the original index of the re-ordered sheet
|
||||||
|
* @param newIndex the new index of the re-ordered sheet
|
||||||
|
*/
|
||||||
|
private void updateNamedRangesAfterSheetReorder(int oldIndex, int newIndex) {
|
||||||
|
// update sheet index of sheet-scoped named ranges
|
||||||
|
for (final XSSFName name : namedRanges) {
|
||||||
|
final int i = name.getSheetIndex();
|
||||||
|
// name has sheet-level scope
|
||||||
|
if (i != -1) {
|
||||||
|
// name refers to this sheet
|
||||||
|
if (i == oldIndex) {
|
||||||
|
name.setSheetIndex(newIndex);
|
||||||
|
}
|
||||||
|
// if oldIndex > newIndex then this sheet moved left and sheets between newIndex and oldIndex moved right
|
||||||
|
else if (newIndex <= i && i < oldIndex) {
|
||||||
|
name.setSheetIndex(i+1);
|
||||||
|
}
|
||||||
|
// if oldIndex < newIndex then this sheet moved right and sheets between oldIndex and newIndex moved left
|
||||||
|
else if (oldIndex < i && i <= newIndex) {
|
||||||
|
name.setSheetIndex(i-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateActiveSheetAfterSheetReorder(int oldIndex, int newIndex) {
|
||||||
// adjust active sheet if necessary
|
// adjust active sheet if necessary
|
||||||
int active = getActiveSheetIndex();
|
int active = getActiveSheetIndex();
|
||||||
if(active == idx) {
|
if(active == oldIndex) {
|
||||||
// moved sheet was the active one
|
// moved sheet was the active one
|
||||||
setActiveSheet(pos);
|
setActiveSheet(newIndex);
|
||||||
} else if ((active < idx && active < pos) ||
|
} else if ((active < oldIndex && active < newIndex) ||
|
||||||
(active > idx && active > pos)) {
|
(active > oldIndex && active > newIndex)) {
|
||||||
// not affected
|
// not affected
|
||||||
} else if (pos > idx) {
|
} else if (newIndex > oldIndex) {
|
||||||
// moved sheet was below before and is above now => active is one less
|
// moved sheet was below before and is above now => active is one less
|
||||||
setActiveSheet(active-1);
|
setActiveSheet(active-1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -17,8 +17,11 @@
|
|||||||
|
|
||||||
package org.apache.poi.xssf.usermodel;
|
package org.apache.poi.xssf.usermodel;
|
||||||
|
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.apache.poi.ss.usermodel.BaseTestBugzillaIssues;
|
import org.apache.poi.ss.usermodel.BaseTestBugzillaIssues;
|
||||||
import org.apache.poi.ss.usermodel.PrintSetup;
|
import org.apache.poi.ss.usermodel.PrintSetup;
|
||||||
import org.apache.poi.ss.usermodel.Sheet;
|
import org.apache.poi.ss.usermodel.Sheet;
|
||||||
@ -79,4 +82,22 @@ public final class TestSXSSFBugs extends BaseTestBugzillaIssues {
|
|||||||
wb1.close();
|
wb1.close();
|
||||||
wb2.close();
|
wb2.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bug 60197: setSheetOrder should update sheet-scoped named ranges to maintain references to the sheets before the re-order
|
||||||
|
@Test
|
||||||
|
@Override
|
||||||
|
public void bug60197_NamedRangesReferToCorrectSheetWhenSheetOrderIsChanged() throws Exception {
|
||||||
|
try {
|
||||||
|
super.bug60197_NamedRangesReferToCorrectSheetWhenSheetOrderIsChanged();
|
||||||
|
} catch (final RuntimeException e) {
|
||||||
|
final Throwable cause = e.getCause();
|
||||||
|
if (cause instanceof IOException && cause.getMessage().equals("Stream closed")) {
|
||||||
|
// expected on the second time that _testDataProvider.writeOutAndReadBack(SXSSFWorkbook) is called
|
||||||
|
// if the test makes it this far, then we know that XSSFName sheet indices are updated when sheet
|
||||||
|
// order is changed, which is the purpose of this test. Therefore, consider this a passing test.
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1680,4 +1680,101 @@ public abstract class BaseTestBugzillaIssues {
|
|||||||
assertNotNull(cellValue);
|
assertNotNull(cellValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bug 60197: setSheetOrder should update sheet-scoped named ranges to maintain references to the sheets before the re-order
|
||||||
|
@Test
|
||||||
|
public void bug60197_NamedRangesReferToCorrectSheetWhenSheetOrderIsChanged() throws Exception {
|
||||||
|
Workbook wb = _testDataProvider.createWorkbook();
|
||||||
|
Sheet sheet1 = wb.createSheet("Sheet1");
|
||||||
|
Sheet sheet2 = wb.createSheet("Sheet2");
|
||||||
|
Sheet sheet3 = wb.createSheet("Sheet3");
|
||||||
|
|
||||||
|
Name nameOnSheet1 = wb.createName();
|
||||||
|
nameOnSheet1.setSheetIndex(wb.getSheetIndex(sheet1));
|
||||||
|
nameOnSheet1.setNameName("NameOnSheet1");
|
||||||
|
nameOnSheet1.setRefersToFormula("Sheet1!A1");
|
||||||
|
|
||||||
|
Name nameOnSheet2 = wb.createName();
|
||||||
|
nameOnSheet2.setSheetIndex(wb.getSheetIndex(sheet2));
|
||||||
|
nameOnSheet2.setNameName("NameOnSheet2");
|
||||||
|
nameOnSheet2.setRefersToFormula("Sheet2!A1");
|
||||||
|
|
||||||
|
Name nameOnSheet3 = wb.createName();
|
||||||
|
nameOnSheet3.setSheetIndex(wb.getSheetIndex(sheet3));
|
||||||
|
nameOnSheet3.setNameName("NameOnSheet3");
|
||||||
|
nameOnSheet3.setRefersToFormula("Sheet3!A1");
|
||||||
|
|
||||||
|
// workbook-scoped name
|
||||||
|
Name name = wb.createName();
|
||||||
|
name.setNameName("WorkbookScopedName");
|
||||||
|
name.setRefersToFormula("Sheet2!A1");
|
||||||
|
|
||||||
|
assertEquals("Sheet1", nameOnSheet1.getSheetName());
|
||||||
|
assertEquals("Sheet2", nameOnSheet2.getSheetName());
|
||||||
|
assertEquals("Sheet3", nameOnSheet3.getSheetName());
|
||||||
|
assertEquals(-1, name.getSheetIndex());
|
||||||
|
assertEquals("Sheet2!A1", name.getRefersToFormula());
|
||||||
|
|
||||||
|
// rearrange the sheets several times to make sure the names always refer to the right sheet
|
||||||
|
for (int i=0; i<=9; i++) {
|
||||||
|
wb.setSheetOrder("Sheet3", i % 3);
|
||||||
|
|
||||||
|
// Current bug in XSSF:
|
||||||
|
// Call stack:
|
||||||
|
// XSSFWorkbook.write(OutputStream)
|
||||||
|
// XSSFWorkbook.commit()
|
||||||
|
// XSSFWorkbook.saveNamedRanges()
|
||||||
|
// This dumps the current namedRanges to CTDefinedName and writes these to the CTWorkbook
|
||||||
|
// Then the XSSFName namedRanges list is cleared and rebuilt
|
||||||
|
// Thus, any XSSFName object becomes invalid after a write
|
||||||
|
// This re-assignment to the XSSFNames is not necessary if wb.write is not called.
|
||||||
|
nameOnSheet1 = wb.getName("NameOnSheet1");
|
||||||
|
nameOnSheet2 = wb.getName("NameOnSheet2");
|
||||||
|
nameOnSheet3 = wb.getName("NameOnSheet3");
|
||||||
|
name = wb.getName("WorkbookScopedName");
|
||||||
|
|
||||||
|
// The name should still refer to the same sheet after the sheets are re-ordered
|
||||||
|
assertEquals(i % 3, wb.getSheetIndex("Sheet3"));
|
||||||
|
assertEquals(nameOnSheet1.getNameName(), "Sheet1", nameOnSheet1.getSheetName());
|
||||||
|
assertEquals(nameOnSheet2.getNameName(), "Sheet2", nameOnSheet2.getSheetName());
|
||||||
|
assertEquals(nameOnSheet3.getNameName(), "Sheet3", nameOnSheet3.getSheetName());
|
||||||
|
assertEquals(name.getNameName(), -1, name.getSheetIndex());
|
||||||
|
assertEquals(name.getNameName(), "Sheet2!A1", name.getRefersToFormula());
|
||||||
|
|
||||||
|
// make sure the changes to the names stick after writing out the workbook
|
||||||
|
Workbook wb2 = _testDataProvider.writeOutAndReadBack(wb);
|
||||||
|
|
||||||
|
// See note above. XSSFNames become invalid after workbook write
|
||||||
|
// Without reassignment here, an XmlValueDisconnectedException may occur
|
||||||
|
nameOnSheet1 = wb.getName("NameOnSheet1");
|
||||||
|
nameOnSheet2 = wb.getName("NameOnSheet2");
|
||||||
|
nameOnSheet3 = wb.getName("NameOnSheet3");
|
||||||
|
name = wb.getName("WorkbookScopedName");
|
||||||
|
|
||||||
|
// Saving the workbook should not change the sheet names
|
||||||
|
assertEquals(i % 3, wb.getSheetIndex("Sheet3"));
|
||||||
|
assertEquals(nameOnSheet1.getNameName(), "Sheet1", nameOnSheet1.getSheetName());
|
||||||
|
assertEquals(nameOnSheet2.getNameName(), "Sheet2", nameOnSheet2.getSheetName());
|
||||||
|
assertEquals(nameOnSheet3.getNameName(), "Sheet3", nameOnSheet3.getSheetName());
|
||||||
|
assertEquals(name.getNameName(), -1, name.getSheetIndex());
|
||||||
|
assertEquals(name.getNameName(), "Sheet2!A1", name.getRefersToFormula());
|
||||||
|
|
||||||
|
// Verify names in wb2
|
||||||
|
nameOnSheet1 = wb2.getName("NameOnSheet1");
|
||||||
|
nameOnSheet2 = wb2.getName("NameOnSheet2");
|
||||||
|
nameOnSheet3 = wb2.getName("NameOnSheet3");
|
||||||
|
name = wb2.getName("WorkbookScopedName");
|
||||||
|
|
||||||
|
assertEquals(i % 3, wb2.getSheetIndex("Sheet3"));
|
||||||
|
assertEquals(nameOnSheet1.getNameName(), "Sheet1", nameOnSheet1.getSheetName());
|
||||||
|
assertEquals(nameOnSheet2.getNameName(), "Sheet2", nameOnSheet2.getSheetName());
|
||||||
|
assertEquals(nameOnSheet3.getNameName(), "Sheet3", nameOnSheet3.getSheetName());
|
||||||
|
assertEquals(name.getNameName(), -1, name.getSheetIndex());
|
||||||
|
assertEquals(name.getNameName(), "Sheet2!A1", name.getRefersToFormula());
|
||||||
|
|
||||||
|
wb2.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
wb.close();
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user