You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XSSFFormulaEvaluator.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.xssf.usermodel;
  16. import java.util.Iterator;
  17. import org.apache.poi.hssf.record.formula.eval.BoolEval;
  18. import org.apache.poi.hssf.record.formula.eval.ErrorEval;
  19. import org.apache.poi.hssf.record.formula.eval.NumberEval;
  20. import org.apache.poi.hssf.record.formula.eval.StringEval;
  21. import org.apache.poi.hssf.record.formula.eval.ValueEval;
  22. import org.apache.poi.hssf.record.formula.udf.UDFFinder;
  23. import org.apache.poi.ss.formula.IStabilityClassifier;
  24. import org.apache.poi.ss.formula.WorkbookEvaluator;
  25. import org.apache.poi.ss.usermodel.Cell;
  26. import org.apache.poi.ss.usermodel.CellValue;
  27. import org.apache.poi.ss.usermodel.FormulaEvaluator;
  28. import org.apache.poi.ss.usermodel.Row;
  29. import org.apache.poi.ss.usermodel.Sheet;
  30. /**
  31. * Evaluates formula cells.<p/>
  32. *
  33. * For performance reasons, this class keeps a cache of all previously calculated intermediate
  34. * cell values. Be sure to call {@link #clearAllCachedResultValues()} if any workbook cells are changed between
  35. * calls to evaluate~ methods on this class.
  36. *
  37. * @author Amol S. Deshmukh &lt; amolweb at ya hoo dot com &gt;
  38. * @author Josh Micich
  39. */
  40. public class XSSFFormulaEvaluator implements FormulaEvaluator {
  41. private WorkbookEvaluator _bookEvaluator;
  42. public XSSFFormulaEvaluator(XSSFWorkbook workbook) {
  43. this(workbook, null, null);
  44. }
  45. /**
  46. * @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
  47. * for the (conservative) assumption that any cell may have its definition changed after
  48. * evaluation begins.
  49. * @deprecated (Sep 2009) (reduce overloading) use {@link #create(XSSFWorkbook, org.apache.poi.ss.formula.IStabilityClassifier, org.apache.poi.hssf.record.formula.udf.UDFFinder)}
  50. */
  51. @Deprecated
  52. public XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier) {
  53. _bookEvaluator = new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier, null);
  54. }
  55. private XSSFFormulaEvaluator(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
  56. _bookEvaluator = new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook), stabilityClassifier, udfFinder);
  57. }
  58. /**
  59. * @param stabilityClassifier used to optimise caching performance. Pass <code>null</code>
  60. * for the (conservative) assumption that any cell may have its definition changed after
  61. * evaluation begins.
  62. * @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
  63. */
  64. public static XSSFFormulaEvaluator create(XSSFWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
  65. return new XSSFFormulaEvaluator(workbook, stabilityClassifier, udfFinder);
  66. }
  67. /**
  68. * Should be called whenever there are major changes (e.g. moving sheets) to input cells
  69. * in the evaluated workbook.
  70. * Failure to call this method after changing cell values will cause incorrect behaviour
  71. * of the evaluate~ methods of this class
  72. */
  73. public void clearAllCachedResultValues() {
  74. _bookEvaluator.clearAllCachedResultValues();
  75. }
  76. public void notifySetFormula(Cell cell) {
  77. _bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell));
  78. }
  79. public void notifyDeleteCell(Cell cell) {
  80. _bookEvaluator.notifyDeleteCell(new XSSFEvaluationCell((XSSFCell)cell));
  81. }
  82. public void notifyUpdateCell(Cell cell) {
  83. _bookEvaluator.notifyUpdateCell(new XSSFEvaluationCell((XSSFCell)cell));
  84. }
  85. /**
  86. * If cell contains a formula, the formula is evaluated and returned,
  87. * else the CellValue simply copies the appropriate cell value from
  88. * the cell and also its cell type. This method should be preferred over
  89. * evaluateInCell() when the call should not modify the contents of the
  90. * original cell.
  91. * @param cell
  92. */
  93. public CellValue evaluate(Cell cell) {
  94. if (cell == null) {
  95. return null;
  96. }
  97. switch (cell.getCellType()) {
  98. case XSSFCell.CELL_TYPE_BOOLEAN:
  99. return CellValue.valueOf(cell.getBooleanCellValue());
  100. case XSSFCell.CELL_TYPE_ERROR:
  101. return CellValue.getError(cell.getErrorCellValue());
  102. case XSSFCell.CELL_TYPE_FORMULA:
  103. return evaluateFormulaCellValue(cell);
  104. case XSSFCell.CELL_TYPE_NUMERIC:
  105. return new CellValue(cell.getNumericCellValue());
  106. case XSSFCell.CELL_TYPE_STRING:
  107. return new CellValue(cell.getRichStringCellValue().getString());
  108. case XSSFCell.CELL_TYPE_BLANK:
  109. return null;
  110. }
  111. throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
  112. }
  113. /**
  114. * If cell contains formula, it evaluates the formula,
  115. * and saves the result of the formula. The cell
  116. * remains as a formula cell.
  117. * Else if cell does not contain formula, this method leaves
  118. * the cell unchanged.
  119. * Note that the type of the formula result is returned,
  120. * so you know what kind of value is also stored with
  121. * the formula.
  122. * <pre>
  123. * int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
  124. * </pre>
  125. * Be aware that your cell will hold both the formula,
  126. * and the result. If you want the cell replaced with
  127. * the result of the formula, use {@link #evaluate(org.apache.poi.ss.usermodel.Cell)} }
  128. * @param cell The cell to evaluate
  129. * @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however)
  130. */
  131. public int evaluateFormulaCell(Cell cell) {
  132. if (cell == null || cell.getCellType() != XSSFCell.CELL_TYPE_FORMULA) {
  133. return -1;
  134. }
  135. CellValue cv = evaluateFormulaCellValue(cell);
  136. // cell remains a formula cell, but the cached value is changed
  137. setCellValue(cell, cv);
  138. return cv.getCellType();
  139. }
  140. /**
  141. * If cell contains formula, it evaluates the formula, and
  142. * puts the formula result back into the cell, in place
  143. * of the old formula.
  144. * Else if cell does not contain formula, this method leaves
  145. * the cell unchanged.
  146. * Note that the same instance of HSSFCell is returned to
  147. * allow chained calls like:
  148. * <pre>
  149. * int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
  150. * </pre>
  151. * Be aware that your cell value will be changed to hold the
  152. * result of the formula. If you simply want the formula
  153. * value computed for you, use {@link #evaluateFormulaCell(org.apache.poi.ss.usermodel.Cell)} }
  154. * @param cell
  155. */
  156. public XSSFCell evaluateInCell(Cell cell) {
  157. if (cell == null) {
  158. return null;
  159. }
  160. XSSFCell result = (XSSFCell) cell;
  161. if (cell.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {
  162. CellValue cv = evaluateFormulaCellValue(cell);
  163. setCellType(cell, cv); // cell will no longer be a formula cell
  164. setCellValue(cell, cv);
  165. }
  166. return result;
  167. }
  168. private static void setCellType(Cell cell, CellValue cv) {
  169. int cellType = cv.getCellType();
  170. switch (cellType) {
  171. case XSSFCell.CELL_TYPE_BOOLEAN:
  172. case XSSFCell.CELL_TYPE_ERROR:
  173. case XSSFCell.CELL_TYPE_NUMERIC:
  174. case XSSFCell.CELL_TYPE_STRING:
  175. cell.setCellType(cellType);
  176. return;
  177. case XSSFCell.CELL_TYPE_BLANK:
  178. // never happens - blanks eventually get translated to zero
  179. case XSSFCell.CELL_TYPE_FORMULA:
  180. // this will never happen, we have already evaluated the formula
  181. }
  182. throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
  183. }
  184. private static void setCellValue(Cell cell, CellValue cv) {
  185. int cellType = cv.getCellType();
  186. switch (cellType) {
  187. case XSSFCell.CELL_TYPE_BOOLEAN:
  188. cell.setCellValue(cv.getBooleanValue());
  189. break;
  190. case XSSFCell.CELL_TYPE_ERROR:
  191. cell.setCellErrorValue(cv.getErrorValue());
  192. break;
  193. case XSSFCell.CELL_TYPE_NUMERIC:
  194. cell.setCellValue(cv.getNumberValue());
  195. break;
  196. case XSSFCell.CELL_TYPE_STRING:
  197. cell.setCellValue(new XSSFRichTextString(cv.getStringValue()));
  198. break;
  199. case XSSFCell.CELL_TYPE_BLANK:
  200. // never happens - blanks eventually get translated to zero
  201. case XSSFCell.CELL_TYPE_FORMULA:
  202. // this will never happen, we have already evaluated the formula
  203. default:
  204. throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
  205. }
  206. }
  207. /**
  208. * Loops over all cells in all sheets of the supplied
  209. * workbook.
  210. * For cells that contain formulas, their formulas are
  211. * evaluated, and the results are saved. These cells
  212. * remain as formula cells.
  213. * For cells that do not contain formulas, no changes
  214. * are made.
  215. * This is a helpful wrapper around looping over all
  216. * cells, and calling evaluateFormulaCell on each one.
  217. */
  218. public static void evaluateAllFormulaCells(XSSFWorkbook wb) {
  219. XSSFFormulaEvaluator evaluator = new XSSFFormulaEvaluator(wb);
  220. for(int i=0; i<wb.getNumberOfSheets(); i++) {
  221. Sheet sheet = wb.getSheetAt(i);
  222. for (Iterator<Row> rit = sheet.rowIterator(); rit.hasNext();) {
  223. Row r = rit.next();
  224. for (Iterator cit = r.cellIterator(); cit.hasNext();) {
  225. XSSFCell c = (XSSFCell) cit.next();
  226. if (c.getCellType() == XSSFCell.CELL_TYPE_FORMULA)
  227. evaluator.evaluateFormulaCell(c);
  228. }
  229. }
  230. }
  231. }
  232. /**
  233. * Returns a CellValue wrapper around the supplied ValueEval instance.
  234. */
  235. private CellValue evaluateFormulaCellValue(Cell cell) {
  236. ValueEval eval = _bookEvaluator.evaluate(new XSSFEvaluationCell((XSSFCell) cell));
  237. if (eval instanceof NumberEval) {
  238. NumberEval ne = (NumberEval) eval;
  239. return new CellValue(ne.getNumberValue());
  240. }
  241. if (eval instanceof BoolEval) {
  242. BoolEval be = (BoolEval) eval;
  243. return CellValue.valueOf(be.getBooleanValue());
  244. }
  245. if (eval instanceof StringEval) {
  246. StringEval ne = (StringEval) eval;
  247. return new CellValue(ne.getStringValue());
  248. }
  249. if (eval instanceof ErrorEval) {
  250. return CellValue.getError(((ErrorEval)eval).getErrorCode());
  251. }
  252. throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
  253. }
  254. }