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 extends POIXMLDocumentPart> 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