简体   繁体   中英

How can Apache POI use formulas in streaming mode?

I am using Apache POI 3.17 (current). When I use HSSFCell.setFormula() to insert a formula like "A1+17" it works. When I do the same in streaming mode, using SXSSFCell.setFormula() the formula appears (with a leading "=") in the input line but the displayed result in the cell is always 0.

I tried with the cell types NUMERIC and FORMULA. Here is my minimal not working example:

 final SXSSFWorkbook wb = new SXSSFWorkbook(); 
 final SXSSFSheet sheet = wb.createSheet("Test-S");
 final SXSSFRow row = sheet.createRow(0);

 final SXSSFCell cell1 = row.createCell(0);
 cell1.setCellType(CellType.NUMERIC);
 cell1.setCellValue(124);

 final SXSSFCell formulaCell1 = row.createCell(1);
 formulaCell1.setCellType(CellType.FORMULA);
 formulaCell1.setCellFormula("A1 + 17");

 final SXSSFCell formulaCell2 = row.createCell(2);
 formulaCell2.setCellType(CellType.NUMERIC);
 formulaCell2.setCellFormula("A1+18");

 FileOutputStream os = new FileOutputStream("/tmp/test-s.xlsx");
 wb.write(os);
 wb.close();
 os.close();

The three cells display as 124/0/0, although in the input line the formulae are displayed correctly.

Any hints are appreciated.

It works for me with Excel 2016, I get the correct results in the cells when I open the sample file. Probably older versions of Excel handle this slightly differently, please try to force evaluation of formulas with the following two things

// evaluate all formulas and store cached results
wb.getCreationHelper().createFormulaEvaluator().evaluateAll();
// suggest to Excel to recalculate the formulas itself as well
sheet.setForceFormulaRecalculation(true);

Hopefully one of those two will make it work for you as well.

The answers does not answer the question why this problem with OpenOffice/Libreoffice only occurs if SXSSFCell is used as a formula cell. When using XSSFCell as a formula cell it does not occur.

The answer is that SXSSFCell always uses a cell value, even if the formula was not evaluated at all. And the worst thing is that it uses the value 0 (zero) if if the formula was not evaluated at all. This is a fundamental misusing of the value 0 in mathematics. The value 0 explicitly does not mean that there is not a value or that there is a unknown value. It means that there is the value 0 and nothing else. So the value 0 should not be used as the cached formula result of a not evaluated formula. Instead no value should be used until the formula is evaluated. Exact as XSSFCell does.

So the really correct answer must be that apache poi should correct their SXSSFCell code.

Workaround until this:

import java.io.FileOutputStream;

import org.apache.poi.xssf.streaming.*;

import org.apache.poi.ss.usermodel.CellType;

import java.lang.reflect.Field;

import java.util.TreeMap;

public class CreateExcelSXSSFFormula {

 public static void main(String[] args) throws Exception {

  SXSSFWorkbook wb = new SXSSFWorkbook();

  SXSSFSheet sheet = wb.createSheet("Test-S");
  SXSSFRow row = sheet.createRow(0);

  SXSSFCell cell = row.createCell(0);
  cell.setCellValue(124);

  SXSSFFormulaonlyCell formulacell = new SXSSFFormulaonlyCell(row, 1);
  formulacell.setCellFormula("A1+17");

  cell = row.createCell(2);
  cell.setCellFormula("A1+17");

  formulacell = new SXSSFFormulaonlyCell(row, 3);
  formulacell.setCellFormula("A1+18");

  cell = row.createCell(4);
  cell.setCellFormula("A1+18");

  wb.write(new FileOutputStream("test-s.xlsx"));
  wb.close();
  wb.dispose();

 }

 private static class SXSSFFormulaonlyCell extends SXSSFCell {

  SXSSFFormulaonlyCell(SXSSFRow row, int cellidx) throws Exception {
   super(row, CellType.BLANK);
   Field _cells = SXSSFRow.class.getDeclaredField("_cells"); 
   _cells.setAccessible(true);
   @SuppressWarnings("unchecked") //we know the problem and expect runtime error if it possibly occurs
   TreeMap<Integer, SXSSFCell> cells = (TreeMap<Integer, SXSSFCell>)_cells.get(row);
   cells.put(cellidx, this);
  }

  @Override
  public CellType getCachedFormulaResultTypeEnum() {
   return CellType.BLANK;
  }

 }

}

Of course I should have mentioned that I use LibreOffice. I have now found that LibreOffice intentionally does not recalculate formulae from an Excel-created sheet, and it considers POI sheets as Excel-created.

See https://ask.libreoffice.org/en/question/12165/calc-auto-recalc-does-not-work/ .

Changing the LibreOffice settings (Tools – Options – LibreOffice Calc – formula – Recalculation on file load) helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM