9

I am trying to put simple VLookup formula in my ".xlsx" file using Java and Apache POI.
This formula is having the external reference and that is NOT working for me.

So to give you more details I am using poi and poi-ooxml version 3.13 and excel 2007.
I am placing the formula into cell this way (where cell is a Cell):

cell.setCellType(Cell.CELL_TYPE_FORMULA);
cell.setCellFormula("StringContainingFormula");

And then evaluate the formula, I have tried three different ways but with NO luck. (wb is XSSFWorkbook).

1

FormulaEvaluator mainWorkbookEvaluator = wb.getCreationHelper().createFormulaEvaluator();
Map<String,FormulaEvaluator> workbooks = new HashMap<String, FormulaEvaluator>();
workbooks.put("SpreadsheetName.xlsx", mainWorkbookEvaluator);
mainWorkbookEvaluator.setupReferencedWorkbooks(workbooks);
mainWorkbookEvaluator.evaluateAll();

2

XSSFEvaluationWorkbook.create(wb);
Workbook nwb = wb;
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
for (Sheet sheet : nwb) {
    for (Row r : sheet) {
        for (Cell c : r) {
            if (c.getCellType() == Cell.CELL_TYPE_FORMULA) {
                try {
                    //evaluator.evaluateFormulaCell(c);
                    evaluator.evaluate(c);
                } catch (Exception e) {
                    System.out.println("Error occured in 'EvaluateFormulas' : " + e);
                }
            }
        }
    }
}

3

XSSFFormulaEvaluator.evaluateAllFormulaCells(wb);  

The problem is, it writes to Excel file and then while evaluating it throws error:

java.lang.IllegalArgumentException: Invalid sheetIndex: -1

Now if I open the Excel file, it gives me warning:

Automatic update of links has been disabled

If I enable the content, formula shows the result properly and if I do not do anything than formula resulting in #N/A.
Now if I select the cell with formula in it and click formula bar and hit enter than formula shows the result.

Update:

So, I disabled the warning message by going into Excel options and it started giving me the formula inside the cell.
BUT, when I tried to get the result value from it using

if (cell.getCachedFormulaResultType == Cell.CELL_TYPE_STRING) {
    System.out.println("Last evaluated as \"" + cell.getRichStringCellValue() + "\"");
}

It never gave me the getCachedFormulaResultType as CELL_TYPE_STRING, it always return CELL_TYPE_NUMERIC. It supposed to return string value. I am expacting URL and someother value (words seperated by "|" - "cat|dog|bird").

I appreciate any help/suggestion.

ManishChristian
  • 3,759
  • 3
  • 22
  • 50
  • Probably best to check the [Formula Evaluation](https://poi.apache.org/spreadsheet/eval.html) documentation carefully. Perhaps something like `wb.setForceFormulaRecalculation(true);` will help you? – K.Nicholas Jan 29 '16 at 23:37
  • Also, the doc's say to `createFormulaEvaluator` for the worksheets you reference. E.g., `workbooks.put("input.xls", WorkbookFactory.create("c:\temp\input22.xls").getCreationHelper().createFormulaEvaluator());` – K.Nicholas Jan 29 '16 at 23:44
  • @Nicholas, thank you so much for your tips, but I couldn't find anything similar to `wb.setForceFormulaRecalculation(true);`and I'ev already tried the `createFormulaEvaluator` reference, but no luck. – ManishChristian Feb 01 '16 at 13:25
  • Well, what I was getting at is that the doc's say that you should have a `createFormulatEvaluator` for each of the spreadsheets, both the main spreadsheet and the spreadsheet that VLookup is referencing. – K.Nicholas Feb 01 '16 at 16:41
  • Well, I tried with both the spreadsheets (added both of them), but get an error `Attempted to register same workbook under names "Spreadsheet one (reference from)" and "Spreadsheet two ((reference to))"`. – ManishChristian Feb 01 '16 at 17:13
  • Well, I can try a simple example over here if you want. Did you map the external reference with `setupReferencedWorkbooks(java.util.Map workbooks)`? – K.Nicholas Feb 01 '16 at 17:18
  • @Nicholas, that would awesome. And FYI, if I use XSSFWorkbook, than it doesn't give me the option of `setupReferencedWorkbooks`. I might have to convert it to Workbook and then try. – ManishChristian Feb 01 '16 at 18:33

3 Answers3

1

Well, it seems to be working. You have to treat excel gently; it's much happier if you open the external workbook first, then the primary workbook. If you don't, it gives a message about unsafe links, but other than that, everything seems okay. The evaluator only works on single cells, so you will have to loop if you want to recalculate the entire spreadsheet. Also, the external reference has to have been already linked by excel, POI has not implemented that feature yet. Here is what I put into github:

    // Open workbooks
    Path pathBook1 = Paths.get("c:/users/karl/scsb/Vlookup/Book1.xlsx");
    InputStream is = Files.newInputStream(pathBook1);
    XSSFWorkbook book1 = new XSSFWorkbook(is);
    // Add Linked Cell
    // The workbook must already have been linked to Book2.xlsx by Excel
    // The linkExternalWorkbook has not yet been implemented: SEE BUG #57184
    Cell cell = book1.getSheetAt(0).createRow(2).createCell(0);
    cell.setCellFormula("A2+[Book2.xlsx]Sheet1!A1");
    // Create evaluator after the new cell has been added.
    FormulaEvaluator mainEvaluator = book1.getCreationHelper().createFormulaEvaluator();
    XSSFWorkbook book2 = new XSSFWorkbook("c:/users/karl/scsb/Vlookup/Book2.xlsx");
    Map<String, FormulaEvaluator> workbooks = new HashMap<String, FormulaEvaluator>();
    workbooks.put("Book1.xlsx", mainEvaluator);
    workbooks.put("Book2.xlsx", book2.getCreationHelper().createFormulaEvaluator());
    mainEvaluator.setupReferencedWorkbooks(workbooks);
//  mainEvaluator.evaluateAll();                            // doesn't work.
//  XSSFFormulaEvaluator.evaluateAllFormulaCells(book1);    // doesn't work.
    mainEvaluator.evaluateFormulaCell(cell);
    System.out.println(cell.getNumericCellValue());
    book2.close();
    // Close and write workbook 1
    is.close();
    OutputStream os = Files.newOutputStream(pathBook1);
    book1.write(os);
    os.close();
    book1.close();
K.Nicholas
  • 10,956
  • 4
  • 46
  • 66
  • It's not working for me. If I use `setCellFormula` (without the complete path (just like you did)) before opening "Referenced" workbook , it gives me error `java.lang.RuntimeException: Book not linked for filename "ReferencedFileName.xlsx"`. And if I use complete path, it again gives me `Invalid sheetIndex: -1` error. – ManishChristian Feb 02 '16 at 14:58
  • Like I mentioned, the external spreadsheet must be linked by excel before running this code. There is a method call to link spreadsheets in POI (`linkExternalWorkbook`), but it is not implemented. There is nothing I can do about that. – K.Nicholas Feb 02 '16 at 15:12
  • First of all, thank you so much @Nicholas for your help. So, in shortest way, can we say that this is not feasible at the moment? Because if we cannot link the workbooks, than there is no point referencing external workbook. – ManishChristian Feb 02 '16 at 15:14
  • Yea, we can say it is not possible to do entirely in Java at the moment. – K.Nicholas Feb 02 '16 at 15:15
0

I has the same problem recently. The problem was that with current POI, one can't write document, add the formula to specific field and perform the formula evaluation on a, at this moment, still not existing document. The trick I used to solve it was:

  1. Write all data and formulas into a workbook
  2. add workbook.setForceFormulaRecalculation(true); before closing this document
  3. Write and close the document
  4. Open the same document again and only do FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); evaluator.evaluateAll();
  5. Write and close again
Vanja Lee
  • 263
  • 4
  • 17
0

So, as I don't have any other option, I had to copy the referenced data to main workbook (I know it's not good idea, in case if the data is huge) to make things working.
So what I did is:

  1. Copy the referenced data from external workbook.
  2. Create new worksheet on main workbook(in which you need to write your formula) and paste the referenced data.
  3. Now write the formula and this time instead of external workbook use newly created worksheet(with pasted data).
    Note*: You need to write all the data (including formulas) first before heading to evaluate the formulas.
  4. Then open workbook for reading if you have closed already and evaluate all the formulas like this (wb is XSSFWorkbook):

XSSFFormulaEvaluator.evaluateAllFormulaCells(wb);

ManishChristian
  • 3,759
  • 3
  • 22
  • 50