/** * 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(Workbook book, Ptg[] ptgs) { if (ptgs == null || ptgs.length == 0) return "#NAME"; java.util.Stack stack = new java.util.Stack(); AttrPtg ifptg = null; // Excel allows to have AttrPtg at position 0 (such as Blanks) which // do not have any operands. Skip them. stack.push(ptgs[0].toFormulaString(book)); for (int i = 1; i < ptgs.length; i++) { if (!(ptgs[i] instanceof OperationPtg)) { stack.push(ptgs[i].toFormulaString(book)); continue; } if (ptgs[i] instanceof AttrPtg && ((AttrPtg) ptgs[i]).isOptimizedIf()) { ifptg = (AttrPtg) ptgs[i]; continue; } final OperationPtg o = (OperationPtg) ptgs[i]; final String[] operands = new String[o.getNumberOfOperands()]; for (int j = operands.length; j > 0; j--) { // TODO: catch stack underflow and throw parse exception. operands[j - 1] = (String) stack.pop(); } stack.push(o.toFormulaString(operands)); if (!(o instanceof AbstractFunctionPtg)) continue; final AbstractFunctionPtg f = (AbstractFunctionPtg) o; final String fname = f.getName(); if (fname == null) continue; if ((ifptg != null) && (fname.equals("specialflag"))) { // this special case will be way different. stack.push(ifptg.toFormulaString(new String[] {(String) stack.pop()})); continue; } if (fname.equals("externalflag")) { final String top = (String) stack.pop(); final int paren = top.indexOf('('); final int comma = top.indexOf(','); if (comma == -1) { final int rparen = top.indexOf(')'); stack.push(top.substring(paren + 1, rparen) + "()"); } else { stack.push(top.substring(paren + 1, comma) + '(' + top.substring(comma + 1)); } } } // TODO: catch stack underflow and throw parse exception. return (String) stack.pop(); }
/** * Generates the variable function ptg for the formula. * * <p>For IF Formulas, additional PTGs are added to the tokens * * @param name * @param numArgs * @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is * handled in this function */ private AbstractFunctionPtg getFunction(String name, byte numArgs) { AbstractFunctionPtg retval = null; if (name.equals("IF")) { retval = new FuncVarPtg(AbstractFunctionPtg.ATTR_NAME, numArgs); // simulated pop, no bounds checking because this list better be populated by function() List argumentPointers = (List) this.functionTokens.get(0); AttrPtg ifPtg = new AttrPtg(); ifPtg.setData((short) 7); // mirroring excel output ifPtg.setOptimizedIf(true); if (argumentPointers.size() != 2 && argumentPointers.size() != 3) { throw new IllegalArgumentException( "[" + argumentPointers.size() + "] Arguments Found - An IF formula requires 2 or 3 arguments. IF(CONDITION, TRUE_VALUE, FALSE_VALUE [OPTIONAL]"); } // Biffview of an IF formula record indicates the attr ptg goes after the condition ptgs and // are // tracked in the argument pointers // The beginning first argument pointer is the last ptg of the condition int ifIndex = tokens.indexOf(argumentPointers.get(0)) + 1; tokens.add(ifIndex, ifPtg); // we now need a goto ptgAttr to skip to the end of the formula after a true condition // the true condition is should be inserted after the last ptg in the first argument int gotoIndex = tokens.indexOf(argumentPointers.get(1)) + 1; AttrPtg goto1Ptg = new AttrPtg(); goto1Ptg.setGoto(true); tokens.add(gotoIndex, goto1Ptg); if (numArgs > 2) { // only add false jump if there is a false condition // second goto to skip past the function ptg AttrPtg goto2Ptg = new AttrPtg(); goto2Ptg.setGoto(true); goto2Ptg.setData((short) (retval.getSize() - 1)); // Page 472 of the Microsoft Excel Developer's kit states that: // The b(or w) field specifies the number byes (or words to skip, minus 1 tokens.add(goto2Ptg); // this goes after all the arguments are defined } // data portion of the if ptg points to the false subexpression (Page 472 of MS Excel // Developer's kit) // count the number of bytes after the ifPtg to the False Subexpression // doesn't specify -1 in the documentation ifPtg.setData((short) (getPtgSize(ifIndex + 1, gotoIndex))); // count all the additional (goto) ptgs but dont count itself int ptgCount = this.getPtgSize(gotoIndex) - goto1Ptg.getSize() + retval.getSize(); if (ptgCount > (int) Short.MAX_VALUE) { throw new RuntimeException( "Ptg Size exceeds short when being specified for a goto ptg in an if"); } goto1Ptg.setData((short) (ptgCount - 1)); } else { retval = new FuncVarPtg(name, numArgs); } return retval; }
public Object clone() { AttrPtg ptg = new AttrPtg(); ptg.field_1_options = field_1_options; ptg.field_2_data = field_2_data; return ptg; }