diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 9a22b181d..5908abf00 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,8 @@ + 46536 - When shifting rows, update formulas on that sheet to point to the new location of those rows + 46663 - Fixed XSSFSheet.shiftRows to properly update references of the shifted cells 46535 - Remove reference from calculation chain when a formula is deleted 46654 - HSSFRow/RowRecord to properly update cell boundary indexes 46643 - Fixed formula parser to encode range operator with tMemFunc diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index e44a12843..4bb55ec39 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,8 @@ + 46536 - When shifting rows, update formulas on that sheet to point to the new location of those rows + 46663 - Fixed XSSFSheet.shiftRows to properly update references of the shifted cells 46535 - Remove reference from calculation chain when a formula is deleted 46654 - HSSFRow/RowRecord to properly update cell boundary indexes 46643 - Fixed formula parser to encode range operator with tMemFunc diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java index b7d2713f5..26fb2dc8a 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -826,17 +826,4 @@ public final class XSSFCell implements Cell { return cell; } - /** - * update cell reference when shifting rows - * - * @param row - */ - protected void modifyCellReference(XSSFRow row) { - this.cell.setR(new CellReference(row.getRowNum(), cellNum).formatAsString()); - - CTCell[] ctCells = row.getCTRow().getCArray(); - for (CTCell ctCell : ctCells) { - ctCell.setR(new CellReference(row.getRowNum(), cellNum).formatAsString()); - } - } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java index 1011ecaf3..88563ec26 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRow.java @@ -21,8 +21,16 @@ import java.util.*; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.ss.formula.FormulaParser; +import org.apache.poi.ss.formula.FormulaType; +import org.apache.poi.ss.formula.FormulaRenderer; +import org.apache.poi.xssf.model.CalculationChain; +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.SharedFormulaRecord; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellFormula; /** * High level representation of a row of a spreadsheet. @@ -391,4 +399,61 @@ public class XSSFRow implements Row, Comparable { public String toString(){ return row.toString(); } + + /** + * update cell references when shifting rows + * + * @param n the number of rows to move + */ + protected void shift(int n) { + XSSFSheet sheet = getSheet(); + CalculationChain calcChain = sheet.getWorkbook().getCalculationChain(); + int rownum = getRowNum() + n; + for(Cell c : this){ + XSSFCell cell = (XSSFCell)c; + + //remove the reference in the calculation chain + if(calcChain != null) calcChain.removeItem((int)sheet.sheet.getSheetId(), cell.getReference()); + + CTCell ctCell = cell.getCTCell(); + String r = new CellReference(rownum, cell.getColumnIndex()).formatAsString(); + ctCell.setR(r); + + if(ctCell.isSetF()){ + CTCellFormula f = ctCell.getF(); + String fmla = f.getStringValue(); + if(fmla.length() > 0) { + String shiftedFmla = shiftFormula(fmla, n); + f.setStringValue(shiftedFmla); + } + if(f.isSetRef()){ //Range of cells which the formula applies to. + String ref = f.getRef(); + String shiftedRef = shiftFormula(ref, n); + f.setRef(shiftedRef); + } + } + } + setRowNum(rownum); + } + + /** + * Shift a formula by the specified number of rows + *

+ * Example: shiftFormula("A1+B1+C1", 3) will return "A4+B4+C4" + *

+ * + * @param formula the formula to shift + * @param n the number of rows to shift + * @return the shifted formula + */ + private String shiftFormula(String formula, int n){ + XSSFSheet sheet = getSheet(); + XSSFWorkbook wb = sheet.getWorkbook(); + int sheetIndex = wb.getSheetIndex(sheet); + XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); + Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, sheetIndex); + Ptg[] fmla = SharedFormulaRecord.convertSharedFormulas(ptgs, n, 0); + return FormulaRenderer.toFormulaString(fpb, fmla); + } + } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index b1ed00935..90f920c3e 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -24,14 +24,17 @@ import java.util.*; import javax.xml.namespace.QName; import org.apache.poi.hssf.util.PaneInformation; -import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.Footer; -import org.apache.poi.ss.usermodel.Header; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.hssf.record.formula.Ptg; +import org.apache.poi.hssf.record.SharedFormulaRecord; +import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; +import org.apache.poi.ss.util.AreaReference; +import org.apache.poi.ss.formula.FormulaParser; +import org.apache.poi.ss.formula.FormulaType; +import org.apache.poi.ss.formula.FormulaRenderer; import org.apache.poi.xssf.model.CommentsTable; +import org.apache.poi.xssf.model.CalculationChain; import org.apache.poi.xssf.usermodel.helpers.ColumnHelper; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.POIXMLException; @@ -1438,7 +1441,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { */ public void shiftRows(int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight) { for (Iterator it = rowIterator() ; it.hasNext() ; ) { - Row row = it.next(); + XSSFRow row = (XSSFRow)it.next(); if (!copyRowHeight) { row.setHeight((short)-1); @@ -1451,10 +1454,7 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { it.remove(); } else if (row.getRowNum() >= startRow && row.getRowNum() <= endRow) { - row.setRowNum(row.getRowNum() + n); - if (row.getFirstCellNum() > -1) { - modifyCellReference((XSSFRow) row); - } + row.shift(n); } } //rebuild the rows map @@ -1463,16 +1463,6 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { rows = map; } - - private void modifyCellReference(XSSFRow row) { - for (int i = row.getFirstCellNum(); i <= row.getLastCellNum(); i++) { - XSSFCell c = row.getCell(i); - if (c != null) { - c.modifyCellReference(row); - } - } - } - /** * Location of the top left visible cell Location of the top left visible cell in the bottom right * pane (when in Left-to-Right mode). diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java index 61a0b5e94..38ad08f64 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java @@ -27,16 +27,11 @@ import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.model.CommentsTable; import org.apache.poi.xssf.model.StylesTable; +import org.apache.poi.xssf.model.CalculationChain; import org.apache.poi.xssf.usermodel.helpers.ColumnHelper; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCol; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCols; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTComments; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRow; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.STPane; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; public class TestXSSFSheet extends TestCase { @@ -608,7 +603,46 @@ public class TestXSSFSheet extends TestCase { assertNull(sheet6.getRow(7)); assertEquals(8, sheet6.getPhysicalNumberOfRows()); } - + + /** + * When shifting rows, update formulas on that sheet to point to the new location of those rows + * (see bugzilla 46536) + */ + public void testShiftRows_46536() { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("46536.xlsx"); + CalculationChain calcChain = wb.getCalculationChain(); + int numItems = calcChain.getCTCalcChain().getCArray().length; + assertEquals(3, numItems); + + XSSFSheet sheet = wb.getSheet("Test"); + XSSFRow row2 = sheet.getRow(1); + XSSFCell cell_A2 = row2.getCell(0); + assertEquals("A2", cell_A2.getReference()); + + XSSFRow row3 = sheet.getRow(2); + XSSFCell cell_B3 = row3.getCell(1); + assertEquals("B3", cell_B3.getReference()); + + XSSFCell cell_E2 = row2.getCell(4); + CTCellFormula f = cell_E2.getCTCell().getF(); + assertEquals("B2+C2+D2", f.getStringValue()); + assertEquals("E2:E3", f.getRef()); + + sheet.shiftRows(1, sheet.getLastRowNum(), 3, false, true); + + assertEquals(4, row2.getRowNum()); + assertEquals(5, row3.getRowNum()); + assertEquals("A5", cell_A2.getReference()); + assertEquals("B6", cell_B3.getReference()); + + assertEquals("B5+C5+D5", f.getStringValue()); + assertEquals("E5:E6", f.getRef()); + + numItems = calcChain.getCTCalcChain().getCArray().length; + assertEquals(1, numItems); + + } + public void testGetCellComment() { XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet(); diff --git a/src/testcases/org/apache/poi/hssf/data/46536.xlsx b/src/testcases/org/apache/poi/hssf/data/46536.xlsx new file mode 100755 index 000000000..9b625bf36 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/46536.xlsx differ