package jxl.biff.formula; import common.Logger; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Iterator; import java.util.Stack; import jxl.WorkbookSettings; import jxl.biff.WorkbookMethods; class StringFormulaParser implements Parser { private static Logger logger = Logger.getLogger(StringFormulaParser.class); private String formula; private String parsedFormula; private ParseItem root; private Stack arguments; private WorkbookSettings settings; private ExternalSheet externalSheet; private WorkbookMethods nameTable; public StringFormulaParser(String f, ExternalSheet es, WorkbookMethods nt, WorkbookSettings ws) { this.formula = f; this.settings = ws; this.externalSheet = es; this.nameTable = nt; } public void parse() throws FormulaException { ArrayList tokens = getTokens(); Iterator i = tokens.iterator(); this.root = parseCurrent(i); } private ParseItem parseCurrent(Iterator i) throws FormulaException { Stack stack = new Stack(); Stack operators = new Stack(); Stack args = null; boolean parenthesesClosed = false; ParseItem lastParseItem = null; while (i.hasNext() && !parenthesesClosed) { ParseItem pi = i.next(); if (pi instanceof Operand) { handleOperand((Operand)pi, stack); } else if (pi instanceof StringFunction) { handleFunction((StringFunction)pi, i, stack); } else if (pi instanceof Operator) { Operator op = (Operator)pi; if (op instanceof StringOperator) { StringOperator sop = (StringOperator)op; if (stack.isEmpty() || lastParseItem instanceof Operator) { op = sop.getUnaryOperator(); } else { op = sop.getBinaryOperator(); } } if (operators.empty()) { operators.push(op); } else { Operator operator = operators.peek(); if (op.getPrecedence() < operator.getPrecedence()) { operators.push(op); } else { operators.pop(); operator.getOperands(stack); stack.push(operator); operators.push(op); } } } else if (pi instanceof ArgumentSeparator) { while (!operators.isEmpty()) { Operator o = operators.pop(); o.getOperands(stack); stack.push(o); } if (args == null) args = new Stack(); args.push(stack.pop()); stack.clear(); } else if (pi instanceof OpenParentheses) { ParseItem pi2 = parseCurrent(i); Parenthesis p = new Parenthesis(); pi2.setParent(p); p.add(pi2); stack.push(p); } else if (pi instanceof CloseParentheses) { parenthesesClosed = true; } lastParseItem = pi; } while (!operators.isEmpty()) { Operator o = operators.pop(); o.getOperands(stack); stack.push(o); } ParseItem rt = !stack.empty() ? stack.pop() : null; if (args != null && rt != null) args.push(rt); this.arguments = args; if (!stack.empty() || !operators.empty()) logger.warn("Formula " + this.formula + " has a non-empty parse stack"); return rt; } private ArrayList getTokens() throws FormulaException { ArrayList tokens = new ArrayList(); StringReader sr = new StringReader(this.formula); Yylex lex = new Yylex(sr); lex.setExternalSheet(this.externalSheet); lex.setNameTable(this.nameTable); try { ParseItem pi = lex.yylex(); while (pi != null) { tokens.add(pi); pi = lex.yylex(); } } catch (IOException e) { logger.warn(e.toString()); } catch (Error e) { throw new FormulaException(FormulaException.lexicalError, this.formula + " at char " + lex.getPos()); } return tokens; } public String getFormula() { if (this.parsedFormula == null) { StringBuffer sb = new StringBuffer(); this.root.getString(sb); this.parsedFormula = sb.toString(); } return this.parsedFormula; } public byte[] getBytes() { byte[] bytes = this.root.getBytes(); if (this.root.isVolatile()) { byte[] newBytes = new byte[bytes.length + 4]; System.arraycopy(bytes, 0, newBytes, 4, bytes.length); newBytes[0] = Token.ATTRIBUTE.getCode(); newBytes[1] = 1; bytes = newBytes; } return bytes; } private void handleFunction(StringFunction sf, Iterator i, Stack stack) throws FormulaException { ParseItem pi2 = parseCurrent(i); if (sf.getFunction(this.settings) == Function.UNKNOWN) throw new FormulaException(FormulaException.unrecognizedFunction); if (sf.getFunction(this.settings) == Function.SUM && this.arguments == null) { Attribute a = new Attribute(sf, this.settings); a.add(pi2); stack.push(a); return; } if (sf.getFunction(this.settings) == Function.IF) { Attribute a = new Attribute(sf, this.settings); VariableArgFunction vaf = new VariableArgFunction(this.settings); int k = this.arguments.size(); for (int j = 0; j < k; j++) { ParseItem pi3 = this.arguments.get(j); vaf.add(pi3); } a.setIfConditions(vaf); stack.push(a); return; } if (sf.getFunction(this.settings).getNumArgs() == 255) { if (this.arguments == null) { int numArgs = (pi2 != null) ? 1 : 0; VariableArgFunction vaf = new VariableArgFunction(sf.getFunction(this.settings), numArgs, this.settings); if (pi2 != null) vaf.add(pi2); stack.push(vaf); } else { int k = this.arguments.size(); VariableArgFunction vaf = new VariableArgFunction(sf.getFunction(this.settings), k, this.settings); ParseItem[] args = new ParseItem[k]; int j; for (j = 0; j < k; j++) { ParseItem pi3 = this.arguments.pop(); args[k - j - 1] = pi3; } for (j = 0; j < args.length; j++) vaf.add(args[j]); stack.push(vaf); this.arguments.clear(); this.arguments = null; } return; } BuiltInFunction bif = new BuiltInFunction(sf.getFunction(this.settings), this.settings); int numargs = sf.getFunction(this.settings).getNumArgs(); if (numargs == 1) { bif.add(pi2); } else { if ((this.arguments == null && numargs != 0) || (this.arguments != null && numargs != this.arguments.size())) throw new FormulaException(FormulaException.incorrectArguments); for (int j = 0; j < numargs; j++) { ParseItem pi3 = this.arguments.get(j); bif.add(pi3); } } stack.push(bif); } public void adjustRelativeCellReferences(int colAdjust, int rowAdjust) { this.root.adjustRelativeCellReferences(colAdjust, rowAdjust); } public void columnInserted(int sheetIndex, int col, boolean currentSheet) { this.root.columnInserted(sheetIndex, col, currentSheet); } public void columnRemoved(int sheetIndex, int col, boolean currentSheet) { this.root.columnRemoved(sheetIndex, col, currentSheet); } public void rowInserted(int sheetIndex, int row, boolean currentSheet) { this.root.rowInserted(sheetIndex, row, currentSheet); } public void rowRemoved(int sheetIndex, int row, boolean currentSheet) { this.root.rowRemoved(sheetIndex, row, currentSheet); } private void handleOperand(Operand o, Stack stack) { if (!(o instanceof IntegerValue)) { stack.push(o); return; } if (o instanceof IntegerValue) { IntegerValue iv = (IntegerValue)o; if (!iv.isOutOfRange()) { stack.push(iv); } else { DoubleValue dv = new DoubleValue(iv.getValue()); stack.push(dv); } } } }