summaryrefslogtreecommitdiff
path: root/xmerge/source/pexcel/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java
diff options
context:
space:
mode:
Diffstat (limited to 'xmerge/source/pexcel/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java')
-rw-r--r--xmerge/source/pexcel/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java271
1 files changed, 271 insertions, 0 deletions
diff --git a/xmerge/source/pexcel/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java b/xmerge/source/pexcel/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java
new file mode 100644
index 000000000000..2e060ca0a148
--- /dev/null
+++ b/xmerge/source/pexcel/java/org/openoffice/xmerge/converter/xml/sxc/pexcel/records/formula/FormulaCompiler.java
@@ -0,0 +1,271 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+package org.openoffice.xmerge.converter.xml.sxc.pexcel.records.formula;
+
+import java.util.*;
+import org.openoffice.xmerge.util.Debug;
+
+/**
+ * FormulaCompiler converts Calc formula string into PocketXL bytes
+ * and PocketXL formula bytes into Calc Formula strings
+ *
+ * For converting from infix to Reverse Polish (or Postfix) notation the string is
+ * converted into a vector of Tokens and then re-ordered based on a modified version
+ * of the standard Infix to RPN conversion algorithms.
+ * <pre>
+ * Infix2Rpn(tokens)
+ * while have more tokens
+ * if token is operand
+ * push to stack
+ * else if token is function, argument separater, or open bracket
+ * push token
+ * extract tokens to matching close bracket into param
+ * Infix2Rpn(param)
+ * else if token is close bracket
+ * pop from stack into result until close bracket or function
+ * else
+ * while stack.top.priority >= token.priority
+ * add stack.pop to result
+ * push token onto stack
+ * </pre>
+ * For converting from RPN to Infix the following algorithm is applied:
+ * <pre>
+ * while have more tokens
+ * if token is operand
+ * push token to stack
+ * else if token is function or operator
+ * pop from stack number of args required by token
+ * apply token to params to make expr
+ * push expr to stack
+ * return stack.pop
+ * </pre>
+ */
+public class FormulaCompiler {
+ /**
+ * Constructs a FormulaCompiler object
+ */
+ public FormulaCompiler() {
+ }
+
+ private boolean isPercent(Token pt) {
+ return pt.getTokenID() == TokenConstants.TPERCENT;
+ }
+
+ private boolean isOpenBrace(Token pt) {
+ return pt.getTokenID() == TokenConstants.TPAREN;
+ }
+
+ private boolean isCloseBrace(Token pt) {
+ return pt.getValue().compareTo(")") == 0;
+ }
+
+ private boolean isParamDelimiter(Token pt) {
+ return pt.getTokenID() == TokenConstants.TARGSEP;
+ }
+
+ private boolean isBinaryOperator(Token pt) {
+ return false;
+ }
+
+ /**
+ * Re-order into Infix format
+ * @param tokens The tokens in RPN form
+ * @return The vector of tokens re-ordered in Infix notation
+ */
+ public Vector RPN2Infix(Vector tokens) {
+ Vector infixExpr = new Vector(15);
+ ListIterator iter = tokens.listIterator();
+ Stack evalStack = new Stack();
+ Stack args = new Stack();
+
+ while (iter.hasNext()) {
+ Token pt = (Token)iter.next();
+ if (pt.isOperand()) {
+ Vector expr = new Vector(5);
+ expr.add(pt);
+ evalStack.push(expr);
+ } else if (pt.isOperator() || pt.isFunction()) {
+ args.clear();
+ for (int i=0; i< pt.getNumArgs(); i++) {
+ args.push(evalStack.pop());
+ }
+ evalStack.push(makeExpression(pt, args));
+ }
+ }
+ return (Vector)evalStack.elementAt(0);
+ }
+
+ /**
+ * Convert the infix expression to RPN. Note that open brackets are saved onto the stack to preserve the users bracketing.
+ * <p>Also note that the open bracket following functions is not pushed onto the stack - it is always implied when
+ * writing out results
+ *
+ * @param tokens The vector of tokens in Infix form
+ *
+ * @return A vector of tokens for the expression in Reverse Polish Notation order
+ */
+ public Vector infix2RPN(Vector tokens) {
+ Vector rpnExpr = new Vector(15);
+ Stack evalStack = new Stack();
+ ListIterator iter = tokens.listIterator();
+ while (iter.hasNext()) {
+ Token pt = (Token)iter.next();
+
+ if (pt.isOperand()) { //Operands are output immediately
+ rpnExpr.add(pt);
+ } else if (pt.isFunction() || isParamDelimiter(pt) || isOpenBrace(pt)) { //Extract parameters after afunction or comma
+ evalStack.push(pt);
+ if (pt.isFunction()) {
+ iter.next();
+ }
+ Vector param = extractParameter(iter);
+ Debug.log(Debug.TRACE, "Extracted parameter " + param);
+ rpnExpr.addAll(infix2RPN(param));
+ } else if (isCloseBrace(pt)) { //Pop off stack till you meet a function or an open bracket
+ Token tmpTok = null;
+ boolean bPop = true;
+ while (bPop) {
+ if (evalStack.isEmpty()) {
+ bPop = false;
+ } else {
+ tmpTok = (Token)evalStack.pop();
+ //if (!(isOpenBrace(tmpTok) || isParamDelimiter(tmpTok))) { //Don't output brackets and commas
+ if (!isParamDelimiter(tmpTok)) { //Don't output commas
+ rpnExpr.add(tmpTok);
+ }
+ if (tmpTok.isFunction() || isOpenBrace(tmpTok)) {
+ bPop = false;
+ }
+ }
+ }
+ } else {
+ if (!evalStack.isEmpty()) {
+ while (!evalStack.isEmpty() &&
+ (((Token)evalStack.peek()).getTokenPriority() >=pt.getTokenPriority())) {
+ Token topTok = (Token)evalStack.peek();
+ if (topTok.isFunction() || isOpenBrace(topTok)) {
+ break;
+ }
+ rpnExpr.add(evalStack.pop());
+ }
+ }
+ evalStack.push(pt);
+ }
+ }
+
+ while (!evalStack.isEmpty()) {
+ Token topTok = (Token)evalStack.peek();
+ if (!(isOpenBrace(topTok) || isParamDelimiter(topTok))) { //Don't output brackets and commas
+ rpnExpr.add(evalStack.pop());
+ }
+ else
+ {
+ evalStack.pop();
+ }
+ }
+ return rpnExpr;
+ }
+
+ /**
+ * Extract a parameter or bracketed sub-expression
+ * @param iter an iterator into the list
+ * @return A complete sub-expression
+ */
+ protected Vector extractParameter(ListIterator iter) {
+ Vector param = new Vector(5);
+ int subExprCount = 0;
+
+ while (iter.hasNext()) {
+ Token pt = (Token)iter.next();
+ Debug.log(Debug.TRACE, "Token is " + pt + " and subExprCount is " + subExprCount);
+ if (isOpenBrace(pt)) {
+ subExprCount++;
+ param.add(pt);
+ } else if (isCloseBrace(pt)) {
+ if (subExprCount == 0) {
+ iter.previous();
+ return param;
+ } else {
+ subExprCount--;
+ param.add(pt);
+ }
+ } else if (isParamDelimiter(pt) && (subExprCount == 0)) {
+ iter.previous();
+ return param;
+ } else {
+ param.add(pt);
+ }
+ }
+ return param;
+ }
+
+ /**
+ * Given the operator and it's operators
+ * @param pt The operator token
+ * @param args The arguments for this operator
+ * @return A correctly ordered expression
+ */
+ protected Vector makeExpression(Token pt, Stack args) {
+ Vector tmp = new Vector(5);
+ TokenFactory tf = new TokenFactory();
+ if (pt.isOperator()) {
+ if (pt.getNumArgs()==2) { //Binary operator
+ tmp.addAll((Vector)args.pop());
+ tmp.add(pt);
+ tmp.addAll((Vector)args.pop());
+ } else if (pt.getNumArgs() == 1) {
+ if(isPercent(pt)) {
+ tmp.addAll((Vector)args.elementAt(0));
+ tmp.add(pt);
+ } else {
+ tmp.add(pt);
+ tmp.addAll((Vector)args.elementAt(0));
+ }
+ if (isOpenBrace(pt)) {
+ tmp.add(tf.getOperatorToken(")",1));
+ }
+ }
+ } else if (pt.isFunction()) {
+ tmp.add(pt);
+ tmp.add(tf.getOperatorToken("(",1));
+ if (!args.isEmpty()) {
+ Vector v = (Vector)args.pop();
+ tmp.addAll(v);
+ }
+ while (!args.isEmpty()) {
+ tmp.add(tf.getOperatorToken(",",1));
+ Vector v = (Vector)args.pop();
+ tmp.addAll(v);
+
+ }
+ tmp.add(tf.getOperatorToken(")",1));
+ }
+
+ return tmp;
+ }
+}