/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the 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. *
 *	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
 * 
* For converting from RPN to Infix the following algorithm is applied: *
 * 		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
 * 
*/ 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. *

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; } }