250 lines
7.7 KiB
Java
250 lines
7.7 KiB
Java
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);
|
|
}
|
|
}
|
|
}
|
|
}
|