Merged revisions 696860,696898 via svnmerge from
https://svn.apache.org/repos/asf/poi/trunk Quite a lot of manual fixing required. Also moved CellValue to top level class. ........ r696860 | josh | 2008-09-18 17:02:21 -0700 (Thu, 18 Sep 2008) | 1 line code clean-up (removed compiler warnings/unused methods) ........ r696898 | josh | 2008-09-18 19:19:58 -0700 (Thu, 18 Sep 2008) | 1 line Partitioning common formula logic. Introduced FormulaParsingWorkbook and EvaluationWorkbook interfaces to make merge with ooxml branch easier ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@696961 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
f9517a6d15
commit
e188e7737d
@ -20,7 +20,7 @@ package org.apache.poi.hssf.dev;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.RecordFactory;
|
||||
@ -181,7 +181,7 @@ public class FormulaViewer
|
||||
|
||||
private static String composeFormula(FormulaRecord record)
|
||||
{
|
||||
return FormulaParser.toFormulaString((HSSFWorkbook)null, record.getParsedExpression());
|
||||
return HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, record.getParsedExpression());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,7 +19,7 @@ package org.apache.poi.hssf.eventusermodel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.BoundSheetRecord;
|
||||
import org.apache.poi.hssf.record.EOFRecord;
|
||||
@ -33,7 +33,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
* When working with the EventUserModel, if you want to
|
||||
* process formulas, you need an instance of
|
||||
* {@link Workbook} to pass to a {@link HSSFWorkbook},
|
||||
* to finally give to {@link FormulaParser},
|
||||
* to finally give to {@link HSSFFormulaParser},
|
||||
* and this will build you stub ones.
|
||||
* Since you're working with the EventUserModel, you
|
||||
* wouldn't want to get a full {@link Workbook} and
|
||||
@ -41,7 +41,7 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
* Instead, you should collect a few key records as they
|
||||
* go past, then call this once you have them to build a
|
||||
* stub {@link Workbook}, and from that a stub
|
||||
* {@link HSSFWorkbook}, to use with the {@link FormulaParser}.
|
||||
* {@link HSSFWorkbook}, to use with the {@link HSSFFormulaParser}.
|
||||
*
|
||||
* The records you should collect are:
|
||||
* * {@link ExternSheetRecord}
|
||||
@ -56,7 +56,7 @@ public class EventWorkbookBuilder {
|
||||
/**
|
||||
* Wraps up your stub {@link Workbook} as a stub
|
||||
* {@link HSSFWorkbook}, ready for passing to
|
||||
* {@link FormulaParser}
|
||||
* {@link HSSFFormulaParser}
|
||||
* @param workbook A stub {@link Workbook}
|
||||
*/
|
||||
public static HSSFWorkbook createStubHSSFWorkbook(Workbook workbook) {
|
||||
@ -65,11 +65,11 @@ public class EventWorkbookBuilder {
|
||||
|
||||
/**
|
||||
* Creates a stub Workbook from the supplied records,
|
||||
* suitable for use with the {@link FormulaParser}
|
||||
* suitable for use with the {@link HSSFFormulaParser}
|
||||
* @param externs The ExternSheetRecords in your file
|
||||
* @param bounds The BoundSheetRecords in your file
|
||||
* @param sst The SSTRecord in your file.
|
||||
* @return A stub Workbook suitable for use with {@link FormulaParser}
|
||||
* @return A stub Workbook suitable for use with {@link HSSFFormulaParser}
|
||||
*/
|
||||
public static Workbook createStubWorkbook(ExternSheetRecord[] externs,
|
||||
BoundSheetRecord[] bounds, SSTRecord sst) {
|
||||
@ -103,10 +103,10 @@ public class EventWorkbookBuilder {
|
||||
|
||||
/**
|
||||
* Creates a stub workbook from the supplied records,
|
||||
* suitable for use with the {@link FormulaParser}
|
||||
* suitable for use with the {@link HSSFFormulaParser}
|
||||
* @param externs The ExternSheetRecords in your file
|
||||
* @param bounds The BoundSheetRecords in your file
|
||||
* @return A stub Workbook suitable for use with {@link FormulaParser}
|
||||
* @return A stub Workbook suitable for use with {@link HSSFFormulaParser}
|
||||
*/
|
||||
public static Workbook createStubWorkbook(ExternSheetRecord[] externs,
|
||||
BoundSheetRecord[] bounds) {
|
||||
|
@ -32,7 +32,7 @@ import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFEventFactory;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFListener;
|
||||
import org.apache.poi.hssf.eventusermodel.HSSFRequest;
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.record.BOFRecord;
|
||||
import org.apache.poi.hssf.record.BoundSheetRecord;
|
||||
import org.apache.poi.hssf.record.CellValueRecordInterface;
|
||||
@ -45,6 +45,7 @@ import org.apache.poi.hssf.record.Record;
|
||||
import org.apache.poi.hssf.record.SSTRecord;
|
||||
import org.apache.poi.hssf.record.StringRecord;
|
||||
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||
|
||||
/**
|
||||
@ -177,7 +178,7 @@ public class EventBasedExcelExtractor extends POIOLE2TextExtractor {
|
||||
thisRow = frec.getRow();
|
||||
|
||||
if(formulasNotResults) {
|
||||
thisText = FormulaParser.toFormulaString(null, frec.getParsedExpression());
|
||||
thisText = HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, frec.getParsedExpression());
|
||||
} else {
|
||||
if(frec.hasCachedResultString()) {
|
||||
// Formula result is a string
|
||||
|
72
src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java
Normal file
72
src/java/org/apache/poi/hssf/model/HSSFFormulaParser.java
Normal file
@ -0,0 +1,72 @@
|
||||
/* ====================================================================
|
||||
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.hssf.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.formula.FormulaParser;
|
||||
import org.apache.poi.ss.formula.FormulaParsingWorkbook;
|
||||
import org.apache.poi.ss.formula.FormulaRenderer;
|
||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||
|
||||
/**
|
||||
* HSSF wrapper for the {@link FormulaParser}
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class HSSFFormulaParser {
|
||||
|
||||
private static FormulaParsingWorkbook createParsingWorkbook(HSSFWorkbook book) {
|
||||
return HSSFEvaluationWorkbook.create(book);
|
||||
}
|
||||
|
||||
private HSSFFormulaParser() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
public static Ptg[] parse(String formula, HSSFWorkbook workbook) {
|
||||
return FormulaParser.parse(formula, createParsingWorkbook(workbook));
|
||||
}
|
||||
|
||||
public static Ptg[] parse(String formula, HSSFWorkbook workbook, int formulaType) {
|
||||
return FormulaParser.parse(formula, createParsingWorkbook(workbook), formulaType);
|
||||
}
|
||||
|
||||
public static String toFormulaString(HSSFWorkbook book, List lptgs) {
|
||||
return toFormulaString(HSSFEvaluationWorkbook.create(book), lptgs);
|
||||
}
|
||||
/**
|
||||
* Convenience method which takes in a list then passes it to the
|
||||
* other toFormulaString signature.
|
||||
* @param book workbook for 3D and named references
|
||||
* @param lptgs list of Ptg, can be null or empty
|
||||
* @return a human readable String
|
||||
*/
|
||||
public static String toFormulaString(FormulaRenderingWorkbook book, List lptgs) {
|
||||
Ptg[] ptgs = new Ptg[lptgs.size()];
|
||||
lptgs.toArray(ptgs);
|
||||
return FormulaRenderer.toFormulaString(book, ptgs);
|
||||
}
|
||||
|
||||
public static String toFormulaString(HSSFWorkbook book, Ptg[] ptgs) {
|
||||
return FormulaRenderer.toFormulaString(HSSFEvaluationWorkbook.create(book), ptgs);
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
|
||||
package org.apache.poi.hssf.record;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.record.cf.BorderFormatting;
|
||||
import org.apache.poi.hssf.record.cf.FontFormatting;
|
||||
import org.apache.poi.hssf.record.cf.PatternFormatting;
|
||||
@ -595,6 +595,6 @@ public final class CFRuleRecord extends Record {
|
||||
if(formula == null) {
|
||||
return null;
|
||||
}
|
||||
return FormulaParser.parse(formula, workbook);
|
||||
return HSSFFormulaParser.parse(formula, workbook);
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ package org.apache.poi.hssf.record;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.record.formula.Area3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
@ -495,7 +495,7 @@ public final class NameRecord extends Record {
|
||||
* @return area reference
|
||||
*/
|
||||
public String getAreaReference(HSSFWorkbook book){
|
||||
return FormulaParser.toFormulaString(book, field_13_name_definition);
|
||||
return HSSFFormulaParser.toFormulaString(book, field_13_name_definition);
|
||||
}
|
||||
|
||||
/** sets the reference , the area only (range)
|
||||
|
@ -24,13 +24,13 @@ import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook;
|
||||
|
||||
public final class AnalysisToolPak {
|
||||
|
||||
private static final FreeRefFunction NotImplemented = new FreeRefFunction() {
|
||||
|
||||
public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet,
|
||||
public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet,
|
||||
int srcCellRow, int srcCellCol) {
|
||||
return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula.atp;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
@ -25,8 +24,7 @@ import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperandResolver;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook;
|
||||
/**
|
||||
* Implementation of Excel 'Analysis ToolPak' function ISEVEN() ISODD()<br/>
|
||||
*
|
||||
@ -42,7 +40,7 @@ final class ParityFunction implements FreeRefFunction {
|
||||
_desiredParity = desiredParity;
|
||||
}
|
||||
|
||||
public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow,
|
||||
public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow,
|
||||
int srcCellCol) {
|
||||
if (args.length != 1) {
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
|
@ -21,7 +21,6 @@ import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EvaluationException;
|
||||
@ -31,8 +30,7 @@ import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook;
|
||||
/**
|
||||
* Implementation of Excel 'Analysis ToolPak' function YEARFRAC()<br/>
|
||||
*
|
||||
@ -61,7 +59,7 @@ final class YearFrac implements FreeRefFunction {
|
||||
// enforce singleton
|
||||
}
|
||||
|
||||
public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow,
|
||||
public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow,
|
||||
int srcCellCol) {
|
||||
|
||||
double result;
|
||||
|
@ -19,7 +19,7 @@ package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.atp.AnalysisToolPak;
|
||||
import org.apache.poi.hssf.record.formula.functions.FreeRefFunction;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook;
|
||||
/**
|
||||
*
|
||||
* Common entry point for all user-defined (non-built-in) functions (where
|
||||
@ -30,7 +30,7 @@ import org.apache.poi.ss.usermodel.Workbook;
|
||||
*/
|
||||
final class ExternalFunction implements FreeRefFunction {
|
||||
|
||||
public ValueEval evaluate(Eval[] args, Workbook workbook,
|
||||
public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook,
|
||||
int srcCellSheet, int srcCellRow,int srcCellCol) {
|
||||
|
||||
int nIncomingArgs = args.length;
|
||||
@ -58,9 +58,9 @@ final class ExternalFunction implements FreeRefFunction {
|
||||
return targetFunc.evaluate(outGoingArgs, workbook, srcCellSheet, srcCellRow, srcCellCol);
|
||||
}
|
||||
|
||||
private FreeRefFunction findExternalUserDefinedFunction(Workbook workbook,
|
||||
private FreeRefFunction findExternalUserDefinedFunction(EvaluationWorkbook workbook,
|
||||
NameXEval n) throws EvaluationException {
|
||||
String functionName = workbook.resolveNameXText(n.getSheetRefIndex(), n.getNameNumber());
|
||||
String functionName = workbook.resolveNameXText(n.getPtg());
|
||||
|
||||
if(false) {
|
||||
System.out.println("received call to external user defined function (" + functionName + ")");
|
||||
@ -75,6 +75,7 @@ final class ExternalFunction implements FreeRefFunction {
|
||||
}
|
||||
|
||||
private FreeRefFunction findInternalUserDefinedFunction(NameEval functionNameEval) throws EvaluationException {
|
||||
|
||||
String functionName = functionNameEval.getFunctionName();
|
||||
if(false) {
|
||||
System.out.println("received call to internal user defined function (" + functionName + ")");
|
||||
|
@ -17,32 +17,27 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula.eval;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
|
||||
/**
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class NameXEval implements Eval {
|
||||
|
||||
/** index to REF entry in externsheet record */
|
||||
private final int _sheetRefIndex;
|
||||
/** index to defined name or externname table(1 based) */
|
||||
private final int _nameNumber;
|
||||
private final NameXPtg _ptg;
|
||||
|
||||
public NameXEval(int sheetRefIndex, int nameNumber) {
|
||||
_sheetRefIndex = sheetRefIndex;
|
||||
_nameNumber = nameNumber;
|
||||
public NameXEval(NameXPtg ptg) {
|
||||
_ptg = ptg;
|
||||
}
|
||||
|
||||
public int getSheetRefIndex() {
|
||||
return _sheetRefIndex;
|
||||
}
|
||||
public int getNameNumber() {
|
||||
return _nameNumber;
|
||||
public NameXPtg getPtg() {
|
||||
return _ptg;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(_sheetRefIndex).append(", ").append(_nameNumber);
|
||||
sb.append(_ptg.getSheetRefIndex()).append(", ").append(_ptg.getNameIndex());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -19,8 +19,7 @@ package org.apache.poi.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook;
|
||||
|
||||
|
||||
/**
|
||||
@ -53,5 +52,5 @@ public interface FreeRefFunction {
|
||||
* a specified Excel error (Exceptions are never thrown to represent Excel errors).
|
||||
*
|
||||
*/
|
||||
ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol);
|
||||
ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol);
|
||||
}
|
||||
|
@ -1,27 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.hssf.record.formula.functions;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook;
|
||||
|
||||
/**
|
||||
* Implementation for Excel function INDIRECT<p/>
|
||||
@ -41,7 +40,7 @@ import org.apache.poi.ss.usermodel.Workbook;
|
||||
*/
|
||||
public final class Indirect implements FreeRefFunction {
|
||||
|
||||
public ValueEval evaluate(Eval[] args, Workbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol) {
|
||||
public ValueEval evaluate(Eval[] args, EvaluationWorkbook workbook, int srcCellSheet, int srcCellRow, int srcCellCol) {
|
||||
// TODO - implement INDIRECT()
|
||||
return ErrorEval.FUNCTION_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
@ -20,10 +20,12 @@ import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
import org.apache.poi.ss.formula.FormulaParser;
|
||||
import org.apache.poi.ss.formula.FormulaType;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -339,7 +341,7 @@ public class DVConstraint {
|
||||
|
||||
if (_explicitListValues == null) {
|
||||
// formula is parsed with slightly different RVA rules: (root node type must be 'reference')
|
||||
return FormulaParser.parse(_formula1, workbook, FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST);
|
||||
return HSSFFormulaParser.parse(_formula1, workbook, FormulaType.DATAVALIDATION_LIST);
|
||||
// To do: Excel places restrictions on the available operations within a list formula.
|
||||
// Some things like union and intersection are not allowed.
|
||||
}
|
||||
@ -369,7 +371,7 @@ public class DVConstraint {
|
||||
if (value != null) {
|
||||
throw new IllegalStateException("Both formula and value cannot be present");
|
||||
}
|
||||
return FormulaParser.parse(formula, workbook);
|
||||
return HSSFFormulaParser.parse(formula, workbook);
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.model.Sheet;
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.BlankRecord;
|
||||
@ -513,19 +513,15 @@ public class HSSFCell implements Cell {
|
||||
}
|
||||
|
||||
/**
|
||||
* set a string value for the cell. Please note that if you are using
|
||||
* full 16 bit unicode you should call <code>setEncoding()</code> first.
|
||||
* set a string value for the cell.
|
||||
*
|
||||
* @param value value to set the cell to. For formulas we'll set the formula
|
||||
* string, for String cells we'll set its value. For other types we will
|
||||
* cached string result, for String cells we'll set its value. For other types we will
|
||||
* change the cell to a string cell and set its value.
|
||||
* If value is null then we will change the cell to a Blank cell.
|
||||
* @deprecated Use setCellValue(HSSFRichTextString) instead.
|
||||
*/
|
||||
|
||||
public void setCellValue(String value)
|
||||
{
|
||||
HSSFRichTextString str = new HSSFRichTextString(value);
|
||||
public void setCellValue(String value) {
|
||||
HSSFRichTextString str = value == null ? null : new HSSFRichTextString(value);
|
||||
setCellValue(str);
|
||||
}
|
||||
|
||||
@ -597,12 +593,12 @@ public class HSSFCell implements Cell {
|
||||
if (rec.getXFIndex() == (short)0) {
|
||||
rec.setXFIndex((short) 0x0f);
|
||||
}
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, book);
|
||||
Ptg[] ptgs = HSSFFormulaParser.parse(formula, book);
|
||||
frec.setParsedExpression(ptgs);
|
||||
}
|
||||
|
||||
public String getCellFormula() {
|
||||
return FormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
|
||||
return HSSFFormulaParser.toFormulaString(book, ((FormulaRecordAggregate)record).getFormulaRecord().getParsedExpression());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.record.CFRuleRecord;
|
||||
import org.apache.poi.hssf.record.CFRuleRecord.ComparisonOperator;
|
||||
import org.apache.poi.hssf.record.cf.BorderFormatting;
|
||||
@ -205,6 +205,6 @@ public final class HSSFConditionalFormattingRule
|
||||
if(parsedExpression ==null) {
|
||||
return null;
|
||||
}
|
||||
return FormulaParser.toFormulaString(workbook, parsedExpression);
|
||||
return HSSFFormulaParser.toFormulaString(workbook, parsedExpression);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,26 @@
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.NameRecord;
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.formula.EvaluationName;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook;
|
||||
import org.apache.poi.ss.formula.FormulaParsingWorkbook;
|
||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
/**
|
||||
* Internal POI use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook {
|
||||
public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {
|
||||
|
||||
private final HSSFWorkbook _uBook;
|
||||
private final Workbook _iBook;
|
||||
|
||||
public static HSSFEvaluationWorkbook create(HSSFWorkbook book) {
|
||||
@ -22,9 +31,58 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook {
|
||||
}
|
||||
|
||||
private HSSFEvaluationWorkbook(HSSFWorkbook book) {
|
||||
_uBook = book;
|
||||
_iBook = book.getWorkbook();
|
||||
}
|
||||
|
||||
public int getExternalSheetIndex(String sheetName) {
|
||||
int sheetIndex = _uBook.getSheetIndex(sheetName);
|
||||
return _iBook.checkExternSheet(sheetIndex);
|
||||
}
|
||||
|
||||
public EvaluationName getName(int index) {
|
||||
return new Name(_iBook.getNameRecord(index), index);
|
||||
}
|
||||
|
||||
public EvaluationName getName(String name) {
|
||||
for(int i=0; i < _iBook.getNumNames(); i++) {
|
||||
NameRecord nr = _iBook.getNameRecord(i);
|
||||
if (name.equalsIgnoreCase(nr.getNameText())) {
|
||||
return new Name(nr, i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getSheetIndex(Sheet sheet) {
|
||||
return _uBook.getSheetIndex(sheet);
|
||||
}
|
||||
|
||||
public String getSheetName(int sheetIndex) {
|
||||
return _uBook.getSheetName(sheetIndex);
|
||||
}
|
||||
|
||||
public int getNameIndex(String name) {
|
||||
return _uBook.getNameIndex(name);
|
||||
}
|
||||
|
||||
public NameXPtg getNameXPtg(String name) {
|
||||
return _iBook.getNameXPtg(name);
|
||||
}
|
||||
|
||||
public Sheet getSheet(int sheetIndex) {
|
||||
return _uBook.getSheetAt(sheetIndex);
|
||||
}
|
||||
|
||||
public Sheet getSheetByExternSheetIndex(int externSheetIndex) {
|
||||
int sheetIndex = _iBook.getSheetIndexFromExternSheetIndex(externSheetIndex);
|
||||
return _uBook.getSheetAt(sheetIndex);
|
||||
}
|
||||
|
||||
public HSSFWorkbook getWorkbook() {
|
||||
return _uBook;
|
||||
}
|
||||
|
||||
public String resolveNameXText(NameXPtg n) {
|
||||
return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex());
|
||||
}
|
||||
@ -35,4 +93,45 @@ public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook {
|
||||
public String getNameText(NamePtg namePtg) {
|
||||
return _iBook.getNameRecord(namePtg.getIndex()).getNameText();
|
||||
}
|
||||
public EvaluationName getName(NamePtg namePtg) {
|
||||
int ix = namePtg.getIndex();
|
||||
return new Name(_iBook.getNameRecord(ix), ix);
|
||||
}
|
||||
public Ptg[] getFormulaTokens(Cell cell) {
|
||||
return HSSFFormulaParser.parse(cell.getCellFormula(), _uBook);
|
||||
}
|
||||
|
||||
private static final class Name implements EvaluationName {
|
||||
|
||||
private final NameRecord _nameRecord;
|
||||
private final int _index;
|
||||
|
||||
public Name(NameRecord nameRecord, int index) {
|
||||
_nameRecord = nameRecord;
|
||||
_index = index;
|
||||
}
|
||||
|
||||
public Ptg[] getNameDefinition() {
|
||||
return _nameRecord.getNameDefinition();
|
||||
}
|
||||
|
||||
public String getNameText() {
|
||||
return _nameRecord.getNameText();
|
||||
}
|
||||
|
||||
public boolean hasFormula() {
|
||||
return _nameRecord.hasFormula();
|
||||
}
|
||||
|
||||
public boolean isFunctionName() {
|
||||
return _nameRecord.isFunctionName();
|
||||
}
|
||||
|
||||
public boolean isRange() {
|
||||
return _nameRecord.hasFormula(); // TODO - is this right?
|
||||
}
|
||||
public NamePtg createPtg() {
|
||||
return new NamePtg(_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,75 +1,272 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/* ====================================================================
|
||||
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.hssf.usermodel;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.formula.WorkbookEvaluator;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
/**
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* Evaluates formula cells.<p/>
|
||||
*
|
||||
* For performance reasons, this class keeps a cache of all previously calculated intermediate
|
||||
* cell values. Be sure to call {@link #clearCache()} if any workbook cells are changed between
|
||||
* calls to evaluate~ methods on this class.
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class HSSFFormulaEvaluator /* almost implements FormulaEvaluator */ {
|
||||
|
||||
private WorkbookEvaluator _bookEvaluator;
|
||||
|
||||
/**
|
||||
* @deprecated (Sep 2008) HSSFSheet parameter is ignored
|
||||
*/
|
||||
public class HSSFFormulaEvaluator extends FormulaEvaluator {
|
||||
public HSSFFormulaEvaluator(HSSFSheet sheet, HSSFWorkbook workbook) {
|
||||
super(sheet, workbook);
|
||||
this(workbook);
|
||||
if (false) {
|
||||
sheet.toString(); // suppress unused parameter compiler warning
|
||||
}
|
||||
}
|
||||
public HSSFFormulaEvaluator(HSSFWorkbook workbook) {
|
||||
super(workbook);
|
||||
_bookEvaluator = new WorkbookEvaluator(HSSFEvaluationWorkbook.create(workbook));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an underlying FormulaParser, for the specified
|
||||
* Formula String and HSSFWorkbook.
|
||||
* This will allow you to generate the Ptgs yourself, if
|
||||
* your needs are more complex than just having the
|
||||
* formula evaluated.
|
||||
* TODO for debug/test use
|
||||
*/
|
||||
public static FormulaParser getUnderlyingParser(Workbook workbook, String formula) {
|
||||
return new FormulaParser(formula, workbook);
|
||||
/* package */ int getEvaluationCount() {
|
||||
return _bookEvaluator.getEvaluationCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* debug method
|
||||
* Does nothing
|
||||
* @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
|
||||
*/
|
||||
void inspectPtgs(String formula) {
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, _workbook);
|
||||
System.out.println("<ptg-group>");
|
||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
||||
System.out.println("<ptg>");
|
||||
System.out.println(ptgs[i]);
|
||||
if (ptgs[i] instanceof OperationPtg) {
|
||||
System.out.println("numoperands: " + ((OperationPtg) ptgs[i]).getNumberOfOperands());
|
||||
public void setCurrentRow(HSSFRow row) {
|
||||
// do nothing
|
||||
if (false) {
|
||||
row.getClass(); // suppress unused parameter compiler warning
|
||||
}
|
||||
System.out.println("</ptg>");
|
||||
}
|
||||
System.out.println("</ptg-group>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility class.
|
||||
* Seems to do more harm than good though
|
||||
* Should be called whenever there are major changes (e.g. moving sheets) 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 static class CellValue extends FormulaEvaluator.CellValue {
|
||||
// public CellValue(int cellType, CreationHelper creationHelper) {
|
||||
// super(cellType, creationHelper);
|
||||
// }
|
||||
// }
|
||||
public void clearAllCachedResultValues() {
|
||||
_bookEvaluator.clearAllCachedResultValues();
|
||||
}
|
||||
/**
|
||||
* Should be called whenever there are changes to individual 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 clearCachedResultValue(Sheet sheet, int rowIndex, int columnIndex) {
|
||||
_bookEvaluator.clearCachedResultValue(sheet, rowIndex, columnIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* If cell contains a formula, the formula is evaluated and returned,
|
||||
* else the CellValue simply copies the appropriate cell value from
|
||||
* the cell and also its cell type. This method should be preferred over
|
||||
* evaluateInCell() when the call should not modify the contents of the
|
||||
* original cell.
|
||||
* @param cell
|
||||
*/
|
||||
public CellValue evaluate(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (cell.getCellType()) {
|
||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
||||
return CellValue.valueOf(cell.getBooleanCellValue());
|
||||
case HSSFCell.CELL_TYPE_ERROR:
|
||||
return CellValue.getError(cell.getErrorCellValue());
|
||||
case HSSFCell.CELL_TYPE_FORMULA:
|
||||
return evaluateFormulaCellValue(cell);
|
||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
||||
return new CellValue(cell.getNumericCellValue());
|
||||
case HSSFCell.CELL_TYPE_STRING:
|
||||
return new CellValue(cell.getRichStringCellValue().getString());
|
||||
}
|
||||
throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If cell contains formula, it evaluates the formula,
|
||||
* and saves the result of the formula. The cell
|
||||
* remains as a formula cell.
|
||||
* Else if cell does not contain formula, this method leaves
|
||||
* the cell unchanged.
|
||||
* Note that the type of the formula result is returned,
|
||||
* so you know what kind of value is also stored with
|
||||
* the formula.
|
||||
* <pre>
|
||||
* int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
|
||||
* </pre>
|
||||
* Be aware that your cell will hold both the formula,
|
||||
* and the result. If you want the cell replaced with
|
||||
* the result of the formula, use {@link #evaluateInCell(HSSFCell)}
|
||||
* @param cell The cell to evaluate
|
||||
* @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however)
|
||||
*/
|
||||
public int evaluateFormulaCell(Cell cell) {
|
||||
if (cell == null || cell.getCellType() != HSSFCell.CELL_TYPE_FORMULA) {
|
||||
return -1;
|
||||
}
|
||||
CellValue cv = evaluateFormulaCellValue(cell);
|
||||
// cell remains a formula cell, but the cached value is changed
|
||||
setCellValue(cell, cv);
|
||||
return cv.getCellType();
|
||||
}
|
||||
|
||||
/**
|
||||
* If cell contains formula, it evaluates the formula, and
|
||||
* puts the formula result back into the cell, in place
|
||||
* of the old formula.
|
||||
* Else if cell does not contain formula, this method leaves
|
||||
* the cell unchanged.
|
||||
* Note that the same instance of HSSFCell is returned to
|
||||
* allow chained calls like:
|
||||
* <pre>
|
||||
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
|
||||
* </pre>
|
||||
* Be aware that your cell value will be changed to hold the
|
||||
* result of the formula. If you simply want the formula
|
||||
* value computed for you, use {@link #evaluateFormulaCell(HSSFCell)}
|
||||
* @param cell
|
||||
*/
|
||||
public HSSFCell evaluateInCell(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
HSSFCell result = (HSSFCell) cell;
|
||||
if (cell.getCellType() == HSSFCell.CELL_TYPE_FORMULA) {
|
||||
CellValue cv = evaluateFormulaCellValue(cell);
|
||||
setCellType(cell, cv); // cell will no longer be a formula cell
|
||||
setCellValue(cell, cv);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private static void setCellType(Cell cell, CellValue cv) {
|
||||
int cellType = cv.getCellType();
|
||||
switch (cellType) {
|
||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
||||
case HSSFCell.CELL_TYPE_ERROR:
|
||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
||||
case HSSFCell.CELL_TYPE_STRING:
|
||||
cell.setCellType(cellType);
|
||||
return;
|
||||
case HSSFCell.CELL_TYPE_BLANK:
|
||||
// never happens - blanks eventually get translated to zero
|
||||
case HSSFCell.CELL_TYPE_FORMULA:
|
||||
// this will never happen, we have already evaluated the formula
|
||||
}
|
||||
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
|
||||
}
|
||||
|
||||
private static void setCellValue(Cell cell, CellValue cv) {
|
||||
int cellType = cv.getCellType();
|
||||
switch (cellType) {
|
||||
case HSSFCell.CELL_TYPE_BOOLEAN:
|
||||
cell.setCellValue(cv.getBooleanValue());
|
||||
break;
|
||||
case HSSFCell.CELL_TYPE_ERROR:
|
||||
cell.setCellErrorValue(cv.getErrorValue());
|
||||
break;
|
||||
case HSSFCell.CELL_TYPE_NUMERIC:
|
||||
cell.setCellValue(cv.getNumberValue());
|
||||
break;
|
||||
case HSSFCell.CELL_TYPE_STRING:
|
||||
cell.setCellValue(new HSSFRichTextString(cv.getStringValue()));
|
||||
break;
|
||||
case HSSFCell.CELL_TYPE_BLANK:
|
||||
// never happens - blanks eventually get translated to zero
|
||||
case HSSFCell.CELL_TYPE_FORMULA:
|
||||
// this will never happen, we have already evaluated the formula
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops over all cells in all sheets of the supplied
|
||||
* workbook.
|
||||
* For cells that contain formulas, their formulas are
|
||||
* evaluated, and the results are saved. These cells
|
||||
* remain as formula cells.
|
||||
* For cells that do not contain formulas, no changes
|
||||
* are made.
|
||||
* This is a helpful wrapper around looping over all
|
||||
* cells, and calling evaluateFormulaCell on each one.
|
||||
*/
|
||||
public static void evaluateAllFormulaCells(HSSFWorkbook wb) {
|
||||
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(wb);
|
||||
for(int i=0; i<wb.getNumberOfSheets(); i++) {
|
||||
HSSFSheet sheet = wb.getSheetAt(i);
|
||||
|
||||
for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
|
||||
HSSFRow r = (HSSFRow)rit.next();
|
||||
|
||||
for (Iterator cit = r.cellIterator(); cit.hasNext();) {
|
||||
HSSFCell c = (HSSFCell)cit.next();
|
||||
if (c.getCellType() == HSSFCell.CELL_TYPE_FORMULA)
|
||||
evaluator.evaluateFormulaCell(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CellValue wrapper around the supplied ValueEval instance.
|
||||
* @param eval
|
||||
*/
|
||||
private CellValue evaluateFormulaCellValue(Cell cell) {
|
||||
ValueEval eval = _bookEvaluator.evaluate(cell);
|
||||
if (eval instanceof NumberEval) {
|
||||
NumberEval ne = (NumberEval) eval;
|
||||
return new CellValue(ne.getNumberValue());
|
||||
}
|
||||
if (eval instanceof BoolEval) {
|
||||
BoolEval be = (BoolEval) eval;
|
||||
return CellValue.valueOf(be.getBooleanValue());
|
||||
}
|
||||
if (eval instanceof StringEval) {
|
||||
StringEval ne = (StringEval) eval;
|
||||
return new CellValue(ne.getStringValue());
|
||||
}
|
||||
if (eval instanceof ErrorEval) {
|
||||
return CellValue.getError(((ErrorEval)eval).getErrorCode());
|
||||
}
|
||||
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import org.apache.poi.ss.usermodel.Name;
|
||||
*
|
||||
* @author Libin Roman (Vista Portal LDT. Developer)
|
||||
*/
|
||||
public class HSSFName implements Name {
|
||||
public final class HSSFName implements Name {
|
||||
private HSSFWorkbook _book;
|
||||
private NameRecord _definedNameRec;
|
||||
|
||||
@ -96,8 +96,7 @@ public class HSSFName implements Name {
|
||||
*/
|
||||
private void setSheetName(String sheetName){
|
||||
int sheetNumber = _book.getSheetIndex(sheetName);
|
||||
short externSheetNumber = (short)
|
||||
_book.getExternalSheetIndex(sheetNumber);
|
||||
short externSheetNumber = _book.getWorkbook().checkExternSheet(sheetNumber);
|
||||
_definedNameRec.setExternSheetNumber(externSheetNumber);
|
||||
}
|
||||
|
||||
|
@ -662,17 +662,8 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* package */ int findSheetIndex(Sheet sheet) {
|
||||
for(int i=0; i<_sheets.size(); i++) {
|
||||
HSSFSheet hSheet = (HSSFSheet) _sheets.get(i);
|
||||
if(hSheet.getSheet() == sheet) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Specified sheet not found in this workbook");
|
||||
}
|
||||
|
||||
/**
|
||||
<<<<<<< .working
|
||||
* Returns the external sheet index of the sheet
|
||||
* with the given internal index, creating one
|
||||
* if needed.
|
||||
@ -709,6 +700,8 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
|
||||
|
||||
|
||||
/**
|
||||
=======
|
||||
>>>>>>> .merge-right.r696898
|
||||
* create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns
|
||||
* the high level representation. Use this to create new sheets.
|
||||
*
|
||||
@ -751,7 +744,7 @@ public class HSSFWorkbook extends POIDocument implements org.apache.poi.ss.userm
|
||||
if (filterDbNameIndex >=0) {
|
||||
NameRecord origNameRecord = workbook.getNameRecord(filterDbNameIndex);
|
||||
// copy original formula but adjust 3D refs to the new external sheet index
|
||||
int newExtSheetIx = getExternalSheetIndex(newSheetIndex);
|
||||
int newExtSheetIx = workbook.checkExternSheet(newSheetIndex);
|
||||
Ptg[] ptgs = origNameRecord.getNameDefinition();
|
||||
for (int i=0; i< ptgs.length; i++) {
|
||||
Ptg ptg = ptgs[i];
|
||||
|
@ -1,184 +0,0 @@
|
||||
/* ====================================================================
|
||||
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.hssf.usermodel;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AddPtg;
|
||||
import org.apache.poi.hssf.record.formula.ConcatPtg;
|
||||
import org.apache.poi.hssf.record.formula.DividePtg;
|
||||
import org.apache.poi.hssf.record.formula.EqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.ExpPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.GreaterEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.GreaterThanPtg;
|
||||
import org.apache.poi.hssf.record.formula.LessEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.LessThanPtg;
|
||||
import org.apache.poi.hssf.record.formula.MultiplyPtg;
|
||||
import org.apache.poi.hssf.record.formula.NotEqualPtg;
|
||||
import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||
import org.apache.poi.hssf.record.formula.PercentPtg;
|
||||
import org.apache.poi.hssf.record.formula.PowerPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.SubtractPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
|
||||
import org.apache.poi.hssf.record.formula.eval.AddEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ConcatEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.DivideEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.EqualEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.FuncVarEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.GreaterEqualEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.GreaterThanEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.LessEqualEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.LessThanEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.MultiplyEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NotEqualEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperationEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.PercentEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.PowerEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.SubtractEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.UnaryMinusEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.UnaryPlusEval;
|
||||
|
||||
/**
|
||||
* This class creates <tt>OperationEval</tt> instances to help evaluate <tt>OperationPtg</tt>
|
||||
* formula tokens.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class OperationEvaluatorFactory {
|
||||
private static final Class[] OPERATION_CONSTRUCTOR_CLASS_ARRAY = new Class[] { Ptg.class };
|
||||
// TODO - use singleton instances directly instead of reflection
|
||||
private static final Map _constructorsByPtgClass = initialiseConstructorsMap();
|
||||
private static final Map _instancesByPtgClass = initialiseInstancesMap();
|
||||
|
||||
private OperationEvaluatorFactory() {
|
||||
// no instances of this class
|
||||
}
|
||||
|
||||
private static Map initialiseConstructorsMap() {
|
||||
Map m = new HashMap(32);
|
||||
add(m, ConcatPtg.class, ConcatEval.class);
|
||||
add(m, FuncPtg.class, FuncVarEval.class);
|
||||
add(m, FuncVarPtg.class, FuncVarEval.class);
|
||||
return m;
|
||||
}
|
||||
private static Map initialiseInstancesMap() {
|
||||
Map m = new HashMap(32);
|
||||
add(m, EqualPtg.class, EqualEval.instance);
|
||||
add(m, GreaterEqualPtg.class, GreaterEqualEval.instance);
|
||||
add(m, GreaterThanPtg.class, GreaterThanEval.instance);
|
||||
add(m, LessEqualPtg.class, LessEqualEval.instance);
|
||||
add(m, LessThanPtg.class, LessThanEval.instance);
|
||||
add(m, NotEqualPtg.class, NotEqualEval.instance);
|
||||
|
||||
add(m, AddPtg.class, AddEval.instance);
|
||||
add(m, DividePtg.class, DivideEval.instance);
|
||||
add(m, MultiplyPtg.class, MultiplyEval.instance);
|
||||
add(m, PercentPtg.class, PercentEval.instance);
|
||||
add(m, PowerPtg.class, PowerEval.instance);
|
||||
add(m, SubtractPtg.class, SubtractEval.instance);
|
||||
add(m, UnaryMinusPtg.class, UnaryMinusEval.instance);
|
||||
add(m, UnaryPlusPtg.class, UnaryPlusEval.instance);
|
||||
return m;
|
||||
}
|
||||
|
||||
private static void add(Map m, Class ptgClass, OperationEval evalInstance) {
|
||||
if(!Ptg.class.isAssignableFrom(ptgClass)) {
|
||||
throw new IllegalArgumentException("Expected Ptg subclass");
|
||||
}
|
||||
m.put(ptgClass, evalInstance);
|
||||
}
|
||||
|
||||
private static void add(Map m, Class ptgClass, Class evalClass) {
|
||||
// perform some validation now, to keep later exception handlers simple
|
||||
if(!Ptg.class.isAssignableFrom(ptgClass)) {
|
||||
throw new IllegalArgumentException("Expected Ptg subclass");
|
||||
}
|
||||
|
||||
if(!OperationEval.class.isAssignableFrom(evalClass)) {
|
||||
throw new IllegalArgumentException("Expected OperationEval subclass");
|
||||
}
|
||||
if (!Modifier.isPublic(evalClass.getModifiers())) {
|
||||
throw new RuntimeException("Eval class must be public");
|
||||
}
|
||||
if (Modifier.isAbstract(evalClass.getModifiers())) {
|
||||
throw new RuntimeException("Eval class must not be abstract");
|
||||
}
|
||||
|
||||
Constructor constructor;
|
||||
try {
|
||||
constructor = evalClass.getDeclaredConstructor(OPERATION_CONSTRUCTOR_CLASS_ARRAY);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException("Missing constructor");
|
||||
}
|
||||
if (!Modifier.isPublic(constructor.getModifiers())) {
|
||||
throw new RuntimeException("Eval constructor must be public");
|
||||
}
|
||||
m.put(ptgClass, constructor);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the OperationEval concrete impl instance corresponding
|
||||
* to the supplied operationPtg
|
||||
*/
|
||||
public static OperationEval create(OperationPtg ptg) {
|
||||
if(ptg == null) {
|
||||
throw new IllegalArgumentException("ptg must not be null");
|
||||
}
|
||||
Object result;
|
||||
|
||||
Class ptgClass = ptg.getClass();
|
||||
|
||||
result = _instancesByPtgClass.get(ptgClass);
|
||||
if (result != null) {
|
||||
return (OperationEval) result;
|
||||
}
|
||||
|
||||
|
||||
Constructor constructor = (Constructor) _constructorsByPtgClass.get(ptgClass);
|
||||
if(constructor == null) {
|
||||
if(ptgClass == ExpPtg.class) {
|
||||
// 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 operation ptg class (" + ptgClass.getName() + ")");
|
||||
}
|
||||
|
||||
Object[] initargs = { ptg };
|
||||
try {
|
||||
result = constructor.newInstance(initargs);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return (OperationEval) result;
|
||||
}
|
||||
}
|
71
src/java/org/apache/poi/ss/formula/CellEvaluationFrame.java
Normal file
71
src/java/org/apache/poi/ss/formula/CellEvaluationFrame.java
Normal file
@ -0,0 +1,71 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
/**
|
||||
* Stores the parameters that identify the evaluation of one cell.<br/>
|
||||
*/
|
||||
final class CellEvaluationFrame {
|
||||
|
||||
private final int _sheetIndex;
|
||||
private final int _srcRowNum;
|
||||
private final int _srcColNum;
|
||||
private final int _hashCode;
|
||||
|
||||
public CellEvaluationFrame(int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
if (sheetIndex < 0) {
|
||||
throw new IllegalArgumentException("sheetIndex must not be negative");
|
||||
}
|
||||
_sheetIndex = sheetIndex;
|
||||
_srcRowNum = srcRowNum;
|
||||
_srcColNum = srcColNum;
|
||||
_hashCode = sheetIndex + 17 * (srcRowNum + 17 * srcColNum);
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
CellEvaluationFrame other = (CellEvaluationFrame) obj;
|
||||
if (_sheetIndex != other._sheetIndex) {
|
||||
return false;
|
||||
}
|
||||
if (_srcRowNum != other._srcRowNum) {
|
||||
return false;
|
||||
}
|
||||
if (_srcColNum != other._srcColNum) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public int hashCode() {
|
||||
return _hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return human readable string for debug purposes
|
||||
*/
|
||||
public String formatAsString() {
|
||||
return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _sheetIndex;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(formatAsString());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
54
src/java/org/apache/poi/ss/formula/CellEvaluator.java
Normal file
54
src/java/org/apache/poi/ss/formula/CellEvaluator.java
Normal file
@ -0,0 +1,54 @@
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
final class CellEvaluator {
|
||||
|
||||
private final WorkbookEvaluator _bookEvaluator;
|
||||
private final EvaluationTracker _tracker;
|
||||
|
||||
public CellEvaluator(WorkbookEvaluator bookEvaluator, EvaluationTracker tracker) {
|
||||
_bookEvaluator = bookEvaluator;
|
||||
_tracker = tracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a cell, find its type and from that create an appropriate ValueEval
|
||||
* impl instance and return that. Since the cell could be an external
|
||||
* reference, we need the sheet that this belongs to.
|
||||
* Non existent cells are treated as empty.
|
||||
*/
|
||||
public ValueEval getEvalForCell(Cell cell) {
|
||||
|
||||
if (cell == null) {
|
||||
return BlankEval.INSTANCE;
|
||||
}
|
||||
switch (cell.getCellType()) {
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
return new NumberEval(cell.getNumericCellValue());
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
return new StringEval(cell.getRichStringCellValue().getString());
|
||||
case Cell.CELL_TYPE_FORMULA:
|
||||
return _bookEvaluator.internalEvaluate(cell, _tracker);
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
return BoolEval.valueOf(cell.getBooleanCellValue());
|
||||
case Cell.CELL_TYPE_BLANK:
|
||||
return BlankEval.INSTANCE;
|
||||
case Cell.CELL_TYPE_ERROR:
|
||||
return ErrorEval.valueOf(cell.getErrorCellValue());
|
||||
}
|
||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
|
||||
public String getSheetName(Sheet sheet) {
|
||||
return _bookEvaluator.getSheetName(sheet);
|
||||
}
|
||||
|
||||
}
|
@ -15,12 +15,14 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
|
||||
/**
|
||||
* Performance optimisation for {@link HSSFFormulaEvaluator}. This class stores previously
|
||||
@ -31,55 +33,29 @@ import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class EvaluationCache {
|
||||
private static final class Key {
|
||||
|
||||
private final int _sheetIndex;
|
||||
private final int _srcRowNum;
|
||||
private final int _srcColNum;
|
||||
private final int _hashCode;
|
||||
|
||||
public Key(int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
_sheetIndex = sheetIndex;
|
||||
_srcRowNum = srcRowNum;
|
||||
_srcColNum = srcColNum;
|
||||
_hashCode = sheetIndex + srcRowNum + srcColNum;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return _hashCode;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
Key other = (Key) obj;
|
||||
if (_hashCode != other._hashCode) {
|
||||
return false;
|
||||
}
|
||||
if (_sheetIndex != other._sheetIndex) {
|
||||
return false;
|
||||
}
|
||||
if (_srcRowNum != other._srcRowNum) {
|
||||
return false;
|
||||
}
|
||||
if (_srcColNum != other._srcColNum) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static final CellEvaluationFrame[] EMPTY_CEF_ARRAY = { };
|
||||
private final Map _valuesByKey;
|
||||
private final Map _consumingCellsByDest;
|
||||
|
||||
/* package */EvaluationCache() {
|
||||
_valuesByKey = new HashMap();
|
||||
_consumingCellsByDest = new HashMap();
|
||||
}
|
||||
|
||||
public ValueEval getValue(int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
Key key = new Key(sheetIndex, srcRowNum, srcColNum);
|
||||
return getValue(new CellEvaluationFrame(sheetIndex, srcRowNum, srcColNum));
|
||||
}
|
||||
|
||||
/* package */ ValueEval getValue(CellEvaluationFrame key) {
|
||||
return (ValueEval) _valuesByKey.get(key);
|
||||
}
|
||||
|
||||
public void setValue(int sheetIndex, int srcRowNum, int srcColNum, ValueEval value) {
|
||||
Key key = new Key(sheetIndex, srcRowNum, srcColNum);
|
||||
setValue(new CellEvaluationFrame(sheetIndex, srcRowNum, srcColNum), value);
|
||||
}
|
||||
|
||||
/* package */ void setValue(CellEvaluationFrame key, ValueEval value) {
|
||||
if (_valuesByKey.containsKey(key)) {
|
||||
throw new RuntimeException("Already have cached value for this cell");
|
||||
}
|
||||
@ -92,4 +68,32 @@ final class EvaluationCache {
|
||||
public void clear() {
|
||||
_valuesByKey.clear();
|
||||
}
|
||||
|
||||
public void clearValue(int sheetIndex, int rowIndex, int columnIndex) {
|
||||
clearValuesRecursive(new CellEvaluationFrame(sheetIndex, rowIndex, columnIndex));
|
||||
|
||||
}
|
||||
|
||||
private void clearValuesRecursive(CellEvaluationFrame cef) {
|
||||
CellEvaluationFrame[] consumingCells = getConsumingCells(cef);
|
||||
for (int i = 0; i < consumingCells.length; i++) {
|
||||
clearValuesRecursive(consumingCells[i]);
|
||||
}
|
||||
_valuesByKey.remove(cef);
|
||||
_consumingCellsByDest.remove(cef);
|
||||
}
|
||||
|
||||
private CellEvaluationFrame[] getConsumingCells(CellEvaluationFrame cef) {
|
||||
List temp = (List) _consumingCellsByDest.get(cef);
|
||||
if (temp == null) {
|
||||
return EMPTY_CEF_ARRAY;
|
||||
}
|
||||
int nItems = temp.size();
|
||||
if (temp.size() < 1) {
|
||||
return EMPTY_CEF_ARRAY;
|
||||
}
|
||||
CellEvaluationFrame[] result = new CellEvaluationFrame[nItems];
|
||||
temp.toArray(result);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -15,15 +15,27 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
import org.apache.poi.hssf.record.formula.AreaI;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
/**
|
||||
* Abstracts a name record for formula evaluation.<br/>
|
||||
*
|
||||
* For POI internal use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class LazyAreaEval extends org.apache.poi.ss.usermodel.LazyAreaEval {
|
||||
public LazyAreaEval(AreaI ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {
|
||||
super(ptg, sheet, evaluator);
|
||||
}
|
||||
public interface EvaluationName {
|
||||
|
||||
String getNameText();
|
||||
|
||||
boolean isFunctionName();
|
||||
|
||||
boolean hasFormula();
|
||||
|
||||
Ptg[] getNameDefinition();
|
||||
|
||||
boolean isRange();
|
||||
NamePtg createPtg();
|
||||
}
|
@ -15,81 +15,31 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
|
||||
/**
|
||||
* Instances of this class keep track of multiple dependent cell evaluations due
|
||||
* to recursive calls to <tt>FormulaEvaluator.internalEvaluate()</tt>.
|
||||
* to recursive calls to {@link WorkbookEvaluator#evaluate(HSSFCell)}
|
||||
* The main purpose of this class is to detect an attempt to evaluate a cell
|
||||
* that is already being evaluated. In other words, it detects circular
|
||||
* references in spreadsheet formulas.
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class EvaluationCycleDetector {
|
||||
|
||||
/**
|
||||
* Stores the parameters that identify the evaluation of one cell.<br/>
|
||||
*/
|
||||
private static final class CellEvaluationFrame {
|
||||
|
||||
private final Workbook _workbook;
|
||||
private final int _sheetIndex;
|
||||
private final int _srcRowNum;
|
||||
private final int _srcColNum;
|
||||
|
||||
public CellEvaluationFrame(Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
if (workbook == null) {
|
||||
throw new IllegalArgumentException("workbook must not be null");
|
||||
}
|
||||
if (sheetIndex < 0) {
|
||||
throw new IllegalArgumentException("sheetIndex must not be negative");
|
||||
}
|
||||
_workbook = workbook;
|
||||
_sheetIndex = sheetIndex;
|
||||
_srcRowNum = srcRowNum;
|
||||
_srcColNum = srcColNum;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
CellEvaluationFrame other = (CellEvaluationFrame) obj;
|
||||
if (_workbook != other._workbook) {
|
||||
return false;
|
||||
}
|
||||
if (_sheetIndex != other._sheetIndex) {
|
||||
return false;
|
||||
}
|
||||
if (_srcRowNum != other._srcRowNum) {
|
||||
return false;
|
||||
}
|
||||
if (_srcColNum != other._srcColNum) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return human readable string for debug purposes
|
||||
*/
|
||||
public String formatAsString() {
|
||||
return "R=" + _srcRowNum + " C=" + _srcColNum + " ShIx=" + _sheetIndex;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(formatAsString());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
final class EvaluationTracker {
|
||||
|
||||
private final List _evaluationFrames;
|
||||
private final EvaluationCache _cache;
|
||||
|
||||
public EvaluationCycleDetector() {
|
||||
public EvaluationTracker(EvaluationCache cache) {
|
||||
_cache = cache;
|
||||
_evaluationFrames = new ArrayList();
|
||||
}
|
||||
|
||||
@ -108,13 +58,16 @@ final class EvaluationCycleDetector {
|
||||
* @return <code>true</code> if the specified cell has not been visited yet in the current
|
||||
* evaluation. <code>false</code> if the specified cell is already being evaluated.
|
||||
*/
|
||||
public boolean startEvaluate(Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
CellEvaluationFrame cef = new CellEvaluationFrame(workbook, sheetIndex, srcRowNum, srcColNum);
|
||||
public ValueEval startEvaluate(int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
CellEvaluationFrame cef = new CellEvaluationFrame(sheetIndex, srcRowNum, srcColNum);
|
||||
if (_evaluationFrames.contains(cef)) {
|
||||
return false;
|
||||
return ErrorEval.CIRCULAR_REF_ERROR;
|
||||
}
|
||||
ValueEval result = _cache.getValue(cef);
|
||||
if (result == null) {
|
||||
_evaluationFrames.add(cef);
|
||||
return true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,8 +81,9 @@ final class EvaluationCycleDetector {
|
||||
* Assuming a well behaved client, parameters to this method would not be
|
||||
* required. However, they have been included to assert correct behaviour,
|
||||
* and form more meaningful error messages.
|
||||
* @param result
|
||||
*/
|
||||
public void endEvaluate(Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
public void endEvaluate(int sheetIndex, int srcRowNum, int srcColNum, ValueEval result) {
|
||||
int nFrames = _evaluationFrames.size();
|
||||
if (nFrames < 1) {
|
||||
throw new IllegalStateException("Call to endEvaluate without matching call to startEvaluate");
|
||||
@ -137,7 +91,7 @@ final class EvaluationCycleDetector {
|
||||
|
||||
nFrames--;
|
||||
CellEvaluationFrame cefExpected = (CellEvaluationFrame) _evaluationFrames.get(nFrames);
|
||||
CellEvaluationFrame cefActual = new CellEvaluationFrame(workbook, sheetIndex, srcRowNum, srcColNum);
|
||||
CellEvaluationFrame cefActual = new CellEvaluationFrame(sheetIndex, srcRowNum, srcColNum);
|
||||
if (!cefActual.equals(cefExpected)) {
|
||||
throw new RuntimeException("Wrong cell specified. "
|
||||
+ "Corresponding startEvaluate() call was for cell {"
|
||||
@ -146,5 +100,7 @@ final class EvaluationCycleDetector {
|
||||
}
|
||||
// else - no problems so pop current frame
|
||||
_evaluationFrames.remove(nFrames);
|
||||
|
||||
_cache.setValue(cefActual, result);
|
||||
}
|
||||
}
|
42
src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java
Normal file
42
src/java/org/apache/poi/ss/formula/EvaluationWorkbook.java
Normal file
@ -0,0 +1,42 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
/**
|
||||
* Abstracts a workbook for the purpose of formula evaluation.<br/>
|
||||
*
|
||||
* For POI internal use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public interface EvaluationWorkbook {
|
||||
String getSheetName(int sheetIndex);
|
||||
int getSheetIndex(Sheet sheet);
|
||||
|
||||
Sheet getSheet(int sheetIndex);
|
||||
|
||||
Sheet getSheetByExternSheetIndex(int externSheetIndex);
|
||||
EvaluationName getName(NamePtg namePtg);
|
||||
String resolveNameXText(NameXPtg ptg);
|
||||
Ptg[] getFormulaTokens(Cell cell);
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.model;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -57,16 +57,10 @@ import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
|
||||
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
|
||||
import org.apache.poi.ss.usermodel.Name;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
|
||||
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
|
||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.hssf.util.AreaReference;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.hssf.util.CellReference.NameType;
|
||||
import org.apache.poi.ss.formula.FormulaRenderer;
|
||||
|
||||
/**
|
||||
* This class parses a formula string into a List of tokens in RPN order.
|
||||
@ -84,6 +78,7 @@ import org.apache.poi.ss.formula.FormulaRenderer;
|
||||
* @author Cameron Riley (criley at ekmail.com)
|
||||
* @author Peter M. Murray (pete at quantrix dot com)
|
||||
* @author Pavel Krupets (pkrupets at palmtreebusiness dot com)
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class FormulaParser {
|
||||
|
||||
@ -100,14 +95,6 @@ public final class FormulaParser {
|
||||
}
|
||||
}
|
||||
|
||||
public static final int FORMULA_TYPE_CELL = 0;
|
||||
public static final int FORMULA_TYPE_SHARED = 1;
|
||||
public static final int FORMULA_TYPE_ARRAY =2;
|
||||
public static final int FORMULA_TYPE_CONDFORMAT = 3;
|
||||
public static final int FORMULA_TYPE_NAMEDRANGE = 4;
|
||||
// this constant is currently very specific. The exact differences from general data
|
||||
// validation formulas or conditional format formulas is not known yet
|
||||
public static final int FORMULA_TYPE_DATAVALIDATION_LIST = 5;
|
||||
|
||||
private final String formulaString;
|
||||
private final int formulaLength;
|
||||
@ -123,7 +110,8 @@ public final class FormulaParser {
|
||||
*/
|
||||
private char look;
|
||||
|
||||
private Workbook book;
|
||||
private FormulaParsingWorkbook book;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@ -134,23 +122,23 @@ public final class FormulaParser {
|
||||
* parse results.
|
||||
* This class is recommended only for single threaded use.
|
||||
*
|
||||
* If you only have a usermodel.Workbook, and not a
|
||||
* If you only have a usermodel.HSSFWorkbook, and not a
|
||||
* model.Workbook, then use the convenience method on
|
||||
* usermodel.HSSFFormulaEvaluator
|
||||
*/
|
||||
public FormulaParser(String formula, Workbook book){
|
||||
private FormulaParser(String formula, FormulaParsingWorkbook book){
|
||||
formulaString = formula;
|
||||
pointer=0;
|
||||
this.book = book;
|
||||
formulaLength = formulaString.length();
|
||||
}
|
||||
|
||||
public static Ptg[] parse(String formula, Workbook book) {
|
||||
return parse(formula, book, FORMULA_TYPE_CELL);
|
||||
public static Ptg[] parse(String formula, FormulaParsingWorkbook book) {
|
||||
return parse(formula, book, FormulaType.CELL);
|
||||
}
|
||||
|
||||
public static Ptg[] parse(String formula, Workbook workbook, int formulaType) {
|
||||
FormulaParser fp = HSSFFormulaEvaluator.getUnderlyingParser(workbook, formula);
|
||||
public static Ptg[] parse(String formula, FormulaParsingWorkbook workbook, int formulaType) {
|
||||
FormulaParser fp = new FormulaParser(formula, workbook);
|
||||
fp.parse();
|
||||
return fp.getRPNPtg(formulaType);
|
||||
}
|
||||
@ -309,7 +297,7 @@ public final class FormulaParser {
|
||||
Match('!');
|
||||
String sheetName = name;
|
||||
String first = parseIdentifier();
|
||||
short externIdx = (short)book.getExternalSheetIndex(book.getSheetIndex(sheetName));
|
||||
int externIdx = book.getExternalSheetIndex(sheetName);
|
||||
areaRef = parseArea(name);
|
||||
if (areaRef != null) {
|
||||
// will happen if dots are used instead of colon
|
||||
@ -331,7 +319,7 @@ public final class FormulaParser {
|
||||
}
|
||||
return new Area3DPtg(first+":"+second,externIdx);
|
||||
}
|
||||
return new Ref3DPtg(first,externIdx);
|
||||
return new Ref3DPtg(first, externIdx);
|
||||
}
|
||||
if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) {
|
||||
return new BoolPtg(name.toUpperCase());
|
||||
@ -347,16 +335,17 @@ public final class FormulaParser {
|
||||
new FormulaParseException("Name '" + name
|
||||
+ "' does not look like a cell reference or named range");
|
||||
}
|
||||
|
||||
for(int i = 0; i < book.getNumberOfNames(); i++) {
|
||||
// named range name matching is case insensitive
|
||||
if(book.getNameAt(i).getNameName().equalsIgnoreCase(name)) {
|
||||
return new NamePtg(i);
|
||||
}
|
||||
}
|
||||
EvaluationName evalName = book.getName(name);
|
||||
if (evalName == null) {
|
||||
throw new FormulaParseException("Specified named range '"
|
||||
+ name + "' does not exist in the current workbook.");
|
||||
}
|
||||
if (evalName.isRange()) {
|
||||
return evalName.createPtg();
|
||||
}
|
||||
throw new FormulaParseException("Specified name '"
|
||||
+ name + "' is not a range as expected");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name an 'identifier' like string (i.e. contains alphanums, and dots)
|
||||
@ -410,9 +399,16 @@ public final class FormulaParser {
|
||||
if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) {
|
||||
// user defined function
|
||||
// in the token tree, the name is more or less the first argument
|
||||
int nameIndex = book.getNameIndex(name);
|
||||
if (nameIndex >= 0) {
|
||||
Name hName = book.getNameAt(nameIndex);
|
||||
|
||||
EvaluationName hName = book.getName(name);
|
||||
if (hName == null) {
|
||||
|
||||
nameToken = book.getNameXPtg(name);
|
||||
if (nameToken == null) {
|
||||
throw new FormulaParseException("Name '" + name
|
||||
+ "' is completely unknown in the current workbook");
|
||||
}
|
||||
} else {
|
||||
if (!hName.isFunctionName()) {
|
||||
throw new FormulaParseException("Attempt to use name '" + name
|
||||
+ "' as a function, but defined name in workbook does not refer to a function");
|
||||
@ -420,15 +416,7 @@ public final class FormulaParser {
|
||||
|
||||
// calls to user-defined functions within the workbook
|
||||
// get a Name token which points to a defined name record
|
||||
nameToken = new NamePtg(nameIndex);
|
||||
} else {
|
||||
if(book instanceof HSSFWorkbook) {
|
||||
nameToken = ((HSSFWorkbook)book).getNameXPtg(name);
|
||||
}
|
||||
if (nameToken == null) {
|
||||
throw new FormulaParseException("Name '" + name
|
||||
+ "' is completely unknown in the current workbook");
|
||||
}
|
||||
nameToken = hName.createPtg();
|
||||
}
|
||||
}
|
||||
|
||||
@ -965,9 +953,9 @@ end;
|
||||
|
||||
/**
|
||||
* API call to execute the parsing of the formula
|
||||
* @deprecated use {@link #parse(String, Workbook)} directly
|
||||
*
|
||||
*/
|
||||
public void parse() {
|
||||
private void parse() {
|
||||
pointer=0;
|
||||
GetChar();
|
||||
_rootNode = comparisonExpression();
|
||||
@ -979,50 +967,10 @@ end;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********************************
|
||||
* PARSER IMPLEMENTATION ENDS HERE
|
||||
* EXCEL SPECIFIC METHODS BELOW
|
||||
*******************************/
|
||||
|
||||
/** API call to retrive the array of Ptgs created as
|
||||
* a result of the parsing
|
||||
*/
|
||||
public Ptg[] getRPNPtg() {
|
||||
return getRPNPtg(FORMULA_TYPE_CELL);
|
||||
}
|
||||
|
||||
public Ptg[] getRPNPtg(int formulaType) {
|
||||
private Ptg[] getRPNPtg(int formulaType) {
|
||||
OperandClassTransformer oct = new OperandClassTransformer(formulaType);
|
||||
// RVA is for 'operand class': 'reference', 'value', 'array'
|
||||
oct.transformFormula(_rootNode);
|
||||
return ParseNode.toTokenArray(_rootNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method which takes in a list then passes it to the
|
||||
* other toFormulaString signature.
|
||||
* @param book workbook for 3D and named references
|
||||
* @param lptgs list of Ptg, can be null or empty
|
||||
* @return a human readable String
|
||||
*/
|
||||
public static String toFormulaString(HSSFWorkbook book, List lptgs) {
|
||||
String retval = null;
|
||||
if (lptgs == null || lptgs.size() == 0) return "#NAME";
|
||||
Ptg[] ptgs = new Ptg[lptgs.size()];
|
||||
ptgs = (Ptg[])lptgs.toArray(ptgs);
|
||||
retval = toFormulaString(book, ptgs);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to convert an array of Ptgs in RPN order
|
||||
* to a human readable string format in infix mode.
|
||||
* @param book workbook for named and 3D references
|
||||
* @param ptgs array of Ptg, can be null or empty
|
||||
* @return a human readable String
|
||||
*/
|
||||
public static String toFormulaString(HSSFWorkbook book, Ptg[] ptgs) {
|
||||
return FormulaRenderer.toFormulaString(HSSFEvaluationWorkbook.create(book), ptgs);
|
||||
}
|
||||
}
|
@ -15,19 +15,23 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class LazyRefEval extends org.apache.poi.ss.usermodel.LazyRefEval {
|
||||
public LazyRefEval(RefPtg ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {
|
||||
super(ptg, sheet, evaluator);
|
||||
}
|
||||
public LazyRefEval(Ref3DPtg ptg, HSSFSheet sheet, HSSFFormulaEvaluator evaluator) {
|
||||
super(ptg, sheet, evaluator);
|
||||
}
|
||||
* Abstracts a workbook for the purpose of formula parsing.<br/>
|
||||
*
|
||||
* For POI internal use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public interface FormulaParsingWorkbook {
|
||||
/**
|
||||
* named range name matching is case insensitive
|
||||
*/
|
||||
EvaluationName getName(String name);
|
||||
|
||||
int getExternalSheetIndex(String sheetName);
|
||||
NameXPtg getNameXPtg(String name);
|
||||
}
|
36
src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetectorManager.java → src/java/org/apache/poi/ss/formula/FormulaType.java
Executable file → Normal file
36
src/java/org/apache/poi/ss/usermodel/EvaluationCycleDetectorManager.java → src/java/org/apache/poi/ss/formula/FormulaType.java
Executable file → Normal file
@ -15,32 +15,26 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
/**
|
||||
* This class makes an <tt>EvaluationCycleDetector</tt> instance available to
|
||||
* each thread via a <tt>ThreadLocal</tt> in order to avoid adding a parameter
|
||||
* to a few protected methods within <tt>HSSFFormulaEvaluator</tt>.
|
||||
* Enumeration of various formula types.<br/>
|
||||
*
|
||||
* For POI internal use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
final class EvaluationCycleDetectorManager {
|
||||
|
||||
ThreadLocal tl = null;
|
||||
private static ThreadLocal _tlEvaluationTracker = new ThreadLocal() {
|
||||
protected synchronized Object initialValue() {
|
||||
return new EvaluationCycleDetector();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static EvaluationCycleDetector getTracker() {
|
||||
return (EvaluationCycleDetector) _tlEvaluationTracker.get();
|
||||
}
|
||||
|
||||
private EvaluationCycleDetectorManager() {
|
||||
public final class FormulaType {
|
||||
private FormulaType() {
|
||||
// no instances of this class
|
||||
}
|
||||
public static final int CELL = 0;
|
||||
public static final int SHARED = 1;
|
||||
public static final int ARRAY =2;
|
||||
public static final int CONDFORMAT = 3;
|
||||
public static final int NAMEDRANGE = 4;
|
||||
// this constant is currently very specific. The exact differences from general data
|
||||
// validation formulas or conditional format formulas is not known yet
|
||||
public static final int DATAVALIDATION_LIST = 5;
|
||||
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AreaI;
|
||||
import org.apache.poi.hssf.record.formula.AreaI.OffsetArea;
|
||||
@ -23,18 +23,21 @@ import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEvalBase;
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class LazyAreaEval extends AreaEvalBase {
|
||||
final class LazyAreaEval extends AreaEvalBase {
|
||||
|
||||
private final Sheet _sheet;
|
||||
private FormulaEvaluator _evaluator;
|
||||
private final CellEvaluator _evaluator;
|
||||
|
||||
public LazyAreaEval(AreaI ptg, Sheet sheet, FormulaEvaluator evaluator) {
|
||||
public LazyAreaEval(AreaI ptg, Sheet sheet, CellEvaluator evaluator) {
|
||||
super(ptg);
|
||||
_sheet = sheet;
|
||||
_evaluator = evaluator;
|
@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AreaI;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
@ -25,24 +25,27 @@ import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEvalBase;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class LazyRefEval extends RefEvalBase {
|
||||
final class LazyRefEval extends RefEvalBase {
|
||||
|
||||
private final Sheet _sheet;
|
||||
private final FormulaEvaluator _evaluator;
|
||||
private final CellEvaluator _evaluator;
|
||||
|
||||
|
||||
public LazyRefEval(RefPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {
|
||||
public LazyRefEval(RefPtg ptg, Sheet sheet, CellEvaluator evaluator) {
|
||||
super(ptg.getRow(), ptg.getColumn());
|
||||
_sheet = sheet;
|
||||
_evaluator = evaluator;
|
||||
}
|
||||
public LazyRefEval(Ref3DPtg ptg, Sheet sheet, FormulaEvaluator evaluator) {
|
||||
public LazyRefEval(Ref3DPtg ptg, Sheet sheet, CellEvaluator evaluator) {
|
||||
super(ptg.getRow(), ptg.getColumn());
|
||||
_sheet = sheet;
|
||||
_evaluator = evaluator;
|
@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.model;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
|
||||
import org.apache.poi.hssf.record.formula.ControlPtg;
|
||||
@ -63,10 +63,10 @@ final class OperandClassTransformer {
|
||||
public void transformFormula(ParseNode rootNode) {
|
||||
byte rootNodeOperandClass;
|
||||
switch (_formulaType) {
|
||||
case FormulaParser.FORMULA_TYPE_CELL:
|
||||
case FormulaType.CELL:
|
||||
rootNodeOperandClass = Ptg.CLASS_VALUE;
|
||||
break;
|
||||
case FormulaParser.FORMULA_TYPE_DATAVALIDATION_LIST:
|
||||
case FormulaType.DATAVALIDATION_LIST:
|
||||
rootNodeOperandClass = Ptg.CLASS_REF;
|
||||
break;
|
||||
default:
|
@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
@ -15,7 +15,7 @@
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
package org.apache.poi.hssf.model;
|
||||
package org.apache.poi.ss.formula;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.AttrPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
348
src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
Normal file
348
src/java/org/apache/poi/ss/formula/WorkbookEvaluator.java
Normal file
@ -0,0 +1,348 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.Area3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.BoolPtg;
|
||||
import org.apache.poi.hssf.record.formula.ControlPtg;
|
||||
import org.apache.poi.hssf.record.formula.ErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.MemErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.MissingArgPtg;
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||
import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnionPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnknownPtg;
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.FunctionEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NameEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NameXEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperationEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
/**
|
||||
* Evaluates formula cells.<p/>
|
||||
*
|
||||
* For performance reasons, this class keeps a cache of all previously calculated intermediate
|
||||
* cell values. Be sure to call {@link #clearCache()} if any workbook cells are changed between
|
||||
* calls to evaluate~ methods on this class.<br/>
|
||||
*
|
||||
* For POI internal use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class WorkbookEvaluator {
|
||||
|
||||
/**
|
||||
* used to track the number of evaluations
|
||||
*/
|
||||
private static final class Counter {
|
||||
public int value;
|
||||
public int depth;
|
||||
public Counter() {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private final EvaluationWorkbook _workbook;
|
||||
private final EvaluationCache _cache;
|
||||
|
||||
private Counter _evaluationCounter;
|
||||
|
||||
public WorkbookEvaluator(EvaluationWorkbook workbook) {
|
||||
_workbook = workbook;
|
||||
_cache = new EvaluationCache();
|
||||
_evaluationCounter = new Counter();
|
||||
}
|
||||
|
||||
/**
|
||||
* for debug use. Used in toString methods
|
||||
*/
|
||||
/* package */ String getSheetName(Sheet sheet) {
|
||||
return getSheetName(getSheetIndex(sheet));
|
||||
}
|
||||
private String getSheetName(int sheetIndex) {
|
||||
return _workbook.getSheetName(sheetIndex);
|
||||
}
|
||||
/**
|
||||
* for debug/test use
|
||||
*/
|
||||
public int getEvaluationCount() {
|
||||
return _evaluationCounter.value;
|
||||
}
|
||||
|
||||
private static boolean isDebugLogEnabled() {
|
||||
return false;
|
||||
}
|
||||
private static void logDebug(String s) {
|
||||
if (isDebugLogEnabled()) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
public void clearCachedResultValue(Sheet sheet, int rowIndex, int columnIndex) {
|
||||
int sheetIndex = getSheetIndex(sheet);
|
||||
_cache.clearValue(sheetIndex, rowIndex, columnIndex);
|
||||
|
||||
}
|
||||
private int getSheetIndex(Sheet sheet) {
|
||||
// TODO cache sheet indexes too
|
||||
return _workbook.getSheetIndex(sheet);
|
||||
}
|
||||
|
||||
public ValueEval evaluate(Cell srcCell) {
|
||||
return internalEvaluate(srcCell, new EvaluationTracker(_cache));
|
||||
}
|
||||
|
||||
/**
|
||||
* Dev. Note: Internal evaluate must be passed only a formula cell
|
||||
* else a runtime exception will be thrown somewhere inside the method.
|
||||
* (Hence this is a private method.)
|
||||
* @return never <code>null</code>, never {@link BlankEval}
|
||||
*/
|
||||
/* package */ ValueEval internalEvaluate(Cell srcCell, EvaluationTracker tracker) {
|
||||
int srcRowNum = srcCell.getRowIndex();
|
||||
int srcColNum = srcCell.getCellNum();
|
||||
|
||||
ValueEval result;
|
||||
|
||||
int sheetIndex = getSheetIndex(srcCell.getSheet());
|
||||
result = tracker.startEvaluate(sheetIndex, srcRowNum, srcColNum);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
_evaluationCounter.value++;
|
||||
_evaluationCounter.depth++;
|
||||
|
||||
try {
|
||||
Ptg[] ptgs = _workbook.getFormulaTokens(srcCell);
|
||||
result = evaluateCell(sheetIndex, srcRowNum, (short)srcColNum, ptgs, tracker);
|
||||
} finally {
|
||||
tracker.endEvaluate(sheetIndex, srcRowNum, srcColNum, result);
|
||||
_evaluationCounter.depth--;
|
||||
}
|
||||
if (isDebugLogEnabled()) {
|
||||
String sheetName = getSheetName(sheetIndex);
|
||||
CellReference cr = new CellReference(srcRowNum, srcColNum);
|
||||
logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, Ptg[] ptgs, EvaluationTracker tracker) {
|
||||
|
||||
Stack stack = new Stack();
|
||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
||||
|
||||
// since we don't know how to handle these yet :(
|
||||
Ptg ptg = ptgs[i];
|
||||
if (ptg instanceof ControlPtg) {
|
||||
// skip Parentheses, Attr, etc
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof MemErrPtg) { continue; }
|
||||
if (ptg instanceof MissingArgPtg) {
|
||||
// TODO - might need to push BlankEval or MissingArgEval
|
||||
continue;
|
||||
}
|
||||
Eval opResult;
|
||||
if (ptg instanceof OperationPtg) {
|
||||
OperationPtg optg = (OperationPtg) ptg;
|
||||
|
||||
if (optg instanceof UnionPtg) { continue; }
|
||||
|
||||
OperationEval operation = OperationEvaluatorFactory.create(optg);
|
||||
|
||||
int numops = operation.getNumberOfOperands();
|
||||
Eval[] ops = new Eval[numops];
|
||||
|
||||
// storing the ops in reverse order since they are popping
|
||||
for (int j = numops - 1; j >= 0; j--) {
|
||||
Eval p = (Eval) stack.pop();
|
||||
ops[j] = p;
|
||||
}
|
||||
// logDebug("invoke " + operation + " (nAgs=" + numops + ")");
|
||||
opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum);
|
||||
} else {
|
||||
opResult = getEvalForPtg(ptg, sheetIndex, tracker);
|
||||
}
|
||||
if (opResult == null) {
|
||||
throw new RuntimeException("Evaluation result must not be null");
|
||||
}
|
||||
// logDebug("push " + opResult);
|
||||
stack.push(opResult);
|
||||
}
|
||||
|
||||
ValueEval value = ((ValueEval) stack.pop());
|
||||
if (!stack.isEmpty()) {
|
||||
throw new IllegalStateException("evaluation stack not empty");
|
||||
}
|
||||
value = dereferenceValue(value, srcRowNum, srcColNum);
|
||||
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().
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
|
||||
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
|
||||
*/
|
||||
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
|
||||
if (evaluationResult instanceof RefEval) {
|
||||
RefEval rv = (RefEval) evaluationResult;
|
||||
return rv.getInnerValueEval();
|
||||
}
|
||||
if (evaluationResult instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) evaluationResult;
|
||||
if (ae.isRow()) {
|
||||
if(ae.isColumn()) {
|
||||
return ae.getRelativeValue(0, 0);
|
||||
}
|
||||
return ae.getValueAt(ae.getFirstRow(), srcColNum);
|
||||
}
|
||||
if (ae.isColumn()) {
|
||||
return ae.getValueAt(srcRowNum, ae.getFirstColumn());
|
||||
}
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return evaluationResult;
|
||||
}
|
||||
|
||||
private static Eval invokeOperation(OperationEval operation, Eval[] ops,
|
||||
EvaluationWorkbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
|
||||
if(operation instanceof FunctionEval) {
|
||||
FunctionEval fe = (FunctionEval) operation;
|
||||
if(fe.isFreeRefFunction()) {
|
||||
return fe.getFreeRefFunction().evaluate(ops, workbook, sheetIndex, srcRowNum, srcColNum);
|
||||
}
|
||||
}
|
||||
return operation.evaluate(ops, srcRowNum, (short)srcColNum);
|
||||
}
|
||||
|
||||
private Sheet getOtherSheet(int externSheetIndex) {
|
||||
return _workbook.getSheetByExternSheetIndex(externSheetIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Eval getEvalForPtg(Ptg ptg, int sheetIndex, EvaluationTracker tracker) {
|
||||
if (ptg instanceof NamePtg) {
|
||||
// named ranges, macro functions
|
||||
NamePtg namePtg = (NamePtg) ptg;
|
||||
EvaluationName nameRecord = _workbook.getName(namePtg);
|
||||
if (nameRecord.isFunctionName()) {
|
||||
return new NameEval(nameRecord.getNameText());
|
||||
}
|
||||
if (nameRecord.hasFormula()) {
|
||||
return evaluateNameFormula(nameRecord.getNameDefinition(), sheetIndex, tracker);
|
||||
}
|
||||
|
||||
throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'");
|
||||
}
|
||||
if (ptg instanceof NameXPtg) {
|
||||
return new NameXEval(((NameXPtg) ptg));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
CellEvaluator ce = new CellEvaluator(this, tracker);
|
||||
Sheet sheet = _workbook.getSheet(sheetIndex);
|
||||
if (ptg instanceof RefPtg) {
|
||||
return new LazyRefEval(((RefPtg) ptg), sheet, ce);
|
||||
}
|
||||
if (ptg instanceof AreaPtg) {
|
||||
return new LazyAreaEval(((AreaPtg) ptg), sheet, ce);
|
||||
}
|
||||
if (ptg instanceof Ref3DPtg) {
|
||||
Ref3DPtg refPtg = (Ref3DPtg) ptg;
|
||||
Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
|
||||
return new LazyRefEval(refPtg, xsheet, ce);
|
||||
}
|
||||
if (ptg instanceof Area3DPtg) {
|
||||
Area3DPtg a3dp = (Area3DPtg) ptg;
|
||||
Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
|
||||
return new LazyAreaEval(a3dp, xsheet, ce);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
|
||||
}
|
||||
private Eval evaluateNameFormula(Ptg[] ptgs, int sheetIndex, EvaluationTracker tracker) {
|
||||
if (ptgs.length > 1) {
|
||||
throw new RuntimeException("Complex name formulas not supported yet");
|
||||
}
|
||||
return getEvalForPtg(ptgs[0], sheetIndex, tracker);
|
||||
}
|
||||
}
|
114
src/java/org/apache/poi/ss/usermodel/CellValue.java
Normal file
114
src/java/org/apache/poi/ss/usermodel/CellValue.java
Normal file
@ -0,0 +1,114 @@
|
||||
/* ====================================================================
|
||||
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.usermodel;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
|
||||
/**
|
||||
* Mimics the 'data view' of a cell. This allows formula evaluator
|
||||
* to return a CellValue instead of precasting the value to String
|
||||
* or Number or boolean type.
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public final class CellValue {
|
||||
public static final CellValue TRUE = new CellValue(Cell.CELL_TYPE_BOOLEAN, 0.0, true, null, 0);
|
||||
public static final CellValue FALSE= new CellValue(Cell.CELL_TYPE_BOOLEAN, 0.0, false, null, 0);
|
||||
|
||||
private final int _cellType;
|
||||
private final double _numberValue;
|
||||
private final boolean _booleanValue;
|
||||
private final String _textValue;
|
||||
private final int _errorCode;
|
||||
|
||||
private CellValue(int cellType, double numberValue, boolean booleanValue,
|
||||
String textValue, int errorCode) {
|
||||
_cellType = cellType;
|
||||
_numberValue = numberValue;
|
||||
_booleanValue = booleanValue;
|
||||
_textValue = textValue;
|
||||
_errorCode = errorCode;
|
||||
}
|
||||
|
||||
|
||||
public CellValue(double numberValue) {
|
||||
this(Cell.CELL_TYPE_NUMERIC, numberValue, false, null, 0);
|
||||
}
|
||||
public static CellValue valueOf(boolean booleanValue) {
|
||||
return booleanValue ? TRUE : FALSE;
|
||||
}
|
||||
public CellValue(String stringValue) {
|
||||
this(Cell.CELL_TYPE_STRING, 0.0, false, stringValue, 0);
|
||||
}
|
||||
public static CellValue getError(int errorCode) {
|
||||
return new CellValue(Cell.CELL_TYPE_ERROR, 0.0, false, null, errorCode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the booleanValue.
|
||||
*/
|
||||
public boolean getBooleanValue() {
|
||||
return _booleanValue;
|
||||
}
|
||||
/**
|
||||
* @return Returns the numberValue.
|
||||
*/
|
||||
public double getNumberValue() {
|
||||
return _numberValue;
|
||||
}
|
||||
/**
|
||||
* @return Returns the stringValue.
|
||||
*/
|
||||
public String getStringValue() {
|
||||
return _textValue;
|
||||
}
|
||||
/**
|
||||
* @return Returns the cellType.
|
||||
*/
|
||||
public int getCellType() {
|
||||
return _cellType;
|
||||
}
|
||||
/**
|
||||
* @return Returns the errorValue.
|
||||
*/
|
||||
public byte getErrorValue() {
|
||||
return (byte) _errorCode;
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(formatAsString());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String formatAsString() {
|
||||
switch (_cellType) {
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
return String.valueOf(_numberValue);
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
return '"' + _textValue + '"';
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
return _booleanValue ? "TRUE" : "FALSE";
|
||||
case Cell.CELL_TYPE_ERROR:
|
||||
return ErrorEval.getText(_errorCode);
|
||||
}
|
||||
return "<error unexpected cell type " + _cellType + ">";
|
||||
}
|
||||
}
|
@ -17,43 +17,6 @@
|
||||
|
||||
package org.apache.poi.ss.usermodel;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Stack;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.record.NameRecord;
|
||||
import org.apache.poi.hssf.record.formula.Area3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.AreaPtg;
|
||||
import org.apache.poi.hssf.record.formula.BoolPtg;
|
||||
import org.apache.poi.hssf.record.formula.ControlPtg;
|
||||
import org.apache.poi.hssf.record.formula.ErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.IntPtg;
|
||||
import org.apache.poi.hssf.record.formula.MemErrPtg;
|
||||
import org.apache.poi.hssf.record.formula.MissingArgPtg;
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.hssf.record.formula.NumberPtg;
|
||||
import org.apache.poi.hssf.record.formula.OperationPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.record.formula.Ref3DPtg;
|
||||
import org.apache.poi.hssf.record.formula.RefPtg;
|
||||
import org.apache.poi.hssf.record.formula.StringPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnionPtg;
|
||||
import org.apache.poi.hssf.record.formula.UnknownPtg;
|
||||
import org.apache.poi.hssf.record.formula.eval.AreaEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BlankEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.Eval;
|
||||
import org.apache.poi.hssf.record.formula.eval.FunctionEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NameEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NameXEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.OperationEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.RefEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
|
||||
/**
|
||||
* Evaluates formula cells.<p/>
|
||||
@ -65,85 +28,15 @@ import org.apache.poi.hssf.util.CellReference;
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class FormulaEvaluator {
|
||||
|
||||
/**
|
||||
* used to track the number of evaluations
|
||||
*/
|
||||
private static final class Counter {
|
||||
public int value;
|
||||
public int depth;
|
||||
public Counter() {
|
||||
value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected final Workbook _workbook;
|
||||
private final EvaluationCache _cache;
|
||||
|
||||
private Counter _evaluationCounter;
|
||||
|
||||
/**
|
||||
* @deprecated (Sep 2008) Sheet parameter is ignored
|
||||
*/
|
||||
public FormulaEvaluator(Sheet sheet, Workbook workbook) {
|
||||
this(workbook);
|
||||
if (false) {
|
||||
sheet.toString(); // suppress unused parameter compiler warning
|
||||
}
|
||||
}
|
||||
public FormulaEvaluator(Workbook workbook) {
|
||||
this(workbook, new EvaluationCache(), new Counter());
|
||||
}
|
||||
|
||||
private FormulaEvaluator(Workbook workbook, EvaluationCache cache, Counter evaluationCounter) {
|
||||
_workbook = workbook;
|
||||
_cache = cache;
|
||||
_evaluationCounter = evaluationCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* for debug use. Used in toString methods
|
||||
*/
|
||||
public String getSheetName(Sheet sheet) {
|
||||
return _workbook.getSheetName(_workbook.getSheetIndex(sheet));
|
||||
}
|
||||
/**
|
||||
* for debug/test use
|
||||
*/
|
||||
public int getEvaluationCount() {
|
||||
return _evaluationCounter.value;
|
||||
}
|
||||
|
||||
private static boolean isDebugLogEnabled() {
|
||||
return false;
|
||||
}
|
||||
private static void logDebug(String s) {
|
||||
if (isDebugLogEnabled()) {
|
||||
System.out.println(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
* @deprecated (Aug 2008) - not needed, since the current row can be derived from the cell
|
||||
*/
|
||||
public void setCurrentRow(Row row) {
|
||||
// do nothing
|
||||
if (false) {
|
||||
row.getClass(); // suppress unused parameter compiler warning
|
||||
}
|
||||
}
|
||||
public interface FormulaEvaluator {
|
||||
|
||||
/**
|
||||
* 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 clearCache() {
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
void clearAllCachedResultValues();
|
||||
void clearCachedResultValue(Sheet sheet, int rowIndex, int columnIndex);
|
||||
/**
|
||||
* If cell contains a formula, the formula is evaluated and returned,
|
||||
* else the CellValue simply copies the appropriate cell value from
|
||||
@ -152,25 +45,7 @@ public class FormulaEvaluator {
|
||||
* original cell.
|
||||
* @param cell
|
||||
*/
|
||||
public CellValue evaluate(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (cell.getCellType()) {
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
return CellValue.valueOf(cell.getBooleanCellValue());
|
||||
case Cell.CELL_TYPE_ERROR:
|
||||
return CellValue.getError(cell.getErrorCellValue());
|
||||
case Cell.CELL_TYPE_FORMULA:
|
||||
return evaluateFormulaCellValue(cell);
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
return new CellValue(cell.getNumericCellValue(), _workbook.getCreationHelper());
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
return new CellValue(cell.getRichStringCellValue().getString(), _workbook.getCreationHelper());
|
||||
}
|
||||
throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
CellValue evaluate(Cell cell);
|
||||
|
||||
|
||||
/**
|
||||
@ -191,15 +66,7 @@ public class FormulaEvaluator {
|
||||
* @param cell The cell to evaluate
|
||||
* @return The type of the formula result (the cell's type remains as Cell.CELL_TYPE_FORMULA however)
|
||||
*/
|
||||
public int evaluateFormulaCell(Cell cell) {
|
||||
if (cell == null || cell.getCellType() != Cell.CELL_TYPE_FORMULA) {
|
||||
return -1;
|
||||
}
|
||||
CellValue cv = evaluateFormulaCellValue(cell);
|
||||
// cell remains a formula cell, but the cached value is changed
|
||||
setCellValue(cell, cv);
|
||||
return cv.getCellType();
|
||||
}
|
||||
int evaluateFormulaCell(Cell cell);
|
||||
|
||||
/**
|
||||
* If cell contains formula, it evaluates the formula, and
|
||||
@ -217,462 +84,5 @@ public class FormulaEvaluator {
|
||||
* value computed for you, use {@link #evaluateFormulaCell(Cell)}
|
||||
* @param cell
|
||||
*/
|
||||
public Cell evaluateInCell(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
|
||||
CellValue cv = evaluateFormulaCellValue(cell);
|
||||
setCellType(cell, cv); // cell will no longer be a formula cell
|
||||
setCellValue(cell, cv);
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
private static void setCellType(Cell cell, CellValue cv) {
|
||||
int cellType = cv.getCellType();
|
||||
switch (cellType) {
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
case Cell.CELL_TYPE_ERROR:
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
cell.setCellType(cellType);
|
||||
return;
|
||||
case Cell.CELL_TYPE_BLANK:
|
||||
// never happens - blanks eventually get translated to zero
|
||||
case Cell.CELL_TYPE_FORMULA:
|
||||
// this will never happen, we have already evaluated the formula
|
||||
}
|
||||
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
|
||||
}
|
||||
|
||||
private static void setCellValue(Cell cell, CellValue cv) {
|
||||
int cellType = cv.getCellType();
|
||||
switch (cellType) {
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
cell.setCellValue(cv.getBooleanValue());
|
||||
break;
|
||||
case Cell.CELL_TYPE_ERROR:
|
||||
cell.setCellErrorValue(cv.getErrorValue());
|
||||
break;
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
cell.setCellValue(cv.getNumberValue());
|
||||
break;
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
cell.setCellValue(cv.getRichTextStringValue());
|
||||
break;
|
||||
case Cell.CELL_TYPE_BLANK:
|
||||
// never happens - blanks eventually get translated to zero
|
||||
case Cell.CELL_TYPE_FORMULA:
|
||||
// this will never happen, we have already evaluated the formula
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops over all cells in all sheets of the supplied
|
||||
* workbook.
|
||||
* For cells that contain formulas, their formulas are
|
||||
* evaluated, and the results are saved. These cells
|
||||
* remain as formula cells.
|
||||
* For cells that do not contain formulas, no changes
|
||||
* are made.
|
||||
* This is a helpful wrapper around looping over all
|
||||
* cells, and calling evaluateFormulaCell on each one.
|
||||
*/
|
||||
public static void evaluateAllFormulaCells(Workbook wb) {
|
||||
FormulaEvaluator evaluator = new FormulaEvaluator(wb);
|
||||
for(int i=0; i<wb.getNumberOfSheets(); i++) {
|
||||
Sheet sheet = wb.getSheetAt(i);
|
||||
|
||||
for (Iterator rit = sheet.rowIterator(); rit.hasNext();) {
|
||||
Row r = (Row)rit.next();
|
||||
|
||||
for (Iterator cit = r.cellIterator(); cit.hasNext();) {
|
||||
Cell c = (Cell)cit.next();
|
||||
if (c.getCellType() == Cell.CELL_TYPE_FORMULA)
|
||||
evaluator.evaluateFormulaCell(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CellValue wrapper around the supplied ValueEval instance.
|
||||
* @param eval
|
||||
*/
|
||||
private CellValue evaluateFormulaCellValue(Cell cell) {
|
||||
ValueEval eval = internalEvaluate(cell);
|
||||
if (eval instanceof NumberEval) {
|
||||
NumberEval ne = (NumberEval) eval;
|
||||
return new CellValue(ne.getNumberValue(), _workbook.getCreationHelper());
|
||||
}
|
||||
if (eval instanceof BoolEval) {
|
||||
BoolEval be = (BoolEval) eval;
|
||||
return CellValue.valueOf(be.getBooleanValue());
|
||||
}
|
||||
if (eval instanceof StringEval) {
|
||||
StringEval ne = (StringEval) eval;
|
||||
return new CellValue(ne.getStringValue(), _workbook.getCreationHelper());
|
||||
}
|
||||
if (eval instanceof ErrorEval) {
|
||||
return CellValue.getError(((ErrorEval)eval).getErrorCode());
|
||||
}
|
||||
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Dev. Note: Internal evaluate must be passed only a formula cell
|
||||
* else a runtime exception will be thrown somewhere inside the method.
|
||||
* (Hence this is a private method.)
|
||||
* @return never <code>null</code>, never {@link BlankEval}
|
||||
*/
|
||||
private ValueEval internalEvaluate(Cell srcCell) {
|
||||
int srcRowNum = srcCell.getRowIndex();
|
||||
int srcColNum = srcCell.getCellNum();
|
||||
|
||||
ValueEval result;
|
||||
|
||||
int sheetIndex = _workbook.getSheetIndex(srcCell.getSheet());
|
||||
result = _cache.getValue(sheetIndex, srcRowNum, srcColNum);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
_evaluationCounter.value++;
|
||||
_evaluationCounter.depth++;
|
||||
|
||||
EvaluationCycleDetector tracker = EvaluationCycleDetectorManager.getTracker();
|
||||
|
||||
if(!tracker.startEvaluate(_workbook, sheetIndex, srcRowNum, srcColNum)) {
|
||||
return ErrorEval.CIRCULAR_REF_ERROR;
|
||||
}
|
||||
try {
|
||||
result = evaluateCell(sheetIndex, srcRowNum, (short)srcColNum, srcCell.getCellFormula());
|
||||
} finally {
|
||||
tracker.endEvaluate(_workbook, sheetIndex, srcRowNum, srcColNum);
|
||||
_cache.setValue(sheetIndex, srcRowNum, srcColNum, result);
|
||||
_evaluationCounter.depth--;
|
||||
}
|
||||
if (isDebugLogEnabled()) {
|
||||
String sheetName = _workbook.getSheetName(sheetIndex);
|
||||
CellReference cr = new CellReference(srcRowNum, srcColNum);
|
||||
logDebug("Evaluated " + sheetName + "!" + cr.formatAsString() + " to " + result.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private ValueEval evaluateCell(int sheetIndex, int srcRowNum, short srcColNum, String cellFormulaText) {
|
||||
|
||||
Ptg[] ptgs = FormulaParser.parse(cellFormulaText, _workbook);
|
||||
|
||||
Stack stack = new Stack();
|
||||
for (int i = 0, iSize = ptgs.length; i < iSize; i++) {
|
||||
|
||||
// since we don't know how to handle these yet :(
|
||||
Ptg ptg = ptgs[i];
|
||||
if (ptg instanceof ControlPtg) {
|
||||
// skip Parentheses, Attr, etc
|
||||
continue;
|
||||
}
|
||||
if (ptg instanceof MemErrPtg) { continue; }
|
||||
if (ptg instanceof MissingArgPtg) {
|
||||
// TODO - might need to push BlankEval or MissingArgEval
|
||||
continue;
|
||||
}
|
||||
Eval opResult;
|
||||
if (ptg instanceof OperationPtg) {
|
||||
OperationPtg optg = (OperationPtg) ptg;
|
||||
|
||||
if (optg instanceof UnionPtg) { continue; }
|
||||
|
||||
OperationEval operation = OperationEvaluatorFactory.create(optg);
|
||||
|
||||
int numops = operation.getNumberOfOperands();
|
||||
Eval[] ops = new Eval[numops];
|
||||
|
||||
// storing the ops in reverse order since they are popping
|
||||
for (int j = numops - 1; j >= 0; j--) {
|
||||
Eval p = (Eval) stack.pop();
|
||||
ops[j] = p;
|
||||
}
|
||||
// logDebug("invoke " + operation + " (nAgs=" + numops + ")");
|
||||
opResult = invokeOperation(operation, ops, _workbook, sheetIndex, srcRowNum, srcColNum);
|
||||
} else {
|
||||
opResult = getEvalForPtg(ptg, sheetIndex);
|
||||
}
|
||||
if (opResult == null) {
|
||||
throw new RuntimeException("Evaluation result must not be null");
|
||||
}
|
||||
// logDebug("push " + opResult);
|
||||
stack.push(opResult);
|
||||
}
|
||||
|
||||
ValueEval value = ((ValueEval) stack.pop());
|
||||
if (!stack.isEmpty()) {
|
||||
throw new IllegalStateException("evaluation stack not empty");
|
||||
}
|
||||
value = dereferenceValue(value, srcRowNum, srcColNum);
|
||||
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().
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 <tt>NumberEval</tt>, <tt>StringEval</tt>, <tt>BoolEval</tt>,
|
||||
* <tt>BlankEval</tt> or <tt>ErrorEval</tt>. Never <code>null</code>.
|
||||
*/
|
||||
private static ValueEval dereferenceValue(ValueEval evaluationResult, int srcRowNum, short srcColNum) {
|
||||
if (evaluationResult instanceof RefEval) {
|
||||
RefEval rv = (RefEval) evaluationResult;
|
||||
return rv.getInnerValueEval();
|
||||
}
|
||||
if (evaluationResult instanceof AreaEval) {
|
||||
AreaEval ae = (AreaEval) evaluationResult;
|
||||
if (ae.isRow()) {
|
||||
if(ae.isColumn()) {
|
||||
return ae.getRelativeValue(0, 0);
|
||||
}
|
||||
return ae.getValueAt(ae.getFirstRow(), srcColNum);
|
||||
}
|
||||
if (ae.isColumn()) {
|
||||
return ae.getValueAt(srcRowNum, ae.getFirstColumn());
|
||||
}
|
||||
return ErrorEval.VALUE_INVALID;
|
||||
}
|
||||
return evaluationResult;
|
||||
}
|
||||
|
||||
private static Eval invokeOperation(OperationEval operation, Eval[] ops,
|
||||
Workbook workbook, int sheetIndex, int srcRowNum, int srcColNum) {
|
||||
|
||||
if(operation instanceof FunctionEval) {
|
||||
FunctionEval fe = (FunctionEval) operation;
|
||||
if(fe.isFreeRefFunction()) {
|
||||
return fe.getFreeRefFunction().evaluate(ops, workbook, sheetIndex, srcRowNum, srcColNum);
|
||||
}
|
||||
}
|
||||
return operation.evaluate(ops, srcRowNum, (short)srcColNum);
|
||||
}
|
||||
|
||||
private Sheet getOtherSheet(int externSheetIndex) {
|
||||
return _workbook.getSheetAt(_workbook.getSheetIndexFromExternSheetIndex(externSheetIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Eval getEvalForPtg(Ptg ptg, int sheetIndex) {
|
||||
if (ptg instanceof NamePtg) {
|
||||
// named ranges, macro functions
|
||||
NamePtg namePtg = (NamePtg) ptg;
|
||||
int numberOfNames = _workbook.getNumberOfNames();
|
||||
int nameIndex = namePtg.getIndex();
|
||||
if(nameIndex < 0 || nameIndex >= numberOfNames) {
|
||||
throw new RuntimeException("Bad name index (" + nameIndex
|
||||
+ "). Allowed range is (0.." + (numberOfNames-1) + ")");
|
||||
}
|
||||
if(_workbook instanceof org.apache.poi.hssf.usermodel.HSSFWorkbook) {
|
||||
NameRecord nameRecord = ((org.apache.poi.hssf.usermodel.HSSFWorkbook)_workbook).getNameRecord(nameIndex);
|
||||
if (nameRecord.isFunctionName()) {
|
||||
return new NameEval(nameRecord.getNameText());
|
||||
}
|
||||
if (nameRecord.hasFormula()) {
|
||||
return evaluateNameFormula(nameRecord.getNameDefinition(), sheetIndex);
|
||||
}
|
||||
|
||||
throw new RuntimeException("Don't now how to evalate name '" + nameRecord.getNameText() + "'");
|
||||
}
|
||||
throw new RuntimeException("Don't now how to evalate name for XSSFWorkbook");
|
||||
}
|
||||
if (ptg instanceof NameXPtg) {
|
||||
NameXPtg nameXPtg = (NameXPtg) ptg;
|
||||
return new NameXEval(nameXPtg.getSheetRefIndex(), nameXPtg.getNameIndex());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
Sheet sheet = _workbook.getSheetAt(sheetIndex);
|
||||
if (ptg instanceof RefPtg) {
|
||||
return new LazyRefEval(((RefPtg) ptg), sheet, this);
|
||||
}
|
||||
if (ptg instanceof AreaPtg) {
|
||||
return new LazyAreaEval(((AreaPtg) ptg), sheet, this);
|
||||
}
|
||||
if (ptg instanceof Ref3DPtg) {
|
||||
Ref3DPtg refPtg = (Ref3DPtg) ptg;
|
||||
Sheet xsheet = getOtherSheet(refPtg.getExternSheetIndex());
|
||||
return new LazyRefEval(refPtg, xsheet, this);
|
||||
}
|
||||
if (ptg instanceof Area3DPtg) {
|
||||
Area3DPtg a3dp = (Area3DPtg) ptg;
|
||||
Sheet xsheet = getOtherSheet(a3dp.getExternSheetIndex());
|
||||
return new LazyAreaEval(a3dp, xsheet, this);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unexpected ptg class (" + ptg.getClass().getName() + ")");
|
||||
}
|
||||
private Eval evaluateNameFormula(Ptg[] ptgs, int sheetIndex) {
|
||||
if (ptgs.length > 1) {
|
||||
throw new RuntimeException("Complex name formulas not supported yet");
|
||||
}
|
||||
return getEvalForPtg(ptgs[0], sheetIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a cell, find its type and from that create an appropriate ValueEval
|
||||
* impl instance and return that. Since the cell could be an external
|
||||
* reference, we need the sheet that this belongs to.
|
||||
* Non existent cells are treated as empty.
|
||||
*/
|
||||
public ValueEval getEvalForCell(Cell cell) {
|
||||
|
||||
if (cell == null) {
|
||||
return BlankEval.INSTANCE;
|
||||
}
|
||||
switch (cell.getCellType()) {
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
return new NumberEval(cell.getNumericCellValue());
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
return new StringEval(cell.getRichStringCellValue().getString());
|
||||
case Cell.CELL_TYPE_FORMULA:
|
||||
return internalEvaluate(cell);
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
return BoolEval.valueOf(cell.getBooleanCellValue());
|
||||
case Cell.CELL_TYPE_BLANK:
|
||||
return BlankEval.INSTANCE;
|
||||
case Cell.CELL_TYPE_ERROR:
|
||||
return ErrorEval.valueOf(cell.getErrorCellValue());
|
||||
}
|
||||
throw new RuntimeException("Unexpected cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimics the 'data view' of a cell. This allows formula evaluator
|
||||
* to return a CellValue instead of precasting the value to String
|
||||
* or Number or boolean type.
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
*/
|
||||
public static final class CellValue {
|
||||
public static final CellValue TRUE = new CellValue(Cell.CELL_TYPE_BOOLEAN, 0.0, true, null, 0, null);
|
||||
public static final CellValue FALSE= new CellValue(Cell.CELL_TYPE_BOOLEAN, 0.0, false, null, 0, null);
|
||||
|
||||
private final int _cellType;
|
||||
private final double _numberValue;
|
||||
private final boolean _booleanValue;
|
||||
private final String _textValue;
|
||||
private final int _errorCode;
|
||||
private CreationHelper _creationHelper;
|
||||
|
||||
private CellValue(int cellType, double numberValue, boolean booleanValue,
|
||||
String textValue, int errorCode, CreationHelper creationHelper) {
|
||||
_cellType = cellType;
|
||||
_numberValue = numberValue;
|
||||
_booleanValue = booleanValue;
|
||||
_textValue = textValue;
|
||||
_errorCode = errorCode;
|
||||
_creationHelper = creationHelper;
|
||||
}
|
||||
|
||||
|
||||
/* package*/ CellValue(double numberValue, CreationHelper creationHelper) {
|
||||
this(Cell.CELL_TYPE_NUMERIC, numberValue, false, null, 0, creationHelper);
|
||||
}
|
||||
/* package*/ static CellValue valueOf(boolean booleanValue) {
|
||||
return booleanValue ? TRUE : FALSE;
|
||||
}
|
||||
/* package*/ CellValue(String stringValue, CreationHelper creationHelper) {
|
||||
this(Cell.CELL_TYPE_STRING, 0.0, false, stringValue, 0, creationHelper);
|
||||
}
|
||||
/* package*/ static CellValue getError(int errorCode) {
|
||||
return new CellValue(Cell.CELL_TYPE_ERROR, 0.0, false, null, errorCode, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Returns the booleanValue.
|
||||
*/
|
||||
public boolean getBooleanValue() {
|
||||
return _booleanValue;
|
||||
}
|
||||
/**
|
||||
* @return Returns the numberValue.
|
||||
*/
|
||||
public double getNumberValue() {
|
||||
return _numberValue;
|
||||
}
|
||||
/**
|
||||
* @return Returns the stringValue.
|
||||
*/
|
||||
public String getStringValue() {
|
||||
return _textValue;
|
||||
}
|
||||
/**
|
||||
* @return Returns the cellType.
|
||||
*/
|
||||
public int getCellType() {
|
||||
return _cellType;
|
||||
}
|
||||
/**
|
||||
* @return Returns the errorValue.
|
||||
*/
|
||||
public byte getErrorValue() {
|
||||
return (byte) _errorCode;
|
||||
}
|
||||
/**
|
||||
* @return Returns the richTextStringValue.
|
||||
* @deprecated (Sep 2008) Text formatting is lost during formula evaluation. Use {@link #getStringValue()}
|
||||
*/
|
||||
public RichTextString getRichTextStringValue() {
|
||||
return _creationHelper.createRichTextString(_textValue);
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer sb = new StringBuffer(64);
|
||||
sb.append(getClass().getName()).append(" [");
|
||||
sb.append(formatAsString());
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String formatAsString() {
|
||||
switch (_cellType) {
|
||||
case Cell.CELL_TYPE_NUMERIC:
|
||||
return String.valueOf(_numberValue);
|
||||
case Cell.CELL_TYPE_STRING:
|
||||
return '"' + _textValue + '"';
|
||||
case Cell.CELL_TYPE_BOOLEAN:
|
||||
return _booleanValue ? "TRUE" : "FALSE";
|
||||
case Cell.CELL_TYPE_ERROR:
|
||||
return ErrorEval.getText(_errorCode);
|
||||
}
|
||||
return "<error unexpected cell type " + _cellType + ">";
|
||||
}
|
||||
}
|
||||
Cell evaluateInCell(Cell cell);
|
||||
}
|
||||
|
@ -121,15 +121,6 @@ public interface Workbook {
|
||||
*/
|
||||
int getSheetIndex(Sheet sheet);
|
||||
|
||||
/**
|
||||
* Returns the external sheet index of the sheet
|
||||
* with the given internal index, creating one
|
||||
* if needed.
|
||||
* Used by some of the more obscure formula and
|
||||
* named range things.
|
||||
*/
|
||||
int getExternalSheetIndex(int internalSheetIndex);
|
||||
|
||||
/**
|
||||
* create an HSSFSheet for this HSSFWorkbook, adds it to the sheets and returns
|
||||
* the high level representation. Use this to create new sheets.
|
||||
@ -164,14 +155,6 @@ public interface Workbook {
|
||||
|
||||
int getNumberOfSheets();
|
||||
|
||||
/**
|
||||
* Finds the sheet index for a particular external sheet number.
|
||||
* @param externSheetNumber The external sheet number to convert
|
||||
* @return The index to the sheet found.
|
||||
*/
|
||||
int getSheetIndexFromExternSheetIndex(int externSheetNumber);
|
||||
|
||||
|
||||
/**
|
||||
* Get the HSSFSheet object at the given index.
|
||||
* @param index of the sheet number (0-based physical & logical)
|
||||
@ -195,8 +178,6 @@ public interface Workbook {
|
||||
|
||||
void removeSheetAt(int index);
|
||||
|
||||
String findSheetNameFromExternSheet(int externSheetIndex);
|
||||
|
||||
/**
|
||||
* determine whether the Excel GUI will backup the workbook when saving.
|
||||
*
|
||||
@ -333,14 +314,6 @@ public interface Workbook {
|
||||
*/
|
||||
String getNameName(int index);
|
||||
|
||||
/**
|
||||
* TODO - make this less cryptic / move elsewhere
|
||||
* @param refIndex Index to REF entry in EXTERNSHEET record in the Link Table
|
||||
* @param definedNameIndex zero-based to DEFINEDNAME or EXTERNALNAME record
|
||||
* @return the string representation of the defined or external name
|
||||
*/
|
||||
String resolveNameXText(int refIndex, int definedNameIndex);
|
||||
|
||||
/**
|
||||
* Sets the printarea for the sheet provided
|
||||
* <p>
|
||||
|
@ -0,0 +1,167 @@
|
||||
package org.apache.poi.xssf.usermodel;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.NameXPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.formula.EvaluationName;
|
||||
import org.apache.poi.ss.formula.EvaluationWorkbook;
|
||||
import org.apache.poi.ss.formula.FormulaParser;
|
||||
import org.apache.poi.ss.formula.FormulaParsingWorkbook;
|
||||
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTDefinedName;
|
||||
|
||||
/**
|
||||
* Internal POI use only
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public final class XSSFEvaluationWorkbook implements FormulaRenderingWorkbook, EvaluationWorkbook, FormulaParsingWorkbook {
|
||||
|
||||
private final XSSFWorkbook _uBook;
|
||||
|
||||
public static XSSFEvaluationWorkbook create(XSSFWorkbook book) {
|
||||
if (book == null) {
|
||||
return null;
|
||||
}
|
||||
return new XSSFEvaluationWorkbook(book);
|
||||
}
|
||||
|
||||
private XSSFEvaluationWorkbook(XSSFWorkbook book) {
|
||||
_uBook = book;
|
||||
}
|
||||
|
||||
private int convertFromExternalSheetIndex(int externSheetIndex) {
|
||||
return externSheetIndex;
|
||||
}
|
||||
/**
|
||||
* @returns the external sheet index of the sheet with the given internal
|
||||
* index, creating one if needed. Used by some of the more obscure
|
||||
* formula and named range things. Fairly easy on XSSF (we
|
||||
* think...) since the internal and external indicies are the same
|
||||
*/
|
||||
private int convertToExternalSheetIndex(int sheetIndex) {
|
||||
return sheetIndex;
|
||||
}
|
||||
|
||||
public int getExternalSheetIndex(String sheetName) {
|
||||
int sheetIndex = _uBook.getSheetIndex(sheetName);
|
||||
return convertToExternalSheetIndex(sheetIndex);
|
||||
}
|
||||
|
||||
public EvaluationName getName(int index) {
|
||||
return new Name(_uBook.getNameAt(index), index, this);
|
||||
}
|
||||
|
||||
public EvaluationName getName(String name) {
|
||||
for(int i=0; i < _uBook.getNumberOfNames(); i++) {
|
||||
String nameText = _uBook.getNameName(i);
|
||||
if (name.equalsIgnoreCase(nameText)) {
|
||||
return new Name(_uBook.getNameAt(i), i, this);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getSheetIndex(Sheet sheet) {
|
||||
return _uBook.getSheetIndex(sheet);
|
||||
}
|
||||
|
||||
public String getSheetName(int sheetIndex) {
|
||||
return _uBook.getSheetName(sheetIndex);
|
||||
}
|
||||
|
||||
public int getNameIndex(String name) {
|
||||
return _uBook.getNameIndex(name);
|
||||
}
|
||||
|
||||
public NameXPtg getNameXPtg(String name) {
|
||||
// may require to return null to make tests pass
|
||||
throw new RuntimeException("Not implemented yet");
|
||||
}
|
||||
|
||||
public Sheet getSheet(int sheetIndex) {
|
||||
return _uBook.getSheetAt(sheetIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Doesn't do anything - returns the same index
|
||||
* TODO - figure out if this is a ole2 specific thing, or
|
||||
* if we need to do something proper here too!
|
||||
*/
|
||||
public Sheet getSheetByExternSheetIndex(int externSheetIndex) {
|
||||
int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);
|
||||
return _uBook.getSheetAt(sheetIndex);
|
||||
}
|
||||
|
||||
public Workbook getWorkbook() {
|
||||
return _uBook;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO - figure out what the hell this methods does in
|
||||
* HSSF...
|
||||
*/
|
||||
public String resolveNameXText(NameXPtg n) {
|
||||
throw new RuntimeException("method not implemented yet");
|
||||
}
|
||||
|
||||
public String getSheetNameByExternSheet(int externSheetIndex) {
|
||||
int sheetIndex = convertFromExternalSheetIndex(externSheetIndex);
|
||||
return _uBook.getSheetName(sheetIndex);
|
||||
}
|
||||
|
||||
public String getNameText(NamePtg namePtg) {
|
||||
return _uBook.getNameAt(namePtg.getIndex()).getNameName();
|
||||
}
|
||||
public EvaluationName getName(NamePtg namePtg) {
|
||||
int ix = namePtg.getIndex();
|
||||
return new Name(_uBook.getNameAt(ix), ix, this);
|
||||
}
|
||||
public Ptg[] getFormulaTokens(Cell cell) {
|
||||
XSSFEvaluationWorkbook frBook = XSSFEvaluationWorkbook.create(_uBook);
|
||||
return FormulaParser.parse(cell.getCellFormula(), frBook);
|
||||
}
|
||||
|
||||
private static final class Name implements EvaluationName {
|
||||
|
||||
private final XSSFName _nameRecord;
|
||||
private final int _index;
|
||||
private final FormulaParsingWorkbook _fpBook;
|
||||
|
||||
public Name(XSSFName name, int index, FormulaParsingWorkbook fpBook) {
|
||||
_nameRecord = name;
|
||||
_index = index;
|
||||
_fpBook = fpBook;
|
||||
}
|
||||
|
||||
public Ptg[] getNameDefinition() {
|
||||
|
||||
return FormulaParser.parse(_nameRecord.getReference(), _fpBook);
|
||||
}
|
||||
|
||||
public String getNameText() {
|
||||
return _nameRecord.getNameName();
|
||||
}
|
||||
|
||||
public boolean hasFormula() {
|
||||
// TODO - no idea if this is right
|
||||
CTDefinedName ctn = _nameRecord.getCTName();
|
||||
String strVal = ctn.getStringValue();
|
||||
return !ctn.getFunction() && strVal != null && strVal.length() > 0;
|
||||
}
|
||||
|
||||
public boolean isFunctionName() {
|
||||
return _nameRecord.isFunctionName();
|
||||
}
|
||||
|
||||
public boolean isRange() {
|
||||
return hasFormula(); // TODO - is this right?
|
||||
}
|
||||
public NamePtg createPtg() {
|
||||
return new NamePtg(_index);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,256 @@
|
||||
/* ====================================================================
|
||||
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.xssf.usermodel;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.poi.hssf.record.formula.eval.BoolEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ErrorEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.NumberEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.StringEval;
|
||||
import org.apache.poi.hssf.record.formula.eval.ValueEval;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.ss.formula.WorkbookEvaluator;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
|
||||
/**
|
||||
* Evaluates formula cells.<p/>
|
||||
*
|
||||
* For performance reasons, this class keeps a cache of all previously calculated intermediate
|
||||
* cell values. Be sure to call {@link #clearCache()} if any workbook cells are changed between
|
||||
* calls to evaluate~ methods on this class.
|
||||
*
|
||||
* @author Amol S. Deshmukh < amolweb at ya hoo dot com >
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class XSSFFormulaEvaluator implements FormulaEvaluator {
|
||||
|
||||
private WorkbookEvaluator _bookEvaluator;
|
||||
|
||||
public XSSFFormulaEvaluator(XSSFWorkbook workbook) {
|
||||
_bookEvaluator = new WorkbookEvaluator(XSSFEvaluationWorkbook.create(workbook));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO for debug/test use
|
||||
*/
|
||||
/* package */ int getEvaluationCount() {
|
||||
return _bookEvaluator.getEvaluationCount();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should be called whenever there are major changes (e.g. moving sheets) 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() {
|
||||
_bookEvaluator.clearAllCachedResultValues();
|
||||
}
|
||||
/**
|
||||
* Should be called whenever there are changes to individual 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 clearCachedResultValue(Sheet sheet, int rowIndex, int columnIndex) {
|
||||
_bookEvaluator.clearCachedResultValue(sheet, rowIndex, columnIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* If cell contains a formula, the formula is evaluated and returned,
|
||||
* else the CellValue simply copies the appropriate cell value from
|
||||
* the cell and also its cell type. This method should be preferred over
|
||||
* evaluateInCell() when the call should not modify the contents of the
|
||||
* original cell.
|
||||
* @param cell
|
||||
*/
|
||||
public CellValue evaluate(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (cell.getCellType()) {
|
||||
case XSSFCell.CELL_TYPE_BOOLEAN:
|
||||
return CellValue.valueOf(cell.getBooleanCellValue());
|
||||
case XSSFCell.CELL_TYPE_ERROR:
|
||||
return CellValue.getError(cell.getErrorCellValue());
|
||||
case XSSFCell.CELL_TYPE_FORMULA:
|
||||
return evaluateFormulaCellValue(cell);
|
||||
case XSSFCell.CELL_TYPE_NUMERIC:
|
||||
return new CellValue(cell.getNumericCellValue());
|
||||
case XSSFCell.CELL_TYPE_STRING:
|
||||
return new CellValue(cell.getRichStringCellValue().getString());
|
||||
}
|
||||
throw new IllegalStateException("Bad cell type (" + cell.getCellType() + ")");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If cell contains formula, it evaluates the formula,
|
||||
* and saves the result of the formula. The cell
|
||||
* remains as a formula cell.
|
||||
* Else if cell does not contain formula, this method leaves
|
||||
* the cell unchanged.
|
||||
* Note that the type of the formula result is returned,
|
||||
* so you know what kind of value is also stored with
|
||||
* the formula.
|
||||
* <pre>
|
||||
* int evaluatedCellType = evaluator.evaluateFormulaCell(cell);
|
||||
* </pre>
|
||||
* Be aware that your cell will hold both the formula,
|
||||
* and the result. If you want the cell replaced with
|
||||
* the result of the formula, use {@link #evaluateInCell(HSSFCell)}
|
||||
* @param cell The cell to evaluate
|
||||
* @return The type of the formula result (the cell's type remains as HSSFCell.CELL_TYPE_FORMULA however)
|
||||
*/
|
||||
public int evaluateFormulaCell(Cell cell) {
|
||||
if (cell == null || cell.getCellType() != XSSFCell.CELL_TYPE_FORMULA) {
|
||||
return -1;
|
||||
}
|
||||
CellValue cv = evaluateFormulaCellValue(cell);
|
||||
// cell remains a formula cell, but the cached value is changed
|
||||
setCellValue(cell, cv);
|
||||
return cv.getCellType();
|
||||
}
|
||||
|
||||
/**
|
||||
* If cell contains formula, it evaluates the formula, and
|
||||
* puts the formula result back into the cell, in place
|
||||
* of the old formula.
|
||||
* Else if cell does not contain formula, this method leaves
|
||||
* the cell unchanged.
|
||||
* Note that the same instance of HSSFCell is returned to
|
||||
* allow chained calls like:
|
||||
* <pre>
|
||||
* int evaluatedCellType = evaluator.evaluateInCell(cell).getCellType();
|
||||
* </pre>
|
||||
* Be aware that your cell value will be changed to hold the
|
||||
* result of the formula. If you simply want the formula
|
||||
* value computed for you, use {@link #evaluateFormulaCell(HSSFCell)}
|
||||
* @param cell
|
||||
*/
|
||||
public XSSFCell evaluateInCell(Cell cell) {
|
||||
if (cell == null) {
|
||||
return null;
|
||||
}
|
||||
XSSFCell result = (XSSFCell) cell;
|
||||
if (cell.getCellType() == XSSFCell.CELL_TYPE_FORMULA) {
|
||||
CellValue cv = evaluateFormulaCellValue(cell);
|
||||
setCellType(cell, cv); // cell will no longer be a formula cell
|
||||
setCellValue(cell, cv);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private static void setCellType(Cell cell, CellValue cv) {
|
||||
int cellType = cv.getCellType();
|
||||
switch (cellType) {
|
||||
case XSSFCell.CELL_TYPE_BOOLEAN:
|
||||
case XSSFCell.CELL_TYPE_ERROR:
|
||||
case XSSFCell.CELL_TYPE_NUMERIC:
|
||||
case XSSFCell.CELL_TYPE_STRING:
|
||||
cell.setCellType(cellType);
|
||||
return;
|
||||
case XSSFCell.CELL_TYPE_BLANK:
|
||||
// never happens - blanks eventually get translated to zero
|
||||
case XSSFCell.CELL_TYPE_FORMULA:
|
||||
// this will never happen, we have already evaluated the formula
|
||||
}
|
||||
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
|
||||
}
|
||||
|
||||
private static void setCellValue(Cell cell, CellValue cv) {
|
||||
int cellType = cv.getCellType();
|
||||
switch (cellType) {
|
||||
case XSSFCell.CELL_TYPE_BOOLEAN:
|
||||
cell.setCellValue(cv.getBooleanValue());
|
||||
break;
|
||||
case XSSFCell.CELL_TYPE_ERROR:
|
||||
cell.setCellErrorValue(cv.getErrorValue());
|
||||
break;
|
||||
case XSSFCell.CELL_TYPE_NUMERIC:
|
||||
cell.setCellValue(cv.getNumberValue());
|
||||
break;
|
||||
case XSSFCell.CELL_TYPE_STRING:
|
||||
cell.setCellValue(new XSSFRichTextString(cv.getStringValue()));
|
||||
break;
|
||||
case XSSFCell.CELL_TYPE_BLANK:
|
||||
// never happens - blanks eventually get translated to zero
|
||||
case XSSFCell.CELL_TYPE_FORMULA:
|
||||
// this will never happen, we have already evaluated the formula
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected cell value type (" + cellType + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops over all cells in all sheets of the supplied
|
||||
* workbook.
|
||||
* For cells that contain formulas, their formulas are
|
||||
* evaluated, and the results are saved. These cells
|
||||
* remain as formula cells.
|
||||
* For cells that do not contain formulas, no changes
|
||||
* are made.
|
||||
* This is a helpful wrapper around looping over all
|
||||
* cells, and calling evaluateFormulaCell on each one.
|
||||
*/
|
||||
public static void evaluateAllFormulaCells(XSSFWorkbook wb) {
|
||||
XSSFFormulaEvaluator evaluator = new XSSFFormulaEvaluator(wb);
|
||||
for(int i=0; i<wb.getNumberOfSheets(); i++) {
|
||||
Sheet sheet = wb.getSheetAt(i);
|
||||
|
||||
for (Iterator<Row> rit = sheet.rowIterator(); rit.hasNext();) {
|
||||
Row r = rit.next();
|
||||
|
||||
for (Iterator cit = r.cellIterator(); cit.hasNext();) {
|
||||
XSSFCell c = (XSSFCell) cit.next();
|
||||
if (c.getCellType() == XSSFCell.CELL_TYPE_FORMULA)
|
||||
evaluator.evaluateFormulaCell(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CellValue wrapper around the supplied ValueEval instance.
|
||||
* @param eval
|
||||
*/
|
||||
private CellValue evaluateFormulaCellValue(Cell cell) {
|
||||
ValueEval eval = _bookEvaluator.evaluate(cell);
|
||||
if (eval instanceof NumberEval) {
|
||||
NumberEval ne = (NumberEval) eval;
|
||||
return new CellValue(ne.getNumberValue());
|
||||
}
|
||||
if (eval instanceof BoolEval) {
|
||||
BoolEval be = (BoolEval) eval;
|
||||
return CellValue.valueOf(be.getBooleanValue());
|
||||
}
|
||||
if (eval instanceof StringEval) {
|
||||
StringEval ne = (StringEval) eval;
|
||||
return new CellValue(ne.getStringValue());
|
||||
}
|
||||
if (eval instanceof ErrorEval) {
|
||||
return CellValue.getError(((ErrorEval)eval).getErrorCode());
|
||||
}
|
||||
throw new RuntimeException("Unexpected eval class (" + eval.getClass().getName() + ")");
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ public class XSSFName implements Name {
|
||||
|
||||
public boolean isFunctionName() {
|
||||
// TODO Figure out how HSSF does this, and do the same!
|
||||
return false;
|
||||
return ctName.getFunction(); // maybe this works - verify
|
||||
}
|
||||
/**
|
||||
* Returns the underlying named range object
|
||||
|
@ -408,15 +408,6 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO - figure out what the hell this methods does in
|
||||
* HSSF...
|
||||
*/
|
||||
public String resolveNameXText(int refIndex, int definedNameIndex) {
|
||||
// TODO Replace with something proper
|
||||
return null;
|
||||
}
|
||||
|
||||
public short getNumCellStyles() {
|
||||
return (short) ((StylesTable)stylesSource).getNumCellStyles();
|
||||
}
|
||||
@ -450,23 +441,6 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Doesn't do anything - returns the same index
|
||||
* TODO - figure out if this is a ole2 specific thing, or
|
||||
* if we need to do something proper here too!
|
||||
*/
|
||||
public int getSheetIndexFromExternSheetIndex(int externSheetNumber) {
|
||||
return externSheetNumber;
|
||||
}
|
||||
/**
|
||||
* Doesn't do anything special - returns the same as getSheetName()
|
||||
* TODO - figure out if this is a ole2 specific thing, or
|
||||
* if we need to do something proper here too!
|
||||
*/
|
||||
public String findSheetNameFromExternSheet(int externSheetIndex) {
|
||||
return getSheetName(externSheetIndex);
|
||||
}
|
||||
|
||||
public Sheet getSheet(String name) {
|
||||
CTSheet[] sheets = this.workbook.getSheets().getSheetArray();
|
||||
for (int i = 0 ; i < sheets.length ; ++i) {
|
||||
@ -495,19 +469,6 @@ public class XSSFWorkbook extends POIXMLDocument implements Workbook {
|
||||
return this.sheets.indexOf(sheet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the external sheet index of the sheet
|
||||
* with the given internal index, creating one
|
||||
* if needed.
|
||||
* Used by some of the more obscure formula and
|
||||
* named range things.
|
||||
* Fairly easy on XSSF (we think...) since the
|
||||
* internal and external indicies are the same
|
||||
*/
|
||||
public int getExternalSheetIndex(int internalSheetIndex) {
|
||||
return internalSheetIndex;
|
||||
}
|
||||
|
||||
public String getSheetName(int sheet) {
|
||||
return this.workbook.getSheets().getSheetArray(sheet).getName();
|
||||
}
|
||||
|
@ -28,10 +28,10 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.formula.eval.TestFormulasFromSpreadsheet;
|
||||
import org.apache.poi.hssf.record.formula.functions.TestMathX;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.openxml4j.opc.Package;
|
||||
|
||||
/**
|
||||
@ -90,7 +90,7 @@ public final class TestFormulaEvaluatorOnXSSF extends TestCase {
|
||||
public static final int NUMBER_OF_ROWS_PER_FUNCTION = 4;
|
||||
}
|
||||
|
||||
private Workbook workbook;
|
||||
private XSSFWorkbook workbook;
|
||||
private Sheet sheet;
|
||||
// Note - multiple failures are aggregated before ending.
|
||||
// If one or more functions fail, a single AssertionFailedError is thrown at the end
|
||||
@ -107,7 +107,7 @@ public final class TestFormulaEvaluatorOnXSSF extends TestCase {
|
||||
}
|
||||
|
||||
|
||||
private static void confirmExpectedResult(String msg, Cell expected, FormulaEvaluator.CellValue actual) {
|
||||
private static void confirmExpectedResult(String msg, Cell expected, CellValue actual) {
|
||||
if (expected == null) {
|
||||
throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
|
||||
}
|
||||
@ -199,7 +199,7 @@ public final class TestFormulaEvaluatorOnXSSF extends TestCase {
|
||||
*/
|
||||
private void processFunctionGroup(int startRowIndex, String testFocusFunctionName) {
|
||||
|
||||
FormulaEvaluator evaluator = new FormulaEvaluator(workbook);
|
||||
FormulaEvaluator evaluator = new XSSFFormulaEvaluator(workbook);
|
||||
|
||||
int rowIndex = startRowIndex;
|
||||
while (true) {
|
||||
@ -256,7 +256,7 @@ public final class TestFormulaEvaluatorOnXSSF extends TestCase {
|
||||
continue;
|
||||
}
|
||||
|
||||
FormulaEvaluator.CellValue actualValue;
|
||||
CellValue actualValue;
|
||||
try {
|
||||
actualValue = evaluator.evaluate(c);
|
||||
} catch (RuntimeException e) {
|
||||
|
@ -17,15 +17,14 @@
|
||||
|
||||
package org.apache.poi.xssf.usermodel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class TestXSSFFormulaEvaluation extends TestCase {
|
||||
public final class TestXSSFFormulaEvaluation extends TestCase {
|
||||
public TestXSSFFormulaEvaluation(String name) {
|
||||
super(name);
|
||||
|
||||
@ -37,7 +36,7 @@ public class TestXSSFFormulaEvaluation extends TestCase {
|
||||
}
|
||||
|
||||
public void testSimpleArithmatic() {
|
||||
Workbook wb = new XSSFWorkbook();
|
||||
XSSFWorkbook wb = new XSSFWorkbook();
|
||||
Sheet s = wb.createSheet();
|
||||
Row r = s.createRow(0);
|
||||
|
||||
@ -49,7 +48,7 @@ public class TestXSSFFormulaEvaluation extends TestCase {
|
||||
c2.setCellFormula("10/2");
|
||||
assertTrue( Double.isNaN(c2.getNumericCellValue()) );
|
||||
|
||||
FormulaEvaluator fe = new FormulaEvaluator(s, wb);
|
||||
FormulaEvaluator fe = new XSSFFormulaEvaluator(wb);
|
||||
|
||||
fe.evaluateFormulaCell(c1);
|
||||
fe.evaluateFormulaCell(c2);
|
||||
@ -59,7 +58,7 @@ public class TestXSSFFormulaEvaluation extends TestCase {
|
||||
}
|
||||
|
||||
public void testSumCount() {
|
||||
Workbook wb = new XSSFWorkbook();
|
||||
XSSFWorkbook wb = new XSSFWorkbook();
|
||||
Sheet s = wb.createSheet();
|
||||
Row r = s.createRow(0);
|
||||
r.createCell(0).setCellValue(2.5);
|
||||
@ -87,7 +86,7 @@ public class TestXSSFFormulaEvaluation extends TestCase {
|
||||
|
||||
|
||||
// Evaluate and test
|
||||
FormulaEvaluator fe = new FormulaEvaluator(s, wb);
|
||||
FormulaEvaluator fe = new XSSFFormulaEvaluator(wb);
|
||||
|
||||
fe.evaluateFormulaCell(c1);
|
||||
fe.evaluateFormulaCell(c2);
|
||||
|
@ -26,7 +26,7 @@ import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder.SheetRecordCollectingListener;
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.model.Workbook;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.Record;
|
||||
@ -101,7 +101,7 @@ public final class TestEventWorkbookBuilder extends TestCase {
|
||||
|
||||
// Check we can get the formula without breaking
|
||||
for(int i=0; i<fRecs.length; i++) {
|
||||
FormulaParser.toFormulaString(stubHSSF, fRecs[i].getParsedExpression());
|
||||
HSSFFormulaParser.toFormulaString(stubHSSF, fRecs[i].getParsedExpression());
|
||||
}
|
||||
|
||||
// Peer into just one formula, and check that
|
||||
@ -123,19 +123,19 @@ public final class TestEventWorkbookBuilder extends TestCase {
|
||||
fr = fRecs[0];
|
||||
assertEquals(1, fr.getRow());
|
||||
assertEquals(0, fr.getColumn());
|
||||
assertEquals("Sheet1!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
assertEquals("Sheet1!A1", HSSFFormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
|
||||
// Sheet 1 A5 is to another sheet
|
||||
fr = fRecs[3];
|
||||
assertEquals(4, fr.getRow());
|
||||
assertEquals(0, fr.getColumn());
|
||||
assertEquals("'S2'!A1", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
assertEquals("'S2'!A1", HSSFFormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
|
||||
// Sheet 1 A7 is to another sheet, range
|
||||
fr = fRecs[5];
|
||||
assertEquals(6, fr.getRow());
|
||||
assertEquals(0, fr.getColumn());
|
||||
assertEquals("SUM(Sh3!A1:A4)", FormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
assertEquals("SUM(Sh3!A1:A4)", HSSFFormulaParser.toFormulaString(stubHSSF, fr.getParsedExpression()));
|
||||
|
||||
|
||||
// Now, load via Usermodel and re-check
|
||||
|
@ -21,7 +21,6 @@ import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.model.FormulaParser.FormulaParseException;
|
||||
import org.apache.poi.hssf.record.constant.ErrorConstant;
|
||||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
|
||||
import org.apache.poi.hssf.record.formula.AddPtg;
|
||||
@ -56,6 +55,7 @@ import org.apache.poi.hssf.usermodel.HSSFName;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.formula.FormulaParserTestHelper;
|
||||
|
||||
/**
|
||||
* Test the low level formula parser functionality. High level tests are to
|
||||
@ -67,10 +67,13 @@ public final class TestFormulaParser extends TestCase {
|
||||
* @return parsed token array already confirmed not <code>null</code>
|
||||
*/
|
||||
/* package */ static Ptg[] parseFormula(String formula) {
|
||||
Ptg[] result = FormulaParser.parse(formula, null);
|
||||
Ptg[] result = HSSFFormulaParser.parse(formula, (HSSFWorkbook)null);
|
||||
assertNotNull("Ptg array should not be null", result);
|
||||
return result;
|
||||
}
|
||||
private static String toFormulaString(Ptg[] ptgs) {
|
||||
return HSSFFormulaParser.toFormulaString((HSSFWorkbook)null, ptgs);
|
||||
}
|
||||
|
||||
public void testSimpleFormula() {
|
||||
Ptg[] ptgs = parseFormula("2+2");
|
||||
@ -133,7 +136,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
HSSFWorkbook w = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
|
||||
HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(w);
|
||||
|
||||
Ptg[] ptg = FormulaParser.parse("myFunc()", w);
|
||||
Ptg[] ptg = HSSFFormulaParser.parse("myFunc()", w);
|
||||
// myFunc() actually takes 1 parameter. Don't know if POI will ever be able to detect this problem
|
||||
|
||||
// the name gets encoded as the first arg
|
||||
@ -405,7 +408,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
Ptg[] ptgs = {
|
||||
new FuncPtg(10),
|
||||
};
|
||||
assertEquals("NA()", FormulaParser.toFormulaString(book, ptgs));
|
||||
assertEquals("NA()", HSSFFormulaParser.toFormulaString(book, ptgs));
|
||||
}
|
||||
|
||||
public void testPercent() {
|
||||
@ -639,11 +642,11 @@ public final class TestFormulaParser extends TestCase {
|
||||
String formulaString;
|
||||
Ptg[] ptgs;
|
||||
ptgs = parseFormula("sum(5, 2, if(3>2, sum(A1:A2), 6))");
|
||||
formulaString = FormulaParser.toFormulaString(null, ptgs);
|
||||
formulaString = toFormulaString(ptgs);
|
||||
assertEquals("SUM(5,2,IF(3>2,SUM(A1:A2),6))", formulaString);
|
||||
|
||||
ptgs = parseFormula("if(1<2,sum(5, 2, if(3>2, sum(A1:A2), 6)),4)");
|
||||
formulaString = FormulaParser.toFormulaString(null, ptgs);
|
||||
formulaString = toFormulaString(ptgs);
|
||||
assertEquals("IF(1<2,SUM(5,2,IF(3>2,SUM(A1:A2),6)),4)", formulaString);
|
||||
}
|
||||
public void testParserErrors() {
|
||||
@ -665,12 +668,9 @@ public final class TestFormulaParser extends TestCase {
|
||||
try {
|
||||
parseFormula(formula);
|
||||
throw new AssertionFailedError("expected parse exception");
|
||||
} catch (FormulaParseException e) {
|
||||
// expected during successful test
|
||||
assertNotNull(e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
fail("Wrong exception:" + e.getMessage());
|
||||
// expected during successful test
|
||||
FormulaParserTestHelper.confirmParseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -697,7 +697,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
Ptg[] ptgs = { spacePtg, new IntPtg(4), };
|
||||
String formulaString;
|
||||
try {
|
||||
formulaString = FormulaParser.toFormulaString(null, ptgs);
|
||||
formulaString = toFormulaString(ptgs);
|
||||
} catch (IllegalStateException e) {
|
||||
if(e.getMessage().equalsIgnoreCase("too much stuff left on the stack")) {
|
||||
throw new AssertionFailedError("Identified bug 44609");
|
||||
@ -709,7 +709,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
assertEquals("4", formulaString);
|
||||
|
||||
ptgs = new Ptg[] { new IntPtg(3), spacePtg, new IntPtg(4), spacePtg, AddPtg.instance, };
|
||||
formulaString = FormulaParser.toFormulaString(null, ptgs);
|
||||
formulaString = toFormulaString(ptgs);
|
||||
assertEquals("3+4", formulaString);
|
||||
}
|
||||
|
||||
@ -725,7 +725,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
DividePtg.instance,
|
||||
};
|
||||
try {
|
||||
FormulaParser.toFormulaString(null, ptgs);
|
||||
toFormulaString(ptgs);
|
||||
fail("Expected exception was not thrown");
|
||||
} catch (IllegalStateException e) {
|
||||
// expected during successful test
|
||||
@ -764,10 +764,10 @@ public final class TestFormulaParser extends TestCase {
|
||||
private static void confirmArgCountMsg(String formula, String expectedMessage) {
|
||||
HSSFWorkbook book = new HSSFWorkbook();
|
||||
try {
|
||||
FormulaParser.parse(formula, book);
|
||||
HSSFFormulaParser.parse(formula, book);
|
||||
throw new AssertionFailedError("Didn't get parse exception as expected");
|
||||
} catch (FormulaParseException e) {
|
||||
assertEquals(expectedMessage, e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
FormulaParserTestHelper.confirmParseException(e, expectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -776,15 +776,17 @@ public final class TestFormulaParser extends TestCase {
|
||||
try {
|
||||
parseFormula("round(3.14;2)");
|
||||
throw new AssertionFailedError("Didn't get parse exception as expected");
|
||||
} catch (FormulaParseException e) {
|
||||
assertEquals("Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'", e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
FormulaParserTestHelper.confirmParseException(e,
|
||||
"Parse error near char 10 ';' in specified formula 'round(3.14;2)'. Expected ',' or ')'");
|
||||
}
|
||||
|
||||
try {
|
||||
parseFormula(" =2+2");
|
||||
throw new AssertionFailedError("Didn't get parse exception as expected");
|
||||
} catch (FormulaParseException e) {
|
||||
assertEquals("The specified formula ' =2+2' starts with an equals sign which is not allowed.", e.getMessage());
|
||||
} catch (RuntimeException e) {
|
||||
FormulaParserTestHelper.confirmParseException(e,
|
||||
"The specified formula ' =2+2' starts with an equals sign which is not allowed.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -819,7 +821,7 @@ public final class TestFormulaParser extends TestCase {
|
||||
|
||||
Ptg[] ptgs;
|
||||
try {
|
||||
ptgs = FormulaParser.parse("count(pfy1)", wb);
|
||||
ptgs = HSSFFormulaParser.parse("count(pfy1)", wb);
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (e.getMessage().equals("Specified colIx (1012) is out of range")) {
|
||||
throw new AssertionFailedError("Identified bug 45354");
|
||||
@ -835,10 +837,9 @@ public final class TestFormulaParser extends TestCase {
|
||||
try {
|
||||
cell.setCellFormula("count(pf1)");
|
||||
throw new AssertionFailedError("Expected formula parse execption");
|
||||
} catch (FormulaParseException e) {
|
||||
if (!e.getMessage().equals("Specified named range 'pf1' does not exist in the current workbook.")) {
|
||||
throw e;
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
FormulaParserTestHelper.confirmParseException(e,
|
||||
"Specified named range 'pf1' does not exist in the current workbook.");
|
||||
}
|
||||
cell.setCellFormula("count(fp1)"); // plain cell ref, col is in range
|
||||
}
|
||||
@ -850,14 +851,14 @@ public final class TestFormulaParser extends TestCase {
|
||||
HSSFWorkbook book = new HSSFWorkbook();
|
||||
book.createSheet("Sheet1");
|
||||
|
||||
ptgs = FormulaParser.parse("Sheet1!A10:A40000", book);
|
||||
ptgs = HSSFFormulaParser.parse("Sheet1!A10:A40000", book);
|
||||
aptg = (AreaI) ptgs[0];
|
||||
if (aptg.getLastRow() == -25537) {
|
||||
throw new AssertionFailedError("Identified bug 45358");
|
||||
}
|
||||
assertEquals(39999, aptg.getLastRow());
|
||||
|
||||
ptgs = FormulaParser.parse("Sheet1!A10:A65536", book);
|
||||
ptgs = HSSFFormulaParser.parse("Sheet1!A10:A65536", book);
|
||||
aptg = (AreaI) ptgs[0];
|
||||
assertEquals(65535, aptg.getLastRow());
|
||||
|
||||
|
@ -17,9 +17,9 @@
|
||||
|
||||
package org.apache.poi.hssf.model;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser.FormulaParseException;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.NamePtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
@ -29,7 +29,8 @@ import org.apache.poi.hssf.usermodel.HSSFName;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.formula.FormulaParserTestHelper;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
|
||||
/**
|
||||
* Test the low level formula parser functionality,
|
||||
@ -51,21 +52,21 @@ public final class TestFormulaParserEval extends TestCase {
|
||||
name.setNameName("testName");
|
||||
name.setReference("A1:A2");
|
||||
|
||||
ptgs = FormulaParser.parse("SUM(testName)", workbook);
|
||||
ptgs = HSSFFormulaParser.parse("SUM(testName)", workbook);
|
||||
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
|
||||
assertEquals(NamePtg.class, ptgs[0].getClass());
|
||||
assertEquals(FuncVarPtg.class, ptgs[1].getClass());
|
||||
|
||||
// Now make it a single cell
|
||||
name.setReference("C3");
|
||||
ptgs = FormulaParser.parse("SUM(testName)", workbook);
|
||||
ptgs = HSSFFormulaParser.parse("SUM(testName)", workbook);
|
||||
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
|
||||
assertEquals(NamePtg.class, ptgs[0].getClass());
|
||||
assertEquals(FuncVarPtg.class, ptgs[1].getClass());
|
||||
|
||||
// And make it non-contiguous
|
||||
name.setReference("A1:A2,C3");
|
||||
ptgs = FormulaParser.parse("SUM(testName)", workbook);
|
||||
ptgs = HSSFFormulaParser.parse("SUM(testName)", workbook);
|
||||
assertTrue("two tokens expected, got "+ptgs.length,ptgs.length == 2);
|
||||
assertEquals(NamePtg.class, ptgs[0].getClass());
|
||||
assertEquals(FuncVarPtg.class, ptgs[1].getClass());
|
||||
@ -89,11 +90,12 @@ public final class TestFormulaParserEval extends TestCase {
|
||||
CellValue result;
|
||||
try {
|
||||
result = fe.evaluate(cell);
|
||||
} catch (FormulaParseException e) {
|
||||
if(e.getMessage().equals("Found reference to named range \"A\", but that named range wasn't defined!")) {
|
||||
fail("Identifed bug 44539");
|
||||
} catch (RuntimeException e) {
|
||||
FormulaParserTestHelper.confirmParseException(e);
|
||||
if (!e.getMessage().equals("Found reference to named range \"A\", but that named range wasn't defined!")) {
|
||||
throw new AssertionFailedError("Identifed bug 44539");
|
||||
}
|
||||
throw new RuntimeException(e);
|
||||
throw e;
|
||||
}
|
||||
assertEquals(HSSFCell.CELL_TYPE_NUMERIC, result.getCellType());
|
||||
assertEquals(42.0, result.getNumberValue(), 0.0);
|
||||
|
@ -23,6 +23,7 @@ import junit.framework.TestCase;
|
||||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
/**
|
||||
* Tests specific formula examples in <tt>OperandClassTransformer</tt>.
|
||||
@ -31,9 +32,15 @@ import org.apache.poi.hssf.record.formula.Ptg;
|
||||
*/
|
||||
public final class TestOperandClassTransformer extends TestCase {
|
||||
|
||||
private static Ptg[] parseFormula(String formula) {
|
||||
Ptg[] result = HSSFFormulaParser.parse(formula, (HSSFWorkbook)null);
|
||||
assertNotNull("Ptg array should not be null", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testMdeterm() {
|
||||
String formula = "MDETERM(ABS(A1))";
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, null);
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
|
||||
confirmTokenClass(ptgs, 0, Ptg.CLASS_ARRAY);
|
||||
confirmFuncClass(ptgs, 1, "ABS", Ptg.CLASS_ARRAY);
|
||||
@ -51,7 +58,7 @@ public final class TestOperandClassTransformer extends TestCase {
|
||||
*/
|
||||
public void DISABLED_testIndexPi1() {
|
||||
String formula = "INDEX(PI(),1)";
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, null);
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
|
||||
confirmFuncClass(ptgs, 1, "PI", Ptg.CLASS_ARRAY); // fails as of POI 3.1
|
||||
confirmFuncClass(ptgs, 2, "INDEX", Ptg.CLASS_VALUE);
|
||||
@ -63,7 +70,7 @@ public final class TestOperandClassTransformer extends TestCase {
|
||||
*/
|
||||
public void testDirectOperandOfValueOperator() {
|
||||
String formula = "COUNT(A1*1)";
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, null);
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
if (ptgs[0].getPtgClass() == Ptg.CLASS_REF) {
|
||||
throw new AssertionFailedError("Identified bug 45348");
|
||||
}
|
||||
@ -78,13 +85,13 @@ public final class TestOperandClassTransformer extends TestCase {
|
||||
public void testRtoV() {
|
||||
|
||||
String formula = "lookup(A1, A3:A52, B3:B52)";
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, null);
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
confirmTokenClass(ptgs, 0, Ptg.CLASS_VALUE);
|
||||
}
|
||||
|
||||
public void testComplexIRR_bug45041() {
|
||||
String formula = "(1+IRR(SUMIF(A:A,ROW(INDIRECT(MIN(A:A)&\":\"&MAX(A:A))),B:B),0))^365-1";
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, null);
|
||||
Ptg[] ptgs = parseFormula(formula);
|
||||
|
||||
FuncVarPtg rowFunc = (FuncVarPtg) ptgs[10];
|
||||
FuncVarPtg sumifFunc = (FuncVarPtg) ptgs[12];
|
||||
|
@ -82,7 +82,7 @@ public final class TestRVA extends TestCase {
|
||||
|
||||
private void confirmCell(HSSFCell formulaCell, String formula, HSSFWorkbook wb) {
|
||||
Ptg[] excelPtgs = FormulaExtractor.getPtgs(formulaCell);
|
||||
Ptg[] poiPtgs = FormulaParser.parse(formula, wb);
|
||||
Ptg[] poiPtgs = HSSFFormulaParser.parse(formula, wb);
|
||||
int nExcelTokens = excelPtgs.length;
|
||||
int nPoiTokens = poiPtgs.length;
|
||||
if (nExcelTokens != nPoiTokens) {
|
||||
|
@ -20,7 +20,7 @@ package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
/**
|
||||
@ -87,7 +87,7 @@ public final class TestAreaPtg extends TestCase {
|
||||
private static String shiftAllColumnsBy1(String formula) {
|
||||
int letUsShiftColumn1By1Column=1;
|
||||
HSSFWorkbook wb = null;
|
||||
Ptg[] ptgs = FormulaParser.parse(formula, wb);
|
||||
Ptg[] ptgs = HSSFFormulaParser.parse(formula, wb);
|
||||
for(int i=0; i<ptgs.length; i++)
|
||||
{
|
||||
Ptg ptg = ptgs[i];
|
||||
@ -98,7 +98,7 @@ public final class TestAreaPtg extends TestCase {
|
||||
aptg.setLastColumn((short)(aptg.getLastColumn()+letUsShiftColumn1By1Column));
|
||||
}
|
||||
}
|
||||
String newFormula = FormulaParser.toFormulaString(wb, ptgs);
|
||||
String newFormula = HSSFFormulaParser.toFormulaString(wb, ptgs);
|
||||
return newFormula;
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,12 @@ import java.io.IOException;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.usermodel.HSSFCell;
|
||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
/**
|
||||
* Tests for functions from external workbooks (e.g. YEARFRAC).
|
||||
*
|
||||
@ -52,7 +52,7 @@ public final class TestExternalFunctionFormulas extends TestCase {
|
||||
|
||||
public void testParse() {
|
||||
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("externalFunctionExample.xls");
|
||||
Ptg[] ptgs = FormulaParser.parse("YEARFRAC(B1,C1)", wb);
|
||||
Ptg[] ptgs = HSSFFormulaParser.parse("YEARFRAC(B1,C1)", wb);
|
||||
assertEquals(4, ptgs.length);
|
||||
assertEquals(NameXPtg.class, ptgs[0].getClass());
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
package org.apache.poi.hssf.record.formula;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
@ -36,7 +36,7 @@ public final class TestFuncVarPtg extends TestCase {
|
||||
*/
|
||||
public void testOperandClass() {
|
||||
HSSFWorkbook book = new HSSFWorkbook();
|
||||
Ptg[] ptgs = FormulaParser.parse("sum(A1:A2)", book);
|
||||
Ptg[] ptgs = HSSFFormulaParser.parse("sum(A1:A2)", book);
|
||||
assertEquals(2, ptgs.length);
|
||||
assertEquals(AreaPtg.class, ptgs[0].getClass());
|
||||
|
||||
|
@ -25,7 +25,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
/**
|
||||
* Tests HSSFFormulaEvaluator for its handling of cell formula circular references.
|
||||
*
|
||||
|
@ -28,7 +28,7 @@ import org.apache.poi.hssf.usermodel.HSSFName;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
|
@ -30,7 +30,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
|
||||
/**
|
||||
* Miscellaneous tests for bugzilla entries.<p/> The test name contains the
|
||||
|
@ -25,12 +25,12 @@ import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.formula.functions.TestMathX;
|
||||
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;;
|
||||
|
||||
/**
|
||||
* Tests formulas and operators as loaded from a test data spreadsheet.<p/>
|
||||
@ -88,7 +88,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
|
||||
public static final int NUMBER_OF_ROWS_PER_FUNCTION = 4;
|
||||
}
|
||||
|
||||
private Workbook workbook;
|
||||
private HSSFWorkbook workbook;
|
||||
private Sheet sheet;
|
||||
// Note - multiple failures are aggregated before ending.
|
||||
// If one or more functions fail, a single AssertionFailedError is thrown at the end
|
||||
@ -105,7 +105,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
|
||||
}
|
||||
|
||||
|
||||
private static void confirmExpectedResult(String msg, Cell expected, FormulaEvaluator.CellValue actual) {
|
||||
private static void confirmExpectedResult(String msg, Cell expected, CellValue actual) {
|
||||
if (expected == null) {
|
||||
throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
|
||||
}
|
||||
@ -178,7 +178,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
|
||||
* Typically pass <code>null</code> to test all functions
|
||||
*/
|
||||
private void processFunctionGroup(int startRowIndex, String testFocusFunctionName) {
|
||||
FormulaEvaluator evaluator = new FormulaEvaluator(workbook);
|
||||
HSSFFormulaEvaluator evaluator = new HSSFFormulaEvaluator(workbook);
|
||||
|
||||
int rowIndex = startRowIndex;
|
||||
while (true) {
|
||||
@ -219,7 +219,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
|
||||
* @return a constant from the local Result class denoting whether there were any evaluation
|
||||
* cases, and whether they all succeeded.
|
||||
*/
|
||||
private int processFunctionRow(FormulaEvaluator evaluator, String targetFunctionName,
|
||||
private int processFunctionRow(HSSFFormulaEvaluator evaluator, String targetFunctionName,
|
||||
Row formulasRow, Row expectedValuesRow) {
|
||||
|
||||
int result = Result.NO_EVALUATIONS_FOUND; // so far
|
||||
@ -232,7 +232,7 @@ public final class TestFormulasFromSpreadsheet extends TestCase {
|
||||
continue;
|
||||
}
|
||||
|
||||
FormulaEvaluator.CellValue actualValue = evaluator.evaluate(c);
|
||||
CellValue actualValue = evaluator.evaluate(c);
|
||||
|
||||
Cell expectedValueCell = getExpectedValueCell(expectedValuesRow, colnum);
|
||||
try {
|
||||
|
@ -27,7 +27,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
|
||||
/**
|
||||
* Test for percent operator evaluator.
|
||||
|
@ -20,7 +20,7 @@ package org.apache.poi.hssf.record.formula.function;
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.model.FormulaParser;
|
||||
import org.apache.poi.hssf.model.HSSFFormulaParser;
|
||||
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncPtg;
|
||||
import org.apache.poi.hssf.record.formula.FuncVarPtg;
|
||||
@ -36,7 +36,7 @@ public final class TestParseMissingBuiltInFuncs extends TestCase {
|
||||
|
||||
private static Ptg[] parse(String formula) {
|
||||
HSSFWorkbook book = new HSSFWorkbook();
|
||||
return FormulaParser.parse(formula, book);
|
||||
return HSSFFormulaParser.parse(formula, book);
|
||||
}
|
||||
private static void confirmFunc(String formula, int expPtgArraySize, boolean isVarArgFunc, int funcIx) {
|
||||
Ptg[] ptgs = parse(formula);
|
||||
@ -58,7 +58,7 @@ public final class TestParseMissingBuiltInFuncs extends TestCase {
|
||||
|
||||
// check that parsed Ptg array converts back to formula text OK
|
||||
HSSFWorkbook book = new HSSFWorkbook();
|
||||
String reRenderedFormula = FormulaParser.toFormulaString(book, ptgs);
|
||||
String reRenderedFormula = HSSFFormulaParser.toFormulaString(book, ptgs);
|
||||
assertEquals(formula, reRenderedFormula);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
|
||||
/**
|
||||
* Test cases for COUNT(), COUNTA() COUNTIF(), COUNTBLANK()
|
||||
|
@ -76,7 +76,7 @@ public final class TestDate extends TestCase {
|
||||
|
||||
private void confirm(String formulaText, double expectedResult) {
|
||||
cell11.setCellFormula(formulaText);
|
||||
evaluator.clearCache();
|
||||
evaluator.clearAllCachedResultValues();
|
||||
double actualValue = evaluator.evaluate(cell11).getNumberValue();
|
||||
assertEquals(expectedResult, actualValue, 0);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
|
||||
/**
|
||||
* Tests INDEX() as loaded from a test data spreadsheet.<p/>
|
||||
@ -66,7 +66,7 @@ public final class TestIndexFunctionFromSpreadsheet extends TestCase {
|
||||
|
||||
|
||||
|
||||
private static void confirmExpectedResult(String msg, HSSFCell expected, HSSFFormulaEvaluator.CellValue actual) {
|
||||
private static void confirmExpectedResult(String msg, HSSFCell expected, CellValue actual) {
|
||||
if (expected == null) {
|
||||
throw new AssertionFailedError(msg + " - Bad setup data expected value is null");
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
/**
|
||||
* Tests for Excel function ISBLANK()
|
||||
*
|
||||
|
@ -30,8 +30,8 @@ import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
|
||||
import org.apache.poi.hssf.usermodel.HSSFRow;
|
||||
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
|
||||
/**
|
||||
* Tests lookup functions (VLOOKUP, HLOOKUP, LOOKUP, MATCH) as loaded from a test data spreadsheet.<p/>
|
||||
|
@ -25,8 +25,8 @@ import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.hssf.record.FormulaRecord;
|
||||
import org.apache.poi.hssf.record.aggregates.FormulaRecordAggregate;
|
||||
import org.apache.poi.hssf.record.formula.Ptg;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
import org.apache.poi.hssf.util.CellReference;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -17,10 +17,10 @@
|
||||
|
||||
package org.apache.poi.hssf.usermodel;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.ss.usermodel.FormulaEvaluator.CellValue;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||
import org.apache.poi.ss.usermodel.CellValue;
|
||||
/**
|
||||
*
|
||||
* @author Josh Micich
|
||||
|
@ -0,0 +1,48 @@
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import org.apache.poi.ss.formula.FormulaParser;
|
||||
import org.apache.poi.ss.formula.FormulaParser.FormulaParseException;
|
||||
/**
|
||||
* Avoids making {@link FormulaParser#FormulaParseException} public
|
||||
*
|
||||
* @author Josh Micich
|
||||
*/
|
||||
public class FormulaParserTestHelper {
|
||||
public static void confirmParseException(RuntimeException e, String expectedMessage) {
|
||||
checkType(e);
|
||||
Assert.assertEquals(expectedMessage, e.getMessage());
|
||||
}
|
||||
public static void confirmParseException(RuntimeException e) {
|
||||
checkType(e);
|
||||
Assert.assertNotNull(e.getMessage());
|
||||
}
|
||||
private static void checkType(RuntimeException e) throws AssertionFailedError {
|
||||
if (!(e instanceof FormulaParseException)) {
|
||||
String failMsg = "Expected FormulaParseException, but got ("
|
||||
+ e.getClass().getName() + "):";
|
||||
System.err.println(failMsg);
|
||||
e.printStackTrace();
|
||||
throw new AssertionFailedError(failMsg);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user