You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by on...@apache.org on 2017/11/04 05:29:52 UTC

svn commit: r1814254 - in /poi/trunk/src: java/org/apache/poi/ss/formula/FormulaShifter.java testcases/org/apache/poi/ss/formula/TestFormulaShifter.java

Author: onealj
Date: Sat Nov  4 05:29:52 2017
New Revision: 1814254

URL: http://svn.apache.org/viewvc?rev=1814254&view=rev
Log:
bug 61474,github-81: add FormulaShifter.createForColumnShift and ShiftMode.ColumnMove

Modified:
    poi/trunk/src/java/org/apache/poi/ss/formula/FormulaShifter.java
    poi/trunk/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java

Modified: poi/trunk/src/java/org/apache/poi/ss/formula/FormulaShifter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/ss/formula/FormulaShifter.java?rev=1814254&r1=1814253&r2=1814254&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/ss/formula/FormulaShifter.java (original)
+++ poi/trunk/src/java/org/apache/poi/ss/formula/FormulaShifter.java Sat Nov  4 05:29:52 2017
@@ -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();

Modified: poi/trunk/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java?rev=1814254&r1=1814253&r2=1814254&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/ss/formula/TestFormulaShifter.java Sat Nov  4 05:29:52 2017
@@ -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
+        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
 
-        confirmAreaShift(aptg, 18, 22,  5, 10, 25); // simple expansion at bottom
+        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 



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org