Partitioning common formula logic. Introduced FormulaRenderingWorkbook interface to make merge with ooxml branch easier

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@696813 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Josh Micich 2008-09-18 21:22:23 +00:00
parent 5f8442cba4
commit c5a00d78c9
53 changed files with 385 additions and 230 deletions

View File

@ -97,7 +97,7 @@ public class FormulaViewer
StringBuffer buf = new StringBuffer();
if (token instanceof ExpPtg) return;
buf.append(((OperationPtg) token).toFormulaString((HSSFWorkbook)null));
buf.append(((OperationPtg) token).toFormulaString());
buf.append(sep);
switch (token.getPtgClass()) {
case Ptg.CLASS_REF :
@ -161,7 +161,7 @@ public class FormulaViewer
Ptg[] tokens = record.getParsedExpression();
for (int i = 0; i < tokens.length; i++) {
Ptg token = tokens[i];
buf.append( token.toFormulaString((HSSFWorkbook)null));
buf.append( token.toFormulaString());
switch (token.getPtgClass()) {
case Ptg.CLASS_REF :
buf.append("(R)");

View File

@ -19,21 +19,53 @@ package org.apache.poi.hssf.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
//import PTGs .. since we need everything, import *
import org.apache.poi.hssf.record.UnicodeString;
import org.apache.poi.hssf.record.constant.ErrorConstant;
import org.apache.poi.hssf.record.formula.*;
import org.apache.poi.hssf.record.formula.AbstractFunctionPtg;
import org.apache.poi.hssf.record.formula.AddPtg;
import org.apache.poi.hssf.record.formula.Area3DPtg;
import org.apache.poi.hssf.record.formula.AreaPtg;
import org.apache.poi.hssf.record.formula.ArrayPtg;
import org.apache.poi.hssf.record.formula.BoolPtg;
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.ErrPtg;
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.IntPtg;
import org.apache.poi.hssf.record.formula.LessEqualPtg;
import org.apache.poi.hssf.record.formula.LessThanPtg;
import org.apache.poi.hssf.record.formula.MissingArgPtg;
import org.apache.poi.hssf.record.formula.MultiplyPtg;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.hssf.record.formula.NotEqualPtg;
import org.apache.poi.hssf.record.formula.NumberPtg;
import org.apache.poi.hssf.record.formula.ParenthesisPtg;
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.Ref3DPtg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.StringPtg;
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.function.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
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.HSSFName;
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.
@ -1001,94 +1033,6 @@ end;
* @return a human readable String
*/
public static String toFormulaString(HSSFWorkbook book, Ptg[] ptgs) {
if (ptgs == null || ptgs.length == 0) {
// TODO - what is the justification for returning "#NAME" (which is not "#NAME?", btw)
return "#NAME";
}
Stack stack = new Stack();
for (int i=0 ; i < ptgs.length; i++) {
Ptg ptg = ptgs[i];
// TODO - what about MemNoMemPtg?
if(ptg instanceof MemAreaPtg || ptg instanceof MemFuncPtg || ptg instanceof MemErrPtg) {
// marks the start of a list of area expressions which will be naturally combined
// by their trailing operators (e.g. UnionPtg)
// TODO - put comment and throw exception in toFormulaString() of these classes
continue;
}
if (ptg instanceof ParenthesisPtg) {
String contents = (String)stack.pop();
stack.push ("(" + contents + ")");
continue;
}
if (ptg instanceof AttrPtg) {
AttrPtg attrPtg = ((AttrPtg) ptg);
if (attrPtg.isOptimizedIf() || attrPtg.isOptimizedChoose() || attrPtg.isGoto()) {
continue;
}
if (attrPtg.isSpace()) {
// POI currently doesn't render spaces in formulas
continue;
// but if it ever did, care must be taken:
// tAttrSpace comes *before* the operand it applies to, which may be consistent
// with how the formula text appears but is against the RPN ordering assumed here
}
if (attrPtg.isSemiVolatile()) {
// similar to tAttrSpace - RPN is violated
continue;
}
if (attrPtg.isSum()) {
String[] operands = getOperands(stack, attrPtg.getNumberOfOperands());
stack.push(attrPtg.toFormulaString(operands));
continue;
}
throw new RuntimeException("Unexpected tAttr: " + attrPtg.toString());
}
if (! (ptg instanceof OperationPtg)) {
stack.push(ptg.toFormulaString(book));
continue;
}
OperationPtg o = (OperationPtg) ptg;
String[] operands = getOperands(stack, o.getNumberOfOperands());
stack.push(o.toFormulaString(operands));
}
if(stack.isEmpty()) {
// inspection of the code above reveals that every stack.pop() is followed by a
// stack.push(). So this is either an internal error or impossible.
throw new IllegalStateException("Stack underflow");
}
String result = (String) stack.pop();
if(!stack.isEmpty()) {
// Might be caused by some tokens like AttrPtg and Mem*Ptg, which really shouldn't
// put anything on the stack
throw new IllegalStateException("too much stuff left on the stack");
}
return result;
}
private static String[] getOperands(Stack stack, int nOperands) {
String[] operands = new String[nOperands];
for (int j = nOperands-1; j >= 0; j--) { // reverse iteration because args were pushed in-order
if(stack.isEmpty()) {
String msg = "Too few arguments supplied to operation. Expected (" + nOperands
+ ") operands but got (" + (nOperands - j - 1) + ")";
throw new IllegalStateException(msg);
}
operands[j] = (String) stack.pop();
}
return operands;
}
/**
* Static method to convert an array of Ptgs in RPN order
* to a human readable string format in infix mode. Works
* on the current workbook for named and 3D references.
* @param ptgs array of Ptg, can be null or empty
* @return a human readable String
*/
public String toFormulaString(Ptg[] ptgs) {
return toFormulaString(book, ptgs);
return FormulaRenderer.toFormulaString(HSSFEvaluationWorkbook.create(book), ptgs);
}
}

View File

@ -17,7 +17,6 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.formula.function.FunctionMetadata;
import org.apache.poi.hssf.record.formula.function.FunctionMetadataRegistry;
@ -71,7 +70,7 @@ public abstract class AbstractFunctionPtg extends OperationPtg {
return field_2_fnc_index == FUNCTION_INDEX_EXTERNAL;
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString() {
return getName();
}

View File

@ -18,7 +18,6 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.util.LittleEndian;
/**
@ -45,7 +44,7 @@ public abstract class Area2DPtgBase extends AreaPtgBase {
public final int getSize() {
return SIZE;
}
public final String toFormulaString(HSSFWorkbook book) {
public final String toFormulaString() {
return formatReferenceAsString();
}
public final String toString() {

View File

@ -19,6 +19,8 @@ package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.util.LittleEndian;
/**
@ -30,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
* @author Jason Height (jheight at chariot dot net dot au)
* @version 1.0-pre
*/
public final class Area3DPtg extends AreaPtgBase {
public final class Area3DPtg extends AreaPtgBase implements WorkbookDependentFormula {
public final static byte sid = 0x3b;
private final static int SIZE = 11; // 10 + 1 for Ptg
@ -87,7 +89,10 @@ public final class Area3DPtg extends AreaPtgBase {
* @return text representation of this area reference that can be used in text
* formulas. The sheet name will get properly delimited if required.
*/
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString(FormulaRenderingWorkbook book) {
return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, formatReferenceAsString());
}
public String toFormulaString() {
throw new RuntimeException("3D references need a workbook to determine formula text");
}
}

View File

@ -19,7 +19,6 @@ package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.util.LittleEndian;
/**
@ -49,7 +48,7 @@ public final class AreaErrPtg extends OperandPtg {
LittleEndian.putInt(array, offset + 5, unused2);
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString() {
return HSSFErrorConstants.getText(HSSFErrorConstants.ERROR_REF);
}

View File

@ -17,14 +17,12 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.util.AreaReference;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
/**
* Specifies a rectangular area of cells A1:A4 for instance.
@ -269,7 +267,7 @@ public abstract class AreaPtgBase extends OperandPtg implements AreaI {
return topLeft.formatAsString() + ":" + botRight.formatAsString();
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString() {
return formatReferenceAsString();
}

View File

@ -184,7 +184,7 @@ public final class ArrayPtg extends Ptg {
+ ConstantValueParser.getEncodedSize(token_3_arrayValues);
}
public String formatAsString() {
public String formatAsString() { // TODO - fold into toFormulaString
StringBuffer b = new StringBuffer();
b.append("{");
for (int y=0;y<getRowCount();y++) {
@ -202,7 +202,7 @@ public final class ArrayPtg extends Ptg {
b.append("}");
return b.toString();
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString() {
return formatAsString();
}

View File

@ -17,12 +17,10 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.BitField;
import org.apache.poi.util.BitFieldFactory;
import org.apache.poi.util.LittleEndian;
/**
* "Special Attributes"
@ -244,11 +242,11 @@ public final class AttrPtg extends ControlPtg {
if(space.isSet(field_1_options)) {
return operands[ 0 ];
} else if (optiIf.isSet(field_1_options)) {
return toFormulaString((HSSFWorkbook)null) + "(" + operands[ 0 ] +")";
return toFormulaString() + "(" + operands[ 0 ] +")";
} else if (optGoto.isSet(field_1_options)) {
return toFormulaString((HSSFWorkbook)null) + operands[0]; //goto isn't a real formula element should not show up
return toFormulaString() + operands[0]; //goto isn't a real formula element should not show up
} else {
return toFormulaString((HSSFWorkbook)null) + "(" + operands[ 0 ] + ")";
return toFormulaString() + "(" + operands[ 0 ] + ")";
}
}
@ -263,7 +261,7 @@ public final class AttrPtg extends ControlPtg {
return -1;
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString() {
if(semiVolatile.isSet(field_1_options)) {
return "ATTR(semiVolatile)";
}

View File

@ -52,7 +52,7 @@ public final class BoolPtg extends ScalarConstantPtg {
return SIZE;
}
protected String toFormulaString() {
public String toFormulaString() {
return _value ? "TRUE" : "FALSE";
}
}

View File

@ -19,7 +19,8 @@ package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.util.LittleEndian;
/**
@ -29,7 +30,7 @@ import org.apache.poi.util.LittleEndian;
* @author Patrick Luby
* @version 1.0-pre
*/
public final class DeletedArea3DPtg extends OperandPtg {
public final class DeletedArea3DPtg extends OperandPtg implements WorkbookDependentFormula {
public final static byte sid = 0x3d;
private final int field_1_index_extern_sheet;
private final int unused1;
@ -46,10 +47,13 @@ public final class DeletedArea3DPtg extends OperandPtg {
unused1 = in.readInt();
unused2 = in.readInt();
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString(FormulaRenderingWorkbook book) {
return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet,
HSSFErrorConstants.getText(HSSFErrorConstants.ERROR_REF));
}
public String toFormulaString() {
throw new RuntimeException("3D references need a workbook to determine formula text");
}
public byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
}

View File

@ -20,7 +20,8 @@ package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.util.LittleEndian;
/**
@ -30,7 +31,7 @@ import org.apache.poi.util.LittleEndian;
* @author Patrick Luby
* @version 1.0-pre
*/
public final class DeletedRef3DPtg extends OperandPtg {
public final class DeletedRef3DPtg extends OperandPtg implements WorkbookDependentFormula {
public final static byte sid = 0x3c;
private final int field_1_index_extern_sheet;
private final int unused1;
@ -46,10 +47,13 @@ public final class DeletedRef3DPtg extends OperandPtg {
unused1 = 0;
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString(FormulaRenderingWorkbook book) {
return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet,
HSSFErrorConstants.getText(HSSFErrorConstants.ERROR_REF));
}
public String toFormulaString() {
throw new RuntimeException("3D references need a workbook to determine formula text");
}
public byte getDefaultOperandClass() {
return Ptg.CLASS_REF;
}

View File

@ -67,7 +67,7 @@ public final class ErrPtg extends ScalarConstantPtg {
array[offset + 1] = (byte)field_1_error_code;
}
protected String toFormulaString() {
public String toFormulaString() {
return HSSFErrorConstants.getText(field_1_error_code);
}

View File

@ -17,10 +17,8 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
/**
@ -61,7 +59,7 @@ public final class ExpPtg extends ControlPtg {
return field_2_first_col;
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
throw new RecordFormatException("Coding Error: Expected ExpPtg to be converted from Shared to Non-Shared Formula by ValueRecordsAggregate, but it wasn't");
}

View File

@ -17,8 +17,7 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
/**
* @author Josh Micich
@ -29,8 +28,8 @@ final class ExternSheetNameResolver {
// no instances of this class
}
public static String prependSheetName(HSSFWorkbook book, int field_1_index_extern_sheet, String cellRefText) {
String sheetName = book.findSheetNameFromExternSheet(field_1_index_extern_sheet);
public static String prependSheetName(FormulaRenderingWorkbook book, int field_1_index_extern_sheet, String cellRefText) {
String sheetName = book.getSheetNameByExternSheet(field_1_index_extern_sheet);
StringBuffer sb = new StringBuffer(sheetName.length() + cellRefText.length() + 4);
if (sheetName.length() < 1) {
// What excel does if sheet has been deleted

View File

@ -68,7 +68,7 @@ public final class IntPtg extends ScalarConstantPtg {
return SIZE;
}
protected String toFormulaString() {
public String toFormulaString() {
return String.valueOf(getValue());
}
}

View File

@ -17,7 +17,6 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* @author Daniel Noll (daniel at nuix dot com dot au)
@ -46,7 +45,7 @@ public final class IntersectionPtg extends OperationPtg {
}
/** Implementation of method from Ptg */
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
return " ";
}

View File

@ -17,9 +17,8 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
/**
* @author Daniel Noll (daniel at nuix dot com dot au)
@ -74,7 +73,7 @@ public class MemAreaPtg extends OperandPtg {
return SIZE;
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
return ""; // TODO: Not sure how to format this. -- DN
}

View File

@ -45,7 +45,7 @@ public final class MemErrPtg extends MemAreaPtg {
array[offset] = (byte) (sid + getPtgClass());
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
return "ERR#";
}

View File

@ -17,9 +17,8 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
/**
* @author Glen Stampoultzis (glens at apache.org)
@ -51,7 +50,7 @@ public final class MemFuncPtg extends OperandPtg {
LittleEndian.putUShort( array, offset + 1, field_1_len_ref_subexpression );
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
return "";
}

View File

@ -42,7 +42,7 @@ public final class MissingArgPtg extends ScalarConstantPtg {
return SIZE;
}
protected String toFormulaString() {
public String toFormulaString() {
return " ";
}
}

View File

@ -18,8 +18,8 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.util.LittleEndian;
/**
@ -27,7 +27,7 @@ import org.apache.poi.util.LittleEndian;
* @author andy
* @author Jason Height (jheight at chariot dot net dot au)
*/
public final class NamePtg extends OperandPtg {
public final class NamePtg extends OperandPtg implements WorkbookDependentFormula {
public final static short sid = 0x23;
private final static int SIZE = 5;
/** one-based index to defined name record */
@ -65,9 +65,12 @@ public final class NamePtg extends OperandPtg {
return SIZE;
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString(FormulaRenderingWorkbook book)
{
return book.getNameName(field_1_label_index - 1);
return book.getNameText(this);
}
public String toFormulaString() {
throw new RuntimeException("3D references need a workbook to determine formula text");
}
public byte getDefaultOperandClass() {

View File

@ -17,15 +17,16 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.util.LittleEndian;
/**
*
* @author aviks
*/
public final class NameXPtg extends OperandPtg {
public final class NameXPtg extends OperandPtg implements WorkbookDependentFormula {
public final static short sid = 0x39;
private final static int SIZE = 7;
@ -65,9 +66,12 @@ public final class NameXPtg extends OperandPtg {
return SIZE;
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString(FormulaRenderingWorkbook book) {
// -1 to convert definedNameIndex from 1-based to zero-based
return book.resolveNameXText(_sheetRefIndex, _nameNumber - 1);
return book.resolveNameXText(this);
}
public String toFormulaString() {
throw new RuntimeException("3D references need a workbook to determine formula text");
}
public byte getDefaultOperandClass() {

View File

@ -63,7 +63,7 @@ public final class NumberPtg extends ScalarConstantPtg {
return SIZE;
}
protected String toFormulaString() {
public String toFormulaString() {
// TODO - java's rendering of double values is not quite same as excel's
return String.valueOf(field_1_value);
}

View File

@ -17,6 +17,7 @@
package org.apache.poi.hssf.record.formula;
/**
* @author Josh Micich
*/

View File

@ -17,6 +17,8 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
/**
* defines a Ptg that is an operation instead of an operand
* @author andy
@ -47,4 +49,9 @@ public abstract class OperationPtg extends Ptg {
throw new RuntimeException("remove this method");
}
public String toFormulaString(FormulaRenderingWorkbook book) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -18,10 +18,6 @@
package org.apache.poi.hssf.record.formula;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordInputStream;
/**
* While formula tokens are stored in RPN order and thus do not need parenthesis for
@ -52,7 +48,7 @@ public final class ParenthesisPtg extends ControlPtg {
return SIZE;
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
return "()";
}

View File

@ -305,7 +305,7 @@ public abstract class Ptg implements Cloneable {
/**
* return a string representation of this token alone
*/
public abstract String toFormulaString(HSSFWorkbook book);
public abstract String toFormulaString();
/**
* dump a debug representation (hexdump) to a string
*/

View File

@ -17,7 +17,6 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* @author Daniel Noll (daniel at nuix dot com dot au)
@ -46,7 +45,7 @@ public final class RangePtg extends OperationPtg {
array[ offset + 0 ] = sid;
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
return ":";
}

View File

@ -18,7 +18,6 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.util.LittleEndian;
/**
@ -49,7 +48,7 @@ abstract class Ref2DPtgBase extends RefPtgBase {
LittleEndian.putByte(array, offset+0, getSid() + getPtgClass());
writeCoordinates(array, offset+1);
}
public final String toFormulaString(HSSFWorkbook book) {
public final String toFormulaString() {
return formatReferenceAsString();
}

View File

@ -20,6 +20,8 @@ package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.ss.formula.WorkbookDependentFormula;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
import org.apache.poi.util.LittleEndian;
/**
@ -30,7 +32,7 @@ import org.apache.poi.util.LittleEndian;
* @author Jason Height (jheight at chariot dot net dot au)
* @version 1.0-pre
*/
public final class Ref3DPtg extends RefPtgBase {
public final class Ref3DPtg extends RefPtgBase implements WorkbookDependentFormula {
public final static byte sid = 0x3a;
private final static int SIZE = 7; // 6 + 1 for Ptg
@ -86,7 +88,10 @@ public final class Ref3DPtg extends RefPtgBase {
* @return text representation of this cell reference that can be used in text
* formulas. The sheet name will get properly delimited if required.
*/
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString(FormulaRenderingWorkbook book) {
return ExternSheetNameResolver.prependSheetName(book, field_1_index_extern_sheet, formatReferenceAsString());
}
public String toFormulaString() {
throw new RuntimeException("3D references need a workbook to determine formula text");
}
}

View File

@ -53,7 +53,7 @@ public final class RefErrorPtg extends OperandPtg {
return SIZE;
}
public String toFormulaString(HSSFWorkbook book) {
public String toFormulaString() {
return HSSFErrorConstants.getText(HSSFErrorConstants.ERROR_REF);
}

View File

@ -17,7 +17,6 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* @author Josh Micich
@ -31,12 +30,6 @@ abstract class ScalarConstantPtg extends Ptg {
return Ptg.CLASS_VALUE;
}
public final String toFormulaString(HSSFWorkbook book) {
return toFormulaString();
}
protected abstract String toFormulaString();
public final String toString() {
StringBuffer sb = new StringBuffer(64);
sb.append(getClass().getName()).append(" [");

View File

@ -97,7 +97,7 @@ public final class StringPtg extends ScalarConstantPtg {
}
}
protected String toFormulaString() {
public String toFormulaString() {
String value = field_3_string;
int len = value.length();
StringBuffer sb = new StringBuffer(len + 4);

View File

@ -17,10 +17,8 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordFormatException;
import org.apache.poi.hssf.record.RecordInputStream;
import org.apache.poi.util.LittleEndian;
/**
@ -68,7 +66,7 @@ public final class TblPtg extends ControlPtg {
return field_2_first_col;
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
// table(....)[][]
throw new RecordFormatException("Table and Arrays are not yet supported");

View File

@ -17,7 +17,6 @@
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* @author Glen Stampoultzis (glens at apache.org)
@ -45,7 +44,7 @@ public final class UnionPtg extends OperationPtg {
array[ offset + 0 ] = sid;
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
return ",";
}

View File

@ -16,7 +16,6 @@
==================================================================== */
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.record.RecordInputStream;
/**
@ -49,7 +48,7 @@ public class UnknownPtg extends Ptg {
return size;
}
public String toFormulaString(HSSFWorkbook book)
public String toFormulaString()
{
return "UNKNOWN";
}

View File

@ -47,7 +47,7 @@ public abstract class ValueOperatorPtg extends OperationPtg {
public final int getSize() {
return 1;
}
public final String toFormulaString(HSSFWorkbook book) {
public final String toFormulaString() {
// TODO - prune this method out of the hierarchy
throw new RuntimeException("toFormulaString(String[] operands) should be used for subclasses of OperationPtgs");
}

View File

@ -0,0 +1,38 @@
package org.apache.poi.hssf.usermodel;
import org.apache.poi.hssf.model.Workbook;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
import org.apache.poi.ss.formula.FormulaRenderingWorkbook;
/**
* Internal POI use only
*
* @author Josh Micich
*/
public final class HSSFEvaluationWorkbook implements FormulaRenderingWorkbook {
private final Workbook _iBook;
public static HSSFEvaluationWorkbook create(HSSFWorkbook book) {
if (book == null) {
return null;
}
return new HSSFEvaluationWorkbook(book);
}
private HSSFEvaluationWorkbook(HSSFWorkbook book) {
_iBook = book.getWorkbook();
}
public String resolveNameXText(NameXPtg n) {
return _iBook.resolveNameXText(n.getSheetRefIndex(), n.getNameIndex());
}
public String getSheetNameByExternSheet(int externSheetIndex) {
return _iBook.findSheetNameFromExternSheet(externSheetIndex);
}
public String getNameText(NamePtg namePtg) {
return _iBook.getNameRecord(namePtg.getIndex()).getNameText();
}
}

View File

@ -0,0 +1,124 @@
package org.apache.poi.ss.formula;
import java.util.List;
import java.util.Stack;
import org.apache.poi.hssf.record.formula.AttrPtg;
import org.apache.poi.hssf.record.formula.MemAreaPtg;
import org.apache.poi.hssf.record.formula.MemErrPtg;
import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.OperationPtg;
import org.apache.poi.hssf.record.formula.ParenthesisPtg;
import org.apache.poi.hssf.record.formula.Ptg;
public class FormulaRenderer {
/**
* 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) {
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(FormulaRenderingWorkbook book, Ptg[] ptgs) {
if (ptgs == null || ptgs.length == 0) {
// TODO - what is the justification for returning "#NAME" (which is not "#NAME?", btw)
return "#NAME";
}
Stack stack = new Stack();
for (int i=0 ; i < ptgs.length; i++) {
Ptg ptg = ptgs[i];
// TODO - what about MemNoMemPtg?
if(ptg instanceof MemAreaPtg || ptg instanceof MemFuncPtg || ptg instanceof MemErrPtg) {
// marks the start of a list of area expressions which will be naturally combined
// by their trailing operators (e.g. UnionPtg)
// TODO - put comment and throw exception in toFormulaString() of these classes
continue;
}
if (ptg instanceof ParenthesisPtg) {
String contents = (String)stack.pop();
stack.push ("(" + contents + ")");
continue;
}
if (ptg instanceof AttrPtg) {
AttrPtg attrPtg = ((AttrPtg) ptg);
if (attrPtg.isOptimizedIf() || attrPtg.isOptimizedChoose() || attrPtg.isGoto()) {
continue;
}
if (attrPtg.isSpace()) {
// POI currently doesn't render spaces in formulas
continue;
// but if it ever did, care must be taken:
// tAttrSpace comes *before* the operand it applies to, which may be consistent
// with how the formula text appears but is against the RPN ordering assumed here
}
if (attrPtg.isSemiVolatile()) {
// similar to tAttrSpace - RPN is violated
continue;
}
if (attrPtg.isSum()) {
String[] operands = getOperands(stack, attrPtg.getNumberOfOperands());
stack.push(attrPtg.toFormulaString(operands));
continue;
}
throw new RuntimeException("Unexpected tAttr: " + attrPtg.toString());
}
if (ptg instanceof WorkbookDependentFormula) {
WorkbookDependentFormula optg = (WorkbookDependentFormula) ptg;
stack.push(optg.toFormulaString(book));
continue;
}
if (! (ptg instanceof OperationPtg)) {
stack.push(ptg.toFormulaString());
continue;
}
OperationPtg o = (OperationPtg) ptg;
String[] operands = getOperands(stack, o.getNumberOfOperands());
stack.push(o.toFormulaString(operands));
}
if(stack.isEmpty()) {
// inspection of the code above reveals that every stack.pop() is followed by a
// stack.push(). So this is either an internal error or impossible.
throw new IllegalStateException("Stack underflow");
}
String result = (String) stack.pop();
if(!stack.isEmpty()) {
// Might be caused by some tokens like AttrPtg and Mem*Ptg, which really shouldn't
// put anything on the stack
throw new IllegalStateException("too much stuff left on the stack");
}
return result;
}
private static String[] getOperands(Stack stack, int nOperands) {
String[] operands = new String[nOperands];
for (int j = nOperands-1; j >= 0; j--) { // reverse iteration because args were pushed in-order
if(stack.isEmpty()) {
String msg = "Too few arguments supplied to operation. Expected (" + nOperands
+ ") operands but got (" + (nOperands - j - 1) + ")";
throw new IllegalStateException(msg);
}
operands[j] = (String) stack.pop();
}
return operands;
}
}

View File

@ -0,0 +1,11 @@
package org.apache.poi.ss.formula;
import org.apache.poi.hssf.record.formula.NamePtg;
import org.apache.poi.hssf.record.formula.NameXPtg;
public interface FormulaRenderingWorkbook {
String getSheetNameByExternSheet(int externSheetIndex);
String resolveNameXText(NameXPtg nameXPtg);
String getNameText(NamePtg namePtg);
}

View File

@ -0,0 +1,5 @@
package org.apache.poi.ss.formula;
public interface WorkbookDependentFormula {
String toFormulaString(FormulaRenderingWorkbook book);
}

View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<!--
====================================================================
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.
====================================================================
-->
<html>
<head>
</head>
<body bgcolor="white">
This package contains common internal POI code for manipulating formulas.
Client applications should not refer to these classes directly.
</body>
</html>

View File

@ -32,6 +32,7 @@ import org.apache.poi.hssf.record.FormulaRecord;
import org.apache.poi.hssf.record.Record;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.Ref3DPtg;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
@ -110,7 +111,8 @@ public final class TestEventWorkbookBuilder extends TestCase {
assertTrue(ptgs[0] instanceof Ref3DPtg);
Ref3DPtg ptg = (Ref3DPtg)ptgs[0];
assertEquals("Sheet1!A1", ptg.toFormulaString(stubHSSF));
HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(stubHSSF);
assertEquals("Sheet1!A1", ptg.toFormulaString(book));
// Now check we get the right formula back for

View File

@ -51,6 +51,7 @@ import org.apache.poi.hssf.record.formula.UnaryMinusPtg;
import org.apache.poi.hssf.record.formula.UnaryPlusPtg;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFErrorConstants;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
@ -130,13 +131,14 @@ public final class TestFormulaParser extends TestCase {
public void testMacroFunction() {
// testNames.xls contains a VB function called 'myFunc'
HSSFWorkbook w = HSSFTestDataSamples.openSampleWorkbook("testNames.xls");
HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(w);
Ptg[] ptg = FormulaParser.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
NamePtg tname = (NamePtg) ptg[0];
assertEquals("myFunc", tname.toFormulaString(w));
assertEquals("myFunc", tname.toFormulaString(book));
AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[1];
assertTrue(tfunc.isExternalFunction());
@ -871,7 +873,7 @@ public final class TestFormulaParser extends TestCase {
assertEquals(2, ptgs.length);
Ptg ptg0 = ptgs[0];
assertEquals(ArrayPtg.class, ptg0.getClass());
assertEquals("{1.0,2.0,2.0,#REF!;FALSE,3.0,3.0,2.0}", ptg0.toFormulaString(null));
assertEquals("{1.0,2.0,2.0,#REF!;FALSE,3.0,3.0,2.0}", ptg0.toFormulaString());
ArrayPtg aptg = (ArrayPtg) ptg0;
Object[][] values = aptg.getTokenArrayValues();

View File

@ -33,7 +33,6 @@ import org.apache.poi.hssf.record.formula.NotEqualPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.RefPtg;
import org.apache.poi.hssf.record.formula.StringPtg;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Tests <tt>FormulaParser</tt> specifically with respect to IF() functions
@ -202,7 +201,7 @@ public final class TestFormulaParserIf extends TestCase {
assertEquals(true, flag.getValue());
assertEquals("Y", y.getValue());
assertEquals("N", n.getValue());
assertEquals("IF", funif.toFormulaString((HSSFWorkbook) null));
assertEquals("IF", funif.toFormulaString());
assertTrue("Goto ptg exists", goto1.isGoto());
}
/**

View File

@ -66,7 +66,7 @@ public final class TestSharedFormulaRecord extends TestCase {
Ptg[] convertedFormula = SharedFormulaRecord.convertSharedFormulas(sharedFormula, 100, 200);
RefPtg refPtg = (RefPtg) convertedFormula[1];
assertEquals("$C101", refPtg.toFormulaString(null));
assertEquals("$C101", refPtg.toFormulaString());
if (refPtg.getPtgClass() == Ptg.CLASS_REF) {
throw new AssertionFailedError("Identified bug 45123");
}

View File

@ -15,9 +15,9 @@
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
@ -35,13 +35,14 @@ public final class TestArea3DPtg extends AbstractPtgTestCase {
Area3DPtg target = new Area3DPtg("A1:B1", (short)0);
String sheetName = "my sheet";
HSSFWorkbook book = createWorkbookWithSheet(sheetName);
HSSFWorkbook wb = createWorkbookWithSheet(sheetName);
HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(wb);
assertEquals("'my sheet'!A1:B1", target.toFormulaString(book));
book.setSheetName(0, "Sheet1");
wb.setSheetName(0, "Sheet1");
assertEquals("Sheet1!A1:B1", target.toFormulaString(book));
book.setSheetName(0, "C64");
wb.setSheetName(0, "C64");
assertEquals("'C64'!A1:B1", target.toFormulaString(book));
}
}

View File

@ -21,6 +21,7 @@ package org.apache.poi.hssf.record.formula;
import junit.framework.TestCase;
import org.apache.poi.hssf.model.FormulaParser;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
* Tests for {@link AreaPtg}.
@ -83,14 +84,10 @@ public final class TestAreaPtg extends TestCase {
assertEquals("Relative references changed", expectedFormula2, newFormula2);
}
private String shiftAllColumnsBy1(String formula)
{
private static String shiftAllColumnsBy1(String formula) {
int letUsShiftColumn1By1Column=1;
FormulaParser parser = new FormulaParser(formula,null);
parser.parse();
final Ptg[] ptgs = parser.getRPNPtg();
HSSFWorkbook wb = null;
Ptg[] ptgs = FormulaParser.parse(formula, wb);
for(int i=0; i<ptgs.length; i++)
{
Ptg ptg = ptgs[i];
@ -101,10 +98,7 @@ public final class TestAreaPtg extends TestCase {
aptg.setLastColumn((short)(aptg.getLastColumn()+letUsShiftColumn1By1Column));
}
}
String newFormula = parser.toFormulaString(ptgs);
String newFormula = FormulaParser.toFormulaString(wb, ptgs);
return newFormula;
}
}

View File

@ -119,7 +119,7 @@ public final class TestArrayPtg extends TestCase {
String actualFormula;
try {
actualFormula = ptg.toFormulaString(null);
actualFormula = ptg.toFormulaString();
} catch (IllegalArgumentException e) {
if (e.getMessage().equals("Unexpected constant class (java.lang.Boolean)")) {
throw new AssertionFailedError("Identified bug 45380");

View File

@ -15,9 +15,9 @@
limitations under the License.
==================================================================== */
package org.apache.poi.hssf.record.formula;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
/**
@ -31,14 +31,14 @@ public final class TestRef3DPtg extends AbstractPtgTestCase {
Ref3DPtg target = new Ref3DPtg("A1", (short)0);
HSSFWorkbook book = createWorkbookWithSheet("my sheet");
HSSFWorkbook wb = createWorkbookWithSheet("my sheet");
HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(wb);
assertEquals("'my sheet'!A1", target.toFormulaString(book));
book.setSheetName(0, "ProfitAndLoss");
wb.setSheetName(0, "ProfitAndLoss");
assertEquals("ProfitAndLoss!A1", target.toFormulaString(book));
book.setSheetName(0, "profit+loss");
wb.setSheetName(0, "profit+loss");
assertEquals("'profit+loss'!A1", target.toFormulaString(book));
}
}

View File

@ -202,7 +202,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
assertEquals(2, ptg.getLastColumn());
assertEquals(0, ptg.getFirstRow());
assertEquals(65535, ptg.getLastRow());
assertEquals("C:C", ptg.toFormulaString(wb));
assertEquals("C:C", ptg.toFormulaString());
// Will show as C:C, but won't know how many
// rows it covers as we don't have the sheet
@ -316,6 +316,7 @@ public final class TestFormulaEvaluatorBugs extends TestCase {
evaluator.evaluate(cell);
int evalCount = evaluator.getEvaluationCount();
// With caching, the evaluationCount is 8 which is a big improvement
assertTrue(evalCount > 0); // make sure the counter is actually working
if (evalCount > 10) {
// Without caching, evaluating cell 'A9' takes 21845 evaluations which consumes
// much time (~3 sec on Core 2 Duo 2.2GHz)

View File

@ -30,6 +30,7 @@ import org.apache.poi.hssf.record.formula.MemFuncPtg;
import org.apache.poi.hssf.record.formula.Ptg;
import org.apache.poi.hssf.record.formula.UnionPtg;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook;
import org.apache.poi.hssf.usermodel.HSSFName;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
@ -190,6 +191,7 @@ public final class TestAreaReference extends TestCase {
InputStream is = HSSFTestDataSamples.openSampleFileStream("44167.xls");
HSSFWB wb = new HSSFWB(is);
Workbook workbook = wb.getWorkbook();
HSSFEvaluationWorkbook eb = HSSFEvaluationWorkbook.create(wb);
assertEquals(1, wb.getNumberOfNames());
String sheetName = "Tabelle1";
@ -211,10 +213,10 @@ public final class TestAreaReference extends TestCase {
Area3DPtg ptgB = (Area3DPtg)def[1];
Area3DPtg ptgC = (Area3DPtg)def[2];
UnionPtg ptgD = (UnionPtg)def[3];
assertEquals("", ptgA.toFormulaString(wb));
assertEquals(refA, ptgB.toFormulaString(wb));
assertEquals(refB, ptgC.toFormulaString(wb));
assertEquals(",", ptgD.toFormulaString(wb));
assertEquals("", ptgA.toFormulaString());
assertEquals(refA, ptgB.toFormulaString(eb));
assertEquals(refB, ptgC.toFormulaString(eb));
assertEquals(",", ptgD.toFormulaString());
assertEquals(ref, nr.getAreaReference(wb));