2008-09-19 03:32:34 -04:00
|
|
|
/* ====================================================================
|
|
|
|
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.
|
|
|
|
==================================================================== */
|
|
|
|
|
|
|
|
package org.apache.poi.ss.formula;
|
|
|
|
|
2014-07-18 12:58:38 -04:00
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.IdentityHashMap;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Stack;
|
|
|
|
import java.util.TreeSet;
|
2008-09-19 03:32:34 -04:00
|
|
|
|
2014-07-18 12:58:38 -04:00
|
|
|
import org.apache.poi.ss.formula.CollaboratingWorkbooksEnvironment.WorkbookNotFoundException;
|
2012-02-26 10:22:43 -05:00
|
|
|
import org.apache.poi.ss.formula.atp.AnalysisToolPak;
|
2014-07-18 12:58:38 -04:00
|
|
|
import org.apache.poi.ss.formula.eval.BlankEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.BoolEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.ErrorEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.EvaluationException;
|
|
|
|
import org.apache.poi.ss.formula.eval.FunctionEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.MissingArgEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.NameEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.NameXEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
|
|
|
import org.apache.poi.ss.formula.eval.NumberEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.OperandResolver;
|
|
|
|
import org.apache.poi.ss.formula.eval.StringEval;
|
|
|
|
import org.apache.poi.ss.formula.eval.ValueEval;
|
|
|
|
import org.apache.poi.ss.formula.functions.Choose;
|
|
|
|
import org.apache.poi.ss.formula.functions.FreeRefFunction;
|
2012-02-26 10:22:43 -05:00
|
|
|
import org.apache.poi.ss.formula.functions.Function;
|
2014-07-18 12:58:38 -04:00
|
|
|
import org.apache.poi.ss.formula.functions.IfFunc;
|
2010-11-24 11:50:47 -05:00
|
|
|
import org.apache.poi.ss.formula.ptg.Area3DPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.AreaErrPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.AreaPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.AttrPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.BoolPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.ControlPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.DeletedArea3DPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.DeletedRef3DPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.ErrPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.ExpPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.FuncVarPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.IntPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.MemAreaPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.MemErrPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.MemFuncPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.MissingArgPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.NamePtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.NameXPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.NumberPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.OperationPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.Ptg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.Ref3DPtg;
|
2014-07-19 08:49:41 -04:00
|
|
|
import org.apache.poi.ss.formula.ptg.Ref3DPxg;
|
2010-11-24 11:50:47 -05:00
|
|
|
import org.apache.poi.ss.formula.ptg.RefErrorPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.RefPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.StringPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.UnionPtg;
|
|
|
|
import org.apache.poi.ss.formula.ptg.UnknownPtg;
|
2010-11-28 07:03:52 -05:00
|
|
|
import org.apache.poi.ss.formula.udf.AggregatingUDFFinder;
|
2010-11-21 07:09:29 -05:00
|
|
|
import org.apache.poi.ss.formula.udf.UDFFinder;
|
2008-09-19 03:32:34 -04:00
|
|
|
import org.apache.poi.ss.usermodel.Cell;
|
2014-07-18 12:58:38 -04:00
|
|
|
import org.apache.poi.ss.util.CellReference;
|
2012-02-06 02:37:11 -05:00
|
|
|
import org.apache.poi.util.POILogFactory;
|
|
|
|
import org.apache.poi.util.POILogger;
|
2008-09-19 03:32:34 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Evaluates formula cells.<p/>
|
|
|
|
*
|
|
|
|
* For performance reasons, this class keeps a cache of all previously calculated intermediate
|
2008-11-07 11:57:23 -05:00
|
|
|
* cell values. Be sure to call {@link #clearAllCachedResultValues()} if any workbook cells are changed between
|
2008-09-19 03:32:34 -04:00
|
|
|
* calls to evaluate~ methods on this class.<br/>
|
|
|
|
*
|
|
|
|
* For POI internal use only
|
|
|
|
*
|
|
|
|
* @author Josh Micich
|
2012-09-04 17:00:41 -04:00
|
|
|
* @author Thies Wellpott (debug output enhancements)
|
2008-09-19 03:32:34 -04:00
|
|
|
*/
|
2008-09-29 16:09:09 -04:00
|
|
|
public final class WorkbookEvaluator {
|
2012-02-06 02:37:11 -05:00
|
|
|
|
|
|
|
private static final POILogger LOG = POILogFactory.getLogger(WorkbookEvaluator.class);
|
|
|
|
|
|
|
|
private final EvaluationWorkbook _workbook;
|
2008-09-29 16:09:09 -04:00
|
|
|
private EvaluationCache _cache;
|
2009-08-21 19:37:17 -04:00
|
|
|
/** part of cache entry key (useful when evaluating multiple workbooks) */
|
Merged revisions 700479,700493,700916,701302,701569,701598,701747 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk
........
r700479 | yegor | 2008-09-30 07:32:37 -0700 (Tue, 30 Sep 2008) | 1 line
reverted the change made in r693085 , see bug #45859
........
r700493 | yegor | 2008-09-30 08:11:26 -0700 (Tue, 30 Sep 2008) | 1 line
initial support for creating hyperlinks in HSLF, units test are still to do
........
r700916 | josh | 2008-10-01 13:56:21 -0700 (Wed, 01 Oct 2008) | 1 line
Fixed bug in CellCacheEntry (support for caching blank evaluation results)
........
r701302 | yegor | 2008-10-02 22:27:06 -0700 (Thu, 02 Oct 2008) | 1 line
fixed bug #45889:rrayIndexOutOfBoundsException when constructing HSLF Table with a single row
........
r701569 | josh | 2008-10-03 16:50:22 -0700 (Fri, 03 Oct 2008) | 1 line
Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord
........
r701598 | josh | 2008-10-03 21:59:26 -0700 (Fri, 03 Oct 2008) | 1 line
changed workbook reference to index in CellLocation
........
r701747 | josh | 2008-10-04 21:43:48 -0700 (Sat, 04 Oct 2008) | 1 line
Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray
........
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703069 13f79535-47bb-0310-9956-ffa450edef68
2008-10-09 02:38:50 -04:00
|
|
|
private int _workbookIx;
|
2008-09-19 03:32:34 -04:00
|
|
|
|
2008-09-23 17:52:49 -04:00
|
|
|
private final IEvaluationListener _evaluationListener;
|
2008-11-13 15:22:17 -05:00
|
|
|
private final Map<EvaluationSheet, Integer> _sheetIndexesBySheet;
|
2009-08-21 19:37:17 -04:00
|
|
|
private final Map<String, Integer> _sheetIndexesByName;
|
2008-09-29 16:09:09 -04:00
|
|
|
private CollaboratingWorkbooksEnvironment _collaboratingWorkbookEnvironment;
|
2008-11-13 15:22:17 -05:00
|
|
|
private final IStabilityClassifier _stabilityClassifier;
|
2010-11-28 07:03:52 -05:00
|
|
|
private final AggregatingUDFFinder _udfFinder;
|
2008-09-19 03:32:34 -04:00
|
|
|
|
2012-02-07 03:11:37 -05:00
|
|
|
private boolean _ignoreMissingWorkbooks = false;
|
|
|
|
|
2009-09-16 20:00:57 -04:00
|
|
|
/**
|
|
|
|
* @param udfFinder pass <code>null</code> for default (AnalysisToolPak only)
|
|
|
|
*/
|
|
|
|
public WorkbookEvaluator(EvaluationWorkbook workbook, IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
|
|
|
|
this (workbook, null, stabilityClassifier, udfFinder);
|
2008-09-23 17:52:49 -04:00
|
|
|
}
|
2008-11-13 15:22:17 -05:00
|
|
|
/* package */ WorkbookEvaluator(EvaluationWorkbook workbook, IEvaluationListener evaluationListener,
|
2009-09-16 20:00:57 -04:00
|
|
|
IStabilityClassifier stabilityClassifier, UDFFinder udfFinder) {
|
2008-09-19 03:32:34 -04:00
|
|
|
_workbook = workbook;
|
2008-09-23 17:52:49 -04:00
|
|
|
_evaluationListener = evaluationListener;
|
|
|
|
_cache = new EvaluationCache(evaluationListener);
|
2008-11-13 15:22:17 -05:00
|
|
|
_sheetIndexesBySheet = new IdentityHashMap<EvaluationSheet, Integer>();
|
2009-08-21 19:37:17 -04:00
|
|
|
_sheetIndexesByName = new IdentityHashMap<String, Integer>();
|
2008-09-29 16:09:09 -04:00
|
|
|
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
|
Merged revisions 700479,700493,700916,701302,701569,701598,701747 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk
........
r700479 | yegor | 2008-09-30 07:32:37 -0700 (Tue, 30 Sep 2008) | 1 line
reverted the change made in r693085 , see bug #45859
........
r700493 | yegor | 2008-09-30 08:11:26 -0700 (Tue, 30 Sep 2008) | 1 line
initial support for creating hyperlinks in HSLF, units test are still to do
........
r700916 | josh | 2008-10-01 13:56:21 -0700 (Wed, 01 Oct 2008) | 1 line
Fixed bug in CellCacheEntry (support for caching blank evaluation results)
........
r701302 | yegor | 2008-10-02 22:27:06 -0700 (Thu, 02 Oct 2008) | 1 line
fixed bug #45889:rrayIndexOutOfBoundsException when constructing HSLF Table with a single row
........
r701569 | josh | 2008-10-03 16:50:22 -0700 (Fri, 03 Oct 2008) | 1 line
Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord
........
r701598 | josh | 2008-10-03 21:59:26 -0700 (Fri, 03 Oct 2008) | 1 line
changed workbook reference to index in CellLocation
........
r701747 | josh | 2008-10-04 21:43:48 -0700 (Sat, 04 Oct 2008) | 1 line
Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray
........
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703069 13f79535-47bb-0310-9956-ffa450edef68
2008-10-09 02:38:50 -04:00
|
|
|
_workbookIx = 0;
|
2008-11-13 15:22:17 -05:00
|
|
|
_stabilityClassifier = stabilityClassifier;
|
2010-11-28 07:03:52 -05:00
|
|
|
|
|
|
|
AggregatingUDFFinder defaultToolkit = // workbook can be null in unit tests
|
|
|
|
workbook == null ? null : (AggregatingUDFFinder)workbook.getUDFFinder();
|
|
|
|
if(defaultToolkit != null && udfFinder != null) {
|
|
|
|
defaultToolkit.add(udfFinder);
|
|
|
|
}
|
|
|
|
_udfFinder = defaultToolkit;
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-09-23 17:52:49 -04:00
|
|
|
* also for debug use. Used in toString methods
|
2008-09-19 03:32:34 -04:00
|
|
|
*/
|
2008-09-23 17:52:49 -04:00
|
|
|
/* package */ String getSheetName(int sheetIndex) {
|
2008-09-19 03:32:34 -04:00
|
|
|
return _workbook.getSheetName(sheetIndex);
|
|
|
|
}
|
|
|
|
|
2009-08-21 19:37:17 -04:00
|
|
|
/* package */ EvaluationSheet getSheet(int sheetIndex) {
|
|
|
|
return _workbook.getSheet(sheetIndex);
|
|
|
|
}
|
2010-06-10 13:07:06 -04:00
|
|
|
|
2011-07-25 08:55:32 -04:00
|
|
|
/* package */ EvaluationWorkbook getWorkbook() {
|
|
|
|
return _workbook;
|
|
|
|
}
|
|
|
|
|
2010-06-10 13:07:06 -04:00
|
|
|
/* package */ EvaluationName getName(String name, int sheetIndex) {
|
2010-11-28 07:03:52 -05:00
|
|
|
NamePtg namePtg = _workbook.getName(name, sheetIndex).createPtg();
|
2010-06-10 13:07:06 -04:00
|
|
|
|
|
|
|
if(namePtg == null) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return _workbook.getName(namePtg);
|
|
|
|
}
|
|
|
|
}
|
2009-08-21 19:37:17 -04:00
|
|
|
|
2008-09-19 03:32:34 -04:00
|
|
|
private static boolean isDebugLogEnabled() {
|
2012-02-06 02:37:11 -05:00
|
|
|
return LOG.check(POILogger.DEBUG);
|
|
|
|
}
|
|
|
|
private static boolean isInfoLogEnabled() {
|
|
|
|
return LOG.check(POILogger.INFO);
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
private static void logDebug(String s) {
|
|
|
|
if (isDebugLogEnabled()) {
|
2012-02-06 02:37:11 -05:00
|
|
|
LOG.log(POILogger.DEBUG, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private static void logInfo(String s) {
|
|
|
|
if (isInfoLogEnabled()) {
|
|
|
|
LOG.log(POILogger.INFO, s);
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
}
|
Merged revisions 700479,700493,700916,701302,701569,701598,701747 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk
........
r700479 | yegor | 2008-09-30 07:32:37 -0700 (Tue, 30 Sep 2008) | 1 line
reverted the change made in r693085 , see bug #45859
........
r700493 | yegor | 2008-09-30 08:11:26 -0700 (Tue, 30 Sep 2008) | 1 line
initial support for creating hyperlinks in HSLF, units test are still to do
........
r700916 | josh | 2008-10-01 13:56:21 -0700 (Wed, 01 Oct 2008) | 1 line
Fixed bug in CellCacheEntry (support for caching blank evaluation results)
........
r701302 | yegor | 2008-10-02 22:27:06 -0700 (Thu, 02 Oct 2008) | 1 line
fixed bug #45889:rrayIndexOutOfBoundsException when constructing HSLF Table with a single row
........
r701569 | josh | 2008-10-03 16:50:22 -0700 (Fri, 03 Oct 2008) | 1 line
Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord
........
r701598 | josh | 2008-10-03 21:59:26 -0700 (Fri, 03 Oct 2008) | 1 line
changed workbook reference to index in CellLocation
........
r701747 | josh | 2008-10-04 21:43:48 -0700 (Sat, 04 Oct 2008) | 1 line
Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray
........
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703069 13f79535-47bb-0310-9956-ffa450edef68
2008-10-09 02:38:50 -04:00
|
|
|
/* package */ void attachToEnvironment(CollaboratingWorkbooksEnvironment collaboratingWorkbooksEnvironment, EvaluationCache cache, int workbookIx) {
|
2008-09-29 16:09:09 -04:00
|
|
|
_collaboratingWorkbookEnvironment = collaboratingWorkbooksEnvironment;
|
|
|
|
_cache = cache;
|
Merged revisions 700479,700493,700916,701302,701569,701598,701747 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk
........
r700479 | yegor | 2008-09-30 07:32:37 -0700 (Tue, 30 Sep 2008) | 1 line
reverted the change made in r693085 , see bug #45859
........
r700493 | yegor | 2008-09-30 08:11:26 -0700 (Tue, 30 Sep 2008) | 1 line
initial support for creating hyperlinks in HSLF, units test are still to do
........
r700916 | josh | 2008-10-01 13:56:21 -0700 (Wed, 01 Oct 2008) | 1 line
Fixed bug in CellCacheEntry (support for caching blank evaluation results)
........
r701302 | yegor | 2008-10-02 22:27:06 -0700 (Thu, 02 Oct 2008) | 1 line
fixed bug #45889:rrayIndexOutOfBoundsException when constructing HSLF Table with a single row
........
r701569 | josh | 2008-10-03 16:50:22 -0700 (Fri, 03 Oct 2008) | 1 line
Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord
........
r701598 | josh | 2008-10-03 21:59:26 -0700 (Fri, 03 Oct 2008) | 1 line
changed workbook reference to index in CellLocation
........
r701747 | josh | 2008-10-04 21:43:48 -0700 (Sat, 04 Oct 2008) | 1 line
Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray
........
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703069 13f79535-47bb-0310-9956-ffa450edef68
2008-10-09 02:38:50 -04:00
|
|
|
_workbookIx = workbookIx;
|
2008-09-29 16:09:09 -04:00
|
|
|
}
|
|
|
|
/* package */ CollaboratingWorkbooksEnvironment getEnvironment() {
|
|
|
|
return _collaboratingWorkbookEnvironment;
|
|
|
|
}
|
2008-09-19 03:32:34 -04:00
|
|
|
|
2009-08-21 19:37:17 -04:00
|
|
|
/**
|
|
|
|
* Discards the current workbook environment and attaches to the default 'empty' environment.
|
|
|
|
* Also resets evaluation cache.
|
|
|
|
*/
|
2008-09-29 16:09:09 -04:00
|
|
|
/* package */ void detachFromEnvironment() {
|
|
|
|
_collaboratingWorkbookEnvironment = CollaboratingWorkbooksEnvironment.EMPTY;
|
|
|
|
_cache = new EvaluationCache(_evaluationListener);
|
Merged revisions 700479,700493,700916,701302,701569,701598,701747 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk
........
r700479 | yegor | 2008-09-30 07:32:37 -0700 (Tue, 30 Sep 2008) | 1 line
reverted the change made in r693085 , see bug #45859
........
r700493 | yegor | 2008-09-30 08:11:26 -0700 (Tue, 30 Sep 2008) | 1 line
initial support for creating hyperlinks in HSLF, units test are still to do
........
r700916 | josh | 2008-10-01 13:56:21 -0700 (Wed, 01 Oct 2008) | 1 line
Fixed bug in CellCacheEntry (support for caching blank evaluation results)
........
r701302 | yegor | 2008-10-02 22:27:06 -0700 (Thu, 02 Oct 2008) | 1 line
fixed bug #45889:rrayIndexOutOfBoundsException when constructing HSLF Table with a single row
........
r701569 | josh | 2008-10-03 16:50:22 -0700 (Fri, 03 Oct 2008) | 1 line
Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord
........
r701598 | josh | 2008-10-03 21:59:26 -0700 (Fri, 03 Oct 2008) | 1 line
changed workbook reference to index in CellLocation
........
r701747 | josh | 2008-10-04 21:43:48 -0700 (Sat, 04 Oct 2008) | 1 line
Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray
........
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703069 13f79535-47bb-0310-9956-ffa450edef68
2008-10-09 02:38:50 -04:00
|
|
|
_workbookIx = 0;
|
2008-09-29 16:09:09 -04:00
|
|
|
}
|
2009-08-21 19:37:17 -04:00
|
|
|
/**
|
|
|
|
* @return the evaluator for another workbook which is part of the same {@link CollaboratingWorkbooksEnvironment}
|
|
|
|
*/
|
|
|
|
/* package */ WorkbookEvaluator getOtherWorkbookEvaluator(String workbookName) throws WorkbookNotFoundException {
|
|
|
|
return _collaboratingWorkbookEnvironment.getWorkbookEvaluator(workbookName);
|
|
|
|
}
|
|
|
|
|
2008-09-29 16:09:09 -04:00
|
|
|
/* package */ IEvaluationListener getEvaluationListener() {
|
|
|
|
return _evaluationListener;
|
|
|
|
}
|
2008-10-09 03:20:46 -04:00
|
|
|
|
2008-09-19 03:32:34 -04:00
|
|
|
/**
|
|
|
|
* Should be called whenever there are changes to input cells in the evaluated workbook.
|
|
|
|
* Failure to call this method after changing cell values will cause incorrect behaviour
|
|
|
|
* of the evaluate~ methods of this class
|
|
|
|
*/
|
|
|
|
public void clearAllCachedResultValues() {
|
|
|
|
_cache.clear();
|
2008-09-23 17:52:49 -04:00
|
|
|
_sheetIndexesBySheet.clear();
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
|
2008-09-23 17:52:49 -04:00
|
|
|
/**
|
2008-11-13 15:22:17 -05:00
|
|
|
* Should be called to tell the cell value cache that the specified (value or formula) cell
|
2008-10-09 03:20:46 -04:00
|
|
|
* has changed.
|
2008-09-23 17:52:49 -04:00
|
|
|
*/
|
2008-10-09 03:20:46 -04:00
|
|
|
public void notifyUpdateCell(EvaluationCell cell) {
|
|
|
|
int sheetIndex = getSheetIndex(cell.getSheet());
|
|
|
|
_cache.notifyUpdateCell(_workbookIx, sheetIndex, cell);
|
2008-09-23 17:52:49 -04:00
|
|
|
}
|
|
|
|
/**
|
2008-10-09 03:20:46 -04:00
|
|
|
* Should be called to tell the cell value cache that the specified cell has just been
|
2008-11-13 15:22:17 -05:00
|
|
|
* deleted.
|
2008-09-23 17:52:49 -04:00
|
|
|
*/
|
2008-10-09 03:20:46 -04:00
|
|
|
public void notifyDeleteCell(EvaluationCell cell) {
|
|
|
|
int sheetIndex = getSheetIndex(cell.getSheet());
|
|
|
|
_cache.notifyDeleteCell(_workbookIx, sheetIndex, cell);
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
2011-05-09 10:04:21 -04:00
|
|
|
|
2008-10-09 03:20:46 -04:00
|
|
|
private int getSheetIndex(EvaluationSheet sheet) {
|
2008-11-13 15:22:17 -05:00
|
|
|
Integer result = _sheetIndexesBySheet.get(sheet);
|
2008-09-23 17:52:49 -04:00
|
|
|
if (result == null) {
|
2008-09-29 16:09:09 -04:00
|
|
|
int sheetIndex = _workbook.getSheetIndex(sheet);
|
|
|
|
if (sheetIndex < 0) {
|
|
|
|
throw new RuntimeException("Specified sheet from a different book");
|
|
|
|
}
|
2009-10-08 18:29:41 -04:00
|
|
|
result = Integer.valueOf(sheetIndex);
|
2008-09-23 17:52:49 -04:00
|
|
|
_sheetIndexesBySheet.put(sheet, result);
|
|
|
|
}
|
|
|
|
return result.intValue();
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
|
2008-10-09 03:20:46 -04:00
|
|
|
public ValueEval evaluate(EvaluationCell srcCell) {
|
2008-09-23 17:52:49 -04:00
|
|
|
int sheetIndex = getSheetIndex(srcCell.getSheet());
|
2008-10-09 03:20:46 -04:00
|
|
|
return evaluateAny(srcCell, sheetIndex, srcCell.getRowIndex(), srcCell.getColumnIndex(), new EvaluationTracker(_cache));
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
|
2009-08-21 19:37:17 -04:00
|
|
|
/**
|
|
|
|
* Case-insensitive.
|
|
|
|
* @return -1 if sheet with specified name does not exist
|
|
|
|
*/
|
|
|
|
/* package */ int getSheetIndex(String sheetName) {
|
|
|
|
Integer result = _sheetIndexesByName.get(sheetName);
|
|
|
|
if (result == null) {
|
|
|
|
int sheetIndex = _workbook.getSheetIndex(sheetName);
|
|
|
|
if (sheetIndex < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2009-10-08 18:29:41 -04:00
|
|
|
result = Integer.valueOf(sheetIndex);
|
2009-08-21 19:37:17 -04:00
|
|
|
_sheetIndexesByName.put(sheetName, result);
|
|
|
|
}
|
|
|
|
return result.intValue();
|
|
|
|
}
|
2010-06-10 13:07:06 -04:00
|
|
|
|
|
|
|
/* package */ int getSheetIndexByExternIndex(int externSheetIndex) {
|
|
|
|
return _workbook.convertFromExternSheetIndex(externSheetIndex);
|
|
|
|
}
|
2009-08-21 19:37:17 -04:00
|
|
|
|
2008-10-09 03:20:46 -04:00
|
|
|
|
2008-09-19 03:32:34 -04:00
|
|
|
/**
|
|
|
|
* @return never <code>null</code>, never {@link BlankEval}
|
|
|
|
*/
|
2008-10-09 03:20:46 -04:00
|
|
|
private ValueEval evaluateAny(EvaluationCell srcCell, int sheetIndex,
|
|
|
|
int rowIndex, int columnIndex, EvaluationTracker tracker) {
|
2008-09-19 03:32:34 -04:00
|
|
|
|
2009-11-13 16:21:23 -05:00
|
|
|
// avoid tracking dependencies to cells that have constant definition
|
2008-11-13 15:22:17 -05:00
|
|
|
boolean shouldCellDependencyBeRecorded = _stabilityClassifier == null ? true
|
|
|
|
: !_stabilityClassifier.isCellFinal(sheetIndex, rowIndex, columnIndex);
|
2008-10-09 03:20:46 -04:00
|
|
|
if (srcCell == null || srcCell.getCellType() != Cell.CELL_TYPE_FORMULA) {
|
|
|
|
ValueEval result = getValueFromNonFormulaCell(srcCell);
|
2008-11-13 15:22:17 -05:00
|
|
|
if (shouldCellDependencyBeRecorded) {
|
|
|
|
tracker.acceptPlainValueDependency(_workbookIx, sheetIndex, rowIndex, columnIndex, result);
|
|
|
|
}
|
2008-10-09 03:20:46 -04:00
|
|
|
return result;
|
|
|
|
}
|
2008-09-19 03:32:34 -04:00
|
|
|
|
2008-10-09 03:20:46 -04:00
|
|
|
FormulaCellCacheEntry cce = _cache.getOrCreateFormulaCellEntry(srcCell);
|
2008-11-13 15:22:17 -05:00
|
|
|
if (shouldCellDependencyBeRecorded || cce.isInputSensitive()) {
|
|
|
|
tracker.acceptFormulaDependency(cce);
|
|
|
|
}
|
2008-09-23 17:52:49 -04:00
|
|
|
IEvaluationListener evalListener = _evaluationListener;
|
2009-03-30 16:46:51 -04:00
|
|
|
ValueEval result;
|
2008-10-09 03:20:46 -04:00
|
|
|
if (cce.getValue() == null) {
|
|
|
|
if (!tracker.startEvaluate(cce)) {
|
|
|
|
return ErrorEval.CIRCULAR_REF_ERROR;
|
2008-09-23 17:52:49 -04:00
|
|
|
}
|
2009-08-21 19:37:17 -04:00
|
|
|
OperationEvaluationContext ec = new OperationEvaluationContext(this, _workbook, sheetIndex, rowIndex, columnIndex, tracker);
|
2008-09-19 03:32:34 -04:00
|
|
|
|
2008-10-09 03:20:46 -04:00
|
|
|
try {
|
|
|
|
|
2008-09-23 17:52:49 -04:00
|
|
|
Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
|
2008-10-09 03:20:46 -04:00
|
|
|
if (evalListener == null) {
|
2009-08-21 19:37:17 -04:00
|
|
|
result = evaluateFormula(ec, ptgs);
|
2008-09-23 17:52:49 -04:00
|
|
|
} else {
|
2009-11-22 20:22:56 -05:00
|
|
|
evalListener.onStartEvaluate(srcCell, cce);
|
2009-08-21 19:37:17 -04:00
|
|
|
result = evaluateFormula(ec, ptgs);
|
2008-10-09 03:20:46 -04:00
|
|
|
evalListener.onEndEvaluate(cce, result);
|
2008-09-23 17:52:49 -04:00
|
|
|
}
|
2008-10-09 03:20:46 -04:00
|
|
|
|
|
|
|
tracker.updateCacheResult(result);
|
2012-02-06 02:37:11 -05:00
|
|
|
}
|
|
|
|
catch (NotImplementedException e) {
|
2009-01-21 19:26:28 -05:00
|
|
|
throw addExceptionInfo(e, sheetIndex, rowIndex, columnIndex);
|
2012-02-06 02:37:11 -05:00
|
|
|
} catch (RuntimeException re) {
|
2012-02-07 03:11:37 -05:00
|
|
|
if (re.getCause() instanceof WorkbookNotFoundException && _ignoreMissingWorkbooks) {
|
2012-02-06 02:37:11 -05:00
|
|
|
logInfo(re.getCause().getMessage() + " - Continuing with cached value!");
|
|
|
|
switch(srcCell.getCachedFormulaResultType()) {
|
|
|
|
case Cell.CELL_TYPE_NUMERIC:
|
|
|
|
result = new NumberEval(srcCell.getNumericCellValue());
|
|
|
|
break;
|
|
|
|
case Cell.CELL_TYPE_STRING:
|
|
|
|
result = new StringEval(srcCell.getStringCellValue());
|
|
|
|
break;
|
|
|
|
case Cell.CELL_TYPE_BLANK:
|
|
|
|
result = BlankEval.instance;
|
|
|
|
break;
|
|
|
|
case Cell.CELL_TYPE_BOOLEAN:
|
|
|
|
result = BoolEval.valueOf(srcCell.getBooleanCellValue());
|
|
|
|
break;
|
|
|
|
case Cell.CELL_TYPE_ERROR:
|
|
|
|
result = ErrorEval.valueOf(srcCell.getErrorCellValue());
|
|
|
|
break;
|
|
|
|
case Cell.CELL_TYPE_FORMULA:
|
|
|
|
default:
|
|
|
|
throw new RuntimeException("Unexpected cell type '" + srcCell.getCellType()+"' found!");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw re;
|
|
|
|
}
|
|
|
|
} finally {
|
2008-10-09 03:20:46 -04:00
|
|
|
tracker.endEvaluate(cce);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(evalListener != null) {
|
|
|
|
evalListener.onCacheHit(sheetIndex, rowIndex, columnIndex, cce.getValue());
|
2008-09-23 17:52:49 -04:00
|
|
|
}
|
2008-10-09 03:20:46 -04:00
|
|
|
return cce.getValue();
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
if (isDebugLogEnabled()) {
|
|
|
|
String sheetName = getSheetName(sheetIndex);
|
2008-09-23 17:52:49 -04:00
|
|
|
CellReference cr = new CellReference(rowIndex, columnIndex);
|
2009-03-30 16:46:51 -04:00
|
|
|
logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
2009-08-17 14:55:05 -04:00
|
|
|
// Usually (result === cce.getValue())
|
2009-03-30 16:46:51 -04:00
|
|
|
// But sometimes: (result==ErrorEval.CIRCULAR_REF_ERROR, cce.getValue()==null)
|
2009-08-17 14:55:05 -04:00
|
|
|
// When circular references are detected, the cache entry is only updated for
|
|
|
|
// the top evaluation frame
|
2009-03-30 16:46:51 -04:00
|
|
|
return result;
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
2009-01-21 19:26:28 -05:00
|
|
|
|
|
|
|
/**
|
2009-08-17 14:55:05 -04:00
|
|
|
* Adds the current cell reference to the exception for easier debugging.
|
2009-01-21 19:26:28 -05:00
|
|
|
* Would be nice to get the formula text as well, but that seems to require
|
2009-08-17 14:55:05 -04:00
|
|
|
* too much digging around and casting to get the FormulaRenderingWorkbook.
|
2009-01-21 19:26:28 -05:00
|
|
|
*/
|
|
|
|
private NotImplementedException addExceptionInfo(NotImplementedException inner, int sheetIndex, int rowIndex, int columnIndex) {
|
2009-08-17 14:55:05 -04:00
|
|
|
|
2009-01-21 19:26:28 -05:00
|
|
|
try {
|
|
|
|
String sheetName = _workbook.getSheetName(sheetIndex);
|
|
|
|
CellReference cr = new CellReference(sheetName, rowIndex, columnIndex, false, false);
|
|
|
|
String msg = "Error evaluating cell " + cr.formatAsString();
|
|
|
|
return new NotImplementedException(msg, inner);
|
|
|
|
} catch (Exception e) {
|
|
|
|
// avoid bombing out during exception handling
|
|
|
|
e.printStackTrace();
|
|
|
|
return inner; // preserve original exception
|
|
|
|
}
|
|
|
|
}
|
2008-09-23 17:52:49 -04:00
|
|
|
/**
|
|
|
|
* Gets the value from a non-formula cell.
|
|
|
|
* @param cell may be <code>null</code>
|
2008-10-09 03:20:46 -04:00
|
|
|
* @return {@link BlankEval} if cell is <code>null</code> or blank, never <code>null</code>
|
2008-09-23 17:52:49 -04:00
|
|
|
*/
|
2008-10-09 03:20:46 -04:00
|
|
|
/* package */ static ValueEval getValueFromNonFormulaCell(EvaluationCell cell) {
|
2008-09-23 17:52:49 -04:00
|
|
|
if (cell == null) {
|
2009-11-27 21:50:47 -05:00
|
|
|
return BlankEval.instance;
|
2008-09-23 17:52:49 -04:00
|
|
|
}
|
|
|
|
int cellType = cell.getCellType();
|
|
|
|
switch (cellType) {
|
|
|
|
case Cell.CELL_TYPE_NUMERIC:
|
|
|
|
return new NumberEval(cell.getNumericCellValue());
|
|
|
|
case Cell.CELL_TYPE_STRING:
|
2008-10-09 03:20:46 -04:00
|
|
|
return new StringEval(cell.getStringCellValue());
|
2008-09-23 17:52:49 -04:00
|
|
|
case Cell.CELL_TYPE_BOOLEAN:
|
|
|
|
return BoolEval.valueOf(cell.getBooleanCellValue());
|
|
|
|
case Cell.CELL_TYPE_BLANK:
|
2009-11-27 21:50:47 -05:00
|
|
|
return BlankEval.instance;
|
2008-09-23 17:52:49 -04:00
|
|
|
case Cell.CELL_TYPE_ERROR:
|
|
|
|
return ErrorEval.valueOf(cell.getErrorCellValue());
|
|
|
|
}
|
|
|
|
throw new RuntimeException("Unexpected cell type (" + cellType + ")");
|
|
|
|
}
|
2012-09-04 17:00:41 -04:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* whether print detailed messages about the next formula evaluation
|
|
|
|
*/
|
|
|
|
private boolean dbgEvaluationOutputForNextEval = false;
|
|
|
|
|
|
|
|
// special logger for formula evaluation output (because of possibly very large output)
|
|
|
|
private final POILogger EVAL_LOG = POILogFactory.getLogger("POI.FormulaEval");
|
|
|
|
// current indent level for evalution; negative value for no output
|
|
|
|
private int dbgEvaluationOutputIndent = -1;
|
|
|
|
|
2008-09-29 16:09:09 -04:00
|
|
|
// visibility raised for testing
|
2009-08-21 19:37:17 -04:00
|
|
|
/* package */ ValueEval evaluateFormula(OperationEvaluationContext ec, Ptg[] ptgs) {
|
2008-09-19 03:32:34 -04:00
|
|
|
|
2012-09-04 17:00:41 -04:00
|
|
|
String dbgIndentStr = ""; // always init. to non-null just for defensive avoiding NPE
|
|
|
|
if (dbgEvaluationOutputForNextEval) {
|
|
|
|
// first evaluation call when ouput is desired, so iit. this evaluator instance
|
|
|
|
dbgEvaluationOutputIndent = 1;
|
|
|
|
dbgEvaluationOutputForNextEval = false;
|
|
|
|
}
|
|
|
|
if (dbgEvaluationOutputIndent > 0) {
|
|
|
|
// init. indent string to needed spaces (create as substring vom very long space-only string;
|
|
|
|
// limit indendation for deep recursions)
|
|
|
|
dbgIndentStr = " ";
|
|
|
|
dbgIndentStr = dbgIndentStr.substring(0, Math.min(dbgIndentStr.length(), dbgEvaluationOutputIndent*2));
|
|
|
|
EVAL_LOG.log(POILogger.WARN, dbgIndentStr
|
|
|
|
+ "- evaluateFormula('" + ec.getRefEvaluatorForCurrentSheet().getSheetName()
|
|
|
|
+ "'/" + new CellReference(ec.getRowIndex(), ec.getColumnIndex()).formatAsString()
|
|
|
|
+ "): " + Arrays.toString(ptgs).replaceAll("\\Qorg.apache.poi.ss.formula.ptg.\\E", ""));
|
|
|
|
dbgEvaluationOutputIndent++;
|
|
|
|
}
|
|
|
|
|
2008-12-31 14:32:44 -05:00
|
|
|
Stack<ValueEval> stack = new Stack<ValueEval>();
|
2008-09-19 03:32:34 -04:00
|
|
|
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
|
|
|
|
|
|
|
// since we don't know how to handle these yet :(
|
|
|
|
Ptg ptg = ptgs[i];
|
2012-09-04 17:00:41 -04:00
|
|
|
if (dbgEvaluationOutputIndent > 0) {
|
|
|
|
EVAL_LOG.log(POILogger.INFO, dbgIndentStr + " * ptg " + i + ": " + ptg);
|
|
|
|
}
|
2008-09-29 16:09:09 -04:00
|
|
|
if (ptg instanceof AttrPtg) {
|
|
|
|
AttrPtg attrPtg = (AttrPtg) ptg;
|
|
|
|
if (attrPtg.isSum()) {
|
|
|
|
// Excel prefers to encode 'SUM()' as a tAttr token, but this evaluator
|
|
|
|
// expects the equivalent function token
|
2009-11-13 21:41:24 -05:00
|
|
|
ptg = FuncVarPtg.SUM;
|
2008-09-29 16:09:09 -04:00
|
|
|
}
|
2009-11-13 16:21:23 -05:00
|
|
|
if (attrPtg.isOptimizedChoose()) {
|
|
|
|
ValueEval arg0 = stack.pop();
|
|
|
|
int[] jumpTable = attrPtg.getJumpTable();
|
|
|
|
int dist;
|
|
|
|
int nChoices = jumpTable.length;
|
|
|
|
try {
|
|
|
|
int switchIndex = Choose.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex());
|
|
|
|
if (switchIndex<1 || switchIndex > nChoices) {
|
|
|
|
stack.push(ErrorEval.VALUE_INVALID);
|
|
|
|
dist = attrPtg.getChooseFuncOffset() + 4; // +4 for tFuncFar(CHOOSE)
|
|
|
|
} else {
|
|
|
|
dist = jumpTable[switchIndex-1];
|
|
|
|
}
|
|
|
|
} catch (EvaluationException e) {
|
|
|
|
stack.push(e.getErrorEval());
|
|
|
|
dist = attrPtg.getChooseFuncOffset() + 4; // +4 for tFuncFar(CHOOSE)
|
|
|
|
}
|
|
|
|
// Encoded dist for tAttrChoose includes size of jump table, but
|
|
|
|
// countTokensToBeSkipped() does not (it counts whole tokens).
|
|
|
|
dist -= nChoices*2+2; // subtract jump table size
|
|
|
|
i+= countTokensToBeSkipped(ptgs, i, dist);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (attrPtg.isOptimizedIf()) {
|
|
|
|
ValueEval arg0 = stack.pop();
|
|
|
|
boolean evaluatedPredicate;
|
|
|
|
try {
|
2009-12-21 18:30:32 -05:00
|
|
|
evaluatedPredicate = IfFunc.evaluateFirstArg(arg0, ec.getRowIndex(), ec.getColumnIndex());
|
2009-11-13 16:21:23 -05:00
|
|
|
} catch (EvaluationException e) {
|
|
|
|
stack.push(e.getErrorEval());
|
|
|
|
int dist = attrPtg.getData();
|
|
|
|
i+= countTokensToBeSkipped(ptgs, i, dist);
|
|
|
|
attrPtg = (AttrPtg) ptgs[i];
|
|
|
|
dist = attrPtg.getData()+1;
|
|
|
|
i+= countTokensToBeSkipped(ptgs, i, dist);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (evaluatedPredicate) {
|
|
|
|
// nothing to skip - true param folows
|
|
|
|
} else {
|
|
|
|
int dist = attrPtg.getData();
|
|
|
|
i+= countTokensToBeSkipped(ptgs, i, dist);
|
|
|
|
Ptg nextPtg = ptgs[i+1];
|
|
|
|
if (ptgs[i] instanceof AttrPtg && nextPtg instanceof FuncVarPtg) {
|
|
|
|
// this is an if statement without a false param (as opposed to MissingArgPtg as the false param)
|
|
|
|
i++;
|
|
|
|
stack.push(BoolEval.FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (attrPtg.isSkip()) {
|
|
|
|
int dist = attrPtg.getData()+1;
|
|
|
|
i+= countTokensToBeSkipped(ptgs, i, dist);
|
|
|
|
if (stack.peek() == MissingArgEval.instance) {
|
|
|
|
stack.pop();
|
2009-11-27 21:50:47 -05:00
|
|
|
stack.push(BlankEval.instance);
|
2009-11-13 16:21:23 -05:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2008-09-29 16:09:09 -04:00
|
|
|
}
|
2008-09-19 03:32:34 -04:00
|
|
|
if (ptg instanceof ControlPtg) {
|
|
|
|
// skip Parentheses, Attr, etc
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-21 19:00:09 -04:00
|
|
|
if (ptg instanceof MemFuncPtg || ptg instanceof MemAreaPtg) {
|
2008-09-29 16:09:09 -04:00
|
|
|
// can ignore, rest of tokens for this expression are in OK RPN order
|
|
|
|
continue;
|
|
|
|
}
|
2009-10-21 19:00:09 -04:00
|
|
|
if (ptg instanceof MemErrPtg) {
|
|
|
|
continue;
|
|
|
|
}
|
2008-10-09 02:46:17 -04:00
|
|
|
|
2008-12-31 14:32:44 -05:00
|
|
|
ValueEval opResult;
|
2008-09-19 03:32:34 -04:00
|
|
|
if (ptg instanceof OperationPtg) {
|
|
|
|
OperationPtg optg = (OperationPtg) ptg;
|
|
|
|
|
|
|
|
if (optg instanceof UnionPtg) { continue; }
|
|
|
|
|
|
|
|
|
2009-11-16 00:41:57 -05:00
|
|
|
int numops = optg.getNumberOfOperands();
|
2008-12-31 14:32:44 -05:00
|
|
|
ValueEval[] ops = new ValueEval[numops];
|
2008-09-19 03:32:34 -04:00
|
|
|
|
|
|
|
// storing the ops in reverse order since they are popping
|
|
|
|
for (int j = numops - 1; j >= 0; j--) {
|
2008-12-31 14:32:44 -05:00
|
|
|
ValueEval p = stack.pop();
|
2008-09-19 03:32:34 -04:00
|
|
|
ops[j] = p;
|
|
|
|
}
|
|
|
|
// logDebug("invoke " + operation + " (nAgs=" + numops + ")");
|
2009-11-16 00:41:57 -05:00
|
|
|
opResult = OperationEvaluatorFactory.evaluate(optg, ops, ec);
|
2008-09-19 03:32:34 -04:00
|
|
|
} else {
|
2009-08-21 19:37:17 -04:00
|
|
|
opResult = getEvalForPtg(ptg, ec);
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
if (opResult == null) {
|
|
|
|
throw new RuntimeException("Evaluation result must not be null");
|
|
|
|
}
|
|
|
|
// logDebug("push " + opResult);
|
|
|
|
stack.push(opResult);
|
2012-09-04 17:00:41 -04:00
|
|
|
if (dbgEvaluationOutputIndent > 0) {
|
|
|
|
EVAL_LOG.log(POILogger.INFO, dbgIndentStr + " = " + opResult);
|
|
|
|
}
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
|
2008-12-31 14:32:44 -05:00
|
|
|
ValueEval value = stack.pop();
|
2008-09-19 03:32:34 -04:00
|
|
|
if (!stack.isEmpty()) {
|
|
|
|
throw new IllegalStateException("evaluation stack not empty");
|
|
|
|
}
|
2012-09-04 17:00:41 -04:00
|
|
|
ValueEval result = dereferenceResult(value, ec.getRowIndex(), ec.getColumnIndex());
|
|
|
|
if (dbgEvaluationOutputIndent > 0) {
|
|
|
|
EVAL_LOG.log(POILogger.INFO, dbgIndentStr + "finshed eval of "
|
|
|
|
+ new CellReference(ec.getRowIndex(), ec.getColumnIndex()).formatAsString()
|
|
|
|
+ ": " + result);
|
|
|
|
dbgEvaluationOutputIndent--;
|
|
|
|
if (dbgEvaluationOutputIndent == 1) {
|
|
|
|
// this evaluation is done, reset indent to stop logging
|
|
|
|
dbgEvaluationOutputIndent = -1;
|
|
|
|
}
|
|
|
|
} // if
|
|
|
|
return result;
|
|
|
|
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
|
2009-11-13 16:21:23 -05:00
|
|
|
/**
|
|
|
|
* Calculates the number of tokens that the evaluator should skip upon reaching a tAttrSkip.
|
|
|
|
*
|
|
|
|
* @return the number of tokens (starting from <tt>startIndex+1</tt>) that need to be skipped
|
|
|
|
* to achieve the specified <tt>distInBytes</tt> skip distance.
|
|
|
|
*/
|
|
|
|
private static int countTokensToBeSkipped(Ptg[] ptgs, int startIndex, int distInBytes) {
|
|
|
|
int remBytes = distInBytes;
|
|
|
|
int index = startIndex;
|
|
|
|
while (remBytes != 0) {
|
|
|
|
index++;
|
|
|
|
remBytes -= ptgs[index].getSize();
|
|
|
|
if (remBytes < 0) {
|
|
|
|
throw new RuntimeException("Bad skip distance (wrong token size calculation).");
|
|
|
|
}
|
|
|
|
if (index >= ptgs.length) {
|
|
|
|
throw new RuntimeException("Skip distance too far (ran out of formula tokens).");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return index-startIndex;
|
|
|
|
}
|
2009-12-16 18:16:30 -05:00
|
|
|
|
2008-09-19 03:32:34 -04:00
|
|
|
/**
|
2009-12-16 18:16:30 -05:00
|
|
|
* Dereferences a single value from any AreaEval or RefEval evaluation
|
|
|
|
* result. If the supplied evaluationResult is just a plain value, it is
|
|
|
|
* returned as-is.
|
|
|
|
*
|
|
|
|
* @return a {@link NumberEval}, {@link StringEval}, {@link BoolEval}, or
|
|
|
|
* {@link ErrorEval}. Never <code>null</code>. {@link BlankEval} is
|
|
|
|
* converted to {@link NumberEval#ZERO}
|
2008-09-19 03:32:34 -04:00
|
|
|
*/
|
2009-12-16 18:16:30 -05:00
|
|
|
public static ValueEval dereferenceResult(ValueEval evaluationResult, int srcRowNum, int srcColNum) {
|
|
|
|
ValueEval value;
|
|
|
|
try {
|
|
|
|
value = OperandResolver.getSingleValue(evaluationResult, srcRowNum, srcColNum);
|
|
|
|
} catch (EvaluationException e) {
|
|
|
|
return e.getErrorEval();
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
2009-12-16 18:16:30 -05:00
|
|
|
if (value == BlankEval.instance) {
|
|
|
|
// Note Excel behaviour here. A blank final final value is converted to zero.
|
|
|
|
return NumberEval.ZERO;
|
|
|
|
// Formulas _never_ evaluate to blank. If a formula appears to have evaluated to
|
|
|
|
// blank, the actual value is empty string. This can be verified with ISBLANK().
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
2009-12-16 18:16:30 -05:00
|
|
|
return value;
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-19 15:23:13 -04:00
|
|
|
/**
|
|
|
|
* returns an appropriate Eval impl instance for the Ptg. The Ptg must be
|
|
|
|
* one of: Area3DPtg, AreaPtg, ReferencePtg, Ref3DPtg, IntPtg, NumberPtg,
|
|
|
|
* StringPtg, BoolPtg <br/>special Note: OperationPtg subtypes cannot be
|
|
|
|
* passed here!
|
|
|
|
*/
|
|
|
|
private ValueEval getEvalForPtg(Ptg ptg, OperationEvaluationContext ec) {
|
|
|
|
// consider converting all these (ptg instanceof XxxPtg) expressions to (ptg.getClass() == XxxPtg.class)
|
|
|
|
|
|
|
|
if (ptg instanceof NamePtg) {
|
|
|
|
// Named ranges, macro functions
|
|
|
|
NamePtg namePtg = (NamePtg) ptg;
|
|
|
|
EvaluationName nameRecord = _workbook.getName(namePtg);
|
|
|
|
return getEvalForNameRecord(nameRecord, ec);
|
|
|
|
}
|
|
|
|
if (ptg instanceof NameXPtg) {
|
|
|
|
// Externally defined named ranges or macro functions
|
|
|
|
NameXPtg nameXPtg = (NameXPtg)ptg;
|
|
|
|
ValueEval eval = ec.getNameXEval(nameXPtg);
|
|
|
|
|
|
|
|
if (eval instanceof NameXEval) {
|
|
|
|
// Could not be directly evaluated, so process as a name
|
|
|
|
return getEvalForNameX(nameXPtg, ec);
|
|
|
|
} else {
|
|
|
|
// Use the evaluated version
|
|
|
|
return eval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptg instanceof IntPtg) {
|
|
|
|
return new NumberEval(((IntPtg)ptg).getValue());
|
|
|
|
}
|
|
|
|
if (ptg instanceof NumberPtg) {
|
|
|
|
return new NumberEval(((NumberPtg)ptg).getValue());
|
|
|
|
}
|
|
|
|
if (ptg instanceof StringPtg) {
|
|
|
|
return new StringEval(((StringPtg) ptg).getValue());
|
|
|
|
}
|
|
|
|
if (ptg instanceof BoolPtg) {
|
|
|
|
return BoolEval.valueOf(((BoolPtg) ptg).getValue());
|
|
|
|
}
|
|
|
|
if (ptg instanceof ErrPtg) {
|
|
|
|
return ErrorEval.valueOf(((ErrPtg) ptg).getErrorCode());
|
|
|
|
}
|
|
|
|
if (ptg instanceof MissingArgPtg) {
|
|
|
|
return MissingArgEval.instance;
|
|
|
|
}
|
|
|
|
if (ptg instanceof AreaErrPtg ||ptg instanceof RefErrorPtg
|
|
|
|
|| ptg instanceof DeletedArea3DPtg || ptg instanceof DeletedRef3DPtg) {
|
|
|
|
return ErrorEval.REF_INVALID;
|
|
|
|
}
|
|
|
|
if (ptg instanceof Ref3DPtg) {
|
|
|
|
return ec.getRef3DEval((Ref3DPtg)ptg);
|
|
|
|
}
|
|
|
|
if (ptg instanceof Ref3DPxg) {
|
|
|
|
return ec.getRef3DEval((Ref3DPxg)ptg);
|
|
|
|
}
|
|
|
|
if (ptg instanceof Area3DPtg) {
|
|
|
|
Area3DPtg aptg = (Area3DPtg) ptg;
|
|
|
|
return ec.getArea3DEval(aptg.getFirstRow(), aptg.getFirstColumn(), aptg.getLastRow(), aptg.getLastColumn(), aptg.getExternSheetIndex());
|
|
|
|
}
|
|
|
|
if (ptg instanceof RefPtg) {
|
|
|
|
RefPtg rptg = (RefPtg) ptg;
|
|
|
|
return ec.getRefEval(rptg.getRow(), rptg.getColumn());
|
|
|
|
}
|
|
|
|
if (ptg instanceof AreaPtg) {
|
|
|
|
AreaPtg aptg = (AreaPtg) ptg;
|
|
|
|
return ec.getAreaEval(aptg.getFirstRow(), aptg.getFirstColumn(), aptg.getLastRow(), aptg.getLastColumn());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptg instanceof UnknownPtg) {
|
|
|
|
// POI uses UnknownPtg when the encoded Ptg array seems to be corrupted.
|
|
|
|
// This seems to occur in very rare cases (e.g. unused name formulas in bug 44774, attachment 21790)
|
|
|
|
// In any case, formulas are re-parsed before execution, so UnknownPtg should not get here
|
|
|
|
throw new RuntimeException("UnknownPtg not allowed");
|
|
|
|
}
|
|
|
|
if (ptg instanceof ExpPtg) {
|
|
|
|
// ExpPtg is used for array formulas and shared formulas.
|
|
|
|
// it is currently unsupported, and may not even get implemented here
|
|
|
|
throw new RuntimeException("ExpPtg currently not supported");
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
|
|
|
|
}
|
2014-07-18 12:58:38 -04:00
|
|
|
|
|
|
|
private ValueEval getEvalForNameRecord(EvaluationName nameRecord, OperationEvaluationContext ec) {
|
|
|
|
if (nameRecord.isFunctionName()) {
|
|
|
|
return new NameEval(nameRecord.getNameText());
|
|
|
|
}
|
|
|
|
if (nameRecord.hasFormula()) {
|
|
|
|
return evaluateNameFormula(nameRecord.getNameDefinition(), ec);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'");
|
|
|
|
}
|
|
|
|
private ValueEval getEvalForNameX(NameXPtg nameXPtg, OperationEvaluationContext ec) {
|
|
|
|
String name = _workbook.resolveNameXText(nameXPtg);
|
|
|
|
|
|
|
|
// Try to parse it as a name
|
|
|
|
int sheetNameAt = name.indexOf('!');
|
|
|
|
EvaluationName nameRecord = null;
|
|
|
|
if (sheetNameAt > -1) {
|
|
|
|
// Sheet based name
|
|
|
|
String sheetName = name.substring(0, sheetNameAt);
|
|
|
|
String nameName = name.substring(sheetNameAt+1);
|
|
|
|
nameRecord = _workbook.getName(nameName, _workbook.getSheetIndex(sheetName));
|
|
|
|
} else {
|
|
|
|
// Workbook based name
|
|
|
|
nameRecord = _workbook.getName(name, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nameRecord != null) {
|
|
|
|
// Process it as a name
|
|
|
|
return getEvalForNameRecord(nameRecord, ec);
|
|
|
|
} else {
|
|
|
|
// Must be an external function
|
|
|
|
return new NameEval(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-14 12:11:08 -05:00
|
|
|
/**
|
|
|
|
* YK: Used by OperationEvaluationContext to resolve indirect names.
|
|
|
|
*/
|
|
|
|
/*package*/ ValueEval evaluateNameFormula(Ptg[] ptgs, OperationEvaluationContext ec) {
|
2012-06-27 10:50:22 -04:00
|
|
|
if (ptgs.length == 1) {
|
|
|
|
return getEvalForPtg(ptgs[0], ec);
|
|
|
|
}
|
|
|
|
return evaluateFormula(ec, ptgs);
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|
2008-10-09 03:20:46 -04:00
|
|
|
|
2008-09-23 17:52:49 -04:00
|
|
|
/**
|
|
|
|
* Used by the lazy ref evals whenever they need to get the value of a contained cell.
|
|
|
|
*/
|
2008-10-09 03:20:46 -04:00
|
|
|
/* package */ ValueEval evaluateReference(EvaluationSheet sheet, int sheetIndex, int rowIndex,
|
2008-09-23 17:52:49 -04:00
|
|
|
int columnIndex, EvaluationTracker tracker) {
|
2008-10-09 03:20:46 -04:00
|
|
|
|
|
|
|
EvaluationCell cell = sheet.getCell(rowIndex, columnIndex);
|
|
|
|
return evaluateAny(cell, sheetIndex, rowIndex, columnIndex, tracker);
|
Merged revisions 700479,700493,700916,701302,701569,701598,701747 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk
........
r700479 | yegor | 2008-09-30 07:32:37 -0700 (Tue, 30 Sep 2008) | 1 line
reverted the change made in r693085 , see bug #45859
........
r700493 | yegor | 2008-09-30 08:11:26 -0700 (Tue, 30 Sep 2008) | 1 line
initial support for creating hyperlinks in HSLF, units test are still to do
........
r700916 | josh | 2008-10-01 13:56:21 -0700 (Wed, 01 Oct 2008) | 1 line
Fixed bug in CellCacheEntry (support for caching blank evaluation results)
........
r701302 | yegor | 2008-10-02 22:27:06 -0700 (Thu, 02 Oct 2008) | 1 line
fixed bug #45889:rrayIndexOutOfBoundsException when constructing HSLF Table with a single row
........
r701569 | josh | 2008-10-03 16:50:22 -0700 (Fri, 03 Oct 2008) | 1 line
Fix for bug 45912 - ArrayIndexOutOfBoundsException in EmbeddedObjectRefSubRecord
........
r701598 | josh | 2008-10-03 21:59:26 -0700 (Fri, 03 Oct 2008) | 1 line
changed workbook reference to index in CellLocation
........
r701747 | josh | 2008-10-04 21:43:48 -0700 (Sat, 04 Oct 2008) | 1 line
Better bounds checking in RecordInputStream. Removed rarely used methods readShortArray and putShortArray
........
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@703069 13f79535-47bb-0310-9956-ffa450edef68
2008-10-09 02:38:50 -04:00
|
|
|
}
|
2009-09-16 20:00:57 -04:00
|
|
|
public FreeRefFunction findUserDefinedFunction(String functionName) {
|
|
|
|
return _udfFinder.findFunction(functionName);
|
|
|
|
}
|
2012-02-07 03:11:37 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether to ignore missing references to external workbooks and
|
|
|
|
* use cached formula results in the main workbook instead.
|
|
|
|
* <p>
|
|
|
|
* In some cases exetrnal workbooks referenced by formulas in the main workbook are not avaiable.
|
|
|
|
* With this method you can control how POI handles such missing references:
|
|
|
|
* <ul>
|
|
|
|
* <li>by default ignoreMissingWorkbooks=false and POI throws {@link WorkbookNotFoundException}
|
|
|
|
* if an external reference cannot be resolved</li>
|
|
|
|
* <li>if ignoreMissingWorkbooks=true then POI uses cached formula result
|
|
|
|
* that already exists in the main workbook</li>
|
|
|
|
* </ul>
|
|
|
|
*
|
|
|
|
* @param ignore whether to ignore missing references to external workbooks
|
2012-11-16 07:00:02 -05:00
|
|
|
* @see <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=52575">Bug 52575 for details</a>
|
2012-02-07 03:11:37 -05:00
|
|
|
*/
|
|
|
|
public void setIgnoreMissingWorkbooks(boolean ignore){
|
|
|
|
_ignoreMissingWorkbooks = ignore;
|
|
|
|
}
|
2012-02-26 10:22:43 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a collection of functions that POI can evaluate
|
|
|
|
*
|
|
|
|
* @return names of functions supported by POI
|
|
|
|
*/
|
|
|
|
public static Collection<String> getSupportedFunctionNames(){
|
|
|
|
Collection<String> lst = new TreeSet<String>();
|
|
|
|
lst.addAll(FunctionEval.getSupportedFunctionNames());
|
|
|
|
lst.addAll(AnalysisToolPak.getSupportedFunctionNames());
|
|
|
|
return lst;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return a collection of functions that POI does not support
|
|
|
|
*
|
|
|
|
* @return names of functions NOT supported by POI
|
|
|
|
*/
|
|
|
|
public static Collection<String> getNotSupportedFunctionNames(){
|
|
|
|
Collection<String> lst = new TreeSet<String>();
|
|
|
|
lst.addAll(FunctionEval.getNotSupportedFunctionNames());
|
|
|
|
lst.addAll(AnalysisToolPak.getNotSupportedFunctionNames());
|
|
|
|
return lst;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a ATP function in runtime.
|
|
|
|
*
|
|
|
|
* @param name the function name
|
|
|
|
* @param func the functoin to register
|
|
|
|
* @throws IllegalArgumentException if the function is unknown or already registered.
|
|
|
|
* @since 3.8 beta6
|
|
|
|
*/
|
|
|
|
public static void registerFunction(String name, FreeRefFunction func){
|
|
|
|
AnalysisToolPak.registerFunction(name, func);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a function in runtime.
|
|
|
|
*
|
|
|
|
* @param name the function name
|
|
|
|
* @param func the functoin to register
|
|
|
|
* @throws IllegalArgumentException if the function is unknown or already registered.
|
|
|
|
* @since 3.8 beta6
|
|
|
|
*/
|
|
|
|
public static void registerFunction(String name, Function func){
|
|
|
|
FunctionEval.registerFunction(name, func);
|
|
|
|
}
|
2012-09-04 17:00:41 -04:00
|
|
|
|
|
|
|
public void setDebugEvaluationOutputForNextEval(boolean value){
|
|
|
|
dbgEvaluationOutputForNextEval = value;
|
|
|
|
}
|
2008-09-19 03:32:34 -04:00
|
|
|
}
|