added suport for Calculation Chain in XSSF, remove reference from calculation chain when a formula is deleted (see bugzilla 46535)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@742077 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2009-02-08 12:45:55 +00:00
parent 771369c409
commit 1273d8e270
8 changed files with 201 additions and 4 deletions

View File

@ -37,6 +37,7 @@
<!-- 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>

View File

@ -34,6 +34,7 @@
<!-- 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>

View File

@ -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;
}
}
}
}

View File

@ -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());
}

View File

@ -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) {

View File

@ -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<X
*/
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
@ -177,6 +183,7 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
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);
}
@ -1260,4 +1267,29 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook, Iterable<X
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;
}
}

View File

@ -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());
}
}

Binary file not shown.