294 lines
13 KiB
Java
294 lines
13 KiB
Java
/* ====================================================================
|
|
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.ss.formula;
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertNotNull;
|
|
import static org.junit.Assert.assertTrue;
|
|
import static org.junit.Assert.fail;
|
|
|
|
import org.apache.poi.ss.SpreadsheetVersion;
|
|
import org.apache.poi.ss.formula.ptg.AreaErrPtg;
|
|
import org.apache.poi.ss.formula.ptg.AreaPtg;
|
|
import org.apache.poi.ss.formula.ptg.Ptg;
|
|
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
|
|
import org.apache.poi.ss.util.CellReference;
|
|
|
|
import org.junit.Test;
|
|
|
|
/**
|
|
* Tests for {@link FormulaShifter}.
|
|
*
|
|
* @author Josh Micich
|
|
*/
|
|
public final class TestFormulaShifter {
|
|
// Note - the expected result row coordinates here were determined/verified
|
|
// in Excel 2007 by manually testing.
|
|
|
|
/**
|
|
* Tests what happens to area refs when a range of rows from inside, or overlapping are
|
|
* moved
|
|
*/
|
|
@Test
|
|
public void testShiftAreasSourceRows() {
|
|
|
|
// all these operations are on an area ref spanning rows 10 to 20
|
|
AreaPtg aptg = createAreaPtg(10, 20);
|
|
|
|
confirmAreaShift(aptg, 9, 21, 20, 30, 40);
|
|
confirmAreaShift(aptg, 10, 21, 20, 30, 40);
|
|
confirmAreaShift(aptg, 9, 20, 20, 30, 40);
|
|
|
|
confirmAreaShift(aptg, 8, 11, -3, 7, 20); // simple expansion of top
|
|
// rows containing area top being shifted down:
|
|
confirmAreaShift(aptg, 8, 11, 3, 13, 20);
|
|
confirmAreaShift(aptg, 8, 11, 7, 17, 20);
|
|
confirmAreaShift(aptg, 8, 11, 8, 18, 20);
|
|
confirmAreaShift(aptg, 8, 11, 9, 12, 20); // note behaviour changes here
|
|
confirmAreaShift(aptg, 8, 11, 10, 12, 21);
|
|
confirmAreaShift(aptg, 8, 11, 12, 12, 23);
|
|
confirmAreaShift(aptg, 8, 11, 13, 10, 20); // ignored
|
|
|
|
// rows from within being moved:
|
|
confirmAreaShift(aptg, 12, 16, 3, 10, 20); // stay within - no change
|
|
confirmAreaShift(aptg, 11, 19, 20, 10, 20); // move completely out - no change
|
|
confirmAreaShift(aptg, 16, 17, -6, 10, 20); // moved exactly to top - no change
|
|
confirmAreaShift(aptg, 16, 17, -7, 11, 20); // truncation at top
|
|
confirmAreaShift(aptg, 12, 16, 4, 10, 20); // moved exactly to bottom - no change
|
|
confirmAreaShift(aptg, 12, 16, 6, 10, 17); // truncation at bottom
|
|
|
|
// rows containing area bottom being shifted up:
|
|
confirmAreaShift(aptg, 18, 22, -1, 10, 19); // simple contraction at bottom
|
|
confirmAreaShift(aptg, 18, 22, -7, 10, 13); // simple contraction at bottom
|
|
confirmAreaShift(aptg, 18, 22, -8, 10, 17); // top calculated differently here
|
|
confirmAreaShift(aptg, 18, 22, -9, 9, 17);
|
|
confirmAreaShift(aptg, 18, 22,-15, 10, 20); // no change because range would be turned inside out
|
|
confirmAreaShift(aptg, 15, 19, -7, 13, 20); // dest truncates top (even though src is from inside range)
|
|
confirmAreaShift(aptg, 19, 23,-12, 7, 18); // complex: src encloses bottom, dest encloses top
|
|
|
|
confirmAreaShift(aptg, 18, 22, 5, 10, 25); // simple expansion at bottom
|
|
}
|
|
|
|
@Test
|
|
public void testCopyAreasSourceRowsRelRel() {
|
|
|
|
// all these operations are on an area ref spanning rows 10 to 20
|
|
final AreaPtg aptg = createAreaPtg(10, 20, true, true);
|
|
|
|
confirmAreaCopy(aptg, 0, 30, 20, 30, 40, true);
|
|
confirmAreaCopy(aptg, 15, 25, -15, -1, -1, true); //DeletedRef
|
|
}
|
|
|
|
@Test
|
|
public void testCopyAreasSourceRowsRelAbs() {
|
|
|
|
// all these operations are on an area ref spanning rows 10 to 20
|
|
final AreaPtg aptg = createAreaPtg(10, 20, true, false);
|
|
|
|
// Only first row should move
|
|
confirmAreaCopy(aptg, 0, 30, 20, 20, 30, true);
|
|
confirmAreaCopy(aptg, 15, 25, -15, -1, -1, true); //DeletedRef
|
|
}
|
|
|
|
@Test
|
|
public void testCopyAreasSourceRowsAbsRel() {
|
|
// aptg is part of a formula in a cell that was just copied to another row
|
|
// aptg row references should be updated by the difference in rows that the cell was copied
|
|
// No other references besides the cells that were involved in the copy need to be updated
|
|
// this makes the row copy significantly different from the row shift, where all references
|
|
// in the workbook need to track the row shift
|
|
|
|
// all these operations are on an area ref spanning rows 10 to 20
|
|
final AreaPtg aptg = createAreaPtg(10, 20, false, true);
|
|
|
|
// Only last row should move
|
|
confirmAreaCopy(aptg, 0, 30, 20, 10, 40, true);
|
|
confirmAreaCopy(aptg, 15, 25, -15, 5, 10, true); //sortTopLeftToBottomRight swapped firstRow and lastRow because firstRow is absolute
|
|
}
|
|
|
|
@Test
|
|
public void testCopyAreasSourceRowsAbsAbs() {
|
|
// aptg is part of a formula in a cell that was just copied to another row
|
|
// aptg row references should be updated by the difference in rows that the cell was copied
|
|
// No other references besides the cells that were involved in the copy need to be updated
|
|
// this makes the row copy significantly different from the row shift, where all references
|
|
// in the workbook need to track the row shift
|
|
|
|
// all these operations are on an area ref spanning rows 10 to 20
|
|
final AreaPtg aptg = createAreaPtg(10, 20, false, false);
|
|
|
|
//AbsFirstRow AbsLastRow references should't change when copied to a different row
|
|
confirmAreaCopy(aptg, 0, 30, 20, 10, 20, false);
|
|
confirmAreaCopy(aptg, 15, 25, -15, 10, 20, false);
|
|
}
|
|
|
|
/**
|
|
* Tests what happens to an area ref when some outside rows are moved to overlap
|
|
* that area ref
|
|
*/
|
|
@Test
|
|
public void testShiftAreasDestRows() {
|
|
// all these operations are on an area ref spanning rows 20 to 25
|
|
AreaPtg aptg = createAreaPtg(20, 25);
|
|
|
|
// no change because no overlap:
|
|
confirmAreaShift(aptg, 5, 10, 9, 20, 25);
|
|
confirmAreaShift(aptg, 5, 10, 21, 20, 25);
|
|
|
|
confirmAreaShift(aptg, 11, 14, 10, 20, 25);
|
|
|
|
confirmAreaShift(aptg, 7, 17, 10, -1, -1); // converted to DeletedAreaRef
|
|
confirmAreaShift(aptg, 5, 15, 7, 23, 25); // truncation at top
|
|
confirmAreaShift(aptg, 13, 16, 10, 20, 22); // truncation at bottom
|
|
}
|
|
|
|
private static void confirmAreaShift(AreaPtg aptg,
|
|
int firstRowMoved, int lastRowMoved, int numberRowsMoved,
|
|
int expectedAreaFirstRow, int expectedAreaLastRow) {
|
|
|
|
FormulaShifter fs = FormulaShifter.createForRowShift(0, "", firstRowMoved, lastRowMoved, numberRowsMoved, SpreadsheetVersion.EXCEL2007);
|
|
boolean expectedChanged = aptg.getFirstRow() != expectedAreaFirstRow || aptg.getLastRow() != expectedAreaLastRow;
|
|
|
|
AreaPtg copyPtg = (AreaPtg) aptg.copy(); // clone so we can re-use aptg in calling method
|
|
Ptg[] ptgs = { copyPtg, };
|
|
boolean actualChanged = fs.adjustFormula(ptgs, 0);
|
|
if (expectedAreaFirstRow < 0) {
|
|
assertEquals(AreaErrPtg.class, ptgs[0].getClass());
|
|
return;
|
|
}
|
|
assertEquals(expectedChanged, actualChanged);
|
|
assertEquals(copyPtg, ptgs[0]); // expected to change in place (although this is not a strict requirement)
|
|
assertEquals(expectedAreaFirstRow, copyPtg.getFirstRow());
|
|
assertEquals(expectedAreaLastRow, copyPtg.getLastRow());
|
|
|
|
}
|
|
|
|
|
|
private static void confirmAreaCopy(AreaPtg aptg,
|
|
int firstRowCopied, int lastRowCopied, int rowOffset,
|
|
int expectedFirstRow, int expectedLastRow, boolean expectedChanged) {
|
|
|
|
final AreaPtg copyPtg = (AreaPtg) aptg.copy(); // clone so we can re-use aptg in calling method
|
|
final Ptg[] ptgs = { copyPtg, };
|
|
final FormulaShifter fs = FormulaShifter.createForRowCopy(0, null, firstRowCopied, lastRowCopied, rowOffset, SpreadsheetVersion.EXCEL2007);
|
|
final boolean actualChanged = fs.adjustFormula(ptgs, 0);
|
|
|
|
// DeletedAreaRef
|
|
if (expectedFirstRow < 0 || expectedLastRow < 0) {
|
|
assertEquals("Reference should have shifted off worksheet, producing #REF! error: " + ptgs[0],
|
|
AreaErrPtg.class, ptgs[0].getClass());
|
|
return;
|
|
}
|
|
|
|
assertEquals("Should this AreaPtg change due to row copy?", expectedChanged, actualChanged);
|
|
assertEquals("AreaPtgs should be modified in-place when a row containing the AreaPtg is copied", copyPtg, ptgs[0]); // expected to change in place (although this is not a strict requirement)
|
|
assertEquals("AreaPtg first row", expectedFirstRow, copyPtg.getFirstRow());
|
|
assertEquals("AreaPtg last row", expectedLastRow, copyPtg.getLastRow());
|
|
|
|
}
|
|
|
|
private static AreaPtg createAreaPtg(int initialAreaFirstRow, int initialAreaLastRow) {
|
|
return createAreaPtg(initialAreaFirstRow, initialAreaLastRow, false, false);
|
|
}
|
|
|
|
private static AreaPtg createAreaPtg(int initialAreaFirstRow, int initialAreaLastRow, boolean firstRowRelative, boolean lastRowRelative) {
|
|
return new AreaPtg(initialAreaFirstRow, initialAreaLastRow, 2, 5, firstRowRelative, lastRowRelative, false, false);
|
|
}
|
|
|
|
@Test
|
|
public void testShiftSheet() {
|
|
// 4 sheets, move a sheet from pos 2 to pos 0, i.e. current 0 becomes 1, current 1 becomes pos 2
|
|
FormulaShifter shifter = FormulaShifter.createForSheetShift(2, 0);
|
|
|
|
Ptg[] ptgs = new Ptg[] {
|
|
new Ref3DPtg(new CellReference("first", 0, 0, true, true), 0),
|
|
new Ref3DPtg(new CellReference("second", 0, 0, true, true), 1),
|
|
new Ref3DPtg(new CellReference("third", 0, 0, true, true), 2),
|
|
new Ref3DPtg(new CellReference("fourth", 0, 0, true, true), 3),
|
|
};
|
|
|
|
shifter.adjustFormula(ptgs, -1);
|
|
|
|
assertEquals("formula previously pointing to sheet 0 should now point to sheet 1",
|
|
1, ((Ref3DPtg)ptgs[0]).getExternSheetIndex());
|
|
assertEquals("formula previously pointing to sheet 1 should now point to sheet 2",
|
|
2, ((Ref3DPtg)ptgs[1]).getExternSheetIndex());
|
|
assertEquals("formula previously pointing to sheet 2 should now point to sheet 0",
|
|
0, ((Ref3DPtg)ptgs[2]).getExternSheetIndex());
|
|
assertEquals("formula previously pointing to sheet 3 should be unchanged",
|
|
3, ((Ref3DPtg)ptgs[3]).getExternSheetIndex());
|
|
}
|
|
|
|
@Test
|
|
public void testShiftSheet2() {
|
|
// 4 sheets, move a sheet from pos 1 to pos 2, i.e. current 2 becomes 1, current 1 becomes pos 2
|
|
FormulaShifter shifter = FormulaShifter.createForSheetShift(1, 2);
|
|
|
|
Ptg[] ptgs = new Ptg[] {
|
|
new Ref3DPtg(new CellReference("first", 0, 0, true, true), 0),
|
|
new Ref3DPtg(new CellReference("second", 0, 0, true, true), 1),
|
|
new Ref3DPtg(new CellReference("third", 0, 0, true, true), 2),
|
|
new Ref3DPtg(new CellReference("fourth", 0, 0, true, true), 3),
|
|
};
|
|
|
|
shifter.adjustFormula(ptgs, -1);
|
|
|
|
assertEquals("formula previously pointing to sheet 0 should be unchanged",
|
|
0, ((Ref3DPtg)ptgs[0]).getExternSheetIndex());
|
|
assertEquals("formula previously pointing to sheet 1 should now point to sheet 2",
|
|
2, ((Ref3DPtg)ptgs[1]).getExternSheetIndex());
|
|
assertEquals("formula previously pointing to sheet 2 should now point to sheet 1",
|
|
1, ((Ref3DPtg)ptgs[2]).getExternSheetIndex());
|
|
assertEquals("formula previously pointing to sheet 3 should be unchanged",
|
|
3, ((Ref3DPtg)ptgs[3]).getExternSheetIndex());
|
|
}
|
|
|
|
@Test
|
|
public void testInvalidArgument() {
|
|
try {
|
|
FormulaShifter.createForRowShift(1, "name", 1, 2, 0, SpreadsheetVersion.EXCEL97);
|
|
fail("Should catch exception here");
|
|
} catch (IllegalArgumentException e) {
|
|
// expected here
|
|
}
|
|
|
|
try {
|
|
FormulaShifter.createForRowShift(1, "name", 2, 1, 2, SpreadsheetVersion.EXCEL97);
|
|
fail("Should catch exception here");
|
|
} catch (IllegalArgumentException e) {
|
|
// expected here
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void testConstructor() {
|
|
assertNotNull(FormulaShifter.createForRowShift(1, "name", 1, 2, 2, SpreadsheetVersion.EXCEL97));
|
|
}
|
|
|
|
@Test
|
|
public void testToString() {
|
|
FormulaShifter shifter = FormulaShifter.createForRowShift(0, "sheet", 123, 456, 789,
|
|
SpreadsheetVersion.EXCEL2007);
|
|
assertNotNull(shifter);
|
|
assertNotNull(shifter.toString());
|
|
assertTrue(shifter.toString().contains("123"));
|
|
assertTrue(shifter.toString().contains("456"));
|
|
assertTrue(shifter.toString().contains("789"));
|
|
}
|
|
}
|