Home » excel » java – How to Get Excel Data Validations Drop Down values for Referenced Cell Ranges

java – How to Get Excel Data Validations Drop Down values for Referenced Cell Ranges

Posted by: admin April 23, 2020 Leave a comment

Questions:

I have stumbled upon a problem when reading in an Excel document, specifically acquiring drop down values (Data Validation) from a Cells. I am able to get the values defined explicitly.enter image description here

I am able to get the values (720×486, etc) with the following by seeing if cell is within CellRangeAddress.:

    Map<CellRangeAddress, String[]> dropDownValues = new HashMap<>();
    List<? extends DataValidation> dataValidations = sheet.getDataValidations();

    for(DataValidation dataValidation : dataValidations)
    {
        for(CellRangeAddress cellRangeAddress : dataValidation.getRegions().getCellRangeAddresses())
        {
            String[] explicitListValues = dataValidation.getValidationConstraint().getExplicitListValues();
            if(explicitListValues == null)
            {
                continue;
            }
            dropDownValues.put(cellRangeAddress, explicitListValues);
        }
    }

The code above works only for explicit values. The problem I see is when a range is defined in the source of the Data Validation for a cell:

enter image description here

sheet.getDataValidations();

Does not return anything in regards to the range or any info on the Data Validations. Has anyone been able to get a hold of the Source and evaluate the formula to attain values?

How to&Answers:

I was able to retrieve the data validations defined by a formula for Excel Sheets newer than 2003.

I had to parse the XSSFSheet for the specific info and then reconstruct and evaluate formula.

Here is what I did to attain all DataValidation values:

Map<CellRangeAddress, String[]> dropDownValues = new HashMap<>();

List<ExtendedDataValidations> extendedDataValidationsList = getExtendedDataValidations(sheet);
for (ExtendedDataValidations extendedDataValidations : extendedDataValidationsList)
{
    AreaReference formulaReference = new AreaReference(extendedDataValidations.formula);
    CellReference[] allReferencedCells = formulaReference.getAllReferencedCells();
    FormulaEvaluator formulaEvaluator = wb.getCreationHelper().createFormulaEvaluator();
    String[] values = new String[allReferencedCells.length];
    for (int j = 0; j < allReferencedCells.length; j++)
    {
        CellReference cellReference = allReferencedCells[j];
        Sheet valueSheet = wb.getSheet(cellReference.getSheetName());
        Cell cell = valueSheet.getRow(cellReference.getRow()).getCell(cellReference.getCol());
        CellValue evaluate = formulaEvaluator.evaluate(cell);
        values[j] = StringUtils.trimToEmpty(StringUtils.removeStart(StringUtils.removeEnd(evaluate.formatAsString(), "\""), "\""));
    }

    String stRef = extendedDataValidations.sqref;
    String[] regions = stRef.split(" ");
    for (String region : regions)
    {
        String[] parts = region.split(":");
        CellReference begin = new CellReference(parts[0]);
        CellReference end = parts.length > 1 ? new CellReference(parts[1]) : begin;
        CellRangeAddress cellRangeAddress = new CellRangeAddress(begin.getRow(), end.getRow(), begin.getCol(), end.getCol());
        dropDownValues.put(cellRangeAddress, values);
    }
}

In addition I defined a Struc for the formula and cell reference.

private static class ExtendedDataValidations
{
    public String formula;
    public String sqref;
}

getExtendedDataValidations grabbed the CTExtensionList where the data validation forumla appeared in the sheet:

public static List<ExtendedDataValidations> getExtendedDataValidations(Sheet sheet)
{
    List<ExtendedDataValidations> extendedDataValidationsList = new ArrayList<>();

    if (sheet instanceof XSSFSheet)
    {
        CTExtensionList extLst = ((XSSFSheet) sheet).getCTWorksheet().getExtLst();
        if (extLst == null)
        {
            return extendedDataValidationsList;
        }

        CTExtension[] extArray = extLst.getExtArray();
        List<Node> dataValidationNodes = new ArrayList<>();
        for (CTExtension anExtArray : extArray)
        {
            searchForDataValidation(anExtArray.getDomNode(), dataValidationNodes);
        }

        for (Node dataValidationNode : dataValidationNodes)
        {
            ExtendedDataValidations dataValidations = new ExtendedDataValidations();
            getDataValidationInfo(dataValidationNode, dataValidations);
            extendedDataValidationsList.add(dataValidations);
        }

    }

    return extendedDataValidationsList;
}

searchForDataValidation had to traverse the DOM nodes of the sheet looking for specific info on DataValidation. If found Save it in List:

private static void searchForDataValidation(Node node, List<Node> nodesInQuestion)
{
    if (StringUtils.equalsIgnoreCase("x14:dataValidation", node.getNodeName()))
    {
        nodesInQuestion.add(node);
        return;
    }

    for (int i = 0; i < node.getChildNodes().getLength(); i++)
    {
        searchForDataValidation(node.getChildNodes().item(i), nodesInQuestion);
    }
}

getDataValidationInfo was in charge of getting the formula and Cell Reference.

private static void getDataValidationInfo(Node node, ExtendedDataValidations dataValidations)
{
    if (StringUtils.equalsIgnoreCase("#text", node.getNodeName()))
    {
        if (StringUtils.equalsIgnoreCase("xm:sqref", node.getParentNode().getNodeName()))
        {
            dataValidations.sqref = node.getNodeValue();
        }
        else if (StringUtils.equalsIgnoreCase("xm:f", node.getParentNode().getNodeName()))
        {
            dataValidations.formula = node.getNodeValue();
        }
        return;
    }

    for (int i = 0; i < node.getChildNodes().getLength(); i++)
    {
        getDataValidationInfo(node.getChildNodes().item(i), dataValidations);
    }
}

Might appear to be complicated, but it does the trick. Hope it helps!