From 56d9c7a5288c7593b3132651c5fa3784040619b9 Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Sun, 8 Feb 2009 16:35:27 +0000 Subject: [PATCH] improved XSSFSheet.shiftRows: 1. properly update cell references of the shifted cells (bugzilla 4663) 2. When shifting rows, update formulas on that sheet to point to the new location of those rows (bugzilla 46536) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@742126 13f79535-47bb-0310-9956-ffa450edef68 --- src/documentation/content/xdocs/changes.xml | 2 + src/documentation/content/xdocs/status.xml | 2 + .../apache/poi/xssf/usermodel/XSSFCell.java | 13 ---- .../apache/poi/xssf/usermodel/XSSFRow.java | 65 ++++++++++++++++++ .../apache/poi/xssf/usermodel/XSSFSheet.java | 30 +++----- .../poi/xssf/usermodel/TestXSSFSheet.java | 50 +++++++++++--- .../org/apache/poi/hssf/data/46536.xlsx | Bin 0 -> 8737 bytes 7 files changed, 121 insertions(+), 41 deletions(-) create mode 100755 src/testcases/org/apache/poi/hssf/data/46536.xlsx 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 0000000000000000000000000000000000000000..9b625bf3696d82d64cb95414eab81e352c478950 GIT binary patch literal 8737 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