POI: Using an Excel template that has conditional formatting - excel

I'm new to using Apache Poi.
I had some questions about using an existing xlsx file as a template when it has conditional formatting.
The template file opens with no errors. I can enter data into it manually and the conditional formatting works by highlighting columns and changing fonts for specific cells in the spreadsheet.
I am using Poi to add data to the file.
I open the template add data to the appropriate cells, save the file as a new file.
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(xlsTemplate);
XSSFWorkbook workbook = XSSFWorkbookFactory.createWorkbook(inputStream);
var tmpSheet = workbook.getSheetAt(0);
int rowCount = tmpStartRow;
for(var tmpRow : tmpList) {
Row row = tmpSheet.getRow(rowCount++);
int columnCount = tmpStartCol;
var tmpMap = tmpRow.objectToMap();
int x = columnCount;
for(var entry : tmpMap.entrySet()) {
Cell cell = row.getCell(x);
var value = "";
if(entry.getValue() != null)
value = (String)entry.getValue();
try {
var dblVal = Double.valueOf(value);
cell.setCellValue(dblVal);
}catch(Exception e) {
cell.setCellValue(value);
}
x++;
}
}
var evaluator = workbook.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateAll();
workbook.setForceFormulaRecalculation(true);
inputStream.close();
try (FileOutputStream outputStream = new FileOutputStream(xlsFileName)) {
workbook.write(outputStream);
workbook.close();
outputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
When I open the new file, Excel does not give any errors.
After reviewing the worksheet, the cells have data in the appropriate fields, but the formatting from the template has not been set.
When I click on "Manage Rules" in the conditional formatting section, I see the rules are still there.
When I click on a cell in the spreadsheet and click on the formula bar and then click away, the conditional formatting gets triggered.
How do I get Poi to handle the conditional formatting for me? Do I need to modify the conditional formatting even though it already exists in the template?
Thanks in advance for your help..
SA

Related

EPPlus corrupt Excel file when having more than 65,530 rows

I'm running into an issue with EPPlus when there are more than 65,530 rows that have a column with a hyperlink. The example below is configured to create 65,530 rows. With this number it will create the Excel file correctly (not corrupt). Once you run it with anything over 65,530, the Excel file will be created but when you open it, Excel will report that is corrupt. Any ideas how to solve this issue?
try
{
int maxRowsToCreate = 65530; //-- no errors will be generated
//int maxRowsToCreate = 65531; //-- error will be generated. The Excel file will be created but will give an error when trying to open it.
string report = string.Format("D:\\temp\\hypelinkIssue-{0}.xlsx", maxRowsToCreate.ToString());
if (File.Exists(report))
{
File.Delete(report);
}
using (ExcelPackage pck = new ExcelPackage(new System.IO.FileInfo(report)))
{
//Add the Content sheet
var ws = pck.Workbook.Worksheets.Add("Catalog");
ws.View.ShowGridLines = true;
var namedStyle = pck.Workbook.Styles.CreateNamedStyle("HyperLink"); //This one is language dependent
namedStyle.Style.Font.UnderLine = true;
namedStyle.Style.Font.Color.SetColor(Color.Blue);
ws.Column(1).Width = 100;
int rowIndex = 0;
for (int i = 0; i < maxRowsToCreate; i++)
{
rowIndex += 1;
string fullFilePath = string.Format("D:\\temp\\{0}", Path.GetRandomFileName());
ws.Cells[rowIndex, 1].StyleName = "HyperLink";
ws.Cells[rowIndex, 1].Hyperlink = new Uri(string.Format(#"file:///{0}", fullFilePath));
ws.Cells[rowIndex, 1].Value = fullFilePath;
}
pck.Save();
}
System.Diagnostics.Process.Start(report);
}
catch (Exception ex)
{
throw ex;
}
The issue occurs when using ".Hyperlink". If instead I use ".Formula" and populate it with the "=HYPERLINK" Excel formula, it works fine. I was able to create 250k records with unique hyperlink using this approach. I did not try more than 250k but hopefully it will work fine.
Thanks for pointing me in the right direction.
/*
This only works with LESS than 65,530 hyperlinks
*/
ws.Cells[rowIndex, 1].StyleName = "HyperLink";
ws.Cells[rowIndex, 1].Hyperlink = new OfficeOpenXml.ExcelHyperLink(fullFilePath, ExcelHyperLink.UriSchemeFile);
ws.Cells[rowIndex, 1].Value = fullFilePath;
/*
This works with more that 65,530 hyperlinks
*/
string cellFormula = string.Format("=HYPERLINK(\"{0}\")", filePath);
ws.Cells[rowIndex, 1].Formula = cellFormula;
This is because Excel limits the amount of unique URLs in a file to 65,530. You should try to insert them as text, instead of a url.
For a possible solution, take a look at this answer.

dropdown Validation not working if it exceeds 50 rows in the Export To Excel

I am generating Excel File(.xlsx) using apache poi jar (poi-ooxml-3.9.jar), I added dropdown validation for 10 columns in my excel file, If I generate the Excel File with 50 rows, drop down validation is working. If it exceeds more than 50 rows, drop down validation is not coming in the Excel File, When I open the excel File I get the message as "We found a problem with some content in fileName.xlsx. Do you want us to try to recover as much as we can ? If you trust the source of this workbook, click Yes ". when click on Yes, all the dropdown validation it is removing. Kindly need solution to fix this issue.
Do not create DataValidationConstraint for each single cell but only for each varying list you need. Then create DataValidation using those DataValidationConstraint for continuous CellRangeAddressList which are as big as possible and also are not all single cells.
Example creates ten different list validations for column 1 to 10 in rows 1 to 10000.
import java.io.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;
class DataValidationList {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook(); // or new HSSFWorkbook
Sheet sheet = workbook.createSheet("Data Validation");
DataValidationHelper dvHelper = sheet.getDataValidationHelper();
for (int col = 0; col < 10; col++) {
DataValidationConstraint dvConstraint = dvHelper.createExplicitListConstraint(
new String[]{"Col "+(col+1)+" one","Col "+(col+1)+" two","Col "+(col+1)+" three"});
CellRangeAddressList addressList = new CellRangeAddressList(0, 9999, 0, col);
DataValidation validation = dvHelper.createValidation(
dvConstraint, addressList);
if(validation instanceof XSSFDataValidation) {
validation.setSuppressDropDownArrow(true);
validation.setShowErrorBox(true);
}
else {
validation.setSuppressDropDownArrow(false);
}
sheet.addValidationData(validation);
}
String filename;
if(workbook instanceof XSSFWorkbook) {
filename = "DataValidationList.xlsx";
} else {
filename = "DataValidationList.xls";
}
FileOutputStream out = new FileOutputStream(filename);
workbook.write(out);
out.close();
workbook.close();
}
}

How to change only one style property in xssf by keeping all the other properties same

How to change only font color of one cell without altering the workbooks previous style properties. Please look at the attachment "workbook" for clear understanding. The column delta contribution font color should be changed but its background style properties should not be altered.
EDIT:
I have changed the code.
The columns rank and mean contribution in the template have a predefined design of some alternate colors which are set in the excel itself. The template is designed by my team and I am afraid I can't change it from Java.
My work is to populate the last column Delta Contribution whose background styles should be same as the total sheet provided the color change according to the conditions.
String deltaContribution = line.getDeltaContribution() != null
? Double.parseDouble(line.getDeltaContribution()) + "" : "";
if (!deltaContribution.equals("")) {
XSSFCell cell = (XSSFCell) row.getCell(8);
XSSFCellStyle style = cell.getCellStyle();
XSSFFont redFont = style.getFont();
XSSFFont blueFont = style.getFont();
XSSFFont greenFont = style.getFont();
if(Double.parseDouble(deltaContribution) >= 0.20) {
redFont.setColor(IndexedColors.RED.getIndex());
CellUtil.setFont(cell, workbook, redFont);
//log.info("The colour is " + colour.getARGBHex());
}
else if(Double.parseDouble(deltaContribution) <= -0.20) {
greenFont.setColor(IndexedColors.GREEN.getIndex());
CellUtil.setFont(cell, workbook, greenFont);
//log.info("The colour is " + colour.getARGBHex());
}
else {
blueFont.setColor(IndexedColors.BLUE.getIndex());
CellUtil.setFont(cell, workbook, blueFont);
//log.info("The colour is " + colour.getARGBHex());
}
row.getCell(8).setCellValue(line.getDeltaContribution() != null
? formatDecimalPlaces(line.getDeltaContribution()) : "");
}
I should not change the previous styles applied to the sheet, I should just edit one property of style. After changing the code, whole column is populated with green color.Last column
workbook:
There are two approaches.
First approach is using conditional formatting. This is my preferred approach.
Example:
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import java.io.FileOutputStream;
class ConditionalFormattingCellValues {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
ConditionalFormattingRule cfRule2 = sheetCF.createConditionalFormattingRule(ComparisonOperator.GE, "0.20");
FontFormatting fontFormatting = cfRule2.createFontFormatting();
fontFormatting.setFontStyle(false, false);
fontFormatting.setFontColorIndex(IndexedColors.RED.index);
ConditionalFormattingRule cfRule1 = sheetCF.createConditionalFormattingRule(ComparisonOperator.LT, "0.20");
fontFormatting = cfRule1.createFontFormatting();
fontFormatting.setFontStyle(false, false);
fontFormatting.setFontColorIndex(IndexedColors.BLUE.index);
ConditionalFormattingRule [] cfRules = {cfRule1, cfRule2};
CellRangeAddress[] regions = {CellRangeAddress.valueOf("I2:I10")};
sheetCF.addConditionalFormatting(regions, cfRules);
for (int r = 1; r < 10; r++) {
Row row = sheet.createRow(r);
Cell cell = row.createCell(8);
cell.setCellValue(1d/Math.sqrt(r)-0.2);
}
FileOutputStream fileOut = new FileOutputStream("ConditionalFormattingCellValues.xlsx");
wb.write(fileOut);
wb.close();
}
}
Second approach is using CellUtil. This provides "Various utility functions that make working with a cells and rows easier. The various methods that deal with style's allow you to create your CellStyles as you need them. When you apply a style change to a cell, the code will attempt to see if a style already exists that meets your needs. If not, then it will create a new style. This is to prevent creating too many styles. there is an upper limit in Excel on the number of styles that can be supported."
Example:
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellUtil;
import java.io.FileOutputStream;
class DirectlyFormattingCellValues {
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook();
Font redFont = wb.createFont();
redFont.setColor(IndexedColors.RED.getIndex());
Font blueFont = wb.createFont();
blueFont.setColor(IndexedColors.BLUE.getIndex());
Sheet sheet = wb.createSheet("Sheet1");
for (int r = 1; r < 10; r++) {
Row row = sheet.createRow(r);
Cell cell = row.createCell(8);
String deltaContribution = String.valueOf(1d/Math.sqrt(r)-0.2);
if(Double.parseDouble(deltaContribution)>=0.20) {
CellUtil.setFont(cell, redFont);
} else {
CellUtil.setFont(cell, blueFont);
}
cell.setCellValue(Double.valueOf(deltaContribution));
}
FileOutputStream fileOut = new FileOutputStream("DirectlyFormattingCellValues.xlsx");
wb.write(fileOut);
wb.close();
}
}
As said already, using conditional formatting should be preferred.
But according your screen-shot all your numbers seems to be text strings instead of really numeric values. This should be avoided since Excel cannot using such text strings in formula calculation. And without changing that, only second approach will be usable since conditional formatting also needs really numeric values for comparison.

apache poi cellIterator skips blank cells but not in first row

I am creating a java program to read an excel sheet and create a comma separated file. When I run my sample excel file, with blank columns, The first row works perfectly, but the rest of the rows skip the blank cells.
I have read about the code changes required to insert blank cells into the rows, but my question is why does the first row work ????
public ArrayList OpenAndReadExcel(){
FileInputStream file = null;
HSSFWorkbook workBook = null;
ArrayList <String> rows = new ArrayList();
//open the file
try {
file = new FileInputStream(new File("Fruity.xls"));
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
System.out.println("Could not open Input File");
e.printStackTrace();
}
// open the input stream as a workbook
try {
workBook = new HSSFWorkbook(file);
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("Can't Open HSSF workbook");
e.printStackTrace();
}
// get the sheet
HSSFSheet sheet = workBook.getSheetAt(0);
// add an iterator for every row and column
Iterator<Row> rowIter = sheet.rowIterator();
while (rowIter.hasNext())
{
String rowHolder = "";
HSSFRow row = (HSSFRow) rowIter.next();
Iterator<Cell> cellIter = row.cellIterator();
Boolean first =true;
while ( cellIter.hasNext())
{
if (!first)
rowHolder = rowHolder + ",";
HSSFCell cell = (HSSFCell) cellIter.next();
rowHolder = rowHolder + cell.toString() ;
first = false;
}
rows.add(rowHolder);
}
return rows;
}
public void WriteOutput(ArrayList<String> rows) {
// TODO Auto-generated method stub
PrintStream outFile ;
try {
outFile = new PrintStream("fruity.txt");
for(String row : rows)
{
outFile.println(row);
}
outFile.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
-----
my Input in .xls file (Sorry don't know how to insert an excel table here )
Name >>>>>>>>>> Country of Origin >>>>>>>>> State of origin >>>>>>> Grade>>>>>> No of months
Apple >>>>>>>> USA >>>>>>>>>>>>>>>>>>>>>> Washington >>>>>>>>>>>>>> A >>>>>>>>> 6
orange >>>>>> USA >>>>>>>>>>>>>>>>>>>>>> Florida >>>>>>>>>>>>>>>>> A >>>>>>>>> 9
pineapple>>>>> USA >>>>>>>>>>>>>>>>>>>>>> Hawaii >>>>>>>>>>>>>>>>>> B >>>>>>>>> 10
strawberry>>>> USA >>>>>>>>>>>>>>>>>>>>>> New Jersey>>>>>>>>>>>>>> C >>>>>>>>>> 3
my output text file
Name ,Country of Origin,State of origin,,,Grade,No of months
Apple,USA,Washington,A,6.0
orange,USA,Florida,A,9.0
pineapple,USA,Hawaii,B,10.0
strawberry,USA,New Jersey,C,3.0
Notice the two extra commas before the Grade column... This is because I have two blank columns there.<br/>
These extra commas are missing in the rest of the output.
I am using Apache Poi-3.9-20121203.jar
You should have a read through the Iterating Over Rows and Cells documentation on the Apache POI website.
The CellIterator will only return cells that have been defined in the file, which largely means ones with either values or formatting. The excel file format is sparse, and doesn't bother storing cells which have neither values nor formatting.
For your case, you must have formatting applied to the first row, which causes them to show up.
You need to read through the documentation and switch to lookups by index. That will also allow you full control over how blank vs never used cells are handled in your code.
CellIterator does not iterate over cells which do not have any formatting or value applied.
First row must have had at least value or formatting applied.
If you want to read such cells as well you need to address it directly by specifying cell number
row.getCell(cellNumber, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
Using Row.MissingCellPolicy.CREATE_NULL_AS_BLANK returns it as a blank cell.

apache poi how to disable external reference or external links?

I've been looking on the web for 30 minutes now and can't find any explanation about that. Here is my problem :
I wrote an application with poi to parse some data from 200 excel files or so and put some of it into a new file. I do some cell evaluation with FormulaEvaluator to know the content of the cells before choosing to keep them or not.
Now, when i test it on a test file with only values in the cells, the program works perfectly but when i use it on my pile of files I get this error :
"could not resolve external workbook name"
Is there any way to ignore external workbook references or set up the environment so that it wont evaluate formula with external references?
Because the ones I need don't contain references...
Thank you
Can you not just catch the error, and skip over that cell?
You're getting the error because you've asked POI to evaluate a the formula in a cell, and that formula refers to a different file. However, you've not told POI where to find the file that's referenced, so it objects.
If you don't care about cells with external references, just catch the exception and move on to the next cell.
If you do care, you'll need to tell POI where to find your files. You do this with the setupEnvironment(String[],Evaluator[]) method - pass it an array of workbook names, and a matching array of evaluators for those workbooks.
In order for POI to be able to evaluate external references, it needs access to the workbooks in question. As these don't necessarily have the same names on your system as in the workbook, you need to give POI a map of external references to open workbooks, through the setupReferencedWorkbooks(java.util.Map<java.lang.String,FormulaEvaluator> workbooks) method.
I have done please see below code that is working fine at my side
public static void writeWithExternalReference(String cellContent, boolean isRowUpdate, boolean isFormula)
{
try
{
File yourFile = new File("E:\\Book1.xlsx");
yourFile.createNewFile();
FileInputStream myxls = null;
myxls = new FileInputStream(yourFile);
XSSFWorkbook workbook = new XSSFWorkbook(myxls);
FormulaEvaluator mainWorkbookEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
XSSFWorkbook workbook1 = new XSSFWorkbook(new File("E:\\elk\\lookup.xlsx"));
// Track the workbook references
Map<String,FormulaEvaluator> workbooks = new HashMap<String, FormulaEvaluator>();
workbooks.put("Book1.xlsx", mainWorkbookEvaluator);
workbooks.put("elk/lookup.xlsx", workbook1.getCreationHelper().createFormulaEvaluator());
workbook2.getCreationHelper().createFormulaEvaluator());
// Attach them
mainWorkbookEvaluator.setupReferencedWorkbooks(workbooks);
XSSFSheet worksheet = workbook.getSheetAt(0);
XSSFRow row = null;
if (isRowUpdate) {
int lastRow = worksheet.getLastRowNum();
row = worksheet.createRow(++lastRow);
}
else {
row = worksheet.getRow(worksheet.getLastRowNum());
}
if (!isFormula) {
Cell cell = row.createCell(row.getLastCellNum()==-1 ? 0 : row.getLastCellNum());
cell.setCellValue(Double.parseDouble(cellContent));
} else {
XSSFCell cell = row.createCell(row.getLastCellNum()==-1 ? 0 : row.getLastCellNum());
System.out.println(cellContent);
cell.setCellFormula(cellContent);
mainWorkbookEvaluator.evaluateInCell(cell);
cell.setCellFormula(cellContent);
// mainWorkbookEvaluator.evaluateInCell(cell);
//System.out.println(cell.getCellFormula() + " = "+cell.getStringCellValue());
}
workbook1.close();
myxls.close();
FileOutputStream output_file =new FileOutputStream(yourFile,false);
//write changes
workbook.write(output_file);
output_file.close();
} catch (Exception e) {
e.printStackTrace();
}
}

Resources