I am creating below conditional formatting rule in Apache POI.
Issue
Expected:
The formula should highlight only invalid values in column 'J'.
Actual:
But all values of the entire column 'J' get highlighted, even for valid values.
Formula
String kkf="IF(AND(NOT(ISBLANK(J4)),ISERROR(SUM(MATCH(FILTERXML(\"<t><s>\"&SUBSTITUTE(J4,\",\",\"</s><s>\")&\"</s></t>\",\"//s\"),FILTERXML(\"<t><s>\"&SUBSTITUTE(PossibleValues!$D$2,\",\",\"</s><s>\")&\"</s></t>\",\"//s\"),0))),\"\"),TRUE())";
Full Code
import java.util.ArrayList;
import org.apache.commons.io.FileUtils;
import org.json.CDL;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.*;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFHyperlink;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.ComparisonOperator;
import org.apache.poi.ss.usermodel.ConditionalFormattingRule;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.SheetConditionalFormatting;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.common.usermodel.Hyperlink;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFRow;
public class Red {
public static void main(String[] args) throws Exception{
String str="{\"rulesetgroupname\":\"Coastal122\",\"downloadtype\":\"Template\",\"rulesetid\":\"638a0ac02944f902b1eebab3\",\"rulesetversion\":\"0.1\",\"rulegroupid\":\"638a09f28bd88c16a76fa599\",\"product\":\"CAT\",\"ruletype\":\"ERWR\",\"dataobjects\":\"Location 24,Class Codes 24,BPP Covg 24,Building Covg 24,Building 24,Policy 24\",\"ruletemplateversion\":\"1\",\"ruleTemplaeResponseId\":\"638a09f28bd88c16a76fa599\",\"dataobject\":\"Location 24\",\"rulegroup\":\"CAT Testing122\",\"metadataheaders\":[\"Rule Name\",\"Rule Label\",\"Modified By\",\"Modified At\",\"Rule ID\",\"Rule Status (Add/Change/Expire/Verify)\",\"Effective Date [MM/DD/YYYY]\",\"Expiration Date [MM/DD/YYYY]\",\"Has Rule Changed?\"],\"conditionheaders\":[{\"label\":\"State\",\"type\":\"Collection\",\"name\":\"State\",\"dataobject\":\"Location 24\",\"values\":\"NC,SC,TX,VA,MA,GA,AL,IL,DE,NY,MS,NH,RI,ME,FL,MN,CT,NJ,MD\"},{\"label\":\"Counties\",\"type\":\"Collection\",\"name\":\"Counties\",\"dataobject\":\"Location 24\",\"values\":\"\"},{\"label\":\"Zip Codes\",\"type\":\"Collection\",\"name\":\"ZipCodes\",\"dataobject\":\"Location 24\",\"values\":\"\"}],\"outcomeheaders\":[{\"label\":\"Output\",\"type\":\"String\",\"name\":\"RuleCode\",\"dataobject\":\"Location 24\"}]}";
JSONObject output;
try {
output = new JSONObject(str);
String filename = "D:\\project\\excel\\color.xlsx" ;
XSSFWorkbook workbook = new XSSFWorkbook();
String rulegroup = output.getString("rulegroup");
String downloadtype = output.getString("downloadtype");
XSSFSheet sheet = workbook.createSheet("Ruleset");
sheet.setDefaultColumnWidth(10);
ArrayList<String> headers = new ArrayList<String>();
ArrayList<String> nameheaders = new ArrayList<String>();
// Top Element Font
Font font31 = workbook.createFont();
font31.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
font31.setBold(true);
XSSFCellStyle style12 = workbook.createCellStyle();
style12.setFillForegroundColor(HSSFColor.HSSFColorPredefined.PALE_BLUE.getIndex());
style12.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style12.setAlignment(HorizontalAlignment.CENTER);
style12.setVerticalAlignment(VerticalAlignment.CENTER);
style12.setBorderTop(BorderStyle.MEDIUM);
style12.setBorderBottom(BorderStyle.MEDIUM);
style12.setBorderLeft(BorderStyle.MEDIUM);
style12.setBorderRight(BorderStyle.MEDIUM);
style12.setWrapText(true);
style12.setFont(font31);
XSSFCellStyle style13 = workbook.createCellStyle();
style13.setFillForegroundColor(HSSFColor.HSSFColorPredefined.GREY_40_PERCENT.getIndex());
style13.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style13.setAlignment(HorizontalAlignment.CENTER);
style13.setVerticalAlignment(VerticalAlignment.CENTER);
style13.setBorderTop(BorderStyle.MEDIUM);
style13.setBorderBottom(BorderStyle.MEDIUM);
style13.setBorderLeft(BorderStyle.MEDIUM);
style13.setBorderRight(BorderStyle.MEDIUM);
style13.setWrapText(true);
style13.setFont(font31);
XSSFCellStyle style14 = workbook.createCellStyle();
Font font32 = workbook.createFont();
font32.setBold(true);
font32.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
style14.setBorderTop(BorderStyle.MEDIUM);
style14.setBorderBottom(BorderStyle.MEDIUM);
style14.setBorderLeft(BorderStyle.MEDIUM);
style14.setBorderRight(BorderStyle.MEDIUM);
style14.setAlignment(HorizontalAlignment.CENTER);
style14.setVerticalAlignment(VerticalAlignment.CENTER);
style14.setWrapText(true);
style14.setFont(font32);
// Get Top elements
String product = output.getString("product");
String ruletype = output.getString("ruletype");
String dataobject = output.getString("dataobjects");
String rulesetid = output.getString("rulesetid");
String rulesetversion = output.getString("rulesetversion");
String rulegroupid = output.getString("rulegroupid");
String ruletemplateversion = output.getString("ruletemplateversion");
String rulesetgroupname = output.getString("rulesetgroupname");
XSSFRow rowhead = sheet.createRow((short) 0);
rowhead.createCell(0).setCellValue("Product/Process");
rowhead.getCell(0).setCellStyle(style14);
rowhead.createCell(1).setCellValue(product);
rowhead.getCell(1).setCellStyle(style13);
rowhead.createCell(2).setCellValue("RuleType");
rowhead.getCell(2).setCellStyle(style14);
rowhead.createCell(3).setCellValue(ruletype);
rowhead.getCell(3).setCellStyle(style13);
rowhead.createCell(4).setCellValue("DataObject");
rowhead.getCell(4).setCellStyle(style14);
rowhead.createCell(5).setCellValue(dataobject);
rowhead.getCell(5).setCellStyle(style13);
rowhead.createCell(6).setCellValue("RuleTemplate");
rowhead.getCell(6).setCellStyle(style14);
rowhead.createCell(7).setCellValue(rulegroup);
rowhead.getCell(7).setCellStyle(style13);
rowhead.createCell(8).setCellValue("RulesetID");
rowhead.getCell(8).setCellStyle(style14);
rowhead.createCell(9).setCellValue(rulesetid);
rowhead.getCell(9).setCellStyle(style13);
rowhead.createCell(10).setCellValue("RuleSetVersion");
rowhead.getCell(10).setCellStyle(style14);
rowhead.createCell(11).setCellValue(rulesetversion);
rowhead.getCell(11).setCellStyle(style13);
rowhead.createCell(12).setCellValue("RuleTemplateID");
rowhead.getCell(12).setCellStyle(style14);
rowhead.createCell(13).setCellValue(rulegroupid);
rowhead.getCell(13).setCellStyle(style13);
rowhead.createCell(14).setCellValue("RuleTemplateVersion");
rowhead.getCell(14).setCellStyle(style14);
rowhead.createCell(15).setCellValue(ruletemplateversion);
rowhead.getCell(15).setCellStyle(style13);
rowhead.createCell(16).setCellValue("RuleSetGroupName");
rowhead.getCell(16).setCellStyle(style14);
rowhead.createCell(17).setCellValue(rulesetgroupname);
rowhead.getCell(17).setCellStyle(style12);
rowhead.setHeightInPoints((2 * sheet.getDefaultRowHeightInPoints()));
JSONArray metadataheader = output.getJSONArray("metadataheaders");
JSONArray conditionheader = output.getJSONArray("conditionheaders");
JSONArray outcomeheader = output.getJSONArray("outcomeheaders");
Integer metheadcount = metadataheader.length();
Integer conheadcount = conditionheader.length();
Integer outheadcount = outcomeheader.length();
metadataheader.getString(0);
for (int i = 0; i < metadataheader.length(); i++) {
headers.add(metadataheader.getString(i));
nameheaders.add(metadataheader.getString(i));
}
for (int i = 0; i < conditionheader.length(); i++) {
JSONObject json = conditionheader.getJSONObject(i);
String conname = json.getString("label");
String contype = json.getString("type");
String dataobject1 = json.getString("dataobject");
contype = "[" + contype + "]";
String fnl = dataobject1 + "." + conname + " " + contype;
headers.add(fnl);
nameheaders.add(conname);
}
for (int i = 0; i < outcomeheader.length(); i++) {
JSONObject json = outcomeheader.getJSONObject(i);
// if(json.isEmpty()) {
// throw new Exception("Outcome Attributes filed should not be empty");
// }
String conname = json.getString("label");
String contype = json.getString("type");
String dataobject1 = json.getString("dataobject");
contype = "[" + contype + "]";
String fnl = dataobject1 + "." + conname + " " + contype;
headers.add(fnl);
nameheaders.add(conname);
}
// Merging Region
// HSSFRow merge = sheet.createRow((short)1);
Row row = sheet.createRow(1);
Cell cell = row.createCell(0);
cell.setCellValue("MetaData");
// Merging cells by providing cell index
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, metheadcount - 1));
Font font3 = workbook.createFont();
font3.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
font3.setBold(true);
XSSFCellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(HSSFColor.HSSFColorPredefined.LIGHT_GREEN.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setWrapText(true);
/*
* style.setBorderTop(BorderStyle.MEDIUM);
* style.setBorderBottom(BorderStyle.MEDIUM);
* style.setBorderLeft(BorderStyle.MEDIUM);
* style.setBorderRight(BorderStyle.MEDIUM);
*/
style.setFont(font3);
cell.setCellStyle(style);
// style.setFont(headerFont);
// style.setFillForegroundColor(IndexedColors.BLACK.getIndex());
Cell cell1 = row.createCell(metheadcount);
cell1.setCellValue("Conditional Attributes");
if (conheadcount > 1) {
sheet.addMergedRegion(new CellRangeAddress(1, 1, metheadcount, metheadcount + conheadcount - 1));
}
XSSFCellStyle style1 = workbook.createCellStyle();
Font font1 = workbook.createFont();
font1.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
font1.setBold(true);
style1.setFont(font1);
style1.setWrapText(true);
style1.setFillForegroundColor(HSSFColor.HSSFColorPredefined.LIGHT_ORANGE.getIndex());
style1.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style1.setAlignment(HorizontalAlignment.CENTER);
style1.setVerticalAlignment(VerticalAlignment.CENTER);
/*
* style1.setBorderTop(BorderStyle.MEDIUM);
* style1.setBorderBottom(BorderStyle.MEDIUM);
* style1.setBorderLeft(BorderStyle.MEDIUM);
* style1.setBorderRight(BorderStyle.MEDIUM);
*/
cell1.setCellStyle(style1);
Cell cell2 = row.createCell(metheadcount + conheadcount);
cell2.setCellValue("Outcome Attributes");
if (outheadcount > 1) {
sheet.addMergedRegion(new CellRangeAddress(1, 1, metheadcount + conheadcount,
metheadcount + conheadcount + outheadcount - 1));
}
XSSFCellStyle style2 = workbook.createCellStyle();
Font font2 = workbook.createFont();
font2.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
font2.setBold(true);
style2.setFont(font2);
style2.setFillForegroundColor(HSSFColor.HSSFColorPredefined.LIGHT_CORNFLOWER_BLUE.getIndex());
style2.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style2.setAlignment(HorizontalAlignment.CENTER);
style2.setVerticalAlignment(VerticalAlignment.CENTER);
style2.setWrapText(true);
/*
* style2.setBorderTop(BorderStyle.MEDIUM);
* style2.setBorderBottom(BorderStyle.MEDIUM);
* style2.setBorderLeft(BorderStyle.MEDIUM);
* style2.setBorderRight(BorderStyle.MEDIUM);
*/
cell2.setCellStyle(style2);
row.setHeightInPoints((2 * sheet.getDefaultRowHeightInPoints()));
/*
* HSSFCellStyle style22 = workbook.createCellStyle(); Font font22 =
* workbook.createFont();
* font22.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
* font22.setBold(true); style22.setFont(font22);
* style22.setAlignment(HorizontalAlignment.CENTER);
*/
XSSFCellStyle style22 = workbook.createCellStyle();
Font font22 = workbook.createFont();
font22.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
font22.setBold(true);
style22.setFont(font22);
style22.setBorderTop(BorderStyle.MEDIUM);
style22.setBorderBottom(BorderStyle.MEDIUM);
style22.setBorderLeft(BorderStyle.MEDIUM);
style22.setBorderRight(BorderStyle.MEDIUM);
style22.setWrapText(true);
// style22.setFillForegroundColor(HSSFColor.HSSFColorPredefined.WHITE.getIndex());
// style22.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style22.setAlignment(HorizontalAlignment.CENTER);
XSSFRow rowheader = sheet.createRow((short) 2);
for (int z = 0; z < headers.size(); z++) {
rowheader.createCell(z).setCellValue(headers.get(z));
String metaDataCellValue = rowheader.getCell(z).getStringCellValue();
if(metaDataCellValue.equals("Rule Name") || metaDataCellValue.equals("Modified By") ||
metaDataCellValue.equals("Modified At") || metaDataCellValue.equals("Rule ID")) {
rowheader.getCell(z).setCellStyle(style13);
} else {
rowheader.getCell(z).setCellStyle(style22);
}
}
rowheader.setHeightInPoints((2 * sheet.getDefaultRowHeightInPoints()));
// Get Details
DataFormatter formatter = new DataFormatter();
if (downloadtype.equalsIgnoreCase("rulesets")) {
JSONArray values = output.getJSONArray("values");
if (!values.isEmpty()) {
short valcell = 3;
for (int val = 0; val < values.length(); val++) {
XSSFRow rowheader2 = sheet.createRow((short) valcell + val);
try {
JSONObject firstvals = values.getJSONObject(val);
JSONArray methead1 = firstvals.optJSONArray("metadata");
JSONArray conhead1 = firstvals.optJSONArray("condition");
JSONArray outhead1 = firstvals.optJSONArray("outcome");
for (int i = 0; i < methead1.length(); i++) {
JSONObject json = methead1.getJSONObject(i);
String val23 = json.get("value").toString();
String name = "";
if (!json.isNull("label")) {
name = (json.getString("label"));
} else {
name = (json.getString("name"));
}
int index = nameheaders.indexOf(name);
rowheader2.createCell(index).setCellValue(val23);
}
for (int y = 0; y < conhead1.length(); y++) {
JSONObject json = conhead1.getJSONObject(y);
String val23 = json.get("value").toString();
String name = "";
if (!json.isNull("label")) {
name = (json.getString("label"));
} else {
name = (json.getString("name"));
}
String oper = (json.getString("operator"));
if (oper.equalsIgnoreCase("not in")) {
val23 = "NOT IN(" + val23 + ")";
} else if (oper.equalsIgnoreCase("neq")) {
val23 = "NOT EQUALS(" + val23 + ")";
} else if (oper.equalsIgnoreCase(">") || oper.equalsIgnoreCase("GT") ) {
val23 = ">" + val23;
} else if (oper.equalsIgnoreCase(">=") || oper.equalsIgnoreCase("GTE")) {
val23 = ">=" + val23;
}else if (oper.equalsIgnoreCase("<") || oper.equalsIgnoreCase("LT")) {
val23 = "<" + val23;
}
else if ( oper.equalsIgnoreCase("<=") || oper.equalsIgnoreCase("LTE")) {
val23 = "<=" + val23;
}
int index = nameheaders.indexOf(name);
if (index > 0) {
rowheader2.createCell(index).setCellValue(val23);
}
}
for (int za = 0; za < outhead1.length(); za++) {
JSONObject json = outhead1.getJSONObject(za);
String val23 = json.get("value").toString();
String name = "";
if (!json.isNull("label")) {
name = (json.getString("label"));
} else {
name = (json.getString("name"));
}
int index = nameheaders.indexOf(name);
if (index > 0) {
rowheader2.createCell(index).setCellValue(val23);
}
}
} catch (JSONException e) {
e.printStackTrace();
String error = e.getMessage();
throw new Exception(error);
} catch (Exception e) {
// String error = e.getMessage();
e.printStackTrace();
throw new Exception(e.getMessage());
}
}
} //
}
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateAll();
// Duplicate Highlighter
String lcollett = "";
String csv1 = "";
SheetConditionalFormatting sheetCF = sheet.getSheetConditionalFormatting();
int s1 = sheet.getNumMergedRegions();
for (int i = 0; i < s1; i++) {
CellRangeAddress region = sheet.getMergedRegion(i);
int colIndex = region.getFirstColumn();
int rowNum = region.getFirstRow();
String val1 = formatter.formatCellValue(sheet.getRow(rowNum).getCell(colIndex));
if (val1.contains("MetaData")) {
int fcol = sheet.getMergedRegion(i).getFirstColumn();
int lcol = sheet.getMergedRegion(i).getLastColumn();
lcollett = CellReference.convertNumToColString(lcol);
for (i = fcol; i <= lcol; i++) {
sheet.autoSizeColumn(i);
}
}
}
for (int i = 0; i < s1; i++) {
CellRangeAddress region = sheet.getMergedRegion(i);
int colIndex = region.getFirstColumn();
int rowNum = region.getFirstRow();
String val1 = formatter.formatCellValue(sheet.getRow(rowNum).getCell(colIndex));
ArrayList<String> xsdf = new ArrayList<String>();
if (val1.contains("Conditional")) {
int fcol = sheet.getMergedRegion(i).getFirstColumn();
int lcol = sheet.getMergedRegion(i).getLastColumn();
String fcollett = "";
lcollett = CellReference.convertNumToColString(lcol);
for (i = fcol; i <= lcol; i++) {
fcollett = CellReference.convertNumToColString(i);
if (i == lcol) {
xsdf.add("$" + fcollett + "$4:$" + fcollett + "$10000,\"*\"&$" + fcollett + "4&\"*\"");
} else {
xsdf.add("$" + fcollett + "$4:$" + fcollett + "$10000,$" + fcollett + "4");
}
sheet.autoSizeColumn(i);
}
csv1 = String.join(",", xsdf);
csv1 = "COUNTIFS(" + csv1 + ")>1";
}
}
for (int i = 0; i < s1; i++) {
CellRangeAddress region = sheet.getMergedRegion(i);
int colIndex = region.getFirstColumn();
int rowNum = region.getFirstRow();
String val1 = formatter.formatCellValue(sheet.getRow(rowNum).getCell(colIndex));
if (val1.contains("Outcome")) {
int fcol = sheet.getMergedRegion(i).getFirstColumn();
int lcol = sheet.getMergedRegion(i).getLastColumn();
lcollett = CellReference.convertNumToColString(lcol);
for (i = fcol; i <= lcol; i++) {
sheet.autoSizeColumn(i);
}
}
}
ConditionalFormattingRule rule1 = sheetCF.createConditionalFormattingRule(csv1);
org.apache.poi.ss.usermodel.FontFormatting font = rule1.createFontFormatting();
font.setFontStyle(false, true);
font.setFontColorIndex(IndexedColors.BLUE.index);
String cellrangeadd = "A4:" + lcollett + "10000";
CellRangeAddress[] regions = new CellRangeAddress[] { CellRangeAddress.valueOf(cellrangeadd) };
sheetCF.addConditionalFormatting(regions, rule1);
// Sheet 3
SheetConditionalFormatting sheetCFa = sheet.getSheetConditionalFormatting();
XSSFSheet sheet21 = workbook.createSheet("PossibleValues");
sheet21.setColumnWidth(0, 20 * 256);
sheet21.setColumnWidth(1, 50 * 256);
XSSFRow rowhead33 = sheet21.createRow((short) 0);
rowhead33.createCell(0).setCellValue("DataObject");
rowhead33.getCell(0).setCellStyle(style14);
rowhead33.createCell(1).setCellValue("AttributeName");
rowhead33.getCell(1).setCellStyle(style14);
rowhead33.createCell(2).setCellValue("DataType");
rowhead33.getCell(2).setCellStyle(style14);
rowhead33.createCell(3).setCellValue("PossibleValues");
rowhead33.getCell(3).setCellStyle(style14);
int attcell=1;
ArrayList<String> xkdf = new ArrayList<String>();
for (int i = 0; i < conditionheader.length(); i++) {
JSONObject json = conditionheader.getJSONObject(i);
String conname = json.getString("label");
String contype = json.getString("type");
String dataobject1 = json.getString("dataobject");
String pv = json.getString("values");
String contypea = "[" + contype + "]";
String fnl = dataobject1 + "." + conname + " " + contypea;
xkdf.add(fnl);
XSSFRow rowhead334 = sheet21.createRow((short) attcell + i);
rowhead334.createCell(0).setCellValue(dataobject1);
rowhead334.createCell(1).setCellValue(conname);
rowhead334.createCell(2).setCellValue(contype);
rowhead334.createCell(3).setCellValue(pv);
}
String kkf = "IF(AND(NOT(ISBLANK(J4)),ISERROR(SUM(MATCH(FILTERXML(\"<t><s>\"&SUBSTITUTE(J4,\",\",\"</s><s>\")&\"</s></t>\",\"//s\"),FILTERXML(\"<t><s>\"&SUBSTITUTE(PossibleValues!$D$2,\",\",\"</s><s>\")&\"</s></t>\",\"//s\"),0))),\"\"),TRUE())";
ConditionalFormattingRule rule13 = sheetCF.createConditionalFormattingRule(kkf);
org.apache.poi.ss.usermodel.FontFormatting font535 = rule13.createFontFormatting();
font535.setFontStyle(false, true);
font535.setFontColorIndex(IndexedColors.RED.index);
String cellrangeadd2 = "J4:J10000";
CellRangeAddress[] regionssd = new CellRangeAddress[] { CellRangeAddress.valueOf(cellrangeadd2) };
sheetCF.addConditionalFormatting(regionssd, rule13);
FileOutputStream fileOut = new FileOutputStream(filename);
workbook.write(fileOut);
fileOut.close();
workbook.close();
System.out.println("Your excel file has been generated!");
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Required manual post-production in Excel
Now I did the below steps
Add three values in column 'J' named "State". All cells are getting highlighted, see image below:
Click Conditional Formatting > Manage Rules > Select Rule (Format Red) > Edit Rules > OK > Apply > OK
After this action the column values are getting highlighted correctly.
Question
Is there anything I missed in my Java code?
How do I avoid this manual activity in Excel?
Short answer
The problem is with the FILTERXML function used in your conditional formatting. This should be prefixed _xlfn in XML sheet storage. To make it work your formula should be changed to following:
String kkf = "IF(AND(NOT(ISBLANK(J4)),ISERROR(SUM(MATCH(_xlfn.FILTERXML(\"<t><s>\"&SUBSTITUTE(J4,\",\",\"</s><s>\")&\"</s></t>\",\"//s\"),_xlfn.FILTERXML(\"<t><s>\"&SUBSTITUTE(PossibleValues!$D$2,\",\",\"</s><s>\")&\"</s></t>\",\"//s\"),0))),\"\"),TRUE())";
Reason
FILTERXML function was introduced in Excel 2013. So it was introduced after Microsoft had published the Office Open XML file format for Microsoft Office files in 2006 for Office 2007. To mark such later introduced functions, Microsoft decided to prefix them with _xlfn in XML storage. After reading the XML, the Excel GUI knows whether the function is known in that Excel version or not. If yes, the prefix gets removed and the function name gets localized (e.g. in German Excel: to XMLFILTER). If not, the prefix remains and the user also knows that this function is unknown in that Excel version. See Issue: An _xlfn. prefix is displayed in front of a formula.
Why we need the storage formula version?
In Apache POI Cell.setCellFormula as well as SheetConditionalFormatting.createConditionalFormattingRule sets the formula string into the XML directly. So that string must be exactly as it shall be stored in XML.
Learn more about this by inspecting the file-formats
Office Open XML files, and so also *.xlsx files, are ZIP archives containing XML files and other files into a specific directory structure. So one simply can unzip a *.xlsx file to have a look into.
So after creating your color.xlsx using your code, unzip it and have a look into xl/worksheets/sheet1.xml.
You will find something like:
<conditionalFormatting sqref="J4:J10000">
<cfRule type="expression" dxfId="1" priority="2">
<formula>
<![CDATA[ IF(AND(NOT(ISBLANK(J4)),ISERROR(SUM(MATCH(FILTERXML("<t><s>"&SUBSTITUTE(J4,",","</s><s>")&"</s></t>","//s"),FILTERXML("<t><s>"&SUBSTITUTE(PossibleValues!$D$2,",","</s><s>")&"</s></t>","//s"),0))),""),TRUE()) ]]>
</formula>
</cfRule>
</conditionalFormatting>
And this does not work in Excel.
Now open the color.xlsx in Excel and make it work. Then save the the color.xlsx, unzip it again and have a look into xl/worksheets/sheet1.xml.
You will find something like:
<x14:conditionalFormattings>
<x14:conditionalFormatting xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main">
<x14:cfRule type="expression" priority="2" id="{00000000-000E-0000-0000-000002000000}">
<xm:f>IF(AND(NOT(ISBLANK(J4)),ISERROR(SUM(MATCH(_xlfn.FILTERXML("<t><s>"&SUBSTITUTE(J4,",","</s><s>")&"</s></t>","//s"),_xlfn.FILTERXML("<t><s>"&SUBSTITUTE(PossibleValues!$D$2,",","</s><s>")&"</s></t>","//s"),0))),""),TRUE())</xm:f>
...
</x14:cfRule>
...
</x14:conditionalFormatting>
</x14:conditionalFormattings>
This shows the _xlfn prefix before FILTERXML in formula string.
It also shows that a totally different version of conditional formatting is used. But that's a secondary problem. The prefixed version of the formula string also works using as formula string within the old version of conditionalFormatting. But, of course that is not guaranteed in all cases. So to be on save side for all cases, one of two possibilities must be given:
either one avoids using all Excel functions introduced after publishing Office Open XML in 2006 (Office 2007)
or Apache POI has to fully support all changes Microsoft made after publishing Office Open XML in 2006 (Office 2007).
To fulfill the latter Apache POI is far away. It not even supports all features of Office 2007 up to now.
If using VBA is an acceptable solution for you, then a possible solution would be to create an .xlsm file containing the functions needed for conditional formatting.
Create manually the color.xlsm file
Add the 2 empty sheets "Ruleset" and "PossibleValues"
Open VBA editor
In sheet "Ruleset" add following Sub:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("$j$4:$j$1000")) Is Nothing Then
Highlight Target
End If
End Sub
Create a VBA Module and add following Sub...
Sub Highlight(ByVal Cell As Range)
Dim Entry() As String
Entry = Split(Cell.Value, ",")
Dim StatesString As String
StatesString = UCase(Worksheets("PossibleValues").Range("D2").Value)
Dim States() As String
States = Split(StatesString, ",")
Dim Matches As Integer
Matches = 0
For EntryIdx = 0 To UBound(Entry)
If (IsInArray(Entry(EntryIdx), States)) Then
Matches = Matches + 1
End If
Next EntryIdx
If (UBound(Entry) + 1 = Matches) Then
Cell.Font.Color = vbBlack
Else
Cell.Font.Color = vbRed
End If End Sub
...and the following function:
Private Function IsInArray(valToBeFound As Variant, arr As Variant) As Boolean
'DEVELOPER: Ryan Wells (wellsr.com)
'DESCRIPTION: Function to check if a value is in an array of values
'INPUT: Pass the function a value to search for and an array of values of any data type.
'OUTPUT: True if is in array, false otherwise
Dim element As Variant
On Error GoTo IsInArrayError: 'array is empty
For Each element In arr
If element = valToBeFound Then
IsInArray = True
Exit Function
End If
Next element
Exit Function
IsInArrayError:
On Error GoTo 0
IsInArray = False
End Function
Finally, you will need to adapt your code to open this manually created .xlsm file rather than creating it programmatically.
Hope it helps
I am trying to copy a sheet from two different XSSF Workbooks. I have written a code that works really well (see below) but it doesn't take into account the pop-up documentation for cells and doesn't copy it (which is logical because I haven't written that functionnality).
I have searched the Apache POI documentation and it looks like there is no way to do this. Any idea on how I could do to copy-paste that information?
public class CopySheets {
public static void copySheet(String sheetName, XSSFWorkbook wbSrc,XSSFWorkbook wbDest) throws IOException {
XSSFSheet srcSheet = null;
XSSFRow srcRow = null;
XSSFCell srcCell = null;
XSSFSheet destSheet = null;
XSSFRow destRow = null;
XSSFCell destCell = null;
int fCell = 0;
int lCell = 0;
int fRow = 0;
int lRow = 0;
System.out.println(sheetName);
srcSheet = wbSrc.getSheet(sheetName);
System.out.println(srcSheet);
if (srcSheet != null) {
destSheet = wbDest.createSheet(sheetName);
fRow = srcSheet.getFirstRowNum();
lRow = srcSheet.getLastRowNum();
for (int iRow = fRow; iRow <= lRow; iRow++) {
srcRow = srcSheet.getRow(iRow);
destRow = destSheet.createRow(iRow);
if (srcRow != null) {
fCell = srcRow.getFirstCellNum();
lCell = srcRow.getLastCellNum();
for (int iCell = fCell; iCell < lCell; iCell++) {
srcCell = srcRow.getCell(iCell);
destCell = destRow.createCell(iCell);
if (srcCell != null) {
switch (srcCell.getCellTypeEnum()) {
case BLANK:
destCell.setCellValue("");
break;
case BOOLEAN:
destCell.setCellValue(srcCell.getBooleanCellValue());
break;
case ERROR:
destCell.setCellErrorValue(srcCell.getErrorCellValue());
break;
case FORMULA:
destCell.setCellFormula(srcCell.getCellFormula());
break;
case NUMERIC:
destCell.setCellValue(srcCell.getNumericCellValue());
break;
case STRING:
destCell.setCellValue(srcCell.getStringCellValue());
break;
default:
destCell.setCellFormula(srcCell.getCellFormula());
}
CellStyle origStyle = srcCell.getCellStyle(); // Or from a cell
CellStyle newStyle = wbDest.createCellStyle();
newStyle.cloneStyleFrom(origStyle);
destCell.setCellStyle(newStyle);
Comment origComment=srcCell.getCellComment();
destCell.setCellComment(origComment);
}
}
}
}
}
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("workbook.xls", true));
wbDest.write(bos);
bos.close();
}
}
Thanks a lot!
Okay, I found the answer, it turns out POI has something that enables us to get that info on the sheet as a whole so I added this bit to my code and it works like a charm!
List<XSSFDataValidation> validations=srcSheet.getDataValidations();
for (XSSFDataValidation validation:validations){
destSheet.addValidationData(validation);
}
Looking at the documentation-
http://poi.apache.org/apidocs/dev/org/apache/poi/ss/usermodel/Comment.html
Seems like the getAddress() method of the src Comment object will return the reference to the cell it was previously attached to (which is the src cell).
You should try creating a new Comment instead of doing
Comment origComment=srcCell.getCellComment();
destCell.setCellComment(origComment);
You should use RangeCopier.
XSSFWorkbook workbookFrom = new XSSFWorkbook(new File("/path/to/workbookFrom.xlsx"));
XSSFSheet sheetFrom = workbookFrom.getSheetAt(0);
XSSFWorkbook workbookTo = new XSSFWorkbook(new File("/path/to/workbookTo.xlsx"));
XSSFSheet sheetTo = workbookTo.createSheet("sheet1");
workbookTo.setSheetOrder("sheet1", 0);
XSSFRangeCopier xssfRangeCopier = new XSSFRangeCopier(sheetFrom, sheetTo);
//copy the row height and column width, and find the max column num
int lastRow = sheetFrom.getLastRowNum();
int lastCol = 0;
for (int i = 0; i < lastRow; i++) {
Row row = sheetFrom.getRow(i);
if (row != null) {
if (row.getLastCellNum() > lastCol) {
lastCol = row.getLastCellNum();
}
sheetTo.setDefaultRowHeight(sheetFrom.getDefaultRowHeight());
}
}
for (int j = 0; j < lastCol; j++) {
sheetTo.setColumnWidth(j, sheetFrom.getColumnWidth(j));
}
//copy contents from source sheet to destination sheet
CellRangeAddress cellAddresses = new CellRangeAddress(0, lastRow, 0, lastCol);
xssfRangeCopier.copyRange(cellAddresses, cellAddresses, true, true);
workbookTo.write(new FileOutputStream(new File("/path/to/worksheetTo.xlsx")));
I try to set the background color of a cell according to a value. Please, find below the corresponding code.
File inputFile = new File(path)
FileInputStream fis = new FileInputStream(inputFile)
XSSFWorkbook inputWorkbook = new XSSFWorkbook(fis)
XSSFWorkbook outputWorkbook = new XSSFWorkbook()
XSSFSheet inputSheet = inputWorkbook.getSheetAt(1)
def inputSheetName = inputWorkbook.getSheetName(1)
XSSFSheet outputSheet = outputWorkbook.createSheet(inputSheetName)
// some code
CellStyle style = outputSheet.getRow(rowIndex).getCell(columnIndex).getCellStyle()
short color = (status.get(listIndex) == 'Passed') ? IndexedColors.GREEN.getIndex() : IndexedColors.RED.getIndex()
style.setFillBackgroundColor(color)
outputSheet.getRow(rowIndex).getCell(columnIndex).setCellStyle(style)
// some code
This code does not make an error, however, the cell background is not set ...
Someone can help me please? Thanks for help!
Update 1
CellStyle style = outputSheet.getRow(rowIndex).getCell(columnIndex).getCellStyle()
short color = (status.get(listIndex) == 'Passed') ? IndexedColors.GREEN.getIndex() : IndexedColors.RED.getIndex()
style.setFillPattern(FillPatternType.SOLID_FOREGROUND)
style.setFillForegroundColor(color)
outputSheet.getRow(rowIndex).getCell(columnIndex).setCellStyle(style)
Update 2
The goal of the following code is to copy and paste content of an excel file to another one. And I want to set the color of the cell according to the status value.
def function(dateTimeOfTestSuite, status, executionDateTime) {
File inputFile = new File(path)
FileInputStream fis = new FileInputStream(inputFile)
XSSFWorkbook inputWorkbook = new XSSFWorkbook(fis)
def inputSheetCount = inputWorkbook.getNumberOfSheets()
def directoryName = 'Data Files\\Reports\\' + name
File directory = new File(directoryName)
if (!directory.exists())
directory.mkdirs()
def excelOutputFileName = directory.path + '\\' + dateTimeOfTestSuite + '.xlsx'
excelOutputFileName = excelOutputFileName.replace(':', '-')
File outputFile = new File(excelOutputFileName)
FileOutputStream fos = new FileOutputStream(outputFile)
XSSFWorkbook outputWorkbook = new XSSFWorkbook()
for(int i = 0; i < inputSheetCount; i++) {
XSSFSheet inputSheet = inputWorkbook.getSheetAt(i)
def inputSheetName = inputWorkbook.getSheetName(i)
XSSFSheet outputSheet = outputWorkbook.createSheet(inputSheetName)
def numberOfRows = inputSheet.getPhysicalNumberOfRows()
def numberOfColumns = inputSheet.getRow(0).getPhysicalNumberOfCells()
def listIndex = 0
def rowIndex = 0
outputSheet.createRow(0)
while (rowIndex < numberOfRows && inputSheet.getRow(rowIndex).getCell(0).getCellType() != Cell.CELL_TYPE_BLANK) {
def columnIndex = 0
while (columnIndex < numberOfColumns && inputSheet.getRow(0).getCell(columnIndex).getCellType() != Cell.CELL_TYPE_BLANK) {
def value = (inputSheet.getRow(rowIndex).getCell(columnIndex).toString() == 'null') ? '' : inputSheet.getRow(rowIndex).getCell(columnIndex).toString()
if (columnIndex == 0)
outputSheet.createRow(rowIndex).createCell(columnIndex).setCellValue(value)
else
outputSheet.getRow(rowIndex).createCell(columnIndex).setCellValue(value)
outputSheet.autoSizeColumn(columnIndex)
columnIndex++
}
if(inputSheetName == "input") {
if(rowIndex == 0) {
outputSheet.getRow(0).createCell(columnIndex).setCellValue('Status')
outputSheet.getRow(0).createCell(columnIndex + 1).setCellValue('ExecutedAt')
}
else if (rowIndex > 1) {
outputSheet.getRow(rowIndex).createCell(columnIndex).setCellValue(status.get(listIndex))
if(status.get(listIndex) != 'No run') {
outputSheet.getRow(rowIndex).createCell(columnIndex + 1).setCellValue(executionDateTime.get(listIndex))
CellStyle style = outputSheet.getRow(rowIndex).getCell(columnIndex).getCellStyle()
def color = (status.get(listIndex) == 'Passed') ? IndexedColors.GREEN.getIndex() : IndexedColors.RED.getIndex()
style.setFillPattern(FillPatternType.SOLID_FOREGROUND)
style.setFillForegroundColor(color)
outputSheet.getRow(rowIndex).getCell(columnIndex).setCellStyle(style)
}
listIndex++
}
outputSheet.autoSizeColumn(columnIndex)
outputSheet.autoSizeColumn(columnIndex + 1)
}
rowIndex++
}
}
outputWorkbook.write(fos)
fos.close()
}
Solution
XSSFCellStyle style = outputWorkbook.createCellStyle()
def color = (status.get(listIndex) == 'Passed') ? IndexedColors.GREEN.getIndex() : IndexedColors.RED.getIndex()
style.setFillPattern(FillPatternType.SOLID_FOREGROUND)
style.setFillForegroundColor(color)
outputSheet.getRow(rowIndex).getCell(columnIndex).setCellStyle(style)
Its actually not about the background. Its the foreground color. Also you need to specify a FillPattern like this:
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
style.setFillForegroundColor(color);
As for the reason, why it is setFillForegroundColor and not setFillBackgroundColor, I couldn't find any information what exactly the difference is, but following code shows how they work:
style.setFillPattern(FillPatternType.LEAST_DOTS);
style.setFillForegroundColor(IndexedColors.BRIGHT_GREEN.getIndex());
style.setFillBackgroundColor(IndexedColors.RED.getIndex());
And the result looks like this:
From this I would guess, that setFillBackgroundColor only works properly in conjuction with setFillForegroundColor to create patterns like this.
I'm working on POI application in order to manipulate excel file.
In fact the user is giving a formula and files and I am applying the formula on a output file.The formula have to modify the value of the column on the cell.
For example on the columns B, I want to apply on all my column a formula.
The user is giving to me LEFT(x,2), and I have to apply this to all the columns.
(x is defining all the columns)
But when I am applying the formula I got the formula as a String. I try to pass the cell value at the formula but of course it is not working...
I think I should copy all my data into a another excel file, work on it and copy paste them in the output file or their is another way ?
Regards,
Code:
for (int i = 0; i < cell[0].length; i++){ //Checking the header
for (int j = 0; j < ruleArray.length; j++){ //Checking the Header of the array with the rule to apply
if (cell[0][i].toString().equals(ruleArray[j][0])){ //Comparing
String testF = ruleArray[j][1];
if (testF.contains("X") || testF.contains("x")){ //Replacing X with value for the formula
for (int k = 0; k < cell.length; k++){
indexT = cell[0][i].getColumnIndex();
indexC = cell[k][i].getRowIndex()+1;
String colLetter = CellReference.convertNumToColString(indexT);
formula = testF.replace("x", colLetter+indexC);
cell[k][i].setCellType(CellType.FORMULA);
cell[k][i].setCellFormula(formula);
}
}
}
}
}
I am not rewrite your code but you can take a help from this. Create a excel file with column City and Formula and then run this code. I have attached some snapshot of excel file. I thin kit will help you. LEFT(X,2) only parse first two char from a string
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class TestProblem
{
public static void main(String[] args) throws IOException {
InputStream inp = null;
inp = new FileInputStream("E:\\Projects\\PoiAdvanceExample\\stackProblem.xlsx");
Workbook wb = WorkbookFactory.create(inp);
Sheet sheet = wb.getSheetAt(0);
int rowsCount = sheet.getLastRowNum();
int columnCount = sheet.getRow(0).getLastCellNum();
String[][] inputData = new String[rowsCount+1][columnCount];
for (int i = 0; i <= rowsCount; i++) {
Row row = sheet.getRow(i);
int colCounts = row.getLastCellNum();
for (int j = 0; j < colCounts; j++) {
Cell cell = row.getCell(j);
if(cell.getCellType() == CellType.NUMERIC) {
inputData[i][j] = Double.toString(cell.getNumericCellValue());
}
if(cell.getCellType() == CellType.FORMULA) {
inputData[i][j] = cell.getCellFormula();
}
if(cell.getCellType() == CellType.STRING) {
inputData[i][j] = cell.getStringCellValue();
}
}
}
writeData(inputData);
}
private static void writeData(String[][] inputData) throws IOException {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
int r = 0;
for (String[] dataRow : inputData) {
Row row = sheet.createRow(r++);
int column = 0;
for (String dataCell : dataRow) {
Cell cell = row.createCell(column++);
if (r == 1 || column == 1) cell.setCellValue(dataCell);
else if (column == 2) {
CellReference cellReference = new CellReference(cell);
String thisR = cellReference.getCellRefParts()[1];
cell.setCellFormula("LEFT(A" + thisR + ",2)");
}
}
}
FileOutputStream fileOut = new FileOutputStream("stackProblem.xlsx");
workbook.write(fileOut);
workbook.close();
}
}
Excel file before run will be like this.
Excel file after run this code will be like this.
I'm using Apache POI and I am running into a weird problem. I can auto-size my rows, but only if there are no merged cells in that row. Here's an example:
new FileOutputStream('test.xlsx').withStream { OutputStream os ->
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
CellStyle wrapStyle = workbook.createCellStyle();
wrapStyle.setWrapText(true);
Row row = sheet.createRow(0); row.setRowStyle(wrapStyle);
Cell cell = row.createCell(0); cell.setCellStyle(wrapStyle);
cell.setCellValue("Very long text that needs to be wrapped")
cell = row.createCell(1); cell.setCellStyle(wrapStyle);
cell.setCellValue("Short text");
cell = row.createCell(2); cell.setCellStyle(wrapStyle);
cell.setCellValue("");
// These two lines break row auto-height!
//
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, 1, 2);
sheet.addMergedRegion(cellRangeAddress);
workbook.write(os);
}
This code generates the following document:
However, as soon as I comment out the lines that merge the two cells, the output looks like this:
Is this a bug? Does anyone know of a workaround?
By using the last part from Val Blant I've did something that's easier to use, and yet much complex.
Please be aware that there is a single line of code that adds one additional line to the cell height-wise, for personal reasons. If you do not wish that, please remove it. Also feel free to change it to a non-static, I've had to use static methods due to the company I'm working for making the specific class static.
PS: It's my first post on stackoverflow, please be gentle. :)
Solution:
public static Boolean isCellMerged(Cell cell) {
Sheet sheet = cell.getSheet();
for (CellRangeAddress mergedRegionRange : sheet.getMergedRegions()) {
Integer cellColumn = cell.getColumnIndex();
Integer cellRow = cell.getRowIndex();
if (mergedRegionRange.containsColumn(cellColumn) && mergedRegionRange.containsRow(cellRow)) {
return true;
}
}
return false;
}
public static List<List<Cell>> getCellsInRowsInsideRegionRange(Cell cell) {
Sheet sheet = cell.getSheet();
List<List<Cell>> mergedRowList = new ArrayList<>();
List<Cell> mergedCellsList = new ArrayList<>();
//Nejdříve musíme zjistit sloučenou sekci dané buňky
for (CellRangeAddress mergedRegionRange : sheet.getMergedRegions()) {
Integer cellColumn = cell.getColumnIndex();
Integer cellRow = cell.getRowIndex();
if (mergedRegionRange.containsColumn(cellColumn) && mergedRegionRange.containsRow(cellRow)) {
//Protože CellRangeAddress nemá moc metod, musíme si pomoci sami a získat z ní buňky a řádky
for (Row row : sheet) {
for (Cell iteratedCell : row) {
Integer iteratedCellColumn = iteratedCell.getColumnIndex();
Integer iteratedCellRow = iteratedCell.getRowIndex();
if (mergedRegionRange.containsColumn(iteratedCellColumn) && mergedRegionRange.containsRow(iteratedCellRow)) {
//Rozdělování jednotlivých řádků
//Není-li řádek bez buněk...
if (!mergedCellsList.isEmpty()) {
//Tak buňku přidáme do Listu buněk...
mergedCellsList.add(iteratedCell);
} else {
//Pokud se jedná o první buňku prvního řádku, tak přidáme rovnou
mergedCellsList.add(iteratedCell);
}
}
}
//Vložíme List buněk daného řádku do Listu řádků
if (!mergedCellsList.isEmpty()) {
mergedRowList.add(mergedCellsList);
}
//A vyresetujeme list buněk (začneme tak nanovo novým řádkem)
mergedCellsList = null;
mergedCellsList = new ArrayList<>();
}
//Vrátíme výsledný List řádků, obsahující Listy buněk ve sloučené sekci.
if (!mergedRowList.isEmpty()) {
return mergedRowList;
} else {
return null;
}
}
}
return null;
}
public static void adjustRowHeightForRowWithNonMergedCells(Row row) {
row.setHeight((short) -1);
}
public static void adjustRowHeightForRowWithMergedCells(Row row) {
Sheet sheet = row.getSheet();
Cell longestTextCell = null;
//Potřebujeme získat buňku s nejdelším textem
for (Cell iteratedCell : row) {
String iteratedTextString = iteratedCell.getStringCellValue();
if (longestTextCell != null && StringUtils.isNotBlank(longestTextCell.getStringCellValue())) {
if (iteratedTextString.length() > longestTextCell.getStringCellValue().length()) {
longestTextCell = iteratedCell;
}
} else {
longestTextCell = iteratedCell;
}
}
//Z textově nejobsáhlejší buňky potřebujeme dostat údaje
String longestText = "";
if (StringUtils.isNotBlank(longestTextCell.getStringCellValue()) && longestTextCell != null) {
longestText = longestTextCell.getStringCellValue();
//Protože textově nejobsáhlejší buňka nemusí nutně být sloučeného typu, je zapotřebí to všude ošetřit
Boolean isLongestTextCellMerged = isCellMerged(longestTextCell);
Float longestCellWidthInPixels = 0f;
Float longestMergedCellWidthInPixels = 0f;
//Získat šířku nesloučené nejobsáhlejší buňky je jednoduché
if (!isLongestTextCellMerged) {
Integer longestCellColumnIndex = longestTextCell.getColumnIndex();
longestCellWidthInPixels = sheet.getColumnWidthInPixels(longestCellColumnIndex);
} else {
//Musíme přijít na šířku sloučené buňky namísto buňky uvnitř sloučené buňky
List<List<Cell>> cellsInMergedRegion = getCellsInRowsInsideRegionRange(longestTextCell);
longestMergedCellWidthInPixels = 0f;
//Projdeme řádky
for (List<Cell> iteratedCell2List : cellsInMergedRegion) {
Float iteratedMergedCell2WidthInPixels = 0f;
//Projdeme jednotlivé buňky ve sloučené buňce na řádku a sečteme jejich šířky
for (Cell iteratedCell2 : iteratedCell2List) {
Integer iteratedCell2ColumnIndex = iteratedCell2.getColumnIndex();
Float iteratedCell2ColumnWidthInPixels = sheet.getColumnWidthInPixels(iteratedCell2ColumnIndex);
iteratedMergedCell2WidthInPixels = iteratedMergedCell2WidthInPixels + iteratedCell2ColumnWidthInPixels;
}
//Získáme šířku nejširší sloučené buňky na řádku
if (iteratedMergedCell2WidthInPixels > longestMergedCellWidthInPixels) {
longestMergedCellWidthInPixels = iteratedMergedCell2WidthInPixels;
}
//Resetujeme sčítání
iteratedMergedCell2WidthInPixels = 0f;
}
}
//Uložíme si nejširší buňku dle toho, zda je sloučená či nikoliv
Float longestWidthInPixels;
if (isLongestTextCellMerged) {
longestWidthInPixels = longestMergedCellWidthInPixels;
} else {
longestWidthInPixels = longestCellWidthInPixels;
}
//Potřebujeme font
Workbook wb = sheet.getWorkbook();
Short fontIndex = longestTextCell.getCellStyle().getFontIndex();
Font excelFont = wb.getFontAt(fontIndex);
//Potřebujeme i jeho styl
Integer excelFontStyle = java.awt.Font.PLAIN;
if (excelFont.getBold()) excelFontStyle = java.awt.Font.BOLD;
if (excelFont.getItalic()) excelFontStyle = java.awt.Font.ITALIC;
//Potřebujeme získat skutečný font i s velikostí
java.awt.Font currentFont = new java.awt.Font(excelFont.getFontName(), excelFontStyle, excelFont.getFontHeightInPoints());
//Získáme řetězec s vlastností
AttributedString attributedString = new AttributedString(longestText);
attributedString.addAttribute(TextAttribute.FONT, currentFont);
//Použijeme LineBreakMeasurer k zjištění kolik řádků bude text potřebovat
FontRenderContext fontRenderContext = new FontRenderContext(null, true, true);
LineBreakMeasurer measurer = new LineBreakMeasurer(attributedString.getIterator(), fontRenderContext);
Integer nextPosition = 0;
Integer lineCount = 0;
while (measurer.getPosition() < longestText.length()) {
nextPosition = measurer.nextOffset(longestWidthInPixels);
//Také musíme ošetřit případ manuálně zadaných LineBreaků pro všechny možné techtle mechtle :-S
String textLine = StringUtils.substring(longestText, measurer.getPosition(), nextPosition);
Boolean containsNewLine = StringUtils.containsIgnoreCase(textLine, "\r") || StringUtils.containsIgnoreCase(textLine, "\\r") || StringUtils.containsIgnoreCase(textLine, "\n") || StringUtils.containsIgnoreCase(textLine, "\\n");
if (containsNewLine) {
if (StringUtils.containsIgnoreCase(textLine, "\r\n") || StringUtils.containsIgnoreCase(textLine, "\\r\\n")) {
lineCount = lineCount + StringUtils.countMatches(textLine, "\n");
} else {
if (StringUtils.containsIgnoreCase(textLine, "\r") || StringUtils.containsIgnoreCase(textLine, "\\r")) {
lineCount = lineCount + StringUtils.countMatches(textLine, "\r");
}
if (StringUtils.containsIgnoreCase(textLine, "\n") || StringUtils.containsIgnoreCase(textLine, "\\n")) {
lineCount = lineCount + StringUtils.countMatches(textLine, "\n");
}
}
lineCount = lineCount + StringUtils.countMatches(textLine, "\\r?\\n");
}
lineCount++;
measurer.setPosition(nextPosition);
}
//Máme počet řádků, zbývá konečný dopočet výšky řádku a jeho použití
if (lineCount > 1) {
Float fontHeight = currentFont.getLineMetrics(longestText, fontRenderContext).getHeight();
//Pro jistotu přidáme jeden řádek navíc, člověk nikdy neví...
lineCount = lineCount + 1;
//Potřebujeme získat poslední řádek
Row lastRow = null;
if (isCellMerged(longestTextCell)) {
List<List<Cell>> mergedCellsInRows = getCellsInRowsInsideRegionRange(longestTextCell);
Integer lastRowInMergedSectionIndex = mergedCellsInRows.size() - 1;
List<Cell> lastRowInMergedSection = mergedCellsInRows.get(lastRowInMergedSectionIndex);
lastRow = lastRowInMergedSection.get(0).getRow();
} else {
lastRow = longestTextCell.getRow();
}
//Je potřeba ošetřit velikosti, pokud má sloučená buňka vícero řádků
Float cellsMergedAboveHeight = 0f;
if (isCellMerged(longestTextCell)) {
if (getCellsInRowsInsideRegionRange(longestTextCell).size() > 1) {
List<List<Cell>> mergedCellsInRows = getCellsInRowsInsideRegionRange(longestTextCell);
for (List<Cell> rowsWithCells : mergedCellsInRows){
if (!lastRow.equals(rowsWithCells.get(0).getRow())){
cellsMergedAboveHeight = cellsMergedAboveHeight + rowsWithCells.get(0).getRow().getHeight();
}
}
}
}
//Vzorec je ((Velikost fontu krát počet řádků plus (počet řádků krát volný prostor mezi řádky)) krát přepočet Excelu) mínus výška sloučených buněk nad posledním řádkem.
Short finalRowHeight = (short) (((fontHeight * lineCount + (lineCount * 15))* 10) - cellsMergedAboveHeight);
//A výsledek nastavíme na poslední řádek, protože jinak to przní sloupce vlevo a vpravo od vyšších řádků
lastRow.setHeight(finalRowHeight);
}
}
}
I was having a similar problem where row height was not adjusting for merged cells.
I had to write a custom function for adjusting height of that particular row. Here is my code:
I have fixed column width in my sheet to be 24, and default font size to be 11.
I needed adjustment in row height whenever my font size > 11 and cell text length is overflowing the column width or isn't visible properly due to larger length.
private void adjustRowHeightMergedCells(final XSSFCell mergedCell) {
DataFormatter dataFormatter = new DataFormatter();
int defaultCharWidth = SheetUtil.getDefaultCharWidth(mergedCell.getRow().getSheet().getWorkbook());
XSSFRow row = mergedCell.getRow();
// Getting merged cell width value
double cellValueWidth = SheetUtil.getCellWidth(mergedCell, defaultCharWidth, dataFormatter, true);
// If cell width value > 24 (Default value), calculate how much extra row height is needed
// This happends when text length is larger than the column width (24)
float extraRowHeightDueToCellTextLength = (float) Math.floor(cellValueWidth / 24) * row.getHeightInPoints();
float extraRowHeightDueToCellTextFontSize = 0;
// If cell font size > 11 (Default value), calculate how much extra row height is needed
short cellTextFontSize = mergedCell.getCellStyle().getFont().getFontHeightInPoints();
if (cellTextFontSize > 11) {
extraRowHeightDueToCellTextFontSize = (cellTextFontSize-ExcelConstants.DEFAULT_FONT_SIZE) * (5f/3f);
}
// 5f/3f in above calculation is custom number which assumed by thinking that for font size 11, my row height shud be 15, and for example custom font size 20, row height shud be 30, hence the factor 5f/ 3f( per extra 1 point increase in font size above 11, row height shud be increased by 5/3)
// Larger of two adjustment values will be taken and added to current row height
float extraRowHeightAdjustment = Math.max(extraRowHeightDueToCellTextFontSize,extraRowHeightDueToCellTextLength);
if(extraRowHeightAdjustment > 0) {
row.setHeightInPoints(row.getHeightInPoints() + extraRowHeightAdjustment);
}
}
Kinda hacky solution but works for my situation, you can modify it per your requirements.
After more research, turns out that this is a problem with Excel itself, not POI. Excel does indeed lose its ability to auto-fit rows to content for all rows that have merged cells in them. For more info see:
http://excel.tips.net/T003207_Automatic_Row_Height_For_Merged_Cells_with_Text_Wrap.html
http://blog.contextures.com/archives/2012/06/07/autofit-merged-cell-row-height/
The workaround is based on predicting the number of lines in the largest cell of the row, and then adjusting the row height manually. The code is based on this discussion:
http://mail-archives.apache.org/mod_mbox/poi-user/200906.mbox/%3C24216153.post#talk.nabble.com%3E
RowInfo and NestedCellInfo below are my custom data structures that keep track of the sheet layout. You should be able to replace these with your equivalents and helper functions.
private void adjustRowHeights(Sheet sheet, List<RowInfo> rows, SortedSet<Integer> createdColumnNumbers) {
SortedMap<Integer, Float> columnWidthsInPx = [] as TreeMap;
createdColumnNumbers.each {
columnWidthsInPx.put(it, sheet.getColumnWidthInPixels(it));
}
rows.each { RowInfo rowInfo ->
if ( rowInfo.hasMergedCells ) {
Row excelRow = sheet.getRow(rowInfo.rowIndex);
// Find the column with the longest text - that's the one that will determine
// the row height
//
NestedCellInfo longestCell = rowInfo.getCellWithLongestContent();
String cellText = longestCell.getText();
if ( cellText != null && cellText.size() > 5 ) {
int colIdx = rowInfo.cells.indexOf(longestCell);
// Figure out available width in pixels, taking colspans into account
//
float columnWidthInPx = columnWidthsInPx[colIdx];
int numberOfMergedColumns = longestCell.colSpan;
(numberOfMergedColumns - 1).times {
columnWidthInPx += columnWidthsInPx[colIdx + it];
}
// Setup the font we'll use for figuring out where the text will be wrapped
//
XSSFFont cellFont = longestCell.getCellFont();
int fontStyle = Font.PLAIN;
if ( cellFont.getBold() ) fontStyle = Font.BOLD;
if ( cellFont.getItalic() ) fontStyle = Font.ITALIC;
java.awt.Font currFont = new java.awt.Font(
cellFont.getFontName(),
fontStyle,
cellFont.getFontHeightInPoints());
AttributedString attrStr = new AttributedString(cellText);
attrStr.addAttribute(TextAttribute.FONT, currFont);
// Use LineBreakMeasurer to count number of lines needed for the text
//
FontRenderContext frc = new FontRenderContext(null, true, true);
LineBreakMeasurer measurer = new LineBreakMeasurer(attrStr.getIterator(), frc);
int nextPos = 0;
int lineCnt = 0;
while (measurer.getPosition() < cellText.length()) {
nextPos = measurer.nextOffset( columnWidthInPx );
lineCnt++;
measurer.setPosition(nextPos);
}
if ( lineCnt > 1 ) {
excelRow.setHeight((short)(excelRow.getHeight() * lineCnt * /* fudge factor */ 0.7));
}
}
}
}
This solution is far from perfect, but it allowed me to move forward.