<!-- Don't forget to update status.xml too! -->
<release version="3.5-beta5" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">46535 - Remove reference from calculation chain when a formula is deleted</action>
<action dev="POI-DEVELOPERS" type="fix">46654 - HSSFRow/RowRecord to properly update cell boundary indexes</action>
<action dev="POI-DEVELOPERS" type="fix">46643 - Fixed formula parser to encode range operator with tMemFunc</action>
<action dev="POI-DEVELOPERS" type="fix">46647 - Fixed COUNTIF NE operator and other special cases involving type conversion</action>
<!-- Don't forget to update changes.xml too! -->
<changes>
<release version="3.5-beta5" date="2008-??-??">
+ <action dev="POI-DEVELOPERS" type="fix">46535 - Remove reference from calculation chain when a formula is deleted</action>
<action dev="POI-DEVELOPERS" type="fix">46654 - HSSFRow/RowRecord to properly update cell boundary indexes</action>
<action dev="POI-DEVELOPERS" type="fix">46643 - Fixed formula parser to encode range operator with tMemFunc</action>
<action dev="POI-DEVELOPERS" type="fix">46647 - Fixed COUNTIF NE operator and other special cases involving type conversion</action>
--- /dev/null
+/* ====================================================================
+ 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
* @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()));
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());
}
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;
"/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) {
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;
*/
private StylesTable stylesSource;
+ /**
+ * TODO
+ */
+ private CalculationChain calcChain;
+
/**
* Used to keep track of the data formatter so that all
* createDataFormatter calls return the same one for a given
for(POIXMLDocumentPart p : getRelations()){
if(p instanceof SharedStringsTable) sharedStringSource = (SharedStringsTable)p;
else if(p instanceof StylesTable) stylesSource = (StylesTable)p;
+ else if(p instanceof CalculationChain) calcChain = (CalculationChain)p;
else if (p instanceof XSSFSheet) {
shIdMap.put(p.getPackageRelationship().getId(), (XSSFSheet)p);
}
ctSheet.setState(STSheetState.Enum.forInt(hidden));
}
+ /**
+ * Fired when a formula is deleted from this workbook,
+ * for example when calling cell.setCellFormula(null)
+ *
+ * @see XSSFCell#setCellFormula(String)
+ */
+ protected void onDeleteFormula(XSSFCell cell){
+ if(calcChain != null) {
+ int sheetId = (int)cell.getSheet().sheet.getSheetId();
+ calcChain.removeItem(sheetId, cell.getReference());
+ }
+ }
+
+ /**
+ * Return the CalculationChain object for this workbook
+ * <p>
+ * The calculation chain object specifies the order in which the cells in a workbook were last calculated
+ * </p>
+ *
+ * @return the <code>CalculationChain</code> object or <code>null</code> if not defined
+ */
+ public CalculationChain getCalculationChain(){
+ return calcChain;
+ }
+
}
--- /dev/null
+/* ====================================================================
+ 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