From: Javen O'Neal Date: Sat, 4 Nov 2017 05:29:52 +0000 (+0000) Subject: bug 61474,github-81: add FormulaShifter.createForColumnShift and ShiftMode.ColumnMove X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=0e540db89bd1c0de7a3f6414172a834520d15b86;p=poi.git bug 61474,github-81: add FormulaShifter.createForColumnShift and ShiftMode.ColumnMove git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1814254 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/ss/formula/FormulaShifter.java b/src/java/org/apache/poi/ss/formula/FormulaShifter.java index b2edea349c..0f9625d623 100644 --- a/src/java/org/apache/poi/ss/formula/FormulaShifter.java +++ b/src/java/org/apache/poi/ss/formula/FormulaShifter.java @@ -43,6 +43,7 @@ public final class FormulaShifter { private static enum ShiftMode { RowMove, RowCopy, + ColumnMove, SheetMove, } @@ -116,6 +117,14 @@ public final class FormulaShifter { return new FormulaShifter(externSheetIndex, sheetName, firstMovedRowIndex, lastMovedRowIndex, numberOfRowsToMove, ShiftMode.RowCopy, version); } + /** + * @since POI 4.0.0 + */ + public static FormulaShifter createForColumnShift(int externSheetIndex, String sheetName, int firstMovedColumnIndex, int lastMovedColumnIndex, int numberOfColumnsToMove, + SpreadsheetVersion version) { + return new FormulaShifter(externSheetIndex, sheetName, firstMovedColumnIndex, lastMovedColumnIndex, numberOfColumnsToMove, ShiftMode.ColumnMove, version); + } + public static FormulaShifter createForSheetShift(int srcSheetIndex, int dstSheetIndex) { return new FormulaShifter(srcSheetIndex, dstSheetIndex); } @@ -156,6 +165,8 @@ public final class FormulaShifter { // * row copy on same sheet // * row copy between different sheetsin the same workbook return adjustPtgDueToRowCopy(ptg); + case ColumnMove: + return adjustPtgDueToColumnMove(ptg, currentExternSheetIx); case SheetMove: return adjustPtgDueToSheetMove(ptg); default: @@ -264,6 +275,66 @@ public final class FormulaShifter { return null; } + /** + * @return in-place modified ptg (if column move would cause Ptg to change), + * deleted ref ptg (if column move causes an error), + * or null (if no Ptg change is needed) + */ + private Ptg adjustPtgDueToColumnMove(Ptg ptg, int currentExternSheetIx) { + if(ptg instanceof RefPtg) { + if (currentExternSheetIx != _externSheetIndex) { + // local refs on other sheets are unaffected + return null; + } + RefPtg rptg = (RefPtg)ptg; + return columnMoveRefPtg(rptg); + } + if(ptg instanceof Ref3DPtg) { + Ref3DPtg rptg = (Ref3DPtg)ptg; + if (_externSheetIndex != rptg.getExternSheetIndex()) { + // only move 3D refs that refer to the sheet with cells being moved + // (currentExternSheetIx is irrelevant) + return null; + } + return columnMoveRefPtg(rptg); + } + if(ptg instanceof Ref3DPxg) { + Ref3DPxg rpxg = (Ref3DPxg)ptg; + if (rpxg.getExternalWorkbookNumber() > 0 || + ! _sheetName.equals(rpxg.getSheetName())) { + // only move 3D refs that refer to the sheet with cells being moved + return null; + } + return columnMoveRefPtg(rpxg); + } + if(ptg instanceof Area2DPtgBase) { + if (currentExternSheetIx != _externSheetIndex) { + // local refs on other sheets are unaffected + return ptg; + } + return columnMoveAreaPtg((Area2DPtgBase)ptg); + } + if(ptg instanceof Area3DPtg) { + Area3DPtg aptg = (Area3DPtg)ptg; + if (_externSheetIndex != aptg.getExternSheetIndex()) { + // only move 3D refs that refer to the sheet with cells being moved + // (currentExternSheetIx is irrelevant) + return null; + } + return columnMoveAreaPtg(aptg); + } + if(ptg instanceof Area3DPxg) { + Area3DPxg apxg = (Area3DPxg)ptg; + if (apxg.getExternalWorkbookNumber() > 0 || + ! _sheetName.equals(apxg.getSheetName())) { + // only move 3D refs that refer to the sheet with cells being moved + return null; + } + return columnMoveAreaPtg(apxg); + } + return null; + } + private Ptg adjustPtgDueToSheetMove(Ptg ptg) { if(ptg instanceof Ref3DPtg) { @@ -522,6 +593,161 @@ public final class FormulaShifter { return changed ? aptg : null; } + + private Ptg columnMoveRefPtg(RefPtgBase rptg) { + int refColumn = rptg.getColumn(); + if (_firstMovedIndex <= refColumn && refColumn <= _lastMovedIndex) { + // Columns being moved completely enclose the ref. + // - move the area ref along with the columns regardless of destination + rptg.setColumn(refColumn + _amountToMove); + return rptg; + } + // else rules for adjusting area may also depend on the destination of the moved columns + + int destFirstColumnIndex = _firstMovedIndex + _amountToMove; + int destLastColumnIndex = _lastMovedIndex + _amountToMove; + + // ref is outside source columns + // check for clashes with destination + + if (destLastColumnIndex < refColumn || refColumn < destFirstColumnIndex) { + // destination columns are completely outside ref + return null; + } + + if (destFirstColumnIndex <= refColumn && refColumn <= destLastColumnIndex) { + // destination columns enclose the area (possibly exactly) + return createDeletedRef(rptg); + } + throw new IllegalStateException("Situation not covered: (" + _firstMovedIndex + ", " + + _lastMovedIndex + ", " + _amountToMove + ", " + refColumn + ", " + refColumn + ")"); + } + + private Ptg columnMoveAreaPtg(AreaPtgBase aptg) { + int aFirstColumn = aptg.getFirstColumn(); + int aLastColumn = aptg.getLastColumn(); + if (_firstMovedIndex <= aFirstColumn && aLastColumn <= _lastMovedIndex) { + // Columns being moved completely enclose the area ref. + // - move the area ref along with the columns regardless of destination + aptg.setFirstColumn(aFirstColumn + _amountToMove); + aptg.setLastColumn(aLastColumn + _amountToMove); + return aptg; + } + // else rules for adjusting area may also depend on the destination of the moved columns + + int destFirstColumnIndex = _firstMovedIndex + _amountToMove; + int destLastColumnIndex = _lastMovedIndex + _amountToMove; + + if (aFirstColumn < _firstMovedIndex && _lastMovedIndex < aLastColumn) { + // Columns moved were originally *completely* within the area ref + + // If the destination of the columns overlaps either the top + // or bottom of the area ref there will be a change + if (destFirstColumnIndex < aFirstColumn && aFirstColumn <= destLastColumnIndex) { + // truncate the top of the area by the moved columns + aptg.setFirstColumn(destLastColumnIndex+1); + return aptg; + } else if (destFirstColumnIndex <= aLastColumn && aLastColumn < destLastColumnIndex) { + // truncate the bottom of the area by the moved columns + aptg.setLastColumn(destFirstColumnIndex-1); + return aptg; + } + // else - columns have moved completely outside the area ref, + // or still remain completely within the area ref + return null; // - no change to the area + } + if (_firstMovedIndex <= aFirstColumn && aFirstColumn <= _lastMovedIndex) { + // Columns moved include the first column of the area ref, but not the last column + // btw: (aLastColumn > _lastMovedIndex) + if (_amountToMove < 0) { + // simple case - expand area by shifting top upward + aptg.setFirstColumn(aFirstColumn + _amountToMove); + return aptg; + } + if (destFirstColumnIndex > aLastColumn) { + // in this case, excel ignores the column move + return null; + } + int newFirstColumnIx = aFirstColumn + _amountToMove; + if (destLastColumnIndex < aLastColumn) { + // end of area is preserved (will remain exact same column) + // the top area column is moved simply + aptg.setFirstColumn(newFirstColumnIx); + return aptg; + } + // else - bottom area column has been replaced - both area top and bottom may move now + int areaRemainingTopColumnIx = _lastMovedIndex + 1; + if (destFirstColumnIndex > areaRemainingTopColumnIx) { + // old top column of area has moved deep within the area, and exposed a new top column + newFirstColumnIx = areaRemainingTopColumnIx; + } + aptg.setFirstColumn(newFirstColumnIx); + aptg.setLastColumn(Math.max(aLastColumn, destLastColumnIndex)); + return aptg; + } + if (_firstMovedIndex <= aLastColumn && aLastColumn <= _lastMovedIndex) { + // Columns moved include the last column of the area ref, but not the first + // btw: (aFirstColumn < _firstMovedIndex) + if (_amountToMove > 0) { + // simple case - expand area by shifting bottom downward + aptg.setLastColumn(aLastColumn + _amountToMove); + return aptg; + } + if (destLastColumnIndex < aFirstColumn) { + // in this case, excel ignores the column move + return null; + } + int newLastColumnIx = aLastColumn + _amountToMove; + if (destFirstColumnIndex > aFirstColumn) { + // top of area is preserved (will remain exact same column) + // the bottom area column is moved simply + aptg.setLastColumn(newLastColumnIx); + return aptg; + } + // else - top area column has been replaced - both area top and bottom may move now + int areaRemainingBottomColumnIx = _firstMovedIndex - 1; + if (destLastColumnIndex < areaRemainingBottomColumnIx) { + // old bottom column of area has moved up deep within the area, and exposed a new bottom column + newLastColumnIx = areaRemainingBottomColumnIx; + } + aptg.setFirstColumn(Math.min(aFirstColumn, destFirstColumnIndex)); + aptg.setLastColumn(newLastColumnIx); + return aptg; + } + // else source columns include none of the columns of the area ref + // check for clashes with destination + + if (destLastColumnIndex < aFirstColumn || aLastColumn < destFirstColumnIndex) { + // destination columns are completely outside area ref + return null; + } + + if (destFirstColumnIndex <= aFirstColumn && aLastColumn <= destLastColumnIndex) { + // destination columns enclose the area (possibly exactly) + return createDeletedRef(aptg); + } + + if (aFirstColumn <= destFirstColumnIndex && destLastColumnIndex <= aLastColumn) { + // destination columns are within area ref (possibly exact on top or bottom, but not both) + return null; // - no change to area + } + + if (destFirstColumnIndex < aFirstColumn && aFirstColumn <= destLastColumnIndex) { + // dest columns overlap top of area + // - truncate the top + aptg.setFirstColumn(destLastColumnIndex+1); + return aptg; + } + if (destFirstColumnIndex <= aLastColumn && aLastColumn < destLastColumnIndex) { + // dest columns overlap bottom of area + // - truncate the bottom + aptg.setLastColumn(destFirstColumnIndex-1); + return aptg; + } + throw new IllegalStateException("Situation not covered: (" + _firstMovedIndex + ", " + + _lastMovedIndex + ", " + _amountToMove + ", " + aFirstColumn + ", " + aLastColumn + ")"); + } + private static Ptg createDeletedRef(Ptg ptg) { if (ptg instanceof RefPtg) { return new RefErrorPtg(); diff --git a/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java b/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java index 7353682521..25ff2da838 100644 --- a/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java +++ b/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java @@ -48,61 +48,101 @@ public final class TestFormulaShifter { public void testShiftAreasSourceRows() { // all these operations are on an area ref spanning rows 10 to 20 - AreaPtg aptg = createAreaPtg(10, 20); + AreaPtg aptg = createAreaPtgRow(10, 20); - confirmAreaShift(aptg, 9, 21, 20, 30, 40); - confirmAreaShift(aptg, 10, 21, 20, 30, 40); - confirmAreaShift(aptg, 9, 20, 20, 30, 40); + confirmAreaRowShift(aptg, 9, 21, 20, 30, 40); + confirmAreaRowShift(aptg, 10, 21, 20, 30, 40); + confirmAreaRowShift(aptg, 9, 20, 20, 30, 40); - confirmAreaShift(aptg, 8, 11, -3, 7, 20); // simple expansion of top + confirmAreaRowShift(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 + confirmAreaRowShift(aptg, 8, 11, 3, 13, 20); + confirmAreaRowShift(aptg, 8, 11, 7, 17, 20); + confirmAreaRowShift(aptg, 8, 11, 8, 18, 20); + confirmAreaRowShift(aptg, 8, 11, 9, 12, 20); // note behaviour changes here + confirmAreaRowShift(aptg, 8, 11, 10, 12, 21); + confirmAreaRowShift(aptg, 8, 11, 12, 12, 23); + confirmAreaRowShift(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 + confirmAreaRowShift(aptg, 12, 16, 3, 10, 20); // stay within - no change + confirmAreaRowShift(aptg, 11, 19, 20, 10, 20); // move completely out - no change + confirmAreaRowShift(aptg, 16, 17, -6, 10, 20); // moved exactly to top - no change + confirmAreaRowShift(aptg, 16, 17, -7, 11, 20); // truncation at top + confirmAreaRowShift(aptg, 12, 16, 4, 10, 20); // moved exactly to bottom - no change + confirmAreaRowShift(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 + confirmAreaRowShift(aptg, 18, 22, -1, 10, 19); // simple contraction at bottom + confirmAreaRowShift(aptg, 18, 22, -7, 10, 13); // simple contraction at bottom + confirmAreaRowShift(aptg, 18, 22, -8, 10, 17); // top calculated differently here + confirmAreaRowShift(aptg, 18, 22, -9, 9, 17); + confirmAreaRowShift(aptg, 18, 22,-15, 10, 20); // no change because range would be turned inside out + confirmAreaRowShift(aptg, 15, 19, -7, 13, 20); // dest truncates top (even though src is from inside range) + confirmAreaRowShift(aptg, 19, 23,-12, 7, 18); // complex: src encloses bottom, dest encloses top + + confirmAreaRowShift(aptg, 18, 22, 5, 10, 25); // simple expansion at bottom + } + + @Test + public void testShiftAreasSourceColumns() { + + // all these operations are on an area ref spanning columns 10 to 20 + AreaPtg aptg = createAreaPtgColumn(10, 20); + + confirmAreaColumnShift(aptg, 9, 21, 20, 30, 40); + confirmAreaColumnShift(aptg, 10, 21, 20, 30, 40); + confirmAreaColumnShift(aptg, 9, 20, 20, 30, 40); + + confirmAreaColumnShift(aptg, 8, 11, -3, 7, 20); // simple expansion of top + // columns containing area top being shifted down: + confirmAreaColumnShift(aptg, 8, 11, 3, 13, 20); + confirmAreaColumnShift(aptg, 8, 11, 7, 17, 20); + confirmAreaColumnShift(aptg, 8, 11, 8, 18, 20); + confirmAreaColumnShift(aptg, 8, 11, 9, 12, 20); // note behaviour changes here + confirmAreaColumnShift(aptg, 8, 11, 10, 12, 21); + confirmAreaColumnShift(aptg, 8, 11, 12, 12, 23); + confirmAreaColumnShift(aptg, 8, 11, 13, 10, 20); // ignored + + // columns from within being moved: + confirmAreaColumnShift(aptg, 12, 16, 3, 10, 20); // stay within - no change + confirmAreaColumnShift(aptg, 11, 19, 20, 10, 20); // move completely out - no change + confirmAreaColumnShift(aptg, 16, 17, -6, 10, 20); // moved exactly to top - no change + confirmAreaColumnShift(aptg, 16, 17, -7, 11, 20); // truncation at top + confirmAreaColumnShift(aptg, 12, 16, 4, 10, 20); // moved exactly to bottom - no change + confirmAreaColumnShift(aptg, 12, 16, 6, 10, 17); // truncation at bottom + + // columns containing area bottom being shifted up: + confirmAreaColumnShift(aptg, 18, 22, -1, 10, 19); // simple contraction at bottom + confirmAreaColumnShift(aptg, 18, 22, -7, 10, 13); // simple contraction at bottom + confirmAreaColumnShift(aptg, 18, 22, -8, 10, 17); // top calculated differently here + confirmAreaColumnShift(aptg, 18, 22, -9, 9, 17); + confirmAreaColumnShift(aptg, 18, 22,-15, 10, 20); // no change because range would be turned inside out + confirmAreaColumnShift(aptg, 15, 19, -7, 13, 20); // dest truncates top (even though src is from inside range) + confirmAreaColumnShift(aptg, 19, 23,-12, 7, 18); // complex: src encloses bottom, dest encloses top + + confirmAreaColumnShift(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); + final AreaPtg aptg = createAreaPtgRow(10, 20, true, true); - confirmAreaCopy(aptg, 0, 30, 20, 30, 40, true); - confirmAreaCopy(aptg, 15, 25, -15, -1, -1, true); //DeletedRef + confirmAreaRowCopy(aptg, 0, 30, 20, 30, 40, true); + confirmAreaRowCopy(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); + final AreaPtg aptg = createAreaPtgRow(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 + confirmAreaRowCopy(aptg, 0, 30, 20, 20, 30, true); + confirmAreaRowCopy(aptg, 15, 25, -15, -1, -1, true); //DeletedRef } @Test @@ -114,11 +154,11 @@ public final class TestFormulaShifter { // 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); + final AreaPtg aptg = createAreaPtgRow(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 + confirmAreaRowCopy(aptg, 0, 30, 20, 10, 40, true); + confirmAreaRowCopy(aptg, 15, 25, -15, 5, 10, true); //sortTopLeftToBottomRight swapped firstRow and lastRow because firstRow is absolute } @Test @@ -130,11 +170,11 @@ public final class TestFormulaShifter { // 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); + final AreaPtg aptg = createAreaPtgRow(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); + confirmAreaRowCopy(aptg, 0, 30, 20, 10, 20, false); + confirmAreaRowCopy(aptg, 15, 25, -15, 10, 20, false); } /** @@ -144,20 +184,41 @@ public final class TestFormulaShifter { @Test public void testShiftAreasDestRows() { // all these operations are on an area ref spanning rows 20 to 25 - AreaPtg aptg = createAreaPtg(20, 25); + AreaPtg aptg = createAreaPtgRow(20, 25); // no change because no overlap: - confirmAreaShift(aptg, 5, 10, 9, 20, 25); - confirmAreaShift(aptg, 5, 10, 21, 20, 25); + confirmAreaRowShift(aptg, 5, 10, 9, 20, 25); + confirmAreaRowShift(aptg, 5, 10, 21, 20, 25); - confirmAreaShift(aptg, 11, 14, 10, 20, 25); + confirmAreaRowShift(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 + confirmAreaRowShift(aptg, 7, 17, 10, -1, -1); // converted to DeletedAreaRef + confirmAreaRowShift(aptg, 5, 15, 7, 23, 25); // truncation at top + confirmAreaRowShift(aptg, 13, 16, 10, 20, 22); // truncation at bottom } - private static void confirmAreaShift(AreaPtg aptg, + /** + * Tests what happens to an area ref when some outside columns are moved to overlap + * that area ref + */ + @Test + public void testShiftAreasDestColumns() { + // all these operations are on an area ref spanning columns 20 to 25 + AreaPtg aptg = createAreaPtgColumn(20, 25); + + // no change because no overlap: + confirmAreaColumnShift(aptg, 5, 10, 9, 20, 25); + confirmAreaColumnShift(aptg, 5, 10, 21, 20, 25); + + confirmAreaColumnShift(aptg, 11, 14, 10, 20, 25); + + confirmAreaColumnShift(aptg, 7, 17, 10, -1, -1); // converted to DeletedAreaRef + confirmAreaColumnShift(aptg, 5, 15, 7, 23, 25); // truncation at top + confirmAreaColumnShift(aptg, 13, 16, 10, 20, 22); // truncation at bottom + } + + private static void confirmAreaRowShift( + AreaPtg aptg, int firstRowMoved, int lastRowMoved, int numberRowsMoved, int expectedAreaFirstRow, int expectedAreaLastRow) { @@ -177,11 +238,33 @@ public final class TestFormulaShifter { assertEquals(expectedAreaLastRow, copyPtg.getLastRow()); } + + private static void confirmAreaColumnShift( + AreaPtg aptg, + int firstColumnMoved, int lastColumnMoved, int numberColumnsMoved, + int expectedAreaFirstColumn, int expectedAreaLastColumn) { + + FormulaShifter fs = FormulaShifter.createForColumnShift(0, "", firstColumnMoved, lastColumnMoved, numberColumnsMoved, SpreadsheetVersion.EXCEL2007); + boolean expectedChanged = aptg.getFirstColumn() != expectedAreaFirstColumn || aptg.getLastColumn() != expectedAreaLastColumn; + + 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 (expectedAreaFirstColumn < 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(expectedAreaFirstColumn, copyPtg.getFirstColumn()); + assertEquals(expectedAreaLastColumn, copyPtg.getLastColumn()); + + } - private static void confirmAreaCopy(AreaPtg aptg, - int firstRowCopied, int lastRowCopied, int rowOffset, - int expectedFirstRow, int expectedLastRow, boolean expectedChanged) { + private static void confirmAreaRowCopy(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, }; @@ -202,14 +285,22 @@ public final class TestFormulaShifter { } - private static AreaPtg createAreaPtg(int initialAreaFirstRow, int initialAreaLastRow) { - return createAreaPtg(initialAreaFirstRow, initialAreaLastRow, false, false); + private static AreaPtg createAreaPtgRow(int initialAreaFirstRow, int initialAreaLastRow) { + return createAreaPtgRow(initialAreaFirstRow, initialAreaLastRow, false, false); + } + + private static AreaPtg createAreaPtgColumn(int initialAreaFirstColumn, int initialAreaLastColumn) { + return createAreaPtgColumn(initialAreaFirstColumn, initialAreaLastColumn, false, false); } - private static AreaPtg createAreaPtg(int initialAreaFirstRow, int initialAreaLastRow, boolean firstRowRelative, boolean lastRowRelative) { + private static AreaPtg createAreaPtgRow(int initialAreaFirstRow, int initialAreaLastRow, boolean firstRowRelative, boolean lastRowRelative) { return new AreaPtg(initialAreaFirstRow, initialAreaLastRow, 2, 5, firstRowRelative, lastRowRelative, false, false); } + private static AreaPtg createAreaPtgColumn(int initialAreaFirstColumn, int initialAreaLastColumn, boolean firstColumnRelative, boolean lastColumnRelative) { + return new AreaPtg(2, 5, initialAreaFirstColumn, initialAreaLastColumn, false, false, firstColumnRelative, lastColumnRelative); + } + @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