Can Apache POI apply Top 10 conditional formatting? - java

I have already created the excel files already has such as createConditionalFormattingRule With defrent kind like if the cell value EQUAL to another one :
XSSFConditionalFormattingRule my_rule1 = ContedFormat.createConditionalFormattingRule(ComparisonOperator.EQUAL, "$M$" + (CountRower + 1));
But my question is that there is a conditional format Used to select like top 10 value on the renege.
can Apache POI Create this kind ?
Edit : I found something like :
CTConditionalFormatting TopScall =
sheet.getCTWorksheet().addNewConditionalFormatting();
TopScall.setSqref(my_range);
CTCfRule myCFRule = TopScall.addNewCfRule();
myCFRule.setType(STCfType.TOP_10);
myCFRule.setPriority(1);
And i tryed it dose add the Formily roll but with no formatting and value is 0
This is Example about Conditional Formatting Color Scale What I want to do is something similar but instead of Color Scale i need Top 10

In current Apache poi 5.0.0 SheetConditionalFormatting does not have a method to create a ConditionalFormattingRule for top 10. But it has SheetConditionalFormatting.createConditionalFormattingColorScaleRule(). So your linked excample which uses underlying org.openxmlformats.schemas.spreadsheetml.x2006.main.* classes for creating color scale rule is outdated.
But top 10 rule settings are more complex than color scale rule settings. For color scale rule all settings are in sheet's CTConditionalFormatting. For top 10 rule a fill pattern formatting needs to be used. That pattern formatting links to the style part of the workbook.
So best way would be creating a XSSFConditionalFormattingRule for top 10 which sets type STCfType.TOP_10 and rank. This ConditionalFormattingRule already provides a method to create pattern formatting.
Unfortunately constructor of XSSFConditionalFormattingRule is not public as well as the method to get CTCfRule. So reflection needs to be used.
Following complete example provides XSSFConditionalFormattingRule createConditionalFormattingRuleTop10(XSSFSheetConditionalFormatting sheetCF, int rank) to create a XSSFConditionalFormattingRule for top 10 given a special rank. All other stuff is like the default stuff for creating conditional formatting as described in Busy Developers' Guide to HSSF and XSSF Features - Conditional Formatting.
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
import java.io.FileOutputStream;
public class CreateXSSFConditionalFormattingTop10 {
static XSSFConditionalFormattingRule createConditionalFormattingRuleTop10(XSSFSheetConditionalFormatting sheetCF, int rank) throws Exception {
Field _sheet = XSSFSheetConditionalFormatting.class.getDeclaredField("_sheet");
_sheet.setAccessible(true);
XSSFSheet sheet = (XSSFSheet)_sheet.get(sheetCF);
Constructor constructor = XSSFConditionalFormattingRule.class.getDeclaredConstructor(XSSFSheet.class);
constructor.setAccessible(true);
XSSFConditionalFormattingRule rule = (XSSFConditionalFormattingRule)constructor.newInstance(sheet);
Field _cfRule = XSSFConditionalFormattingRule.class.getDeclaredField("_cfRule");
_cfRule.setAccessible(true);
CTCfRule cfRule = (CTCfRule)_cfRule.get(rule);
cfRule.setType(STCfType.TOP_10);
cfRule.setRank(rank);
return rule;
}
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook(); String filePath = "./CreateXSSFConditionalFormattingTop10.xlsx";
Sheet sheet = workbook.createSheet();
java.util.Random random = new java.util.Random();
for (int r = 0; r < 100; r++) {
sheet.createRow(r).createCell(0).setCellValue(random.nextInt(100)+r);
}
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
if (sheetCF instanceof XSSFSheetConditionalFormatting) {
XSSFConditionalFormattingRule rule = createConditionalFormattingRuleTop10((XSSFSheetConditionalFormatting)sheetCF, 10);
XSSFPatternFormatting fill = rule.createPatternFormatting();
fill.setFillBackgroundColor(IndexedColors.LIGHT_GREEN.index);
fill.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
XSSFConditionalFormattingRule[] cfRules = new XSSFConditionalFormattingRule[]{rule};
CellRangeAddress[] regions = new CellRangeAddress[]{CellRangeAddress.valueOf("A1:A100")};
sheetCF.addConditionalFormatting(regions, cfRules);
}
FileOutputStream out = new FileOutputStream(filePath);
workbook.write(out);
out.close();
workbook.close();
}
}

Related

apache poi , DataFormatter trimming values beyond ten places of decimal

So i have a simple code that populate values to database
try {
for(int indexRow = 1; indexRow < numberOfRecords; indexRow++) {
record = new String[noOfColumns];
for(int indexColumn = 0;indexColumn < noOfColumns; indexColumn++) {
indexError = indexColumn;
String value = "";
XSSFRow row = sheet.getRow(indexRow);
if(row != null) {
XSSFCell cell = sheet.getRow(indexRow).getCell(indexColumn);
if(cell != null) {
value = fmt.formatCellValue(cell);
}
record[indexColumn] = value;
}
}
records.add(record);
}
}
Now i have ran through the source code as well but i cannot find a way by which i can set the default DataFormatter in a way that it can change the DecimalFormat in a way to accomodate the extra changes .
Any help would be greatly appreciated .
Eg :in excel i have
-5.57055337362326
but through code it writes into db as
-5.5705533736
Important: The purpose of the DataFormatter.formatCellValue() method is to return cell's value in the way it is shown in the Excel document.
Let's say if you will define numeric format in Excel to show 4 fractional digits and your document looks so:
Your code sample will return -5,5706; if you will change numeric format to show 8 fractional digits - result will be -5,57055337.
By default numeric format in Excel is 10 digits based (in Apache POI please check ExcelGeneralNumberFormat.decimalFormat constant), and looks like it is the one used in your document based on the output you have.
Solution
As it is mentioned by #samabcde (adding my answer to fix couple issues in his answer and to provide additional details), solution is to use cell.getNumericCellValue() instead:
DecimalFormat decimalFormat = new DecimalFormat("#.###############");
String cellValue = decimalFormat.format(cell.getNumericCellValue());
Here we've used "#.###############" format with 15 digits since it is a maximum precision for Excel >>
Additional information
Please pay attention to this article: When reading Excel with POI, beware of floating points
In terms of configuration of DataFromatter you can set up default number format using DataFormatter#setDefaultNumberFormat(Format format), and it will be used when you call format.formatCellValue(cell), but only in case of usage of unknown/broken formats in the Excel document.
P.S.: Answer to the first comment
It is not fully clear from your comment all the cases you want to cover, assumption is that DataFormatter works for you in all cases except numeric values, and DecimalFormat with "#.###############" pattern works in that case for you. Anyway in case you will want more specific logic it will be needed just to check some other conditions.
Please find utility method you can use in this case:
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#.###############");
private static final DataFormatter DATA_FORMATTER = new DataFormatter();
public static String formatCellValue(HSSFCell cell) {
if (cell != null && cell.getCellTypeEnum() == CellType.NUMERIC
&& !DateUtil.isCellDateFormatted(cell)) {
return DECIMAL_FORMAT.format(cell.getNumericCellValue());
} else {
return DATA_FORMATTER.formatCellValue(cell);
}
}
For the Excel file below:
Field A1 has format of 4 fractional digits with a real value 28,9999999999999
Field A2 has format of 4 fractional digits with a real value -5.5
Field A3 has default Excel format with a real value 28,9999999999999
Utility method above will return real values here, i.e.: 28,9999999999999, -5.5 and 28,9999999999999
DataFormatter.formatCellValue() will return values how they look in the Excel itself, i.e.: 29,0000, -5,5000 and 29.
Reason for losing digit
The below code is from POI version 3.14, for the DataFormatter.formatCellValue method, with numeric cell, it will eventually call getFormattedNumberString method. The code is as
private String getFormattedNumberString(Cell cell, ConditionalFormattingEvaluator
cfEvaluator) {
Format numberFormat = getFormat(cell, cfEvaluator);
double d = cell.getNumericCellValue();
if (numberFormat == null) {
return String.valueOf(d);
}
String formatted = numberFormat.format(new Double(d));
return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation
}
The numberFormat will be a DecimalFormat with 10 decimal places by default, suppose you have not set any format, a cell with value '-5.57055337362326' will return '-5.5705533736' as expected.
Solution
If only the exact value is needed, using cell.getNumericValue method and create a DecimalFormat can solve this problem. If the display in Excel is required to change also, then we need to created a custom DataFormatter. Both solutions are illustrated in following example:
import java.io.IOException;
import java.text.DecimalFormat;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class TestDataFormatter {
public static void main(String[] args) throws IOException {
Double testValue = Double.valueOf("-5.57055337362326");
System.out.println("test value:\t\t" + testValue.toString());
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue(testValue);
// 10 decimal place shown by default
DataFormatter dataFormatter = new DataFormatter();
String defaultFormatted = dataFormatter.formatCellValue(cell);
System.out.println("default formatted:\t" + defaultFormatted);
// Create custom format
XSSFCellStyle style = workbook.createCellStyle();
DataFormat customDataFormat = workbook.createDataFormat();
int dataFormatIndex = customDataFormat.getFormat("0.00000000000000");
style.setDataFormat(dataFormatIndex);
cell.setCellStyle(style);
String customFormatted = dataFormatter.formatCellValue(cell);
System.out.println("custom formatted:\t" + customFormatted);
// Get numeric value and then format by DecimalFormat
System.out.println("get numeric:\t\t" + cell.getNumericCellValue());
System.out.println(
"format numeric:\t\t" + new DecimalFormat("0.00000000000000").format(cell.getNumericCellValue()));
workbook.close();
}
}

Apache POI conditional formatting search for string starts with some text

How can I create a conditional formatting rule for searching all content in the specified range that starts with some text?
I can create a rule that searches all content in the specified range that equals to some text.
XSSFSheet sheet1 = workbook.getSheet("sheet1");
XSSFSheetConditionalFormatting sheet1cf = sheet1.getSheetConditionalFormatting();
XSSFConditionalFormattingRule aRule = sheet1cf.createConditionalFormattingRule(ComparisonOperator.EQUAL,"\"a\"");
//that search value="a"
However, I cannot figure out how to create a rule for searching all content in the specified range that starts with some text?
I am using Excel 2007.
I am using the following steps to create the rule.
Click conditional formatting drop down
Select "new rule"
Select "Format only cells that contain" in "Select rule type"
In "Format only cells", select "Specific Text" in the first drop down box
Select "contains" in the second drop down box
In my example, enter "a" in the third drop down box
Finally, set the background colour for the cells that contain "a".
So there are two different answers to this question.
First: You wants using XSSF only and having exactly the same as Excel does according your description.
Then we need using the low level underlying Objects of apache poi since apache poi dos not support ComparisonOperator BEGINS_WITH although there is STConditionalFormattingOperator.BEGINS_WITH.
So we need at first creating a conditional formatting rule having any dummy ComparisonOperator and any dummy formula. Then we can replace those dummys with STConditionalFormattingOperator.BEGINS_WITH and a proper formula.
Example:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.openxmlformats.schemas.spreadsheetml.x2006.main.*;
import java.lang.reflect.Field;
import java.io.FileOutputStream;
public class XSSFConditionalFormattingBeginsWith {
static XSSFConditionalFormattingRule createConditionalFormattingRuleBeginsWith(
XSSFSheetConditionalFormatting sheetCF,
String text) throws Exception {
XSSFConditionalFormattingRule rule = sheetCF.createConditionalFormattingRule(
ComparisonOperator.EQUAL /*only dummy*/,
"" /*only dummy*/);
Field _cfRule = XSSFConditionalFormattingRule.class.getDeclaredField("_cfRule");
_cfRule.setAccessible(true);
CTCfRule ctCfRule = (CTCfRule)_cfRule.get(rule);
ctCfRule.setType(STCfType.BEGINS_WITH);
ctCfRule.setOperator(STConditionalFormattingOperator.BEGINS_WITH);
ctCfRule.setText(text);
ctCfRule.addFormula("(LEFT(INDEX($1:$1048576, ROW(), COLUMN())," + text.length() + ")=\""+ text + "\")");
_cfRule.set(rule, ctCfRule);
return rule;
}
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("new sheet");
XSSFSheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
XSSFConditionalFormattingRule rule = createConditionalFormattingRuleBeginsWith(sheetCF, "bla");
PatternFormatting fill = rule.createPatternFormatting();
fill.setFillBackgroundColor(IndexedColors.YELLOW.index);
fill.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
XSSFConditionalFormattingRule[] cfRules = new XSSFConditionalFormattingRule[]{rule};
CellRangeAddress[] regions = new CellRangeAddress[]{CellRangeAddress.valueOf("A1:B1000")};
sheetCF.addConditionalFormatting(regions, cfRules);
workbook.write(new FileOutputStream("XSSFConditionalFormattingBeginsWith.xlsx"));
workbook.close();
}
}
Second: You wants using high level classes of apache poi and having both HSSF and XSSF supported.
Then we only can use conditional formatting rule based on a formula. Excel itself supports this also by selecting Use a formula to determine which cells to format in step 3 of your described process.
Example:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.util.CellRangeAddress;
import java.io.FileOutputStream;
public class ConditionalFormattingBeginsWith {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook();
//Workbook workbook = new HSSFWorkbook();
Sheet sheet = workbook.createSheet("new sheet");
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
String text = "bla";
int lastRow = 1000;
ConditionalFormattingRule rule = sheetCF.createConditionalFormattingRule(
"(LEFT(INDEX($1:$" + lastRow + ",ROW(),COLUMN())," + text.length() + ")=\"" + text + "\")");
PatternFormatting fill = rule.createPatternFormatting();
fill.setFillBackgroundColor(IndexedColors.YELLOW.index);
fill.setFillPattern(PatternFormatting.SOLID_FOREGROUND);
ConditionalFormattingRule[] cfRules = new ConditionalFormattingRule[]{rule};
CellRangeAddress[] regions = new CellRangeAddress[]{CellRangeAddress.valueOf("A1:B" + lastRow)};
sheetCF.addConditionalFormatting(regions, cfRules);
if (workbook instanceof XSSFWorkbook) {
workbook.write(new FileOutputStream("ConditionalFormattingBeginsWith.xlsx"));
} else if (workbook instanceof HSSFWorkbook) {
workbook.write(new FileOutputStream("ConditionalFormattingBeginsWith.xls"));
}
workbook.close();
}
}

Apache poi cell diagonal strikethrough

How can I strikethrough diagonally a cell in excel using Apache poi?
I want to get something like this:
I have tried:
cell.getCellStyle().setFillPattern(CellStyle.THIN_BACKWARD_DIAG)
but then the cell is having many diagonal lines instead of one from top right to bottom left.
Edit:
This is how I create the workbook:
Workbook workbook = new SXSSFWorkbook(SXSSFWorkbook.DEFAULT_WINDOW_SIZE);
Sheet sheet = workbook.createSheet("Test");
Then creating the columns and the rows, I don't see there any special to show.
Any idea what can I do?
Sorry for my previous answer, currently there is no diagonal border available readily in apache poi, though we can write our own custom method. You can use below method which will add a diagonal border in a cell.
public static void setDiagonal(StylesTable stylesSource, CTXf cellXtraFormating, ThemesTable theme) {
CTBorder ct = CTBorder.Factory.newInstance();
CTBorderPr pr = ct.addNewDiagonal();
ct.setDiagonalUp(true);
pr.setStyle(STBorderStyle.Enum.forInt(BorderFormatting.BORDER_THIN + 1));
int idx = stylesSource.putBorder(new XSSFCellBorder(ct, theme));
cellXtraFormating.setBorderId(idx);
cellXtraFormating.setApplyBorder(true);
}
call the above method in your class like this
CellStyle cs = workbook.createCellStyle();
StylesTable styleSource = ((XSSFWorkbook) workbook).getStylesSource();
ThemesTable theme = styleSource.getTheme();
CTXf cellXtraFormating = ((XSSFCellStyle) cs).getCoreXf();
-------your custom code---------
setDiagonal(styleSource, cellXtraFormating, theme);
cell.setCellStyle(cs);
I hope this answers your question

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.

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

Categories