git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1763939 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_16_BETA1
@@ -505,16 +505,54 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss | |||
} | |||
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 | |||
int active = getActiveSheetIndex(); | |||
if(active == oldSheetIndex) { | |||
if(active == oldIndex) { | |||
// moved sheet was the active one | |||
setActiveSheet(pos); | |||
} else if ((active < oldSheetIndex && active < pos) || | |||
(active > oldSheetIndex && active > pos)) { | |||
setActiveSheet(newIndex); | |||
} else if ((active < oldIndex && active < newIndex) || | |||
(active > oldIndex && active > newIndex)) { | |||
// not affected | |||
} else if (pos > oldSheetIndex) { | |||
} else if (newIndex > oldIndex) { | |||
// moved sheet was below before and is above now => active is one less | |||
setActiveSheet(active-1); | |||
} else { |
@@ -1687,16 +1687,51 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook { | |||
for(int i=0; i < sheetArray.length; 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 | |||
int active = getActiveSheetIndex(); | |||
if(active == idx) { | |||
if(active == oldIndex) { | |||
// moved sheet was the active one | |||
setActiveSheet(pos); | |||
} else if ((active < idx && active < pos) || | |||
(active > idx && active > pos)) { | |||
setActiveSheet(newIndex); | |||
} else if ((active < oldIndex && active < newIndex) || | |||
(active > oldIndex && active > newIndex)) { | |||
// not affected | |||
} else if (pos > idx) { | |||
} else if (newIndex > oldIndex) { | |||
// moved sheet was below before and is above now => active is one less | |||
setActiveSheet(active-1); | |||
} else { |
@@ -17,8 +17,11 @@ | |||
package org.apache.poi.xssf.usermodel; | |||
import static org.junit.Assert.fail; | |||
import static org.junit.Assert.assertEquals; | |||
import java.io.IOException; | |||
import org.apache.poi.ss.usermodel.BaseTestBugzillaIssues; | |||
import org.apache.poi.ss.usermodel.PrintSetup; | |||
import org.apache.poi.ss.usermodel.Sheet; | |||
@@ -79,4 +82,22 @@ public final class TestSXSSFBugs extends BaseTestBugzillaIssues { | |||
wb1.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); | |||
} | |||
} | |||
// 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(); | |||
} | |||
} |