Apache POI - Not implemented error with CONCAT function - java

I'm trying to edit a excel document that contains formulas, the editing works fine but the formulas don't update.
I'm trying to use the following code to get it to evaluate the formulas, however i get an error
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
for (Row r : sheet) {
for (Cell c : r) {
evaluator.evaluateFormulaCell(c);
}
}
Exception in thread "AWT-EventQueue-0" org.apache.poi.ss.formula.eval.NotImplementedException: Error evaluating cell Sheet1!C17
at org.apache.poi.ss.formula.WorkbookEvaluator.addExceptionInfo(WorkbookEvaluator.java:344)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:285)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluate(WorkbookEvaluator.java:216)
at org.apache.poi.xssf.usermodel.BaseXSSFFormulaEvaluator.evaluateFormulaCellValue(BaseXSSFFormulaEvaluator.java:56)
at org.apache.poi.ss.formula.BaseFormulaEvaluator.evaluateFormulaCell(BaseFormulaEvaluator.java:185)
at Timetable.ExcelAPI.calculateFormula(ExcelAPI.java:139)
Cell C17 has the following formula.
=IF(C3="","",CONCAT($A17,$B17,C3,$B17,$A$16))
I've also tried
=IF(C3="","",CONCATENATE($A17,$B17,C3,$B17,$A$16))
If i programmatically create the formula it works
cell.setCellFormula("IF(C3=\"\",\"\",CONCAT($A17,$B17,C" + (start + 1) + ",$B17,$A$16))");

Since Excel function support of apache poi is at Excel 2007 standard, CONCATENATE is implemented but CONCAT is not. Furthermore functions which are introduced after Excel 2007 are prefixed with _xlfn..
So your full stacktrace should contain:
...
Caused by: org.apache.poi.ss.formula.eval.NotImplementedFunctionException: _xlfn.CONCAT
...
A work around could be replacing "_xlfn.CONCAT" by "CONCATENATE" in formulas before evaluating.
Following code works for me and evaluates CONCATENATE as well as CONCAT formulas.
import org.apache.poi.ss.usermodel.*;
import java.io.FileInputStream;
class ReadExcel {
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelExampleConcatenate.xlsx"));
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
DataFormatter dataFormatter = new DataFormatter();
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
if (cell.getCellType() == CellType.FORMULA && cell.getCellFormula().contains("_xlfn.CONCAT")) {
cell.setCellFormula(cell.getCellFormula().replace("_xlfn.CONCAT", "CONCATENATE"));
}
String value = dataFormatter.formatCellValue(cell, evaluator);
System.out.println(value);
}
}
workbook.close();
}
}

Good news - the CONCAT function is now supported! Bad news - not in your version...
If you upgrade (once available) to Apache POI 5.0.1 or later, the CONCAT function is now supported, see https://bz.apache.org/bugzilla/show_bug.cgi?id=65185
If you're impatient and want to backport the missing function, it's http://svn.apache.org/viewvc?view=revision&revision=1887656

Resolved it by changing the call to the function to simply
workbook.setForceFormulaRecalculation(true);

Related

Reading blank cell Apache POI 3.17

I'm using Apache POI 3.17 to read some excel data. My second column (index of 1 because of 0 index) is empty and I want to be able to read it, but can't get my code to read the cell as empty. I have this which isn't working:
Cell c = row.getCell(1, Row.RETURN_BLANK_AS_NULL);
if (c == null) {
// do whatever
}
But the second parameter can't be taken in. I get "RETURN_BLANK_AS_NULL cannot be resolved or is not a field"
The constants in the Row class itself were deprecated as of POI-3.15-beta2, marked for removal as of POI-3.17. This diff shows when those constants were deprecated in June 2016. They were removed in 3.17.
Before 3.17, the enum Row.MissingCellPolicy was already defined as a replacement. If you're using 3.17, then you must use that enum; it is defined as a member of the Row interface. Try
Cell c = row.getCell(1, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL);
Try using org.apache.poi.ss.usermodel.DataFormatter.
DataFormatter formatter = new DataFormatter();
Workbook workbook = WorkbookFactory.create(new File("yourFileName.xls"));
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for(Cell cell : row)
{
String text = formatter.formatCellValue(cell);
System.out.println(text);
}
}
workbook.close();

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.

Calculating the value of a recursion formula in EXCEL using POI

I have an EXCEL Spreadsheet that has many computations some including recursive formula's (perfectly fine in excel which computes these with no problem).
I'm using version 3.8 of the Apache Poi API to open the existing spreadsheet, add updated values for the computations, save the spreadsheet and then read the results into my program.
All of this works except the in memory poi version of the spreadsheet does not perform the recursive calculation.
I'm calling the POI code to compute the entire workbook, each sheet and then each formula cell, but the cell formula never computes.
If I open the saved spreadsheet and compute it the formula computes the value accurately. I need to avoid having the user manually open the saved spreadsheet if possible.
Any insight in to this problem would be greatly appreciated.
Here is the code I have tried. It all runs with no error but does not compute the formula.
The formula is already in the spreadsheet an works fine so I'm trying to force the formula to re-compute.
I have tried all these variations. The code runs and executes but does not compute the formula
wb.getCreationHelper().createFormulaEvaluator().evaluateAll();
/*
//wb.getCreationHelper().createFormulaEvaluator().evaluateAll();
System.out.println(wb.getSheet("Rate Report").getRow(16).getCell(0).getStringCellValue());
System.out.println(wb.getSheet("Rate Report").getRow(16).getCell(1).getNumericCellValue());
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
//String wbsheets[] = {"Original","Main_DB","Parameters","Translator","Workers Comp.","Rate Report","Notes Fields"};
String wbsheets[] = {"Notes Fields","Translator","Parameters","Rate Report"};
for(String sheetName : wbsheets) {
Sheet sheet = wb.getSheet(sheetName);
System.out.println("processing sheet: " + sheet.getSheetName());
for(Row r : sheet) {
for(Cell c : r) {
if(c.getCellType() == Cell.CELL_TYPE_FORMULA) {
System.out.println("Recalcing: "+r.getRowNum()+c.getColumnIndex() + "in "+ sheet.getSheetName());
evaluator.evaluateFormulaCell(c);
}
}
}
}
XSSFFormulaEvaluator.evaluateAllFormulaCells((XSSFWorkbook) wb);

Java POI : How to read Excel cell value and not the formula computing it?

I am using Apache POI API to getting values from an Excel file.
Everything is working great except with cells containing formulas. In fact, the cell.getStringCellValue() is returning the formula used in the cell and not the value of the cell.
I tried to use evaluateFormulaCell() method but it's not working because I am using GETPIVOTDATA Excel formula and this formula is not implemented in the API:
Exception in thread "main" org.apache.poi.ss.formula.eval.NotImplementedException: Error evaluating cell Landscape!K11
at org.apache.poi.ss.formula.WorkbookEvaluator.addExceptionInfo(WorkbookEvaluator.java:321)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluateAny(WorkbookEvaluator.java:288)
at org.apache.poi.ss.formula.WorkbookEvaluator.evaluate(WorkbookEvaluator.java:221)
at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateFormulaCellValue(HSSFFormulaEvaluator.java:320)
at org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator.evaluateFormulaCell(HSSFFormulaEvaluator.java:213)
at fromExcelToJava.ExcelSheetReader.unAutreTest(ExcelSheetReader.java:193)
at fromExcelToJava.ExcelSheetReader.main(ExcelSheetReader.java:224)
Caused by: org.apache.poi.ss.formula.eval.NotImplementedException: GETPIVOTDATA
at org.apache.poi.hssf.record.formula.functions.NotImplementedFunction.evaluate(NotImplementedFunction.java:42)
For formula cells, excel stores two things. One is the Formula itself, the other is the "cached" value (the last value that the forumla was evaluated as)
If you want to get the last cached value (which may no longer be correct, but as long as Excel saved the file and you haven't changed it it should be), you'll want something like:
for(Cell cell : row) {
if(cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
System.out.println("Formula is " + cell.getCellFormula());
switch(cell.getCachedFormulaResultType()) {
case Cell.CELL_TYPE_NUMERIC:
System.out.println("Last evaluated as: " + cell.getNumericCellValue());
break;
case Cell.CELL_TYPE_STRING:
System.out.println("Last evaluated as \"" + cell.getRichStringCellValue() + "\"");
break;
}
}
}
Previously posted solutions did not work for me. cell.getRawValue() returned the same formula as stated in the cell. The following function worked for me:
public void readFormula() throws IOException {
FileInputStream fis = new FileInputStream("Path of your file");
Workbook wb = new XSSFWorkbook(fis);
Sheet sheet = wb.getSheetAt(0);
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
CellReference cellReference = new CellReference("C2"); // pass the cell which contains the formula
Row row = sheet.getRow(cellReference.getRow());
Cell cell = row.getCell(cellReference.getCol());
CellValue cellValue = evaluator.evaluate(cell);
switch (cellValue.getCellType()) {
case Cell.CELL_TYPE_BOOLEAN:
System.out.println(cellValue.getBooleanValue());
break;
case Cell.CELL_TYPE_NUMERIC:
System.out.println(cellValue.getNumberValue());
break;
case Cell.CELL_TYPE_STRING:
System.out.println(cellValue.getStringValue());
break;
case Cell.CELL_TYPE_BLANK:
break;
case Cell.CELL_TYPE_ERROR:
break;
// CELL_TYPE_FORMULA will never happen
case Cell.CELL_TYPE_FORMULA:
break;
}
}
There is an alternative command where you can get the raw value of a cell where formula is put on. It's returns type is String. Use:
cell.getRawValue();
If the need is to read values from Excel sheets and having them as strings then, for example to present them somewhere or to use them in text file formats, then using DataFormatter will be the best.
DataFormatter is able to get a string from each cell value, whether the cell value itself is string, boolean, number, error or date. This string then looks the same as Excel will show it in the cells in it's GUI.
Only problem are formula cells. Up to apache poi 5.1.0 a FormulaEvaluator is needed to evaluate the formulas while using DataFormatter. This fails when apache poi is not able evaluating the formula. From 5.2.0 on the DataFormatter can be set to use cached values for formula cells. Then no formula evaluation is needed if Excel had evaluated the formulas before.
Complete example:
import org.apache.poi.ss.usermodel.*;
import java.io.FileInputStream;
class ReadExcel {
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("./ExcelExample.xlsx"));
// up to apache poi 5.1.0 a FormulaEvaluator is needed to evaluate the formulas while using DataFormatter
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
DataFormatter dataFormatter = new DataFormatter(new java.util.Locale("en", "US"));
// from 5.2.0 on the DataFormatter can set to use cached values for formula cells
dataFormatter.setUseCachedValuesForFormulaCells(true);
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
//String value = dataFormatter.formatCellValue(cell, evaluator); // up to apache poi 5.1.0
String value = dataFormatter.formatCellValue(cell); // from apache poi 5.2.0 on
System.out.println(value);
}
}
workbook.close();
}
}
If you want to extract a raw-ish value from a HSSF cell, you can use something like this code fragment:
CellBase base = (CellBase) cell;
CellType cellType = cell.getCellType();
base.setCellType(CellType.STRING);
String result = cell.getStringCellValue();
base.setCellType(cellType);
At least for strings that are completely composed of digits (and automatically converted to numbers by Excel), this returns the original string (e.g. "12345") instead of a fractional value (e.g. "12345.0"). Note that setCellType is available in interface Cell(as of v. 4.1) but deprecated and announced to be eliminated in v 5.x, whereas this method is still available in class CellBase. Obviously, it would be nicer either to have getRawValue in the Cell interface or at least to be able use getStringCellValue on non STRING cell types. Unfortunately, all replacements of setCellType mentioned in the description won't cover this use case (maybe a member of the POI dev team reads this answer).
SelThroughJava's answer was very helpful I had to modify a bit to my code to be worked .
I used https://mvnrepository.com/artifact/org.apache.poi/poi and https://mvnrepository.com/artifact/org.testng/testng as dependencies .
Full code is given below with exact imports.
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.sl.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ReadExcelFormulaValue {
private static final CellType NUMERIC = null;
public static void main(String[] args) {
try {
readFormula();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void readFormula() throws IOException {
FileInputStream fis = new FileInputStream("C:eclipse-workspace\\sam-webdbriver-diaries\\resources\\tUser_WS.xls");
org.apache.poi.ss.usermodel.Workbook workbook = WorkbookFactory.create(fis);
org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
CellReference cellReference = new CellReference("G2"); // pass the cell which contains the formula
Row row = sheet.getRow(cellReference.getRow());
Cell cell = row.getCell(cellReference.getCol());
CellValue cellValue = evaluator.evaluate(cell);
System.out.println("Cell type month is "+cellValue.getCellTypeEnum());
System.out.println("getNumberValue month is "+cellValue.getNumberValue());
// System.out.println("getStringValue "+cellValue.getStringValue());
cellReference = new CellReference("H2"); // pass the cell which contains the formula
row = sheet.getRow(cellReference.getRow());
cell = row.getCell(cellReference.getCol());
cellValue = evaluator.evaluate(cell);
System.out.println("getNumberValue DAY is "+cellValue.getNumberValue());
}
}

How to get the formatted value of a number for a cell in Apache POI?

I wanted to get the value of a Numeric cell as a simple string.
Suppose there the type of cell is numeric with value 90%.
Now I cannot use cell.getStringCellValue() as it will throw exception.
I also cannot use cell.getNumericCellValue() as it will return me .9 and not 90%.
I want to store in db which is of type varchar2, so I want the value in string only.
I cannot change the cell type in xls as its the end user job, I have to handle this in code itself.
Also formatter does't work well as there could be different cell types in the xls...dd:mm,dd:mm:ss,formula etc.
All I want is that whatever the cell type is I need to get its value as simple String.
You can force the value to be returned as a String using the methods below
HSSFDataFormatter hdf = new HSSFDataFormatter();
System.out.println (hdf.formatCellValue(mycell));
will return "90%"
The API for this method is at http://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/DataFormatter.html#formatCellValue%28org.apache.poi.ss.usermodel.Cell%29
This works directly even with an HSSFCell
it worked for me even when my Cell is an HSSFCell
i've also tried this cast - which works.
HSSFCell cell1 = (HSSFCell) row1.getCell(2);
HSSFDataFormatter hdf = new HSSFDataFormatter();
System.out.println ("formatted "+ hdf.formatCellValue(cell1));
Try
cell.getRichStringCellValue ().getString();
Have a look at this example
Here is Doc
The following code is using current apache poi versions of 2021. Now DataFormatter can be used for XSSF (Office Open XML *.xlsx) as well as for HSSF (BIFF *.xls) formats. It should be used together with FormulaEvaluator to get values from formula cells too.
import org.apache.poi.ss.usermodel.*;
import java.io.FileInputStream;
class ReadExcel {
public static void main(String[] args) throws Exception {
Workbook workbook = WorkbookFactory.create(new FileInputStream("Excel.xlsx"));
//Workbook workbook = WorkbookFactory.create(new FileInputStream("Excel.xls"));
DataFormatter dataFormatter = new DataFormatter(java.util.Locale.US);
FormulaEvaluator formulaEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
String cellValue = "";
for (Sheet sheet: workbook) {
System.out.println(sheet.getSheetName());
for (Row row : sheet) {
for (Cell cell : row) {
cellValue = dataFormatter.formatCellValue(cell, formulaEvaluator);
System.out.println(cell.getAddress() + ":" + cellValue);
// do something with cellValue
}
}
}
workbook.close();
}
}

Categories