| <?xml version="1.0" encoding="UTF-8"?> |
| <!-- |
| ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== |
| --> |
| <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN" "../dtd/document-v11.dtd"> |
| |
| <document> |
| <header> |
| <title>Formula Evaluation</title> |
| <authors> |
| <person email="amoweb@yahoo.com" name="Amol Deshmukh" id="AD"/> |
| </authors> |
| </header> |
| <body> |
| <section><title>Introduction</title> |
| <p>The POI formula evaluation code enables you to calculate the result of |
| formulas in Excels sheets read-in, or created in POI. This document explains |
| how to use the API to evaluate your formulas. |
| </p> |
| <note>In versions of POI before 3.0.3, this code lived in the |
| scratchpad area of the POI SVN repository. If using an such an older |
| version of POI, ensure that you have the scratchpad jar or the |
| scratchpad build area in your classpath before experimenting with this |
| code. Users of all versions of POI may wish to make use of a recent |
| SVN checkout, as new functions are currently being added fairly frequently. |
| </note> |
| </section> |
| |
| <anchor id="Status"/> |
| <section><title>Status</title> |
| <p> The code currently provides implementations for all the arithmatic operators. |
| It also provides implementations for approx. 100 built in |
| functions in Excel. The framework however makes is easy to add |
| implementation of new functions. See the <link href="eval-devguide.html"> Formula |
| evaluation development guide</link> for details. </p> |
| <p> Both HSSFWorkbook and XSSFWorkbook are supported, so you can |
| evaluate formulas on both .xls and .xlsx files.</p> |
| <p> Note that user-defined functions are not supported, and is not likely to done |
| any time soon... at least, not till there is a VB implementation in Java! |
| </p> |
| </section> |
| <section><title>User API How-TO</title> |
| <p>The following code demonstrates how to use the FormulaEvaluator |
| in the context of other POI excel reading code. |
| </p> |
| <p>There are several ways in which you can use the FormulaEvalutator API.</p> |
| |
| <anchor id="Evaluate"/> |
| <section><title>Using FormulaEvaluator.<strong>evaluate</strong>(Cell cell)</title> |
| <p>This evaluates a given cell, and returns the new value, |
| without affecting the cell</p> |
| <source> |
| FileInputStream fis = new FileInputStream("c:/temp/test.xls"); |
| Workbook wb = new HSSFWorkbook(fis); |
| Sheet sheet = wb.getSheetAt(0); |
| FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb); |
| |
| // suppose your formula is in B3 |
| CellReference cellReference = new CellReference("B3"); |
| Row row = sheet.getRow(cellReference.getRow()); |
| Cell cell = row.getCell(cellReference.getCol()); |
| |
| evaluator.setCurrentRow(row); |
| FormulaEvaluator.CellValue cellValue = evaluator.evaluate(cell); |
| |
| switch (cellValue.getCellType()) { |
| case Cell.CELL_TYPE_BOOLEAN: |
| System.out.println(cellValue.getBooleanValue()); |
| break; |
| case Cell.CELL_TYPE_NUMERIC: |
| System.out.println(cellValue.getNumberValue()); |
| break; |
| case Cell.CELL_TYPE_STRING: |
| System.out.println(cellValue.getStringValue()); |
| break; |
| case Cell.CELL_TYPE_BLANK: |
| break; |
| case Cell.CELL_TYPE_ERROR: |
| break; |
| |
| // CELL_TYPE_FORMULA will never happen |
| case Cell.CELL_TYPE_FORMULA: |
| break; |
| } |
| </source> |
| <p>Thus using the retrieved value (of type |
| FormulaEvaluator.CellValue - a nested class) returned |
| by FormulaEvaluator is similar to using a Cell object |
| containing the value of the formula evaluation. CellValue is |
| a simple value object and does not maintain reference |
| to the original cell. |
| </p> |
| </section> |
| |
| <anchor id="EvaluateFormulaCell"/> |
| <section><title>Using FormulaEvaluator.<strong>evaluateFormulaCell</strong>(Cell cell)</title> |
| <p><strong>evaluateFormulaCell</strong>(Cell cell) |
| will check to see if the supplied cell is a formula cell. |
| If it isn't, then no changes will be made to it. If it is, |
| then the formula is evaluated. The value for the formula |
| is saved alongside it, to be displayed in excel. The |
| formula remains in the cell, just with a new value</p> |
| <p>The return of the function is the type of the |
| formula result, such as Cell.CELL_TYPE_BOOLEAN</p> |
| <source> |
| FileInputStream fis = new FileInputStream("/somepath/test.xls"); |
| Workbook wb = new HSSFWorkbook(fis); |
| Sheet sheet = wb.getSheetAt(0); |
| FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb); |
| |
| // suppose your formula is in B3 |
| CellReference cellReference = new CellReference("B3"); |
| Row row = sheet.getRow(cellReference.getRow()); |
| Cell cell = row.getCell(cellReference.getCol()); |
| evaluator.setCurrentRow(row); |
| |
| if (cell!=null) { |
| switch (<strong>evaluator.evaluateFormulaCell</strong>(cell)) { |
| case Cell.CELL_TYPE_BOOLEAN: |
| System.out.println(cell.getBooleanCellValue()); |
| break; |
| case Cell.CELL_TYPE_NUMERIC: |
| System.out.println(cell.getNumberCellValue()); |
| break; |
| case Cell.CELL_TYPE_STRING: |
| System.out.println(cell.getStringCellValue()); |
| break; |
| case Cell.CELL_TYPE_BLANK: |
| break; |
| case Cell.CELL_TYPE_ERROR: |
| System.out.println(cell.getErrorCellValue()); |
| break; |
| |
| // CELL_TYPE_FORMULA will never occur |
| case Cell.CELL_TYPE_FORMULA: |
| break; |
| } |
| } |
| </source> |
| </section> |
| |
| <anchor id="EvaluateInCell"/> |
| <section><title>Using FormulaEvaluator.<strong>evaluateInCell</strong>(Cell cell)</title> |
| <p><strong>evaluateInCell</strong>(Cell cell) will check to |
| see if the supplied cell is a formula cell. If it isn't, |
| then no changes will be made to it. If it is, then the |
| formula is evaluated, and the new value saved into the cell, |
| in place of the old formula.</p> |
| <source> |
| FileInputStream fis = new FileInputStream("/somepath/test.xls"); |
| Workbook wb = new HSSFWorkbook(fis); |
| Sheet sheet = wb.getSheetAt(0); |
| FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb); |
| |
| // suppose your formula is in B3 |
| CellReference cellReference = new CellReference("B3"); |
| Row row = sheet.getRow(cellReference.getRow()); |
| Cell cell = row.getCell(cellReference.getCol()); |
| evaluator.setCurrentRow(row); |
| |
| if (cell!=null) { |
| switch (<strong>evaluator.evaluateInCell</strong>(cell).getCellType()) { |
| case Cell.CELL_TYPE_BOOLEAN: |
| System.out.println(cell.getBooleanCellValue()); |
| break; |
| case Cell.CELL_TYPE_NUMERIC: |
| System.out.println(cell.getNumberCellValue()); |
| break; |
| case Cell.CELL_TYPE_STRING: |
| System.out.println(cell.getStringCellValue()); |
| break; |
| case Cell.CELL_TYPE_BLANK: |
| break; |
| case Cell.CELL_TYPE_ERROR: |
| System.out.println(cell.getErrorCellValue()); |
| break; |
| |
| // CELL_TYPE_FORMULA will never occur |
| case Cell.CELL_TYPE_FORMULA: |
| break; |
| } |
| } |
| </source> |
| </section> |
| |
| <anchor id="EvaluateAll"/> |
| <section><title>Re-calculating all formulas in a Workbook</title> |
| <source> |
| FileInputStream fis = new FileInputStream("/somepath/test.xls"); |
| Workbook wb = new HSSFWorkbook(fis); |
| for(int sheetNum = 0; sheetNum < wb.getNumberOfSheets(); sheetNum++) { |
| Sheet sheet = wb.getSheetAt(sheetNum); |
| FormulaEvaluator evaluator = new FormulaEvaluator(sheet, wb); |
| |
| for(Iterator rit = sheet.rowIterator(); rit.hasNext();) { |
| Row r = (Row)rit.next(); |
| evaluator.setCurrentRow(r); |
| |
| for(Iterator cit = r.cellIterator(); cit.hasNext();) { |
| Cell c = (Cell)cit.next(); |
| if(c.getCellType() == Cell.CELL_TYPE_FORMULA) { |
| evaluator.evaluateFormulaCell(c); |
| } |
| } |
| } |
| } |
| wb.write(new FileOutputStream("/somepath/changed.xls")); |
| </source> |
| </section> |
| </section> |
| |
| <anchor id="Performance"/> |
| <section><title>Performance Notes</title> |
| <ul> |
| <li>Generally you should have to create only one FormulaEvaluator |
| instance per sheet, but there really is no overhead in creating |
| multiple FormulaEvaluators per sheet other than that of the |
| FormulaEvaluator object creation. |
| </li> |
| <li>Also note that FormulaEvaluator maintains a reference to |
| the sheet and workbook, so ensure that the evaluator instance |
| is available for garbage collection when you are done with it |
| (in other words don't maintain long lived reference to |
| FormulaEvaluator if you don't really need to - unless |
| all references to the sheet and workbook are removed, these |
| don't get garbage collected and continue to occupy potentially |
| large amounts of memory). |
| </li> |
| <li>CellValue instances however do not maintain reference to the |
| Cell or the sheet or workbook, so these can be long-lived |
| objects without any adverse effect on performance. |
| </li> |
| </ul> |
| </section> |
| </body> |
| </document> |