diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFCell.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFCell.java index c120e05a8a6..43f1334e73b 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFCell.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/streaming/SXSSFCell.java @@ -584,7 +584,10 @@ public void setCellStyle(CellStyle style) } /** - * Return the cell's style. + * Return the cell's style. Since POI v5.2.3, this returns the column style if the + * cell has no style of its own. If no column default style is set, the row default style is checked. + * This method has always fallen back to return the default style + * if there is no other style to return. * * @return the cell's style. Never null. Default cell style has zero index and can be obtained as * workbook.getCellStyleAt(0) @@ -593,24 +596,37 @@ public void setCellStyle(CellStyle style) @Override public CellStyle getCellStyle() { - if (_style == null) { - CellStyle style = getDefaultCellStyleFromColumn(); - if (style == null) { - SXSSFWorkbook wb = getSheet().getWorkbook(); - style = wb.getCellStyleAt(0); - } - _style = style; + if (_style != null) { + return _style; + } + CellStyle style = getDefaultCellStyleFromColumn(); + if (style == null) { + style = getDefaultCellStyleFromRow(); + } + if (style == null) { + SXSSFWorkbook wb = getSheet().getWorkbook(); + style = wb.getCellStyleAt(0); } - return _style; + return style; } private CellStyle getDefaultCellStyleFromColumn() { - CellStyle style = null; SXSSFSheet sheet = getSheet(); if (sheet != null) { - style = sheet.getColumnStyle(getColumnIndex()); + int idx = sheet._sh.getColumnHelper().getColDefaultStyle(getColumnIndex()); + if (idx >= 0) { + return getSheet().getWorkbook().getCellStyleAt(idx); + } } - return style; + return null; + } + + private CellStyle getDefaultCellStyleFromRow() { + SXSSFRow row = (SXSSFRow) getRow(); + if (row != null && row.isFormatted()) { + return row.getRowStyle(); + } + return null; } /** diff --git a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCell.java b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCell.java index d9da22cefc2..d0f56ed0d8d 100644 --- a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCell.java +++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFCell.java @@ -572,6 +572,12 @@ public XSSFCellStyle getCellStyle() { if (style == null) { style = getDefaultCellStyleFromColumn(); } + if (style == null) { + style = getDefaultCellStyleFromRow(); + } + if (style == null && _stylesSource.getNumCellStyles() > 0) { + style = _stylesSource.getStyleAt(0); + } return style; } @@ -587,24 +593,27 @@ private XSSFCellStyle getExplicitCellStyle() { } private XSSFCellStyle getDefaultCellStyleFromColumn() { - XSSFCellStyle style = null; XSSFSheet sheet = getSheet(); if (sheet != null) { - style = (XSSFCellStyle) sheet.getColumnStyle(getColumnIndex()); + int idx = sheet.getColumnHelper().getColDefaultStyle(getColumnIndex()); + if (idx >= 0) { + return _stylesSource.getStyleAt(idx); + } } - return style; + return null; + } + + private XSSFCellStyle getDefaultCellStyleFromRow() { + XSSFRow row = getRow(); + if (row != null && row.isFormatted()) { + return row.getRowStyle(); + } + return null; } protected void applyDefaultCellStyleIfNecessary() { - XSSFCellStyle style = getExplicitCellStyle(); - if (style == null) { - XSSFSheet sheet = getSheet(); - if (sheet != null) { - XSSFCellStyle defaultStyle = getDefaultCellStyleFromColumn(); - if (defaultStyle != null) { - setCellStyle(defaultStyle); - } - } + if (getExplicitCellStyle() == null) { + setCellStyle(getCellStyle()); } } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFRow.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFRow.java index 00596484876..cdf178017c6 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFRow.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/streaming/TestSXSSFRow.java @@ -20,7 +20,14 @@ package org.apache.poi.xssf.streaming; import org.apache.poi.ss.tests.usermodel.BaseTestXRow; +import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.xssf.SXSSFITestDataProvider; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFFont; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -28,6 +35,9 @@ import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * Tests for XSSFRow @@ -66,4 +76,85 @@ void testCellColumn() throws IOException { } } + @Test + void testSetRowStylePropagatedToCells() throws IOException { + try (SXSSFWorkbook wb = new SXSSFWorkbook()) { + SXSSFSheet sheet = wb.createSheet("test"); + + // create a bold style + XSSFCellStyle boldStyle = (XSSFCellStyle) wb.createCellStyle(); + XSSFFont boldFont = (XSSFFont) wb.createFont(); + boldFont.setBold(true); + boldStyle.setFont(boldFont); + + // apply style to row, then create cells + SXSSFRow row = sheet.createRow(0); + row.setRowStyle(boldStyle); + + SXSSFCell cell0 = row.createCell(0); + cell0.setCellValue("Header A"); + SXSSFCell cell1 = row.createCell(1); + cell1.setCellValue("Header B"); + + // cells without explicit style should inherit the row style via getCellStyle() + CellStyle cellStyle0 = cell0.getCellStyle(); + assertNotNull(cellStyle0); + assertTrue(wb.getFontAt(cellStyle0.getFontIndex()).getBold(), + "cell should inherit bold font from row style"); + + CellStyle cellStyle1 = cell1.getCellStyle(); + assertNotNull(cellStyle1); + assertTrue(wb.getFontAt(cellStyle1.getFontIndex()).getBold(), + "cell should inherit bold font from row style"); + + // a cell with an explicit style should NOT be overridden by row style + CellStyle plainStyle = wb.createCellStyle(); + SXSSFCell cell2 = row.createCell(2); + cell2.setCellStyle(plainStyle); + cell2.setCellValue("Plain"); + + CellStyle cellStyle2 = cell2.getCellStyle(); + assertFalse(wb.getFontAt(cellStyle2.getFontIndex()).getBold(), + "cell with explicit non-bold style should remain non-bold"); + } + } + + @Test + void testSetRowStylePropagatedAfterWrite() throws IOException { + try (SXSSFWorkbook wb = new SXSSFWorkbook()) { + SXSSFSheet sheet = wb.createSheet("test"); + + // create a bold style + XSSFCellStyle boldStyle = (XSSFCellStyle) wb.createCellStyle(); + XSSFFont boldFont = (XSSFFont) wb.createFont(); + boldFont.setBold(true); + boldStyle.setFont(boldFont); + + // apply style to row, then create cells + SXSSFRow row = sheet.createRow(0); + row.setRowStyle(boldStyle); + row.createCell(0).setCellValue("Column A"); + row.createCell(1).setCellValue("Column B"); + row.createCell(2).setCellValue("Column C"); + + // write and read back — SXSSFITestDataProvider returns XSSFWorkbook + XSSFWorkbook wb2 = SXSSFITestDataProvider.instance.writeOutAndReadBack(wb); + + XSSFSheet sheet2 = wb2.getSheet("test"); + XSSFRow row2 = sheet2.getRow(0); + assertNotNull(row2); + + for (int i = 0; i < 3; i++) { + XSSFCell cell = row2.getCell(i); + assertNotNull(cell, "cell " + i + " should exist after read-back"); + XSSFCellStyle style = cell.getCellStyle(); + assertNotNull(style, "cell " + i + " should have a style after read-back"); + assertTrue(wb2.getFontAt(style.getFontIndex()).getBold(), + "cell " + i + " should be bold after write/read-back"); + } + + wb2.close(); + } + } + } diff --git a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFRow.java b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFRow.java index 90d04efd6cd..22cf5f45d72 100644 --- a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFRow.java +++ b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFRow.java @@ -485,6 +485,86 @@ void duplicateRows() throws IOException { } } + @Test + void testSetRowStylePropagatedToCells() throws IOException { + try (XSSFWorkbook workbook = new XSSFWorkbook()) { + final XSSFSheet sheet = workbook.createSheet("test"); + + // create a bold style + XSSFCellStyle boldStyle = workbook.createCellStyle(); + XSSFFont boldFont = workbook.createFont(); + boldFont.setBold(true); + boldStyle.setFont(boldFont); + + // apply style to row, then create cells + final XSSFRow row = sheet.createRow(0); + row.setRowStyle(boldStyle); + + XSSFCell cell0 = row.createCell(0); + cell0.setCellValue("Header A"); + XSSFCell cell1 = row.createCell(1); + cell1.setCellValue("Header B"); + + // cells without explicit style should inherit the row style via getCellStyle() + XSSFCellStyle cellStyle0 = cell0.getCellStyle(); + assertNotNull(cellStyle0); + assertTrue(workbook.getFontAt(cellStyle0.getFontIndex()).getBold(), + "cell should inherit bold font from row style"); + + XSSFCellStyle cellStyle1 = cell1.getCellStyle(); + assertNotNull(cellStyle1); + assertTrue(workbook.getFontAt(cellStyle1.getFontIndex()).getBold(), + "cell should inherit bold font from row style"); + + // a cell with an explicit style should NOT be overridden by row style + XSSFCellStyle plainStyle = workbook.createCellStyle(); + XSSFCell cell2 = row.createCell(2); + cell2.setCellStyle(plainStyle); + cell2.setCellValue("Plain"); + + XSSFCellStyle cellStyle2 = cell2.getCellStyle(); + assertFalse(workbook.getFontAt(cellStyle2.getFontIndex()).getBold(), + "cell with explicit non-bold style should remain non-bold"); + } + } + + @Test + void testSetRowStylePropagatedAfterWrite() throws IOException { + try (XSSFWorkbook workbook = new XSSFWorkbook()) { + final XSSFSheet sheet = workbook.createSheet("test"); + + // create a bold style + XSSFCellStyle boldStyle = workbook.createCellStyle(); + XSSFFont boldFont = workbook.createFont(); + boldFont.setBold(true); + boldStyle.setFont(boldFont); + + // apply style to row, then create cells + final XSSFRow row = sheet.createRow(0); + row.setRowStyle(boldStyle); + row.createCell(0, CellType.STRING).setCellValue("Column A"); + row.createCell(1, CellType.STRING).setCellValue("Column B"); + row.createCell(2, CellType.STRING).setCellValue("Column C"); + + // write and read back — exercises onDocumentWrite() -> + // applyDefaultCellStyleIfNecessary() + try (XSSFWorkbook wb2 = XSSFTestDataSamples.writeOutAndReadBack(workbook)) { + XSSFSheet sheet2 = wb2.getSheet("test"); + XSSFRow row2 = sheet2.getRow(0); + assertNotNull(row2); + + for (int i = 0; i < 3; i++) { + XSSFCell cell = row2.getCell(i); + assertNotNull(cell, "cell " + i + " should exist after read-back"); + XSSFCellStyle style = cell.getCellStyle(); + assertNotNull(style, "cell " + i + " should have a style after read-back"); + assertTrue(wb2.getFontAt(style.getFontIndex()).getBold(), + "cell " + i + " should be bold after write/read-back"); + } + } + } + } + private void fillData(int startAtRow, Sheet sheet) { Row header = sheet.createRow(0); for (int rownum = startAtRow; rownum < 2; rownum++) { diff --git a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFCell.java b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFCell.java index b4d5f4b62ca..4e46848e0b4 100644 --- a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFCell.java +++ b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFCell.java @@ -978,7 +978,8 @@ public void setCellStyle(HSSFCellStyle style) { /** * get the style for the cell. This is a reference to a cell style contained in the workbook - * object. + * object. If the cell does not have an explicit style assigned, this method will fall back + * to returning the column or row style (if any). * @see org.apache.poi.hssf.usermodel.HSSFWorkbook#getCellStyleAt(int) */ @Override @@ -986,6 +987,21 @@ public HSSFCellStyle getCellStyle() { short styleIndex=_record.getXFIndex(); ExtendedFormatRecord xf = _book.getWorkbook().getExFormatAt(styleIndex); + + if (styleIndex == 0x0f) { + // Fallback to column style + HSSFCellStyle colStyle = _sheet.getColumnStyle(getColumnIndex()); + if (colStyle != null) { + return colStyle; + } + + // Fallback to row style + HSSFCellStyle rowStyle = getRow().getRowStyle(); + if (rowStyle != null) { + return rowStyle; + } + } + return new HSSFCellStyle(styleIndex, xf, _book); } diff --git a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFCell.java b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFCell.java index 48ef715a929..7d1b67f2be0 100644 --- a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFCell.java +++ b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFCell.java @@ -441,4 +441,65 @@ void setFillForegroundColor() throws IOException { } } } + + @Test + void testGetCellStyleFallbackToColumnStyle() throws IOException { + try (HSSFWorkbook wb = new HSSFWorkbook()) { + HSSFSheet sheet = wb.createSheet(); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); + + HSSFCellStyle colStyle = wb.createCellStyle(); + colStyle.setFillBackgroundColor((short) 10); + sheet.setDefaultColumnStyle(0, colStyle); + + assertEquals(colStyle, cell.getCellStyle(), "Should fallback to column style"); + } + } + + @Test + void testGetCellStyleFallbackToRowStyle() throws IOException { + try (HSSFWorkbook wb = new HSSFWorkbook()) { + HSSFSheet sheet = wb.createSheet(); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); + + HSSFCellStyle rowStyle = wb.createCellStyle(); + rowStyle.setFillBackgroundColor((short) 10); + row.setRowStyle(rowStyle); + + assertEquals(rowStyle, cell.getCellStyle(), "Should fallback to row style"); + } + } + + @Test + void testGetCellStyleNoFallbackWhenExplicitStyleAssigned() throws IOException { + try (HSSFWorkbook wb = new HSSFWorkbook()) { + HSSFSheet sheet = wb.createSheet(); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); + + HSSFCellStyle rowStyle = wb.createCellStyle(); + rowStyle.setFillBackgroundColor((short) 10); + row.setRowStyle(rowStyle); + + HSSFCellStyle explicitStyle = wb.createCellStyle(); + explicitStyle.setFillBackgroundColor((short) 20); + cell.setCellStyle(explicitStyle); + + assertEquals(explicitStyle, cell.getCellStyle(), "Should use explicit cell style, not row style"); + } + } + + @Test + void testGetCellStyleNoRowOrColumnStyle() throws IOException { + try (HSSFWorkbook wb = new HSSFWorkbook()) { + HSSFSheet sheet = wb.createSheet(); + HSSFRow row = sheet.createRow(0); + HSSFCell cell = row.createCell(0); + + HSSFCellStyle defaultStyle = cell.getCellStyle(); + assertEquals(0x0f, defaultStyle.getIndex(), "Should return default style when no fallback exists"); + } + } } diff --git a/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestSheet.java b/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestSheet.java index 61aceb97cba..df303b85d81 100644 --- a/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestSheet.java +++ b/poi/src/test/java/org/apache/poi/ss/usermodel/BaseTestSheet.java @@ -701,8 +701,8 @@ protected void defaultRowStyle() throws IOException { Cell cell = r0.createCell(0); CellStyle style2 = cell.getCellStyle(); assertNotNull(style2); - //current implementations mean that cells inherit column style but not row style - assertNotEquals(style.getIndex(), style2.getIndex(), "style should not match"); + //cells should now inherit row style (just like column style) + assertEquals(style.getIndex(), style2.getIndex(), "style should match"); try (Workbook wb2 = _testDataProvider.writeOutAndReadBack(wb)) { Sheet wb2Sheet = wb2.getSheetAt(0);