]> source.dussan.org Git - poi.git/commitdiff
merged 696038 from trunk - (Fix for bug 45780 - update area refs during HSSFSheet...
authorJosh Micich <josh@apache.org>
Wed, 17 Sep 2008 21:23:58 +0000 (21:23 +0000)
committerJosh Micich <josh@apache.org>
Wed, 17 Sep 2008 21:23:58 +0000 (21:23 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@696453 13f79535-47bb-0310-9956-ffa450edef68

28 files changed:
src/documentation/content/xdocs/changes.xml
src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/hssf/model/Sheet.java
src/java/org/apache/poi/hssf/record/NameRecord.java
src/java/org/apache/poi/hssf/record/aggregates/RowRecordsAggregate.java
src/java/org/apache/poi/hssf/record/aggregates/ValueRecordsAggregate.java
src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java
src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java
src/java/org/apache/poi/hssf/record/formula/DeletedArea3DPtg.java
src/java/org/apache/poi/hssf/record/formula/DeletedRef3DPtg.java
src/java/org/apache/poi/hssf/record/formula/ExternSheetNameResolver.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/FormulaShifter.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/Ref2DPtgBase.java [new file with mode: 0644]
src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java
src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java
src/java/org/apache/poi/hssf/record/formula/RefNPtg.java
src/java/org/apache/poi/hssf/record/formula/RefPtg.java
src/java/org/apache/poi/hssf/record/formula/RefPtgBase.java
src/java/org/apache/poi/hssf/usermodel/HSSFCell.java
src/java/org/apache/poi/hssf/usermodel/HSSFName.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
src/testcases/org/apache/poi/hssf/data/ForShifting.xls
src/testcases/org/apache/poi/hssf/record/formula/AllFormulaTests.java
src/testcases/org/apache/poi/hssf/record/formula/TestFormulaShifter.java [new file with mode: 0644]
src/testcases/org/apache/poi/hssf/usermodel/TestFormulas.java
src/testcases/org/apache/poi/hssf/usermodel/TestNamedRange.java
src/testcases/org/apache/poi/hssf/usermodel/TestSheetShiftRows.java

index f26223742ff45eaaffe09f2154926432d83b6ac5..923209302444e6aff46b0cad567b580ab5e551fb 100644 (file)
@@ -67,6 +67,7 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45780 - Fixed HSSFSheet.shiftRows to also update Area refs</action>
            <action dev="POI-DEVELOPERS" type="fix">45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type</action>
            <action dev="POI-DEVELOPERS" type="add">Expose the name of Named Cell Styles via HSSFCellStyle (normally held on the parent style though)</action>
            <action dev="POI-DEVELOPERS" type="fix">45978 - Fixed IOOBE in Ref3DPtg.toFormulaString() due eager initialisation of SheetReferences</action>
index 8bf188faa1782a34bf8ee3730610058de0d0cbf1..42633cbdf78a694e2465b83e31957fe1b553add9 100644 (file)
@@ -64,6 +64,7 @@
            <action dev="POI-DEVELOPERS" type="add">Created a common interface for handling Excel files, irrespective of if they are .xls or .xlsx</action>
         </release>
         <release version="3.2-alpha1" date="2008-??-??">
+           <action dev="POI-DEVELOPERS" type="fix">45780 - Fixed HSSFSheet.shiftRows to also update Area refs</action>
            <action dev="POI-DEVELOPERS" type="fix">45804 - Update HSMF to handle Outlook 3.0 msg files, which have a different string chunk type</action>
            <action dev="POI-DEVELOPERS" type="add">Expose the name of Named Cell Styles via HSSFCellStyle (normally held on the parent style though)</action>
            <action dev="POI-DEVELOPERS" type="fix">45978 - Fixed IOOBE in Ref3DPtg.toFormulaString() due eager initialisation of SheetReferences</action>
index 8bbc30435a0e5e1797a5a39664bbcfaecceec8fa..164399939a514e2d728be4f5533a5e3a235464cb 100644 (file)
@@ -62,7 +62,6 @@ import org.apache.poi.hssf.record.aggregates.CFRecordsAggregate;
 import org.apache.poi.hssf.record.aggregates.ColumnInfoRecordsAggregate;
 import org.apache.poi.hssf.record.aggregates.ConditionalFormattingTable;
 import org.apache.poi.hssf.record.aggregates.DataValidityTable;
-import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
 import org.apache.poi.hssf.record.aggregates.MergedCellsTable;
 import org.apache.poi.hssf.record.aggregates.PageSettingsBlock;
 import org.apache.poi.hssf.record.aggregates.RecordAggregate;
@@ -127,7 +126,8 @@ public final class Sheet implements Model {
     /*package*/ColumnInfoRecordsAggregate _columnInfos;
     /** the DimensionsRecord is always present */
     private DimensionsRecord             _dimensions;
-    protected RowRecordsAggregate        _rowsAggregate              =     null;
+    /** always present */
+    protected RowRecordsAggregate        _rowsAggregate;
     private   DataValidityTable          _dataValidityTable=     null;
     private   ConditionalFormattingTable condFormatting;
 
@@ -329,10 +329,13 @@ public final class Sheet implements Model {
         if (retval.windowTwo == null) {
             throw new RuntimeException("WINDOW2 was not found");
         }
+        if (retval._rowsAggregate == null) {
+               retval._rowsAggregate = new RowRecordsAggregate();
+            records.add(retval.dimsloc + 1, retval._rowsAggregate);
+        }
         // put merged cells table in the right place (regardless of where the first MergedCellsRecord was found */
         RecordOrderer.addNewSheetRecord(records, retval._mergedCellsTable);
         retval.records = records;
-        retval.checkRows();
         if (log.check( POILogger.DEBUG ))
             log.log(POILogger.DEBUG, "sheet createSheet (existing file) exited");
         return retval;
@@ -441,6 +444,8 @@ public final class Sheet implements Model {
         retval._dimensions = createDimensions();
         records.add(retval._dimensions);
         retval.dimsloc = records.size()-1;
+        retval._rowsAggregate = new RowRecordsAggregate();
+        records.add(retval._rowsAggregate);
         // 'Sheet View Settings'
         records.add(retval.windowTwo = retval.createWindowTwo());
         retval.selection = createSelection();
@@ -456,14 +461,10 @@ public final class Sheet implements Model {
         return retval;
     }
 
-    private void checkRows()
-    {
-        if (_rowsAggregate == null)
-        {
-            _rowsAggregate = new RowRecordsAggregate();
-            records.add(dimsloc + 1, _rowsAggregate);
-        }
+    public RowRecordsAggregate getRowsAggregate() {
+       return _rowsAggregate;
     }
+
     private MergedCellsTable getMergedRecords() {
         // always present
         return _mergedCellsTable;
@@ -623,13 +624,6 @@ public final class Sheet implements Model {
         return result;
     }
 
-    /**
-     * Create a row record.  (does not add it to the records contained in this sheet)
-     */
-    private static RowRecord createRow(int row) {
-        return RowRecordsAggregate.createRow( row );
-    }
-
     /**
      * Adds a value record to the sheet's contained binary records
      * (i.e. LabelSSTRecord or NumberRecord).
@@ -714,7 +708,6 @@ public final class Sheet implements Model {
 
     public void addRow(RowRecord row)
     {
-        checkRows();
         if (log.check( POILogger.DEBUG ))
             log.log(POILogger.DEBUG, "addRow ");
         DimensionsRecord d = _dimensions;
@@ -748,7 +741,6 @@ public final class Sheet implements Model {
      * @param row  the row record to remove
      */
     public void removeRow(RowRecord row) {
-        checkRows();
         _rowsAggregate.removeRow(row);
     }
 
@@ -1295,7 +1287,7 @@ public final class Sheet implements Model {
     }
 
     /**
-     * Returns the first occurance of a record matching a particular sid.
+     * Returns the first occurrence of a record matching a particular sid.
      */
 
     public Record findFirstRecordBySid(short sid)
@@ -1781,13 +1773,12 @@ public final class Sheet implements Model {
 
     public void groupRowRange(int fromRow, int toRow, boolean indent)
     {
-        checkRows();
         for (int rowNum = fromRow; rowNum <= toRow; rowNum++)
         {
             RowRecord row = getRow( rowNum );
             if (row == null)
             {
-                row = createRow( rowNum );
+                row = RowRecordsAggregate.createRow(rowNum);
                 addRow( row );
             }
             int level = row.getOutlineLevel();
@@ -1817,17 +1808,6 @@ public final class Sheet implements Model {
         guts.setLeftRowGutter( (short) ( 29 + (12 * (maxLevel)) ) );
     }
 
-    public void setRowGroupCollapsed( int row, boolean collapse )
-    {
-        if (collapse)
-        {
-            _rowsAggregate.collapseRow( row );
-        }
-        else
-        {
-            _rowsAggregate.expandRow( row );
-        }
-    }
     public DataValidityTable getOrCreateDataValidityTable() {
         if (_dataValidityTable == null) {
             DataValidityTable result = new DataValidityTable();
@@ -1836,8 +1816,4 @@ public final class Sheet implements Model {
         }
         return _dataValidityTable;
     }
-
-    public FormulaRecordAggregate createFormula(int row, int col) {
-        return _rowsAggregate.createFormula(row, col);
-    }
 }
index 0716c448d85959a6c269a30937f7b99218962cc5..215c20feec24d5486546ee13972f2cd1814d5f69 100644 (file)
@@ -537,9 +537,7 @@ public final class NameRecord extends Record {
                                temp.add(ptg);
                        }
                } else {
-                       Ptg ptg = new Ref3DPtg();
-                       ((Ref3DPtg) ptg).setExternSheetIndex(externSheetIndex);
-                       ((Ref3DPtg) ptg).setArea(ref);
+                       Ref3DPtg ptg = new Ref3DPtg(ra.getFromCell(), externSheetIndex);
                        temp.add(ptg);
                }
                Ptg[] ptgs = new Ptg[temp.size()];
index c5bbc3119935f0079661d800bb57d2b7a9b58b11..c7610de1eaab3302e427794d59d1cde9f4aef403 100644 (file)
@@ -35,6 +35,7 @@ import org.apache.poi.hssf.record.RowRecord;
 import org.apache.poi.hssf.record.SharedFormulaRecord;
 import org.apache.poi.hssf.record.TableRecord;
 import org.apache.poi.hssf.record.UnknownRecord;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
 
 /**
  *
@@ -507,4 +508,7 @@ public final class RowRecordsAggregate extends RecordAggregate {
         fr.setColumn((short) col);
         return new FormulaRecordAggregate(fr, null, _sharedValueManager);
     }
+    public void updateFormulasAfterRowShift(FormulaShifter formulaShifter, int currentExternSheetIndex) {
+        _valuesAgg.updateFormulasAfterRowShift(formulaShifter, currentExternSheetIndex);
+    }
 }
index 4552be797aa42de2441fef6cc7919bb676cead74..a9b1de2531f392f1a85c7d9a6c2309f858b40ab8 100644 (file)
@@ -28,6 +28,8 @@ import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.RecordBase;
 import org.apache.poi.hssf.record.StringRecord;
 import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
+import org.apache.poi.hssf.record.formula.Ptg;
 
 /**
  *
@@ -225,6 +227,25 @@ public final class ValueRecordsAggregate {
         }
     }
 
+    public void updateFormulasAfterRowShift(FormulaShifter shifter, int currentExternSheetIndex) {
+        for (int i = 0; i < records.length; i++) {
+            CellValueRecordInterface[] rowCells = records[i];
+            if (rowCells == null) {
+                continue;
+            }
+            for (int j = 0; j < rowCells.length; j++) {
+                CellValueRecordInterface cell = rowCells[j];
+                if (cell instanceof FormulaRecordAggregate) {
+                       FormulaRecord fr = ((FormulaRecordAggregate)cell).getFormulaRecord();
+                    Ptg[] ptgs = fr.getParsedExpression(); // needs clone() inside this getter?
+                    if (shifter.adjustFormula(ptgs, currentExternSheetIndex)) {
+                        fr.setParsedExpression(ptgs);
+                    }
+                }
+            }
+        }
+    }
+
     public CellValueRecordInterface[] getValueRecords() {
         List temp = new ArrayList();
 
index 2183cc2dd02c6923e440c695b3c9fc78c3014668..0c97d03d29cf70efe3af92e7be9d63dcbcbcba8c 100644 (file)
@@ -90,24 +90,6 @@ public final class Area3DPtg extends AreaPtgBase {
         *  formulas. The sheet name will get properly delimited if required.
         */
        public String toFormulaString(Workbook book) {
-               // First do the sheet name
-               StringBuffer retval = new StringBuffer();
-               String sheetName = book.findSheetNameFromExternSheet(field_1_index_extern_sheet);
-               if(sheetName != null) {
-                       if(sheetName.length() == 0) {
-                               // What excel does if sheet has been deleted
-                               sheetName = "#REF";
-                               retval.append(sheetName);
-                       } else {
-                               // Normal
-                               SheetNameFormatter.appendFormat(retval, sheetName);
-                       }
-                       retval.append( '!' );
-               }
-               
-               // Now the normal area bit
-               retval.append(formatReferenceAsString());
-               
-               return retval.toString();
+               return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, formatReferenceAsString());
        }
 }
index f1af9b68d1bf6c4d6a2d1986068bb1635fea168a..1387e76856f491276695c12f03a7cee1cec59def 100644 (file)
@@ -18,6 +18,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.ss.usermodel.ErrorConstants;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.util.LittleEndian;
 
@@ -27,23 +28,31 @@ import org.apache.poi.util.LittleEndian;
  * @author Daniel Noll (daniel at nuix dot com dot au)
  */
 public final class AreaErrPtg extends OperandPtg {
-    public final static byte sid  = 0x2b;
-
-    public AreaErrPtg(RecordInputStream in) {
-       // 8 bytes unused:
-        in.readInt();
-        in.readInt();
-    }
-
-    public void writeBytes(byte [] array, int offset) {
-        array[offset] = (byte) (sid + getPtgClass());
-        LittleEndian.putInt(array, offset+1, 0);
-        LittleEndian.putInt(array, offset+5, 0);
-    }
-
-    public String toFormulaString(Workbook book) {
-        return "#REF!";
-    }
+       public final static byte sid = 0x2B;
+       private final int unused1;
+       private final int unused2;
+
+       public AreaErrPtg() {
+               unused1 = 0;
+               unused2 = 0;
+       }
+
+       public AreaErrPtg(RecordInputStream in) {
+               // 8 bytes unused:
+               unused1 = in.readInt();
+               unused2 = in.readInt();
+       }
+
+       public void writeBytes(byte[] array, int offset) {
+               LittleEndian.putByte(array, offset + 0, sid + getPtgClass());
+               LittleEndian.putInt(array, offset + 1, unused1);
+               LittleEndian.putInt(array, offset + 5, unused2);
+       }
+
+
+       public String toFormulaString(Workbook book) {
+               return ErrorConstants.getText(ErrorConstants.ERROR_REF);
+       }
 
        public byte getDefaultOperandClass() {
                return Ptg.CLASS_REF;
index a1c5b3db59e126a444756bff04c4c4fefb21c1af..92506dfbe723499e0f6d5499d3dc22750b93e7f6 100644 (file)
@@ -35,13 +35,21 @@ public final class DeletedArea3DPtg extends OperandPtg {
        private final int unused1;
        private final int unused2;
 
-       public DeletedArea3DPtg( RecordInputStream in) {
+       public DeletedArea3DPtg(int externSheetIndex) {
+               field_1_index_extern_sheet = externSheetIndex;
+               unused1 = 0;
+               unused2 = 0;
+       }
+       
+       public DeletedArea3DPtg(RecordInputStream in) {
                field_1_index_extern_sheet = in.readUShort();
                unused1 = in.readInt();
                unused2 = in.readInt();
        }
+
        public String toFormulaString(Workbook book) {
-               return ErrorConstants.getText(ErrorConstants.ERROR_REF);
+               return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, 
+                               ErrorConstants.getText(ErrorConstants.ERROR_REF));
        }
        public byte getDefaultOperandClass() {
                return Ptg.CLASS_REF;
index 9312b2d766d416d1177b5946f83775a23cec6e87..900a48fe8e3ccc849a4a6cbd55e4f6dcf2eafb34 100644 (file)
@@ -41,8 +41,14 @@ public final class DeletedRef3DPtg extends OperandPtg {
                unused1 = in.readInt();
        }
 
+       public DeletedRef3DPtg(int externSheetIndex) {
+               field_1_index_extern_sheet = externSheetIndex;
+               unused1 = 0;
+       }
+
        public String toFormulaString(Workbook book) {
-               return ErrorConstants.getText(ErrorConstants.ERROR_REF);
+               return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, 
+                               ErrorConstants.getText(ErrorConstants.ERROR_REF));
        }
        public byte getDefaultOperandClass() {
                return Ptg.CLASS_REF;
diff --git a/src/java/org/apache/poi/hssf/record/formula/ExternSheetNameResolver.java b/src/java/org/apache/poi/hssf/record/formula/ExternSheetNameResolver.java
new file mode 100644 (file)
index 0000000..b928931
--- /dev/null
@@ -0,0 +1,44 @@
+/* ====================================================================
+   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.hssf.record.formula;
+
+import org.apache.poi.ss.usermodel.Workbook;
+
+/**
+ * @author Josh Micich
+ */
+final class ExternSheetNameResolver {
+
+       private ExternSheetNameResolver() {
+               // no instances of this class
+       }
+
+       public static String prependSheetName(Workbook book, int field_1_index_extern_sheet, String cellRefText) {
+               String sheetName = book.findSheetNameFromExternSheet(field_1_index_extern_sheet);
+               StringBuffer sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
+               if (sheetName.length() < 1) {
+                       // What excel does if sheet has been deleted
+                       sb.append("#REF"); // note - '!' added just once below
+               } else {
+                       SheetNameFormatter.appendFormat(sb, sheetName);
+               }
+                       sb.append('!');
+               sb.append(cellRefText);
+               return sb.toString();
+       }
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/FormulaShifter.java b/src/java/org/apache/poi/hssf/record/formula/FormulaShifter.java
new file mode 100644 (file)
index 0000000..87683d6
--- /dev/null
@@ -0,0 +1,294 @@
+/* ====================================================================
+   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.hssf.record.formula;
+
+
+/**
+ * @author Josh Micich
+ */
+public final class FormulaShifter {
+
+       /**
+        * Extern sheet index of sheet where moving is occurring
+        */
+       private final int _externSheetIndex;
+       private final int _firstMovedIndex;
+       private final int _lastMovedIndex;
+       private final int _amountToMove;
+
+       private FormulaShifter(int externSheetIndex, int firstMovedIndex, int lastMovedIndex, int amountToMove) {
+               if (amountToMove == 0) {
+                       throw new IllegalArgumentException("amountToMove must not be zero");
+               }
+               if (firstMovedIndex > lastMovedIndex) {
+                       throw new IllegalArgumentException("firstMovedIndex, lastMovedIndex out of order");
+               }
+               _externSheetIndex = externSheetIndex;
+               _firstMovedIndex = firstMovedIndex;
+               _lastMovedIndex = lastMovedIndex;
+               _amountToMove = amountToMove;
+       }
+
+       public static FormulaShifter createForRowShift(int externSheetIndex, int firstMovedRowIndex, int lastMovedRowIndex, int numberOfRowsToMove) {
+               return new FormulaShifter(externSheetIndex, firstMovedRowIndex, lastMovedRowIndex, numberOfRowsToMove);
+       }
+
+       public String toString() {
+               StringBuffer sb = new StringBuffer();
+
+               sb.append(getClass().getName());
+               sb.append(" [");
+               sb.append(_firstMovedIndex);
+               sb.append(_lastMovedIndex);
+               sb.append(_amountToMove);
+               return sb.toString();
+       }
+
+       /**
+        * @param ptgs - if necessary, will get modified by this method
+        * @param currentExternSheetIx - the extern sheet index of the sheet that contains the formula being adjusted
+        * @return <code>true</code> if a change was made to the formula tokens
+        */
+       public boolean adjustFormula(Ptg[] ptgs, int currentExternSheetIx) {
+               boolean refsWereChanged = false;
+               for(int i=0; i<ptgs.length; i++) {
+                       Ptg newPtg = adjustPtg(ptgs[i], currentExternSheetIx);
+                       if (newPtg != null) {
+                               refsWereChanged = true;
+                               ptgs[i] = newPtg;
+                       }
+               }
+               return refsWereChanged;
+       }
+
+       private Ptg adjustPtg(Ptg ptg, int currentExternSheetIx) {
+               return adjustPtgDueToRowMove(ptg, currentExternSheetIx);
+       }
+       /**
+        * @return <code>true</code> if this Ptg needed to be changed
+        */
+       private Ptg adjustPtgDueToRowMove(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 rowMoveRefPtg(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 rowMoveRefPtg(rptg);
+               }
+               if(ptg instanceof Area2DPtgBase) {
+                       if (currentExternSheetIx != _externSheetIndex) {
+                               // local refs on other sheets are unaffected
+                               return ptg;
+                       }
+                       return rowMoveAreaPtg((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 rowMoveAreaPtg(aptg);
+               }
+               return null;
+       }
+
+       private Ptg rowMoveRefPtg(RefPtgBase rptg) {
+               int refRow = rptg.getRow();
+               if (_firstMovedIndex <= refRow && refRow <= _lastMovedIndex) {
+                       // Rows being moved completely enclose the ref.
+                       // - move the area ref along with the rows regardless of destination
+                       rptg.setRow(refRow + _amountToMove);
+                       return rptg;
+               }
+               // else rules for adjusting area may also depend on the destination of the moved rows
+
+               int destFirstRowIndex = _firstMovedIndex + _amountToMove;
+               int destLastRowIndex = _lastMovedIndex + _amountToMove;
+
+               // ref is outside source rows
+               // check for clashes with destination
+
+               if (destLastRowIndex < refRow || refRow < destFirstRowIndex) {
+                       // destination rows are completely outside ref
+                       return null;
+               }
+
+               if (destFirstRowIndex <= refRow && refRow <= destLastRowIndex) {
+                       // destination rows enclose the area (possibly exactly)
+                       return createDeletedRef(rptg);
+               }
+               throw new IllegalStateException("Situation not covered: (" + _firstMovedIndex + ", " +
+                                       _lastMovedIndex + ", " + _amountToMove + ", " + refRow + ", " + refRow + ")");
+       }
+
+       private Ptg rowMoveAreaPtg(AreaPtgBase aptg) {
+               int aFirstRow = aptg.getFirstRow();
+               int aLastRow = aptg.getLastRow();
+               if (_firstMovedIndex <= aFirstRow && aLastRow <= _lastMovedIndex) {
+                       // Rows being moved completely enclose the area ref.
+                       // - move the area ref along with the rows regardless of destination
+                       aptg.setFirstRow(aFirstRow + _amountToMove);
+                       aptg.setLastRow(aLastRow + _amountToMove);
+                       return aptg;
+               }
+               // else rules for adjusting area may also depend on the destination of the moved rows
+
+               int destFirstRowIndex = _firstMovedIndex + _amountToMove;
+               int destLastRowIndex = _lastMovedIndex + _amountToMove;
+
+               if (aFirstRow < _firstMovedIndex && _lastMovedIndex < aLastRow) {
+                       // Rows moved were originally *completely* within the area ref
+
+                       // If the destination of the rows overlaps either the top
+                       // or bottom of the area ref there will be a change
+                       if (destFirstRowIndex < aFirstRow && aFirstRow <= destLastRowIndex) {
+                               // truncate the top of the area by the moved rows
+                               aptg.setFirstRow(destLastRowIndex+1);
+                               return aptg;
+                       } else if (destFirstRowIndex <= aLastRow && aLastRow < destLastRowIndex) {
+                               // truncate the bottom of the area by the moved rows
+                               aptg.setLastRow(destFirstRowIndex-1);
+                               return aptg;
+                       }
+                       // else - rows have moved completely outside the area ref,
+                       // or still remain completely within the area ref
+                       return null; // - no change to the area
+               }
+               if (_firstMovedIndex <= aFirstRow && aFirstRow <= _lastMovedIndex) {
+                       // Rows moved include the first row of the area ref, but not the last row
+                       // btw: (aLastRow > _lastMovedIndex)
+                       if (_amountToMove < 0) {
+                               // simple case - expand area by shifting top upward
+                               aptg.setFirstRow(aFirstRow + _amountToMove);
+                               return aptg;
+                       }
+                       if (destFirstRowIndex > aLastRow) {
+                               // in this case, excel ignores the row move
+                               return null;
+                       }
+                       int newFirstRowIx = aFirstRow + _amountToMove;
+                       if (destLastRowIndex < aLastRow) {
+                               // end of area is preserved (will remain exact same row)
+                               // the top area row is moved simply
+                               aptg.setFirstRow(newFirstRowIx);
+                               return aptg;
+                       }
+                       // else - bottom area row has been replaced - both area top and bottom may move now
+                       int areaRemainingTopRowIx = _lastMovedIndex + 1;
+                       if (destFirstRowIndex > areaRemainingTopRowIx) {
+                               // old top row of area has moved deep within the area, and exposed a new top row
+                               newFirstRowIx = areaRemainingTopRowIx;
+                       }
+                       aptg.setFirstRow(newFirstRowIx);
+                       aptg.setLastRow(Math.max(aLastRow, destLastRowIndex));
+                       return aptg;
+               }
+               if (_firstMovedIndex <= aLastRow && aLastRow <= _lastMovedIndex) {
+                       // Rows moved include the last row of the area ref, but not the first
+                       // btw: (aFirstRow < _firstMovedIndex)
+                       if (_amountToMove > 0) {
+                               // simple case - expand area by shifting bottom downward
+                               aptg.setLastRow(aLastRow + _amountToMove);
+                               return aptg;
+                       }
+                       if (destLastRowIndex < aFirstRow) {
+                               // in this case, excel ignores the row move
+                               return null;
+                       }
+                       int newLastRowIx = aLastRow + _amountToMove;
+                       if (destFirstRowIndex > aFirstRow) {
+                               // top of area is preserved (will remain exact same row)
+                               // the bottom area row is moved simply
+                               aptg.setLastRow(newLastRowIx);
+                               return aptg;
+                       }
+                       // else - top area row has been replaced - both area top and bottom may move now
+                       int areaRemainingBottomRowIx = _firstMovedIndex - 1;
+                       if (destLastRowIndex < areaRemainingBottomRowIx) {
+                               // old bottom row of area has moved up deep within the area, and exposed a new bottom row
+                               newLastRowIx = areaRemainingBottomRowIx;
+                       }
+                       aptg.setFirstRow(Math.min(aFirstRow, destFirstRowIndex));
+                       aptg.setLastRow(newLastRowIx);
+                       return aptg;
+               }
+               // else source rows include none of the rows of the area ref
+               // check for clashes with destination
+
+               if (destLastRowIndex < aFirstRow || aLastRow < destFirstRowIndex) {
+                       // destination rows are completely outside area ref
+                       return null;
+               }
+
+               if (destFirstRowIndex <= aFirstRow && aLastRow <= destLastRowIndex) {
+                       // destination rows enclose the area (possibly exactly)
+                       return createDeletedRef(aptg);
+               }
+
+               if (aFirstRow <= destFirstRowIndex && destLastRowIndex <= aLastRow) {
+                       // destination rows are within area ref (possibly exact on top or bottom, but not both)
+                       return null; // - no change to area
+               }
+
+               if (destFirstRowIndex < aFirstRow && aFirstRow <= destLastRowIndex) {
+                       // dest rows overlap top of area
+                       // - truncate the top
+                       aptg.setFirstRow(destLastRowIndex+1);
+                       return aptg;
+               }
+               if (destFirstRowIndex < aLastRow && aLastRow <= destLastRowIndex) {
+                       // dest rows overlap bottom of area
+                       // - truncate the bottom
+                       aptg.setLastRow(destFirstRowIndex-1);
+                       return aptg;
+               }
+               throw new IllegalStateException("Situation not covered: (" + _firstMovedIndex + ", " +
+                                       _lastMovedIndex + ", " + _amountToMove + ", " + aFirstRow + ", " + aLastRow + ")");
+       }
+
+       private static Ptg createDeletedRef(Ptg ptg) {
+               if (ptg instanceof RefPtg) {
+                       return new RefErrorPtg();
+               }
+               if (ptg instanceof Ref3DPtg) {
+                       Ref3DPtg rptg = (Ref3DPtg) ptg;
+                       return new DeletedRef3DPtg(rptg.getExternSheetIndex());
+               }
+               if (ptg instanceof AreaPtg) {
+                       return new AreaErrPtg();
+               }
+               if (ptg instanceof Area3DPtg) {
+                       Area3DPtg area3DPtg = (Area3DPtg) ptg;
+                       return new DeletedArea3DPtg(area3DPtg.getExternSheetIndex());
+               }
+
+               throw new IllegalArgumentException("Unexpected ref ptg class (" + ptg.getClass().getName() + ")");
+       }
+}
diff --git a/src/java/org/apache/poi/hssf/record/formula/Ref2DPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/Ref2DPtgBase.java
new file mode 100644 (file)
index 0000000..2fff41d
--- /dev/null
@@ -0,0 +1,68 @@
+/* ====================================================================
+   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.hssf.record.formula;
+
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.util.LittleEndian;
+
+/**
+ * @author Josh Micich
+ */
+abstract class Ref2DPtgBase extends RefPtgBase {
+    private final static int SIZE = 5;
+
+    /**
+     * Takes in a String representation of a cell reference and fills out the
+     * numeric fields.
+     */
+    protected Ref2DPtgBase(String cellref) {
+       super(cellref);
+    }
+
+    protected Ref2DPtgBase(int row, int column, boolean isRowRelative, boolean isColumnRelative) {
+      setRow(row);
+      setColumn(column);
+      setRowRelative(isRowRelative);
+      setColRelative(isColumnRelative);
+    }
+
+    protected Ref2DPtgBase(RecordInputStream in) {
+        readCoordinates(in);
+    }
+    public final void writeBytes(byte [] array, int offset) {
+       LittleEndian.putByte(array, offset+0, getSid() + getPtgClass());
+       writeCoordinates(array, offset+1);
+    }
+    public final String toFormulaString(Workbook book) {
+       return formatReferenceAsString();
+    }
+
+       protected abstract byte getSid();
+    public final int getSize() {
+        return SIZE;
+    }
+    public final String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(getClass().getName());
+        sb.append(" [");
+        sb.append(formatReferenceAsString());
+        sb.append("]");
+        return sb.toString();
+    }
+}
index e72cbbc30a1b5e1d707cf88ebfdddd094dd9f862..308e9f0b53a3e5306bf6ee217274bc4de478af7a 100644 (file)
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.hssf.record.RecordInputStream;
-import org.apache.poi.hssf.util.RangeAddress;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.ss.util.SheetReferences;
-import org.apache.poi.util.BitField;
-import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -34,49 +30,36 @@ import org.apache.poi.util.LittleEndian;
  * @author Jason Height (jheight at chariot dot net dot au)
  * @version 1.0-pre
  */
-public final class Ref3DPtg extends OperandPtg {
+public final class Ref3DPtg extends RefPtgBase {
     public final static byte sid  = 0x3a;
 
-    private static final BitField rowRelative = BitFieldFactory.getInstance(0x8000);
-    private static final BitField colRelative = BitFieldFactory.getInstance(0x4000);
-
     private final static int  SIZE = 7; // 6 + 1 for Ptg
     private int             field_1_index_extern_sheet;
-    /** The row index - zero based unsigned 16 bit value */
-    private int            field_2_row;
-    /** Field 2 
-     * - lower 8 bits is the zero based unsigned byte column index 
-     * - bit 16 - isRowRelative
-     * - bit 15 - isColumnRelative 
-     */
-    private int             field_3_column;
 
     /** Creates new AreaPtg */
     public Ref3DPtg() {}
 
     public Ref3DPtg(RecordInputStream in) {
         field_1_index_extern_sheet = in.readShort();
-        field_2_row          = in.readShort();
-        field_3_column        = in.readShort();
+        readCoordinates(in);
     }
     
     public Ref3DPtg(String cellref, short externIdx ) {
         CellReference c= new CellReference(cellref);
         setRow(c.getRow());
-        setColumn((short)c.getCol());
+        setColumn(c.getCol());
         setColRelative(!c.isColAbsolute());
         setRowRelative(!c.isRowAbsolute());   
         setExternSheetIndex(externIdx);
     }
 
     public String toString() {
-        CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(),!isColRelative());
         StringBuffer sb = new StringBuffer();
         sb.append(getClass().getName());
         sb.append(" [");
         sb.append("sheetIx=").append(getExternSheetIndex());
         sb.append(" ! ");
-        sb.append(cr.formatAsString());
+        sb.append(formatReferenceAsString());
         sb.append("]");
         return sb.toString();
     }
@@ -84,8 +67,7 @@ public final class Ref3DPtg extends OperandPtg {
     public void writeBytes(byte [] array, int offset) {
         array[ 0 + offset ] = (byte) (sid + getPtgClass());
         LittleEndian.putShort(array, 1 + offset , getExternSheetIndex());
-        LittleEndian.putShort(array, 3 + offset , (short)getRow());
-        LittleEndian.putShort(array, 5 + offset , (short)getColumnRaw());
+        writeCoordinates(array, offset+3);
     }
 
     public int getSize() {
@@ -100,83 +82,11 @@ public final class Ref3DPtg extends OperandPtg {
         field_1_index_extern_sheet = index;
     }
 
-    public int getRow() {
-        return field_2_row;
-    }
-
-    public void setRow(int row) {
-        field_2_row = row;
-    }
-
-    public int getColumn() {
-        return field_3_column & 0xFF;
-    }
-
-    public int getColumnRaw() {
-        return field_3_column;
-    }
-
-     public boolean isRowRelative()
-    {
-        return rowRelative.isSet(field_3_column);
-    }
-    
-    public void setRowRelative(boolean rel) {
-        field_3_column=rowRelative.setBoolean(field_3_column,rel);
-    }
-    
-    public boolean isColRelative()
-    {
-        return colRelative.isSet(field_3_column);
-    }
-    
-    public void setColRelative(boolean rel) {
-        field_3_column=colRelative.setBoolean(field_3_column,rel);
-    }
-    public void setColumn(short column) {
-        field_3_column &= 0xFF00;
-        field_3_column |= column & 0xFF;
-    }
-
-    public void setColumnRaw(short column) {
-        field_3_column = column;
-    }
-
-   /* public String getArea(){
-        RangeAddress ra = new RangeAddress("");
-
-        String result = (ra.numTo26Sys(getColumn()) + (getRow() + 1));
-
-        return result;
-    }*/
-
-    public void setArea(String ref){
-        RangeAddress ra = new RangeAddress(ref);
-
-        String from = ra.getFromCell();
-
-        setColumn((short) (ra.getXPosition(from) -1));
-        setRow((short) (ra.getYPosition(from) -1));
-
-    }
-
     /**
      * @return text representation of this cell reference that can be used in text 
      * formulas. The sheet name will get properly delimited if required.
      */
-    public String toFormulaString(Workbook book)
-    {
-        StringBuffer retval = new StringBuffer();
-        String sheetName = book.findSheetNameFromExternSheet(field_1_index_extern_sheet);
-        if(sheetName != null) {
-            SheetNameFormatter.appendFormat(retval, sheetName);
-            retval.append( '!' );
-        }
-        retval.append((new CellReference(getRow(),getColumn(),!isRowRelative(),!isColRelative())).formatAsString()); 
-        return retval.toString();
+    public String toFormulaString(Workbook book) {
+               return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, formatReferenceAsString());
     }
-
-   public byte getDefaultOperandClass() {
-               return Ptg.CLASS_REF;
-       }
 }
index 6adbe43c51999634a3921ed90a215548592491be..a3030e1bd719ed329fe4d92062a057fe2e788a06 100755 (executable)
@@ -18,6 +18,7 @@
 package org.apache.poi.hssf.record.formula;
 
 import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.ss.usermodel.ErrorConstants;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.util.LittleEndian;
 
@@ -28,48 +29,32 @@ import org.apache.poi.util.LittleEndian;
 public final class RefErrorPtg extends OperandPtg {
 
     private final static int SIZE = 5;
-    public final static byte sid  = 0x2a;
+    public final static byte sid  = 0x2A;
     private int              field_1_reserved;
 
-    public RefErrorPtg(RecordInputStream in)
-    {
+    public RefErrorPtg() {
+        field_1_reserved = 0;
+    }
+    public RefErrorPtg(RecordInputStream in) {
         field_1_reserved = in.readInt();
-
     }
 
-    public String toString()
-    {
-        StringBuffer buffer = new StringBuffer("[RefError]\n");
-
-        buffer.append("reserved = ").append(getReserved()).append("\n");
-        return buffer.toString();
+    public String toString() {
+        return getClass().getName();
     }
 
-    public void writeBytes(byte [] array, int offset)
-    {
-        array[offset] = (byte) (sid + getPtgClass());
+    public void writeBytes(byte [] array, int offset) {
+        LittleEndian.putByte(array, offset+0, sid + getPtgClass());
         LittleEndian.putInt(array,offset+1,field_1_reserved);
     }
 
-    public void setReserved(int reserved)
-    {
-        field_1_reserved = reserved;
-    }
-
-    public int getReserved()
-    {
-        return field_1_reserved;
-    }
-
     public int getSize()
     {
         return SIZE;
     }
 
-    public String toFormulaString(Workbook book)
-    {
-        //TODO -- should we store a cellreference instance in this ptg?? but .. memory is an issue, i believe!
-        return "#REF!";
+    public String toFormulaString(Workbook book) {
+        return ErrorConstants.getText(ErrorConstants.ERROR_REF);
     }
     
     public byte getDefaultOperandClass() {
index 5ef4a413b31ac125e1346a6f824ae72b642508f5..07114265d0c1a3858d5a66addd0537feb0b0d7da 100644 (file)
@@ -23,16 +23,14 @@ import org.apache.poi.hssf.record.RecordInputStream;
  * RefNPtg
  * @author Jason Height (jheight at apache dot com)
  */
-public final class RefNPtg extends RefPtgBase {
-    public final static byte sid  = 0x2C;
+public final class RefNPtg extends Ref2DPtgBase {
+       public final static byte sid = 0x2C;
 
-    /** Creates new ValueReferencePtg */
+       public RefNPtg(RecordInputStream in) {
+               super(in);
+       }
 
-    public RefNPtg(RecordInputStream in) {
-      super(in);
-    }
-    
-    protected byte getSid() {
-       return sid;
-    }
+       protected byte getSid() {
+               return sid;
+       }
 }
index a324ce70f97dfd98048ddfb6b1e743b33795a7f7..94a1b3301c314c225d24bcc4217551893e599c4b 100644 (file)
@@ -24,30 +24,26 @@ import org.apache.poi.hssf.record.RecordInputStream;
  * @author  Andrew C. Oliver (acoliver@apache.org)\r
  * @author Jason Height (jheight at chariot dot net dot au)\r
  */\r
-public final class RefPtg extends RefPtgBase {\r
-    public final static byte sid  = 0x24;\r
-\r
-    /**\r
-     * Takes in a String representation of a cell reference and fills out the\r
-     * numeric fields.\r
-     */\r
-    public RefPtg(String cellref) {\r
-       super(cellref);\r
-    }\r
-\r
-    public RefPtg(int row, int column, boolean isRowRelative, boolean isColumnRelative) {\r
-      setRow(row);\r
-      setColumn(column);\r
-      setRowRelative(isRowRelative);\r
-      setColRelative(isColumnRelative);\r
-    }\r
-\r
-    public RefPtg(RecordInputStream in) {\r
-        super(in);\r
-    }\r
+public final class RefPtg extends Ref2DPtgBase {\r
+       public final static byte sid = 0x24;\r
+\r
+       /**\r
+        * Takes in a String representation of a cell reference and fills out the\r
+        * numeric fields.\r
+        */\r
+       public RefPtg(String cellref) {\r
+               super(cellref);\r
+       }\r
+\r
+       public RefPtg(int row, int column, boolean isRowRelative, boolean isColumnRelative) {\r
+               super(row, column, isRowRelative, isColumnRelative);\r
+       }\r
+\r
+       public RefPtg(RecordInputStream in) {\r
+               super(in);\r
+       }\r
 \r
        protected byte getSid() {\r
                return sid;\r
        }\r
-    \r
 }\r
index 33dff9ec04af1d56f292f05b7c13d41be06ee01f..f04ec0ac8ff05d04b6d498d8c6c5dc24f20b64fc 100644 (file)
 
 package org.apache.poi.hssf.record.formula;
 
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
-
-import org.apache.poi.ss.util.CellReference;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.apache.poi.hssf.record.RecordInputStream;
+import org.apache.poi.util.LittleEndian;
 
 /**
  * ReferencePtgBase - handles references (such as A1, A2, IA4)
@@ -32,7 +30,6 @@ import org.apache.poi.hssf.record.RecordInputStream;
  */
 public abstract class RefPtgBase extends OperandPtg {
 
-    private final static int SIZE = 5;
     private final static int MAX_ROW_NUMBER = 65536;
 
    /** The row index - zero based unsigned 16 bit value */
@@ -70,29 +67,14 @@ public abstract class RefPtgBase extends OperandPtg {
       setColRelative(isColumnRelative);
     }
 
-    protected RefPtgBase(RecordInputStream in) {
+    protected final void readCoordinates(RecordInputStream in) {
         field_1_row = in.readUShort();
         field_2_col = in.readUShort();
     }
-
-    public final String toString() {
-        CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(),!isColRelative());
-        StringBuffer sb = new StringBuffer();
-        sb.append(getClass().getName());
-        sb.append(" [");
-        sb.append(cr.formatAsString());
-        sb.append("]");
-        return sb.toString();
-    }
-
-    public final void writeBytes(byte [] array, int offset) {
-        array[offset] = (byte) (getSid() + getPtgClass());
-
-        LittleEndian.putShort(array, offset+1, (short)field_1_row);
-        LittleEndian.putShort(array, offset+3, (short)field_2_col);
+    protected final void writeCoordinates(byte[] array, int offset) {
+        LittleEndian.putUShort(array, offset + 0, field_1_row);
+        LittleEndian.putUShort(array, offset + 2, field_2_col);
     }
-    
-    protected abstract byte getSid();
 
     public final void setRow(int row) {
         if(row < 0 || row >= MAX_ROW_NUMBER) {
@@ -102,18 +84,11 @@ public abstract class RefPtgBase extends OperandPtg {
     }
 
     /**
-     * Returns the row number as a short, which will be
-     *  wrapped (negative) for values between 32769 and 65535
+     * @return the row number as an int, between 0 and 65535
      */
     public final int getRow(){
         return field_1_row;
     }
-    /**
-     * Returns the row number as an int, between 0 and 65535
-     */
-    public final int getRowAsInt() {
-        return field_1_row;
-    }
 
     public final boolean isRowRelative() {
         return rowRelative.isSet(field_2_col);
@@ -135,20 +110,16 @@ public abstract class RefPtgBase extends OperandPtg {
         if(col < 0 || col >= 0x100) {
             throw new IllegalArgumentException("Specified colIx (" + col + ") is out of range");
         }
-       field_2_col = column.setValue(field_2_col, col);
+       field_2_col = column.setValue(field_2_col, col);
     }
 
     public final int getColumn() {
-       return column.getValue(field_2_col);
-    }
-
-    public final int getSize() {
-        return SIZE;
+       return column.getValue(field_2_col);
     }
-
-    public final String toFormulaString(Workbook book) {
-        //TODO -- should we store a cellreference instance in this ptg?? but .. memory is an issue, i believe!
-        return (new CellReference(getRowAsInt(),getColumn(),!isRowRelative(),!isColRelative())).formatAsString();
+    protected final String formatReferenceAsString() {
+        // Only make cell references as needed. Memory is an issue
+        CellReference cr = new CellReference(getRow(), getColumn(), !isRowRelative(), !isColRelative());
+        return cr.formatAsString();
     }
 
     public final byte getDefaultOperandClass() {
index 1f88cce2bd132059d506e3eeb5b8ce4ec00145cc..4fbedb874553793235d1cd78c10066d9a8a36f36 100644 (file)
@@ -300,7 +300,7 @@ public class HSSFCell implements Cell {
                 FormulaRecordAggregate frec;
 
                 if (cellType != this.cellType) {
-                    frec = sheet.getSheet().createFormula(row, col);
+                    frec = sheet.getSheet().getRowsAggregate().createFormula(row, col);
                 } else {
                     frec = (FormulaRecordAggregate) record;
                     frec.setRow(row);
@@ -600,12 +600,6 @@ public class HSSFCell implements Cell {
         Ptg[] ptgs = FormulaParser.parse(formula, book);
         frec.setParsedExpression(ptgs);
     }
-    /* package */ void setFormulaOnly(Ptg[] ptgs) {
-        if (ptgs == null) {
-            throw new IllegalArgumentException("ptgs must not be null");
-        }
-        ((FormulaRecordAggregate)record).getFormulaRecord().setParsedExpression(ptgs);
-    }
 
     public String getCellFormula() {
         return FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
index 4fde9e6cc3879bfe19001c1c6ed8497232697ead..6de2293794c57d5f11b78ce93c493be059107f2a 100644 (file)
@@ -126,8 +126,16 @@ public class HSSFName implements Name {
      * @return true if the name refers to a deleted cell, false otherwise
      */
     public boolean isDeleted(){
-        String ref = getReference();
-        return "#REF!".endsWith(ref);
+        String formulaText = getReference();
+        if (formulaText.startsWith("#REF!")) {
+               // sheet deleted
+               return true;
+        }
+        if (formulaText.endsWith("#REF!")) {
+               // cell range deleted
+               return true;
+        }
+        return false;
     }
     public boolean isFunctionName() {
         return _definedNameRec.isFunctionName();
index 4d6a1f72539f884e966ae4481d578eca9a3fce65..231942f44046eae4541375d18ecc49f5b4dc05bc 100644 (file)
@@ -31,7 +31,6 @@ import java.util.List;
 import java.util.TreeMap;
 
 import org.apache.poi.ddf.EscherRecord;
-import org.apache.poi.hssf.model.FormulaParser;
 import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.model.Workbook;
 import org.apache.poi.hssf.record.CellValueRecordInterface;
@@ -43,8 +42,7 @@ import org.apache.poi.hssf.record.SCLRecord;
 import org.apache.poi.hssf.record.WSBoolRecord;
 import org.apache.poi.hssf.record.WindowTwoRecord;
 import org.apache.poi.hssf.record.aggregates.DataValidityTable;
-import org.apache.poi.hssf.record.formula.Ptg;
-import org.apache.poi.hssf.record.formula.RefPtg;
+import org.apache.poi.hssf.record.formula.FormulaShifter;
 import org.apache.poi.hssf.util.PaneInformation;
 import org.apache.poi.ss.usermodel.CellStyle;
 import org.apache.poi.ss.usermodel.Row;
@@ -1180,33 +1178,26 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
      * @param resetOriginalRowHeight whether to set the original row's height to the default
      * @param moveComments whether to move comments at the same time as the cells they are attached to
      */
-    public void shiftRows( int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight, boolean moveComments)
-    {
-        int s, e, inc;
-        if ( n < 0 )
-        {
+    public void shiftRows(int startRow, int endRow, int n, 
+            boolean copyRowHeight, boolean resetOriginalRowHeight, boolean moveComments) {
+        int s, inc;
+        if (n < 0) {
             s = startRow;
-            e = endRow;
             inc = 1;
-        }
-        else
-        {
+        } else {
             s = endRow;
-            e = startRow;
             inc = -1;
         }
 
         shiftMerged(startRow, endRow, n, true);
         sheet.getPageSettings().shiftRowBreaks(startRow, endRow, n);
 
-        for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc )
-        {
+        for ( int rowNum = s; rowNum >= startRow && rowNum <= endRow && rowNum >= 0 && rowNum < 65536; rowNum += inc ) {
             HSSFRow row = getRow( rowNum );
             HSSFRow row2Replace = getRow( rowNum + n );
             if ( row2Replace == null )
                 row2Replace = createRow( rowNum + n );
 
-            HSSFCell cell;
             
             // Remove all the old cells from the row we'll
             //  be writing too, before we start overwriting 
@@ -1236,7 +1227,7 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
             // Copy each cell from the source row to
             //  the destination row
             for(Iterator cells = row.cellIterator(); cells.hasNext(); ) {
-                cell = (HSSFCell)cells.next();
+                HSSFCell cell = (HSSFCell)cells.next();
                 row.removeCell( cell );
                 CellValueRecordInterface cellRecord = cell.getCellValueRecord();
                 cellRecord.setRow( rowNum + n );
@@ -1263,49 +1254,21 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
 
         // Update any formulas on this sheet that point to
         //  rows which have been moved
-        updateFormulasAfterShift(startRow, endRow, n);
-    }
-
-    /**
-     * Called by shiftRows to update formulas on this sheet
-     *  to point to the new location of moved rows
-     */
-    private void updateFormulasAfterShift(int startRow, int endRow, int n) {
-        // Need to look at every cell on the sheet
-        // Not just those that were moved
-        Iterator ri = rowIterator();
-        while(ri.hasNext()) {
-            HSSFRow r = (HSSFRow)ri.next();
-            Iterator ci = r.cellIterator();
-            while(ci.hasNext()) {
-                HSSFCell c = (HSSFCell)ci.next();
-                if(c.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
-                    // Since it's a formula cell, process the
-                    //  formula string, and look to see if
-                    //  it contains any references
-
-                    // Look for references, and update if needed
-                    Ptg[] ptgs = FormulaParser.parse(c.getCellFormula(), workbook);
-                    boolean changed = false;
-                    for(int i=0; i<ptgs.length; i++) {
-                        if(ptgs[i] instanceof RefPtg) {
-                            RefPtg rptg = (RefPtg)ptgs[i];
-                            if(startRow <= rptg.getRowAsInt() &&
-                                    rptg.getRowAsInt() <= endRow) {
-                                // References a row that moved
-                                rptg.setRow(rptg.getRowAsInt() + n);
-                                changed = true;
-                            }
-                        }
-                    }
-                    // If any references were changed, then
-                    //  re-create the formula string
-                    if(changed) {
-                        c.setFormulaOnly(ptgs);
-                    }
-                }
+        int sheetIndex = workbook.getSheetIndex(this);
+        short externSheetIndex = book.checkExternSheet(sheetIndex);
+        FormulaShifter shifter = FormulaShifter.createForRowShift(externSheetIndex, startRow, endRow, n);
+        sheet.getRowsAggregate().updateFormulasAfterRowShift(shifter, externSheetIndex);
+
+        int nSheets = workbook.getNumberOfSheets();
+        for(int i=0; i<nSheets; i++) {
+            Sheet otherSheet = workbook.getSheetAt(i).getSheet();
+            if (otherSheet == this.sheet) {
+                continue;
             }
+            short otherExtSheetIx = book.checkExternSheet(i);
+            otherSheet.getRowsAggregate().updateFormulasAfterRowShift(shifter, otherExtSheetIx);
         }
+        // TODO - adjust formulas in named ranges
     }
 
     protected void insertChartRecords( List records )
@@ -1652,9 +1615,12 @@ public class HSSFSheet implements org.apache.poi.ss.usermodel.Sheet
         sheet.groupRowRange( fromRow, toRow, false );
     }
 
-    public void setRowGroupCollapsed( int row, boolean collapse )
-    {
-        sheet.setRowGroupCollapsed( row, collapse );
+    public void setRowGroupCollapsed(int rowIndex, boolean collapse) {
+        if (collapse) {
+            sheet.getRowsAggregate().collapseRow(rowIndex);
+        } else {
+            sheet.getRowsAggregate().expandRow(rowIndex);
+        }
     }
 
     /**
index b0da561faee457a9b4fdf9a955db32178664ad6a..83bae3e43b1913517270855c6d68651330dcfdc5 100644 (file)
@@ -678,10 +678,35 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
      *  if needed.
      * Used by some of the more obscure formula and
      *  named range things.
+     * @deprecated for POI internal use only (formula parsing).  This method is likely to
+     * be removed in future versions of POI.
      */
     public int getExternalSheetIndex(int internalSheetIndex) {
         return workbook.checkExternSheet(internalSheetIndex);
     }
+    /**
+     * @deprecated for POI internal use only (formula rendering).  This method is likely to
+     * be removed in future versions of POI.
+     */
+    public String findSheetNameFromExternSheet(int externSheetIndex){
+        // TODO - don't expose internal ugliness like externSheet indexes to the user model API
+        return workbook.findSheetNameFromExternSheet(externSheetIndex);
+    }
+    /**
+     * @deprecated for POI internal use only (formula rendering).  This method is likely to
+     * be removed in future versions of POI.
+     * 
+     * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
+     * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
+     * @return the string representation of the defined or external name
+     */
+    public String resolveNameXText(int refIndex, int definedNameIndex) {
+        // TODO - make this less cryptic / move elsewhere
+        return workbook.resolveNameXText(refIndex, definedNameIndex);
+    }
+
+
+
 
     /**
      * create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns
@@ -865,15 +890,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         return retval;
     }
 
-    /**
-     * @deprecated for POI internal use only (formula rendering).  This method is likely to
-     * be removed in future versions of POI.
-     */
-    public String findSheetNameFromExternSheet(int externSheetIndex){
-        // TODO - don't expose internal ugliness like externSheet indexes to the user model API
-        return workbook.findSheetNameFromExternSheet(externSheetIndex);
-    }
-
     /**
      * Removes sheet at the given index.<p/>
      *
@@ -1384,20 +1400,6 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
         return result;
     }
 
-    /**
-     * @deprecated for POI internal use only (formula rendering).  This method is likely to
-     * be removed in future versions of POI.
-     * 
-     * @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
-     * @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
-     * @return the string representation of the defined or external name
-     */
-    public String resolveNameXText(int refIndex, int definedNameIndex) {
-        // TODO - make this less cryptic / move elsewhere
-        return workbook.resolveNameXText(refIndex, definedNameIndex);
-    }
-
-
     /**
      * Sets the printarea for the sheet provided
      * <p>
index 13e5920e521391ba54beefba8e5788f569f55bf3..7cb3ebd7d13dcba01b131170ea98926806a92051 100755 (executable)
Binary files a/src/testcases/org/apache/poi/hssf/data/ForShifting.xls and b/src/testcases/org/apache/poi/hssf/data/ForShifting.xls differ
index a685fd2cb690254059e8a3448b73a3d90ee9abaf..5f63f31a0bee6d64c26653bbeb1eae47cbd7db2c 100644 (file)
@@ -43,6 +43,7 @@ public final class AllFormulaTests {
                result.addTestSuite(TestArrayPtg.class);
                result.addTestSuite(TestErrPtg.class);
                result.addTestSuite(TestExternalFunctionFormulas.class);
+               result.addTestSuite(TestFormulaShifter.class);
                result.addTestSuite(TestFuncPtg.class);
                result.addTestSuite(TestFuncVarPtg.class);
                result.addTestSuite(TestIntersectionPtg.class);
diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestFormulaShifter.java b/src/testcases/org/apache/poi/hssf/record/formula/TestFormulaShifter.java
new file mode 100644 (file)
index 0000000..55f79e6
--- /dev/null
@@ -0,0 +1,115 @@
+/* ====================================================================
+   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.hssf.record.formula;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link FormulaShifter}.
+ *
+ * @author Josh Micich
+ */
+public final class TestFormulaShifter extends TestCase {
+       // 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
+        */
+       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
+       }
+       /**
+        * Tests what happens to an area ref when some outside rows are moved to overlap
+        * that area ref
+        */
+       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);
+               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 AreaPtg createAreaPtg(int initialAreaFirstRow, int initialAreaLastRow) {
+               return new AreaPtg(initialAreaFirstRow, initialAreaLastRow, 2, 5, false, false, false, false);
+       }
+}
index 36eaf92f32bdffca8d6088eafd63ecedf3522876..397c1e52c9b5e339916f0ff4ae369dcc03c4aea0 100644 (file)
@@ -1052,25 +1052,25 @@ public final class TestFormulas extends TestCase {
          sb.write(new FileOutputStream(file));
     }
 
-    /*Unknown Ptg 3C*/
+    /*Unknown Ptg 3C*/
     public void test27272_1() throws Exception {
         HSSFWorkbook wb = openSample("27272_1.xls");
         wb.getSheetAt(0);
-        assertEquals("Reference for named range ", "#REF!",wb.getNameAt(0).getReference());
+        assertEquals("Reference for named range ", "Compliance!#REF!",wb.getNameAt(0).getReference());
         File outF = File.createTempFile("bug27272_1",".xls");
         wb.write(new FileOutputStream(outF));
         System.out.println("Open "+outF.getAbsolutePath()+" in Excel");
     }
-    /*Unknown Ptg 3D*/
+    /*Unknown Ptg 3D*/
     public void test27272_2() throws Exception {
         HSSFWorkbook wb = openSample("27272_2.xls");
-        assertEquals("Reference for named range ", "#REF!",wb.getNameAt(0).getReference());
+        assertEquals("Reference for named range ", "'LOAD.POD_HISTORIES'!#REF!",wb.getNameAt(0).getReference());
         File outF = File.createTempFile("bug27272_2",".xls");
         wb.write(new FileOutputStream(outF));
         System.out.println("Open "+outF.getAbsolutePath()+" in Excel");
     }
 
-    /* MissingArgPtg */
+    /** MissingArgPtg */
     public void testMissingArgPtg() throws Exception {
         HSSFWorkbook wb = new HSSFWorkbook();
         HSSFCell cell = wb.createSheet("Sheet1").createRow(4).createCell(0);
index f3430d8bcb62ca8138436a917077bec27a57d322..b3afee5bf98c2ec25b981bbb8fec9f8346b4ab2f 100644 (file)
@@ -476,7 +476,7 @@ public final class TestNamedRange extends TestCase {
 
                HSSFName name2 = wb.getNameAt(1);
                assertEquals("b", name2.getNameName());
-               assertEquals("#REF!", name2.getReference());
+               assertEquals("Sheet1!#REF!", name2.getReference());
                assertTrue(name2.isDeleted());
                try {
                        AreaReference ref2 = new AreaReference(name2.getReference());
index 101bd325df893703ee12d6b616b6281ba667daa2..ccd92298da9d03dc26c194fd35357eab06a83a70 100755 (executable)
@@ -19,6 +19,10 @@ package org.apache.poi.hssf.usermodel;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 
 import junit.framework.TestCase;
 
@@ -201,36 +205,69 @@ public final class TestSheetShiftRows extends TestCase {
         HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("ForShifting.xls");
 
         HSSFSheet sheet = wb.getSheet("Sheet1");
-        assertEquals(19, sheet.getLastRowNum());
-
-        assertEquals("cell B1 (ref)", sheet.getRow(0).getCell(3).getRichStringCellValue().toString());
-        assertEquals("CONCATENATE(B1,\" (ref)\")", sheet.getRow(0).getCell(3).getCellFormula());
-        assertEquals("cell B2 (ref)", sheet.getRow(1).getCell(3).getRichStringCellValue().toString());
-        assertEquals("CONCATENATE(B2,\" (ref)\")", sheet.getRow(1).getCell(3).getCellFormula());
-        assertEquals("cell B3 (ref)", sheet.getRow(2).getCell(3).getRichStringCellValue().toString());
-        assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell(3).getCellFormula());
-        assertEquals("cell B2 (ref)", sheet.getRow(6).getCell(1).getRichStringCellValue().toString());
-        assertEquals("CONCATENATE(B2,\" (ref)\")", sheet.getRow(6).getCell(1).getCellFormula());
-
+        assertEquals(20, sheet.getLastRowNum());
+        
+        confirmRow(sheet, 0, 1, 171, 1, "ROW(D1)", "100+B1", "COUNT(D1:E1)");
+        confirmRow(sheet, 1, 2, 172, 1, "ROW(D2)", "100+B2", "COUNT(D2:E2)");
+        confirmRow(sheet, 2, 3, 173, 1, "ROW(D3)", "100+B3", "COUNT(D3:E3)");
+        
+        confirmCell(sheet, 6, 1, 271, "200+B1");
+        confirmCell(sheet, 7, 1, 272, "200+B2");
+        confirmCell(sheet, 8, 1, 273, "200+B3");
+
+        confirmCell(sheet, 14, 0, 0.0, "A12"); // the cell referred to by this formula will be replaced
+        
+        // -----------
+        // Row index 1 -> 11 (row "2" -> row "12")
         sheet.shiftRows(1, 1, 10);
+        
+        // Now check what sheet looks like after move
 
-        // Row 1 => Row 11
-        // So strings on row 11 unchanged, but reference in formula is
-        assertEquals("cell B1 (ref)", sheet.getRow(0).getCell(3).getRichStringCellValue().toString());
-        assertEquals("CONCATENATE(B1,\" (ref)\")", sheet.getRow(0).getCell(3).getCellFormula());
+        // no changes on row "1"
+        confirmRow(sheet, 0, 1, 171, 1, "ROW(D1)", "100+B1", "COUNT(D1:E1)");
+        
+        // row "2" is now empty
         assertEquals(0, sheet.getRow(1).getPhysicalNumberOfCells());
 
-        // still save b2
-        assertEquals("cell B2 (ref)", sheet.getRow(11).getCell(3).getRichStringCellValue().toString());
-        // but points to b12
-        assertEquals("CONCATENATE(B12,\" (ref)\")", sheet.getRow(11).getCell(3).getCellFormula());
+        // Row "2" moved to row "12", and the formula has been updated.
+        // note however that the cached formula result (2) has not been updated. (POI differs from Excel here)
+        confirmRow(sheet, 11, 2, 172, 1, "ROW(D12)", "100+B12", "COUNT(D12:E12)");
+
+        // no changes on row "3"
+        confirmRow(sheet, 2, 3, 173, 1, "ROW(D3)", "100+B3", "COUNT(D3:E3)");
+
+        
+        confirmCell(sheet, 14, 0, 0.0, "#REF!");  
+        
+        
+        // Formulas on rows that weren't shifted:
+        confirmCell(sheet, 6, 1, 271, "200+B1");
+        confirmCell(sheet, 7, 1, 272, "200+B12"); // this one moved
+        confirmCell(sheet, 8, 1, 273, "200+B3");
+        
+        // check formulas on other sheets
+        HSSFSheet sheet2 = wb.getSheet("Sheet2");
+        confirmCell(sheet2,  0, 0, 371, "300+Sheet1!B1");
+        confirmCell(sheet2,  1, 0, 372, "300+Sheet1!B12");
+        confirmCell(sheet2,  2, 0, 373, "300+Sheet1!B3");
+        
+        confirmCell(sheet2, 11, 0, 300, "300+Sheet1!#REF!");
+        
+        
+        // Note - named ranges formulas have not been updated
+    }
 
-        assertEquals("cell B3 (ref)", sheet.getRow(2).getCell(3).getRichStringCellValue().toString());
-        assertEquals("CONCATENATE(B3,\" (ref)\")", sheet.getRow(2).getCell(3).getCellFormula());
+    private static void confirmRow(HSSFSheet sheet, int rowIx, double valA, double valB, double valC,
+                String formulaA, String formulaB, String formulaC) {
+        confirmCell(sheet, rowIx, 4, valA, formulaA);
+        confirmCell(sheet, rowIx, 5, valB, formulaB);
+        confirmCell(sheet, rowIx, 6, valC, formulaC);
+    }
 
-        // one on a non-shifted row also updated
-        assertEquals("cell B2 (ref)", sheet.getRow(6).getCell(1).getRichStringCellValue().toString());
-        assertEquals("CONCATENATE(B12,\" (ref)\")", sheet.getRow(6).getCell(1).getCellFormula());
+    private static void confirmCell(HSSFSheet sheet, int rowIx, int colIx, 
+            double expectedValue, String expectedFormula) {
+        HSSFCell cell = sheet.getRow(rowIx).getCell(colIx);
+        assertEquals(expectedValue, cell.getNumericCellValue(), 0.0);
+        assertEquals(expectedFormula, cell.getCellFormula());
     }
 }
-