diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java index ce142b78c..441f42fdb 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFColor.java @@ -16,8 +16,11 @@ ==================================================================== */ package org.apache.poi.xssf.usermodel; +import java.util.Arrays; + import org.apache.poi.ss.usermodel.Color; import org.apache.poi.ss.usermodel.ExtendedColor; +import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.util.Internal; import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor; @@ -50,6 +53,11 @@ public class XSSFColor extends ExtendedColor { this(); ctColor.setRgb(rgb); } + + public XSSFColor(IndexedColors indexedColor) { + this(); + ctColor.setIndexed(indexedColor.index); + } /** * A boolean value indicating the ctColor is automatic and system ctColor dependent. @@ -304,7 +312,17 @@ public class XSSFColor extends ExtendedColor { return ctColor; } + /** + * Checked type cast color to an XSSFColor. + * + * @param color the color to type cast + * @return the type casted color + * @throws IllegalArgumentException if color is null or is not an instance of XSSFColor + */ public static XSSFColor toXSSFColor(Color color) { + // FIXME: this method would be more useful if it could convert any Color to an XSSFColor + // Currently the only benefit of this method is to throw an IllegalArgumentException + // instead of a ClassCastException. if (color != null && !(color instanceof XSSFColor)) { throw new IllegalArgumentException("Only XSSFColor objects are supported"); } @@ -316,13 +334,62 @@ public class XSSFColor extends ExtendedColor { return ctColor.toString().hashCode(); } + // Helper methods for {@link #equals(Object)} + private boolean sameIndexed(XSSFColor other) { + if (isIndexed() == other.isIndexed()) { + if (isIndexed()) { + return getIndexed() == other.getIndexed(); + } + return true; + } + return false; + } + private boolean sameARGB(XSSFColor other) { + if (isRGB() == other.isRGB()) { + if (isRGB()) { + return Arrays.equals(getARGB(), other.getARGB()); + } + return true; + } + return false; + } + private boolean sameTheme(XSSFColor other) { + if (isThemed() == other.isThemed()) { + if (isThemed()) { + return getTheme() == other.getTheme(); + } + return true; + } + return false; + } + private boolean sameTint(XSSFColor other) { + if (hasTint() == other.hasTint()) { + if (hasTint()) { + return getTint() == other.getTint(); + } + return true; + } + return false; + } + private boolean sameAuto(XSSFColor other) { + return isAuto() == other.isAuto(); + } + @Override public boolean equals(Object o){ if(!(o instanceof XSSFColor)) { return false; } - XSSFColor cf = (XSSFColor)o; - return ctColor.toString().equals(cf.getCTColor().toString()); + XSSFColor other = (XSSFColor)o; + + // Compare each field in ctColor. + // Cannot compare ctColor's XML string representation because equivalent + // colors may have different relation namespace URI's + return sameARGB(other) + && sameTheme(other) + && sameIndexed(other) + && sameTint(other) + && sameAuto(other); } } diff --git a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java index 501f6f507..98d7f6c1c 100644 --- a/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java +++ b/src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFSheet.java @@ -63,6 +63,7 @@ import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.Footer; import org.apache.poi.ss.usermodel.Header; import org.apache.poi.ss.usermodel.IgnoredErrorType; +import org.apache.poi.ss.usermodel.IndexedColors; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.AreaReference; @@ -3846,21 +3847,45 @@ public class XSSFSheet extends POIXMLDocumentPart implements Sheet { public XSSFSheetConditionalFormatting getSheetConditionalFormatting(){ return new XSSFSheetConditionalFormatting(this); } + + /** + * Get background color of the sheet tab. + * Returns null if no sheet tab color is set. + * + * @return the background color of the sheet tab + */ + public XSSFColor getTabColor() { + CTSheetPr pr = worksheet.getSheetPr(); + if(pr == null) pr = worksheet.addNewSheetPr(); + if (!pr.isSetTabColor()) { + return null; + } + return new XSSFColor(pr.getTabColor()); + } /** * Set background color of the sheet tab * - * @param colorIndex the indexed color to set, must be a constant from {@link IndexedColors} + * @param colorIndex the indexed color to set, must be a constant from {@link org.apache.poi.ss.usermodel.IndexedColors} + * @deprecated 3.15-beta2. Removed in 3.17. Use {@link #setTabColor(XSSFColor)}. */ - public void setTabColor(int colorIndex){ + public void setTabColor(int colorIndex) { + IndexedColors indexedColor = IndexedColors.fromInt(colorIndex); + XSSFColor color = new XSSFColor(indexedColor); + setTabColor(color); + } + + /** + * Set background color of the sheet tab + * + * @param color the color to set + */ + public void setTabColor(XSSFColor color) { CTSheetPr pr = worksheet.getSheetPr(); if(pr == null) pr = worksheet.addNewSheetPr(); - CTColor color = CTColor.Factory.newInstance(); - color.setIndexed(colorIndex); - pr.setTabColor(color); + pr.setTabColor(color.getCTColor()); } - @Override public CellRangeAddress getRepeatingRows() { return getRepeatingRowsOrColums(true); diff --git a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java index 7bcd67835..1e641c64e 100644 --- a/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java +++ b/src/ooxml/testcases/org/apache/poi/xssf/usermodel/TestXSSFSheet.java @@ -1911,7 +1911,7 @@ public final class TestXSSFSheet extends BaseTestSheet { try { XSSFSheet sh = wb.createSheet(); assertTrue(sh.getCTWorksheet().getSheetPr() == null || !sh.getCTWorksheet().getSheetPr().isSetTabColor()); - sh.setTabColor(IndexedColors.RED); + sh.setTabColor(new XSSFColor(IndexedColors.RED)); assertTrue(sh.getCTWorksheet().getSheetPr().isSetTabColor()); assertEquals(IndexedColors.RED.index, sh.getCTWorksheet().getSheetPr().getTabColor().getIndexed()); @@ -1919,4 +1919,40 @@ public final class TestXSSFSheet extends BaseTestSheet { wb.close(); } } + + @Test + public void getTabColor() throws IOException { + XSSFWorkbook wb = new XSSFWorkbook(); + try { + XSSFSheet sh = wb.createSheet(); + assertTrue(sh.getCTWorksheet().getSheetPr() == null || !sh.getCTWorksheet().getSheetPr().isSetTabColor()); + assertNull(sh.getTabColor()); + sh.setTabColor(new XSSFColor(IndexedColors.RED)); + XSSFColor expected = new XSSFColor(IndexedColors.RED); + assertEquals(expected, sh.getTabColor()); + } finally { + wb.close(); + } + } + + // Test using an existing workbook saved by Excel + @Test + public void tabColor() throws IOException { + XSSFWorkbook wb = openSampleWorkbook("SheetTabColors.xlsx"); + try { + // non-colored sheets do not have a color + assertNull(wb.getSheet("default").getTabColor()); + + // test indexed-colored sheet + XSSFColor expected = new XSSFColor(IndexedColors.RED); + assertEquals(expected, wb.getSheet("indexedRed").getTabColor()); + + // test regular-colored (non-indexed, ARGB) sheet + expected = new XSSFColor(); + expected.setARGBHex("FF7F2700"); + assertEquals(expected, wb.getSheet("customOrange").getTabColor()); + } finally { + wb.close(); + } + } } diff --git a/test-data/spreadsheet/SheetTabColors.xlsx b/test-data/spreadsheet/SheetTabColors.xlsx new file mode 100644 index 000000000..1894f69ea Binary files /dev/null and b/test-data/spreadsheet/SheetTabColors.xlsx differ