diff --git a/src/documentation/content/xdocs/changes.xml b/src/documentation/content/xdocs/changes.xml index 292f202f8..9a22b181d 100644 --- a/src/documentation/content/xdocs/changes.xml +++ b/src/documentation/content/xdocs/changes.xml @@ -37,6 +37,7 @@ + 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 46647 - Fixed COUNTIF NE operator and other special cases involving type conversion diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml index d4f168efe..e44a12843 100644 --- a/src/documentation/content/xdocs/status.xml +++ b/src/documentation/content/xdocs/status.xml @@ -34,6 +34,7 @@ + 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 46647 - Fixed COUNTIF NE operator and other special cases involving type conversion diff --git a/src/ooxml/java/org/apache/poi/xssf/model/CalculationChain.java b/src/ooxml/java/org/apache/poi/xssf/model/CalculationChain.java new file mode 100755 index 000000000..2d6aadc0f --- /dev/null +++ b/src/ooxml/java/org/apache/poi/xssf/model/CalculationChain.java @@ -0,0 +1,98 @@ +/* ==================================================================== + 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.xssf.model; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.poi.POIXMLDocumentPart; +import org.apache.xmlbeans.XmlException; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; + +/** + * The cells in a workbook can be calculated in different orders depending on various optimizations and + * dependencies. The calculation chain object specifies the order in which the cells in a workbook were last calculated. + * + * @author Yegor Kozlov + */ +public class CalculationChain extends POIXMLDocumentPart { + private CTCalcChain chain; + + public CalculationChain() { + super(); + chain = CTCalcChain.Factory.newInstance(); + } + + public CalculationChain(PackagePart part, PackageRelationship rel) throws IOException { + super(part, rel); + readFrom(part.getInputStream()); + } + + public void readFrom(InputStream is) throws IOException { + try { + CalcChainDocument doc = CalcChainDocument.Factory.parse(is); + chain = doc.getCalcChain(); + } catch (XmlException e) { + throw new IOException(e.getLocalizedMessage()); + } + } + public void writeTo(OutputStream out) throws IOException { + CalcChainDocument doc = CalcChainDocument.Factory.newInstance(); + doc.setCalcChain(chain); + doc.save(out, DEFAULT_XML_OPTIONS); + } + + @Override + protected void commit() throws IOException { + PackagePart part = getPackagePart(); + OutputStream out = part.getOutputStream(); + writeTo(out); + out.close(); + } + + + public CTCalcChain getCTCalcChain(){ + return chain; + } + + /** + * Remove a formula reference from the calculation chain + * + * @param sheetId the sheet Id of a sheet the formula belongs to. + * @param ref A1 style reference to the cell containing the formula. + */ + public void removeItem(int sheetId, String ref){ + //sheet Id of a sheet the cell belongs to + int id = -1; + CTCalcCell[] c = chain.getCArray(); + for (int i = 0; i < c.length; i++){ + //If sheet Id is omitted, it is assumed to be the same as the value of the previous cell. + if(c[i].isSetI()) id = c[i].getI(); + + if(id == sheetId && c[i].getR().equals(ref)){ + if(c[i].isSetI() && i < c.length - 1 && !c[i+1].isSetI()) { + c[i+1].setI(id); + } + chain.removeC(i); + break; + } + } + } +} \ No newline at end of file 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 0fdfa2c69..b7d2713f5 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -369,12 +369,13 @@ public final class XSSFCell implements Cell { * @throws IllegalArgumentException if the formula is invalid */ public void setCellFormula(String formula) { + XSSFWorkbook wb = row.getSheet().getWorkbook(); if (formula == null && cell.isSetF()) { + wb.onDeleteFormula(this); cell.unsetF(); return; } - XSSFWorkbook wb = row.getSheet().getWorkbook(); XSSFEvaluationWorkbook fpb = XSSFEvaluationWorkbook.create(wb); try { Ptg[] ptgs = FormulaParser.parse(formula, fpb, FormulaType.CELL, wb.getSheetIndex(getSheet())); @@ -482,9 +483,8 @@ public final class XSSFCell implements Cell { return CELL_TYPE_ERROR; case STCellType.INT_S: // String is in shared strings case STCellType.INT_INLINE_STR: // String is inline in cell - return CELL_TYPE_STRING; case STCellType.INT_STR: - return CELL_TYPE_FORMULA; + return CELL_TYPE_STRING; default: throw new IllegalStateException("Illegal cell type: " + this.cell.getT()); } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java index b985d6440..73fe263a2 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFRelation.java @@ -28,6 +28,7 @@ import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.xssf.model.StylesTable; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.CommentsTable; +import org.apache.poi.xssf.model.CalculationChain; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; @@ -184,7 +185,12 @@ public final class XSSFRelation extends POIXMLRelation { "/xl/theme/theme#.xml", null ); - + public static final XSSFRelation CALC_CHAIN = new XSSFRelation( + "application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml", + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/calcChain", + "/xl/calcChain.xml", + CalculationChain.class + ); private XSSFRelation(String type, String rel, String defaultName, Class cls) { diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java index 9a68eb7fb..d3a6a4f60 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFWorkbook.java @@ -47,6 +47,7 @@ import org.apache.poi.util.POILogger; import org.apache.poi.util.PackageHelper; import org.apache.poi.xssf.model.SharedStringsTable; import org.apache.poi.xssf.model.StylesTable; +import org.apache.poi.xssf.model.CalculationChain; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; @@ -110,6 +111,11 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable + * The calculation chain object specifies the order in which the cells in a workbook were last calculated + *

+ * + * @return the CalculationChain object or null if not defined + */ + public CalculationChain getCalculationChain(){ + return calcChain; + } + } diff --git a/src/ooxml/testcases/org/apache/poi/xssf/model/TestCalculationChain.java b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCalculationChain.java new file mode 100755 index 000000000..191b3283c --- /dev/null +++ b/src/ooxml/testcases/org/apache/poi/xssf/model/TestCalculationChain.java @@ -0,0 +1,59 @@ +/* ==================================================================== + 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.xssf.model; + +import org.apache.poi.xssf.usermodel.*; +import org.apache.poi.xssf.XSSFTestDataSamples; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.*; + +import junit.framework.TestCase; + + +public class TestCalculationChain extends TestCase { + + public void test46535() throws Exception { + XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("46535.xlsx"); + + CalculationChain chain = wb.getCalculationChain(); + //the bean holding the reference to the formula to be deleted + CTCalcCell c = chain.getCTCalcChain().getCArray(0); + int cnt = chain.getCTCalcChain().getCArray().length; + assertEquals(10, c.getI()); + assertEquals("E1", c.getR()); + + XSSFSheet sheet = wb.getSheet("Test"); + XSSFCell cell = sheet.getRow(0).getCell(4); + + assertEquals(XSSFCell.CELL_TYPE_FORMULA, cell.getCellType()); + cell.setCellFormula(null); + + //the count of items is less by one + c = chain.getCTCalcChain().getCArray(0); + int cnt2 = chain.getCTCalcChain().getCArray().length; + assertEquals(cnt - 1, cnt2); + //the first item in the calculation chain is the former second one + assertEquals(10, c.getI()); + assertEquals("C1", c.getR()); + + assertEquals(XSSFCell.CELL_TYPE_STRING, cell.getCellType()); + cell.setCellValue("ABC"); + assertEquals(XSSFCell.CELL_TYPE_STRING, cell.getCellType()); + } + + +} \ No newline at end of file diff --git a/src/testcases/org/apache/poi/hssf/data/46535.xlsx b/src/testcases/org/apache/poi/hssf/data/46535.xlsx new file mode 100755 index 000000000..555145b07 Binary files /dev/null and b/src/testcases/org/apache/poi/hssf/data/46535.xlsx differ