From: Josh Micich Date: Thu, 18 Sep 2008 23:14:48 +0000 (+0000) Subject: Merged revisions 696813 via svnmerge from X-Git-Tag: REL_3_5_BETA3~4 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e78a288ddfc77af316d6be4b7eaa535ba08badd4;p=poi.git Merged revisions 696813 via svnmerge from https://svn.apache.org/repos/asf/poi/trunk ........ r696813 | josh | 2008-09-18 14:22:23 -0700 (Thu, 18 Sep 2008) | 1 line Partitioning common formula logic. Introduced FormulaRenderingWorkbook interface to make merge with ooxml branch easier ........ git-svn-id: https://svn.apache.org/repos/asf/poi/branches/ooxml@696847 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/hssf/dev/FormulaViewer.java b/src/java/org/apache/poi/hssf/dev/FormulaViewer.java index 36e527ae5b..167eaba532 100644 --- a/src/java/org/apache/poi/hssf/dev/FormulaViewer.java +++ b/src/java/org/apache/poi/hssf/dev/FormulaViewer.java @@ -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)"); diff --git a/src/java/org/apache/poi/hssf/model/FormulaParser.java b/src/java/org/apache/poi/hssf/model/FormulaParser.java index a3fc9c1c49..e849dbf6eb 100644 --- a/src/java/org/apache/poi/hssf/model/FormulaParser.java +++ b/src/java/org/apache/poi/hssf/model/FormulaParser.java @@ -19,22 +19,54 @@ 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.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. @@ -974,7 +1006,7 @@ end; * @param lptgs list of Ptg, can be null or empty * @return a human readable String */ - public static String toFormulaString(Workbook book, List lptgs) { + 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()]; @@ -982,16 +1014,6 @@ end; retval = toFormulaString(book, ptgs); return retval; } - /** - * Convenience method which takes in a list then passes it to the - * other toFormulaString signature. Works on the current - * workbook for 3D and named references - * @param lptgs list of Ptg, can be null or empty - * @return a human readable String - */ - public String toFormulaString(List lptgs) { - return toFormulaString(book, lptgs); - } /** * Static method to convert an array of Ptgs in RPN order @@ -1000,95 +1022,7 @@ end; * @param ptgs array of Ptg, can be null or empty * @return a human readable String */ - public static String toFormulaString(Workbook 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); + public static String toFormulaString(HSSFWorkbook book, Ptg[] ptgs) { + return FormulaRenderer.toFormulaString(HSSFEvaluationWorkbook.create(book), ptgs); } } diff --git a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java index 800f45dbf5..037ebb83ae 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AbstractFunctionPtg.java @@ -17,7 +17,6 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.ss.usermodel.Workbook; 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(Workbook book) { + public String toFormulaString() { return getName(); } diff --git a/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java index 68ae5b48c1..f16a200779 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/Area2DPtgBase.java @@ -18,7 +18,6 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.ss.usermodel.Workbook; 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(Workbook book) { + public final String toFormulaString() { return formatReferenceAsString(); } public final String toString() { diff --git a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java index 0c97d03d29..9a538cf70d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Area3DPtg.java @@ -17,10 +17,9 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.ss.util.AreaReference; -import org.apache.poi.ss.util.CellReference; +import org.apache.poi.ss.formula.WorkbookDependentFormula; +import org.apache.poi.ss.formula.FormulaRenderingWorkbook; import org.apache.poi.util.LittleEndian; /** @@ -32,7 +31,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 @@ -89,7 +88,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(Workbook 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"); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java b/src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java index 1387e76856..bfe247897d 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaErrPtg.java @@ -18,8 +18,7 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.ss.usermodel.ErrorConstants; -import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.hssf.usermodel.HSSFErrorConstants; import org.apache.poi.util.LittleEndian; /** @@ -49,9 +48,8 @@ public final class AreaErrPtg extends OperandPtg { LittleEndian.putInt(array, offset + 5, unused2); } - - public String toFormulaString(Workbook book) { - return ErrorConstants.getText(ErrorConstants.ERROR_REF); + public String toFormulaString() { + return HSSFErrorConstants.getText(HSSFErrorConstants.ERROR_REF); } public byte getDefaultOperandClass() { diff --git a/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java index cf3d82cacf..7bfefc5526 100644 --- a/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/AreaPtgBase.java @@ -17,14 +17,12 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.util.LittleEndian; +import org.apache.poi.hssf.record.RecordInputStream; +import org.apache.poi.ss.util.AreaReference; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.util.BitField; import org.apache.poi.util.BitFieldFactory; - -import org.apache.poi.ss.util.CellReference; -import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.util.AreaReference; -import org.apache.poi.hssf.record.RecordInputStream; +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(Workbook book) { + public String toFormulaString() { return formatReferenceAsString(); } diff --git a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java index 48acaac205..5e9a3682e9 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ArrayPtg.java @@ -21,7 +21,6 @@ import org.apache.poi.hssf.record.RecordInputStream; import org.apache.poi.hssf.record.UnicodeString; import org.apache.poi.hssf.record.constant.ConstantValueParser; import org.apache.poi.hssf.record.constant.ErrorConstant; -import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.util.LittleEndian; /** @@ -184,7 +183,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;yPtg represents a syntactic token in a formula. 'PTG' is an acronym for @@ -305,7 +305,7 @@ public abstract class Ptg implements Cloneable { /** * return a string representation of this token alone */ - public abstract String toFormulaString(Workbook book); + public abstract String toFormulaString(); /** * dump a debug representation (hexdump) to a string */ diff --git a/src/java/org/apache/poi/hssf/record/formula/RangePtg.java b/src/java/org/apache/poi/hssf/record/formula/RangePtg.java index d6076f2baf..658e2f47b4 100644 --- a/src/java/org/apache/poi/hssf/record/formula/RangePtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/RangePtg.java @@ -17,7 +17,6 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.ss.usermodel.Workbook; /** * @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(Workbook book) + public String toFormulaString() { return ":"; } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ref2DPtgBase.java b/src/java/org/apache/poi/hssf/record/formula/Ref2DPtgBase.java index 2fff41d6b7..545ff0bfc4 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ref2DPtgBase.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ref2DPtgBase.java @@ -18,7 +18,6 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.ss.usermodel.Workbook; 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(Workbook book) { + public final String toFormulaString() { return formatReferenceAsString(); } diff --git a/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java b/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java index a6ca3db2eb..790d3cd1a7 100644 --- a/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/Ref3DPtg.java @@ -18,8 +18,9 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.CellReference; +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 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 +87,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(Workbook 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"); + } } diff --git a/src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java b/src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java index a3030e1bd7..699f6344a3 100755 --- a/src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/RefErrorPtg.java @@ -18,8 +18,8 @@ package org.apache.poi.hssf.record.formula; import org.apache.poi.hssf.record.RecordInputStream; -import org.apache.poi.ss.usermodel.ErrorConstants; -import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.hssf.usermodel.HSSFErrorConstants; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.util.LittleEndian; /** @@ -53,8 +53,8 @@ public final class RefErrorPtg extends OperandPtg { return SIZE; } - public String toFormulaString(Workbook book) { - return ErrorConstants.getText(ErrorConstants.ERROR_REF); + public String toFormulaString() { + return HSSFErrorConstants.getText(HSSFErrorConstants.ERROR_REF); } public byte getDefaultOperandClass() { diff --git a/src/java/org/apache/poi/hssf/record/formula/ScalarConstantPtg.java b/src/java/org/apache/poi/hssf/record/formula/ScalarConstantPtg.java index bb1613c9ac..869e38d2e0 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ScalarConstantPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ScalarConstantPtg.java @@ -17,7 +17,6 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.ss.usermodel.Workbook; /** * @author Josh Micich @@ -31,12 +30,6 @@ abstract class ScalarConstantPtg extends Ptg { return Ptg.CLASS_VALUE; } - public final String toFormulaString(Workbook book) { - return toFormulaString(); - } - - protected abstract String toFormulaString(); - public final String toString() { StringBuffer sb = new StringBuffer(64); sb.append(getClass().getName()).append(" ["); diff --git a/src/java/org/apache/poi/hssf/record/formula/StringPtg.java b/src/java/org/apache/poi/hssf/record/formula/StringPtg.java index 7007b48fdb..2f34e2e360 100644 --- a/src/java/org/apache/poi/hssf/record/formula/StringPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/StringPtg.java @@ -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); diff --git a/src/java/org/apache/poi/hssf/record/formula/TblPtg.java b/src/java/org/apache/poi/hssf/record/formula/TblPtg.java index 9cc7334605..0b17e268e2 100644 --- a/src/java/org/apache/poi/hssf/record/formula/TblPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/TblPtg.java @@ -17,10 +17,8 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.ss.usermodel.Workbook; 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(Workbook book) + public String toFormulaString() { // table(....)[][] throw new RecordFormatException("Table and Arrays are not yet supported"); diff --git a/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java b/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java index 266f7decab..fdd82ce549 100644 --- a/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/UnionPtg.java @@ -17,7 +17,6 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.ss.usermodel.Workbook; /** * @author Glen Stampoultzis (glens at apache.org) @@ -45,7 +44,7 @@ public final class UnionPtg extends OperationPtg { array[ offset + 0 ] = sid; } - public String toFormulaString(Workbook book) + public String toFormulaString() { return ","; } diff --git a/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java b/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java index 07749022ed..d073878707 100644 --- a/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/UnknownPtg.java @@ -16,7 +16,6 @@ ==================================================================== */ package org.apache.poi.hssf.record.formula; -import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.hssf.record.RecordInputStream; /** @@ -49,7 +48,7 @@ public class UnknownPtg extends Ptg { return size; } - public String toFormulaString(Workbook book) + public String toFormulaString() { return "UNKNOWN"; } diff --git a/src/java/org/apache/poi/hssf/record/formula/ValueOperatorPtg.java b/src/java/org/apache/poi/hssf/record/formula/ValueOperatorPtg.java index 7f5d86cac1..7bb632b595 100644 --- a/src/java/org/apache/poi/hssf/record/formula/ValueOperatorPtg.java +++ b/src/java/org/apache/poi/hssf/record/formula/ValueOperatorPtg.java @@ -17,7 +17,7 @@ package org.apache.poi.hssf.record.formula; -import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; /** * Common superclass of all value operators. @@ -47,7 +47,7 @@ public abstract class ValueOperatorPtg extends OperationPtg { public final int getSize() { return 1; } - public final String toFormulaString(Workbook 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"); } diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java new file mode 100644 index 0000000000..f9399ee8c5 --- /dev/null +++ b/src/java/org/apache/poi/hssf/usermodel/HSSFEvaluationWorkbook.java @@ -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(); + } +} diff --git a/src/java/org/apache/poi/ss/formula/FormulaRenderer.java b/src/java/org/apache/poi/ss/formula/FormulaRenderer.java new file mode 100644 index 0000000000..21386cd0dd --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/FormulaRenderer.java @@ -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; + } +} diff --git a/src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java b/src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java new file mode 100644 index 0000000000..3a92aa8cbc --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/FormulaRenderingWorkbook.java @@ -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); +} diff --git a/src/java/org/apache/poi/ss/formula/WorkbookDependentFormula.java b/src/java/org/apache/poi/ss/formula/WorkbookDependentFormula.java new file mode 100644 index 0000000000..420527dd81 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/WorkbookDependentFormula.java @@ -0,0 +1,5 @@ +package org.apache.poi.ss.formula; + +public interface WorkbookDependentFormula { + String toFormulaString(FormulaRenderingWorkbook book); +} diff --git a/src/java/org/apache/poi/ss/formula/package.html b/src/java/org/apache/poi/ss/formula/package.html new file mode 100644 index 0000000000..1af2f84ac7 --- /dev/null +++ b/src/java/org/apache/poi/ss/formula/package.html @@ -0,0 +1,29 @@ + + + + + + + +This package contains common internal POI code for manipulating formulas. +Client applications should not refer to these classes directly. + + + diff --git a/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java b/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java index e88d274eac..a17414af74 100644 --- a/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java +++ b/src/testcases/org/apache/poi/hssf/eventusermodel/TestEventWorkbookBuilder.java @@ -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 diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java index e1ffc2538d..bb9f3de605 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParser.java @@ -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(); diff --git a/src/testcases/org/apache/poi/hssf/model/TestFormulaParserIf.java b/src/testcases/org/apache/poi/hssf/model/TestFormulaParserIf.java index 6f7c4747cf..415cca33f1 100644 --- a/src/testcases/org/apache/poi/hssf/model/TestFormulaParserIf.java +++ b/src/testcases/org/apache/poi/hssf/model/TestFormulaParserIf.java @@ -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 FormulaParser 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()); } /** diff --git a/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java b/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java index 3742342360..7a13cfe5fd 100644 --- a/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java +++ b/src/testcases/org/apache/poi/hssf/record/TestSharedFormulaRecord.java @@ -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"); } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestArea3DPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestArea3DPtg.java index e83a59b42d..b4aa683833 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestArea3DPtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestArea3DPtg.java @@ -14,10 +14,10 @@ See the License for the specific language governing permissions and 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; /** @@ -27,21 +27,22 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook; */ public final class TestArea3DPtg extends AbstractPtgTestCase { - /** - * confirms that sheet names get properly escaped - */ + /** + * confirms that sheet names get properly escaped + */ public void testToFormulaString() { - + 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"); - assertEquals("Sheet1!A1:B1", target.toFormulaString(book)); - - book.setSheetName(0, "C64"); - assertEquals("'C64'!A1:B1", target.toFormulaString(book)); + + wb.setSheetName(0, "Sheet1"); + assertEquals("Sheet1!A1:B1", target.toFormulaString(book)); + + wb.setSheetName(0, "C64"); + assertEquals("'C64'!A1:B1", target.toFormulaString(book)); } } diff --git a/src/testcases/org/apache/poi/hssf/record/formula/TestAreaPtg.java b/src/testcases/org/apache/poi/hssf/record/formula/TestAreaPtg.java index 3a7f2f29a4..f5b80f6dac 100644 --- a/src/testcases/org/apache/poi/hssf/record/formula/TestAreaPtg.java +++ b/src/testcases/org/apache/poi/hssf/record/formula/TestAreaPtg.java @@ -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 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) diff --git a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java index c9356b8527..f19aa0ca4d 100644 --- a/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java +++ b/src/testcases/org/apache/poi/hssf/util/TestAreaReference.java @@ -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; @@ -192,6 +193,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"; @@ -213,10 +215,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));