Read a Color from a Blank Cell in Excel to Java using Apache POI

2.4k views Asked by At

BACKGROUND: I am trying to read an Excel file into a Java Program. My Excel file is meant to represent a grid or raster map, so I made the height and width of each cell one inch. The idea is that I can "draw" a map or image by shading in each cell with a color. Then, I can read the Excel file to a Java program I created myself with a "Pixel" object and create a more literal image. I am an undergraduate in Computer Science, and I've only had four computer science classes at this point. I understand OOP and can program in Java. This is not for a class; this is a side project. I am using XSSF (Microsoft 2007 and after).

RESEARCH: I have discovered that the solution to this is to use Apache POI. I have already downloaded the required Apache POI jar files and configured the BuildPath in Eclipse to read them. I have discovered that using an Iterator hasNext() method will skip over blank cells, so the solution is to use the more direct getCell() method. I have discovered that there are two getCell() methods -- one with only the index as an input, and one that uses both the index input and MissingCellPolicy. However, when I tried using the MissingCellPolicy method by placing a RETURN_NULL_AND_BLANK as the input, it made the cell blank, but made the color null in the process. The MissingCellPolicy CREATE_NULL_AS_BLANK has the same problem.

AN INEFFICIENT SOLUTION: When I put text in a cell, it correctly reads the color. Even the iterator method can correctly read cells that have text in them. This is because once I put text in them, the cell is initialized. However, the grid I am trying to make is too large for me to put text in every cell. There is probably a way to set every cell on the sheet to have the same text, but I can't do this either because I already have many cells with specific text throughout my grid, and they can't be erased. That would also probably make all of the cells the same color, which I also can't do at this point. Besides, I would prefer it if I could have cells with no text.

TL;DR: I need to read the color of a cell in Excel into Java by using Apache POI without writing text into the cell. From my understanding, the method getCell() with a MissingCellPolicy does not work because the Policy creates a new blank cell, overwriting the existing color. I have seen a lot of questions regarding reading blank cells in Apache POI, but I did not see one about accessing color.

MAIN CODE:

    try {
        FileInputStream file = new FileInputStream(new File("My FilePath"));
        XSSFWorkbook workbook = new XSSFWorkbook(file);
        XSSFSheet sheet = workbook.getSheetAt(0);
        for(int i=0; i<5040; i++) {
            Row row = sheet.getRow(i);
        for(int j=0; j<10080; j++) {
            Cell cell = row.getCell(j, Row.MissingCellPolicy.RETURN_NULL_AND_BLANK);
            ExtendedColor color = (ExtendedColor) cell.getCellStyle().getFillForegroundColorColor(); 
            //NOTE: getFillBackgroundColorColor did not work! It only returns the color black.
            byte[] bytes = color.getRGB();
            RGBColor rgb = new RGBColor(bytes);
            String text = cell.getStringCellValue();
            Coordinate coordinate = new Coordinate(j, i);
            Tile tile = new Tile(rgb, text);
            map[j][i] = tile;
            // Coordinate and Tile are other objects I made myself. 
            // The map is a two-dimensional array of Tiles, declared previously. 
            // I left this code here because it works.
        }
    }
    workbook.close();
    file.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

RGBColor Constructor Code:

    public RGBColor(byte[] bytes) {
        if(bytes != null) {
        this.red = (int) bytes[0];
        this.green = (int) bytes[1];
        this.blue = (int) bytes[2];
        if(red<0) {red = red+256;}
        if(green<0) {green = green+256;}
        if(blue<0) {blue = blue+256;}
    }

RESULT: The above code correctly reads the color of a cell if it has text in it and creates an RGBColor object from the color. The above code can also read text from a cell. However, as soon as it reaches a cell without text, it causes a NullPointerException at the ExtendedColor line (So the cell is null). When the MissingCellPolicy CREATE_NULL_AS_BLANK is used instead, it causes a NullPointerException at the byte[] line (So the color is null). Any help is appreciated, even if it isn't exactly what I ask for, because I am new to Apache POI!

2

There are 2 answers

0
Axel Richter On BEST ANSWER

A single colored cell can never be null. It must exists. So we can loop over existing cells only. CellStyle is not-null per definition. But CellStyle.getFillForegroundColorColor can return null if there is not a color. So we need to check.

Supposing the following sheet:

enter image description here

Code:

import org.apache.poi.ss.usermodel.*;
import java.io.*;

import java.util.Arrays;

class ReadColorsFromExcel {

 public static void main(String[] args) throws Exception{

  InputStream inp = new FileInputStream("MyFile.xlsx");
  Workbook workbook = WorkbookFactory.create(inp);
  Sheet sheet = workbook.getSheetAt(0);
  for (Row row : sheet) {
   for (Cell cell : row) { // cell will always be not-null only existing cells are in loop
    CellStyle cellStyle = cell.getCellStyle(); // cellStyle is always not-null
    ExtendedColor extendedColor = (ExtendedColor)cellStyle.getFillForegroundColorColor(); // extendedColor may be null
    String color = "none";
    if (extendedColor != null) {
     byte[] bytes = extendedColor.getRGB();
     color = Arrays.toString(bytes);
    }
    System.out.println("Cell " + cell.getAddress() + " of type " + cell.getCellType() + " has color " + color);
   }
  }
 }
}

Will print:

Cell A1 of type 1 has color none
Cell B1 of type 0 has color [-1, -1, 0]
Cell C1 of type 2 has color none
Cell B3 of type 0 has color none
Cell C3 of type 3 has color [-110, -48, 80]
Cell D4 of type 1 has color none
Cell B6 of type 3 has color [0, 112, -64]
Cell D7 of type 3 has color [-1, 0, 0]
Cell A9 of type 3 has color [-1, -64, 0]
Cell F12 of type 3 has color [0, -80, 80]

But in Excel whole columns and whole rows also may have styles. If so, not the cells will have the color but the columns and/or rows. So if the need is also to get not existing cells, such which current are not stored and so be null, and also take into account that there may be column styles (whole columns) and row styles (whole rows), then consider the following:

Sheet:

enter image description here

All cells (all columns) have white background, row 8 has light blue background, column E has light green background. Some cells have their own cell background.

Code:

import org.apache.poi.ss.usermodel.*;
import java.io.*;

import java.util.Arrays;

class ReadColorsFromExcel {

 public static void main(String[] args) throws Exception{

  InputStream inp = new FileInputStream("MyFile.xlsx");
  Workbook workbook = WorkbookFactory.create(inp);
  Sheet sheet = workbook.getSheetAt(0);

  int lastRow = 12;
  int lastCol = 6;

  for (int rowNum = 0; rowNum < lastRow; rowNum++) {
   Row row = sheet.getRow(rowNum);
   if (row == null) {
    row = sheet.createRow(rowNum);
   }
   CellStyle rowStyle = row.getRowStyle(); // if the whole row has a style
   for (int colNum = 0; colNum < lastCol; colNum++) {
    CellStyle colStyle = sheet.getColumnStyle(colNum); // if the whole column has a style
    Cell cell = row.getCell(colNum, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
    CellStyle cellStyle = cell.getCellStyle(); // cellStyle is always not-null
    String color = "none";
    ExtendedColor extendedColor = (ExtendedColor)cellStyle.getFillForegroundColorColor(); // first we try cellStyle
    if (extendedColor == null && rowStyle != null) extendedColor = (ExtendedColor)rowStyle.getFillForegroundColorColor(); // now we try rowStyle
    if (extendedColor == null && colStyle != null) extendedColor = (ExtendedColor)colStyle.getFillForegroundColorColor(); // at last we try colStyle
    if (extendedColor != null) {
     byte[] bytes = extendedColor.getRGB();
     color = Arrays.toString(bytes);
    }
    System.out.println("Cell " + cell.getAddress() + " of type " + cell.getCellType() + " has color " + color);
   }
  }
 }
}

Now all possible styles should be taken into account.

1
Richard Yhip On

Ultimately what's happening is you're trying to deal with null or blank cells and not handling them when you encounter them. What you want to do is something like this after calling getCell():

if (cell == null || cell.getCellType() == Cell.CELL_TYPE_BLANK) {
    // assign whatever color you want for a blank cell to rgb
} else {
    // do your logic to get the ExtendedColor and turn it into an RGBColor
}