Back to index

wims  3.65+svn20090927
Public Member Functions | Static Public Member Functions | Package Functions | Package Attributes | Private Member Functions | Static Private Member Functions | Private Attributes | Static Private Attributes
SharpTools.Formula Class Reference

This is the class for formula processing. More...

Collaboration diagram for SharpTools.Formula:
Collaboration graph
[legend]

List of all members.

Public Member Functions

boolean isBad ()
 Check for bad formula.
boolean needsRecalc ()
 Check whether needs a recalc.
void setNeedsRecalc (boolean needs)
 Mark it as needsRecalc.
TreeSet getDependency ()
 From the Node list; Creates the dependency set.
String toString ()
 This returns the string value of the formula.

Static Public Member Functions

static boolean isSafe (Formula formula, int rowOff, int colOff)
 Check whether the paste of a formula to a new location is safe.
static String fixRelAddr (String oldFormula, int rowOffset, int colOffset)
 This takes the old formula string and the change of column/row return the fixed formula string.
static Number evaluate (SharpTableModel table, int row, int col) throws ParserException
 Evaluates the cell (row, col) of table.
static Number evaluate (SharpTableModel table, LinkedList postfix, int row, int col) throws ParserException
 It evaluates the postfix expression by a stack.
static final CellPoint parseAddress (String s)
static void registerFunctions ()
 Registers the functions on the funcTable.
static Function getFuncHandler (String fname)

Package Functions

 Formula (String input, int row, int col, ParserException e)
 Formula contructor.
 Formula (String input, int row, int col) throws ParserException
 Formula contructor.
 Formula (Formula formula, int row, int col) throws ParserException
 Formula constructor.

Package Attributes

int col

Private Member Functions

LinkedList tokenize (String input) throws ParserException
 Tokenize the formula string into a list of Nodes.
LinkedList convertParams (final LinkedList tokens) throws ParserException
 Convert function parameters.
LinkedList toPostfix (LinkedList tokens) throws ParserException
 This converts tokens to postfix format using stack.
TreeSet createDependency (LinkedList tokens)
 From a list of tokens generate a set of cells that this cell depends on.
Number evaluate (SharpTableModel table) throws ParserException
 This is a private function only used internally.
void throwError (Object s) throws ParserException
 Label the bad cells and throw ParserException.
String getCellString ()
 Gets the string form of the cell address ("A1", "B2", etc).

Static Private Member Functions

static int getPriority (char op)
 This gets the priority of an operator.
static int getPriority (Node node)
 This returns the highest-priority node.
static Number calc (char op, Number op1, Number op2)
 This takes an operator and two operands and returns the result.
static Number evalFunction (SharpTableModel table, Node node, int row, int col) throws ParserException
 This evaluates the function.
final static int translateRow (String row)
 This translates the string form of row into row number ('12' -> 12), and vice versa.
final static String translateRow (int row)
 This translates the int form of row into row string (12 -> '12').
final static int translateColumn (String column)
 This translates the int form of column into column string (1 -> 'A')
final static String translateColumn (int column)
 This translates the string form of column into column number ('A' -> 1)
static final String getCellString (int row, int col)
 Gets the string form of the cell address ("A1", "B2", etc).
static void register (String funcName, Function func)
 Adds a function to the function table.

Private Attributes

TreeSet dependency
LinkedList postfix
int row
String formulaString
ParserException error
boolean needsRecalc

Static Private Attributes

static HashMap funcTable

Detailed Description

This is the class for formula processing.

The major public interfaces are:

  1. Constructors
  2. Number evaluate(TableModel table, int row, int col)
  3. String toString()
  4. TreeSet getDependency()
See also:
Node
Function
ParserException
Author:
Hua Zhong huaz@.nosp@m.cs.c.nosp@m.olumb.nosp@m.ia.e.nosp@m.du
Version:
Revision:
1.74

Definition at line 32 of file Formula.java.


Constructor & Destructor Documentation

SharpTools.Formula.Formula ( String  input,
int  row,
int  col,
ParserException  e 
) [inline, package]

Formula contructor.

This is used to construct a Formula object without any parsing process.

Parameters:
inputthe formula string
rowthe current row where the formula is stored
colthe current column where the forluma is stored
ea ParserException

Definition at line 59 of file Formula.java.

                                                               {
       formulaString = input.toUpperCase();
       this.col = col;
       this.row = row;
       error = e;
    }
SharpTools.Formula.Formula ( String  input,
int  row,
int  col 
) throws ParserException [inline, package]

Formula contructor.

Parse the input string and translate into postfix form.

Parameters:
inputthe formula string
rowthe current row where the formula is stored
colthe current column where the forluma is stored
Exceptions:
ParserException
See also:
toPostfix

Definition at line 77 of file Formula.java.

                                                                   {

       this.col = col;
       this.row = row;
       formulaString = input.toUpperCase();
       try {
           // tokenize and convert the formula to postfix form
           LinkedList tokens = tokenize(formulaString);
           //     Debug.println("Tokens: "+tokens);
           dependency = createDependency(tokens);
           //     Debug.println("Dependency: "+dependency);
           
           postfix = toPostfix(convertParams(tokens));
           Debug.println("Postfix: "+postfix);
       }catch (ParserException e) {
           Debug.println("Formula constructor: "+e);
           throwError(e);
       }
    }

Here is the call graph for this function:

SharpTools.Formula.Formula ( Formula  formula,
int  row,
int  col 
) throws ParserException [inline, package]

Formula constructor.

This is used for copy/paste. Take a formula and put it into a new position, and convert the formula string to make relative addresses correct.

Parameters:
formulathe original formula
rowthe current(new) row
colthe current(old) column
Exceptions:
ParserException
See also:
fixRelAddr

Definition at line 110 of file Formula.java.

                                                                      {

       this.col = col;
       this.row = row;
       try {
           // do necessary conversion so the formula string
           // is still right after changing the position
           formulaString = fixRelAddr(formula.formulaString,
                                   row-formula.row,
                                   col-formula.col);
           if (formulaString == null) {
              formulaString = "$REFS$0";
              error = new ParserException("REFS");
              return;
           }
           // tokenize and convert the formula to postfix form
           LinkedList tokens = tokenize(formulaString);
           dependency = createDependency(tokens);
           postfix = toPostfix(convertParams(tokens));
       } catch (ParserException e) {
           System.err.println("Shouldn't happen!");
           throwError(e);
       }
    }

Here is the call graph for this function:


Member Function Documentation

static Number SharpTools.Formula.calc ( char  op,
Number  op1,
Number  op2 
) [inline, static, private]

This takes an operator and two operands and returns the result.

Parameters:
opthe operator
op1operand 1
op2operand 2
Returns:
the float value of operand 1 operator operand 2

Definition at line 806 of file Formula.java.

                                                                {
       float n1 = op1.floatValue();
       float n2 = op2.floatValue();
       float result;
       switch (op) {
           case '+': result = n1+n2; break;
           case '-': result = n1-n2; break;
           case '*': result = n1*n2; break;
           case '/': result = n1/n2; break;
           case '^': result = (float)Math.pow(n1, n2); break;
           case '%': result = (float)((int)n1%(int)n2); break;
           default: result = 0; break;
       }

       return new Float(result);
    }

Here is the caller graph for this function:

LinkedList SharpTools.Formula.convertParams ( final LinkedList  tokens) throws ParserException [inline, private]

Convert function parameters.

From a linear sequence of nodes, output a tree-like structure, with all the functions having a linked list of parameters, and each parameter having a linked list of nodes (that is, each parameter can be a formula).

The basic rules are:

  1. Pass values to the output (a linked list used as a stack) except the following.
  2. If a function name is encountered, it's set to "pending" (meaning it's expecting an enclosing parenthesis) and passed to the output, and its following '(' is discarded.
  3. If a left parenthesis is encountered, it's set to "pending" and passed to the output.
  4. If a comma is encountered, pop up all the previous nodes to a list until an unpending function node is found. Then set the list having all the popped nodes as the function's last parameter. The function node is pushed back.
  5. For a ')', pop all the previous nodes to a list until an unpending left parenthesis or an unpending function is found. For the former, the left parenthesis is set to "unpending", and push back all the popped nodes (including the right parenthesis). For the latter, it's the same as the comma case, except that the function node is set to "unpending".

Definition at line 512 of file Formula.java.

                              {
       
       if (tokens == null) {
           throw error;
       }

       LinkedList stack = new LinkedList();

       Iterator it = tokens.iterator();

       try {
           while (it.hasNext()) {
              Node node = (Node)it.next();

              if (node.isType(Node.FUNCTION)) {
                  node.setPending(true);
                  stack.add(node);
                  node = (Node)it.next();
                  // should be LParen
                  if (!node.isType(Node.LPAREN)) // ( expected
                     throwError("#NO(?");
              }             
              else if (node.isType(Node.LPAREN)) {
                  node.setPending(true);
                  stack.add(node);
              }             
              else if (node.isType(Node.COMMA)) {
                  Node exp = new Node();
                  LinkedList list = new LinkedList();
                  Node param = (Node)stack.removeLast();//pop();
                  // pop out until the unpending FUNCTION
                  while (!param.isType(Node.FUNCTION) ||
                        !param.isPending()) {
                     list.addFirst(param);
                     param = (Node)stack.removeLast();//pop();
                  }

                  exp.setType(Node.EXP);
                  exp.setExp(list);

                  param.addParam(exp);

                  // still pending
                  //            stack.push(param);
                  stack.add(param);
              }
              else if (node.isType(Node.RPAREN)) {
                  // we don't know whether this is for a function.
                  Node exp = new Node();
                  LinkedList list = new LinkedList();
                  Node param = (Node)stack.removeLast(); //stack.pop();

                  // process the last parameter
                  while (!param.isPending() ||
                        !param.isType(Node.FUNCTION) &&
                        !param.isType(Node.LPAREN)) {
                     list.addFirst(param);
                     param = (Node)stack.removeLast();//pop();
                  }

                  // set to unpending
                  if (param.isType(Node.LPAREN)) {
                     // this is a normal left paren
                     param.setPending(false);
                     // push back
                     stack.add(param);                  
                     stack.addAll(list);
                     stack.add(node);
                  }
                  else {
                     // this is a function left paren
                     //                   Debug.println("exp is "+list);
                     // set the expression of that parameter
                     exp.setType(Node.EXP);
                     exp.setExp(list);
                     // add a parameter for the function
                     param.addParam(exp);
                     param.setPending(false);
                     stack.add(param);
                  }
              }
              else
                  stack.add(node); //push(node);
                  
           }
              
       }
       catch (ParserException e) {
           throw e;
       }
       catch (Exception e) {
           Debug.println(e);
           // general param error
           throwError("#PARAM?");
       }

       return stack;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

TreeSet SharpTools.Formula.createDependency ( LinkedList  tokens) [inline, private]

From a list of tokens generate a set of cells that this cell depends on.

We do this before the postfix thing since the original token list has no nested stuff.

Parameters:
tokensa list of tokens (nodes)
Returns:
a set of cells being referenced

Definition at line 722 of file Formula.java.

                                                        {
       TreeSet dependency = new TreeSet();
       
       Iterator it = tokens.iterator();
       while (it.hasNext()) {
           Node node = (Node)it.next();
           if (node.isType(Node.REL_ADDR) || node.isType(Node.ABS_ADDR)) {
              // for addresses, translate into CellPoint (absolute point)
              CellPoint newCell = node.toCellPoint(row, col);
              dependency.add(newCell);
           } else if (node.isType(Node.COLON)) {
              // all the cells in this range are referenced
              CellPoint[] addr = node.getAddressRange(row, col);
              for (int i = addr[0].getRow(); i <= addr[1].getRow(); i++)
                  for (int j = addr[0].getCol(); j <= addr[1].getCol();
                      j++)
                     dependency.add(new CellPoint(i, j));
           }
       }
       return dependency;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

static Number SharpTools.Formula.evalFunction ( SharpTableModel  table,
Node  node,
int  row,
int  col 
) throws ParserException [inline, static, private]

This evaluates the function.

Parameters:
tablethe TableModel object
nodethe head node of the function
Returns:
the value as a Float object
Exceptions:
ParserException

Definition at line 831 of file Formula.java.

                                                                                                                  {
       String funcName = node.getData();
       // get function handler from the funcTable
       Function func=getFuncHandler(funcName);

       if (func == null) {
           // not registered function
           throw new ParserException("#FUNC?");
           
       }
       else
       {
           return func.evaluate(table, node, row, col);
       }    
    }

Here is the call graph for this function:

Here is the caller graph for this function:

static Number SharpTools.Formula.evaluate ( SharpTableModel  table,
int  row,
int  col 
) throws ParserException [inline, static]

Evaluates the cell (row, col) of table.

Parameters:
tablethe TableModel object
rowthe row of the cell to be evaluated
colthe column of the cell to be evaluated
Returns:
the result as a Float object
Exceptions:
ParserException

Definition at line 856 of file Formula.java.

                              {

       if (Debug.isDebug())
           Debug.println("recalculating "+new CellPoint(row, col));

       
       // get the formula object
       
       Formula formula = table.getCellAt(row, col).getFormula();
       formula.setNeedsRecalc(false);
       
       if (formula == null)     
           return new Integer(0);      
       else
           return formula.evaluate(table);
       
    }

Here is the call graph for this function:

Here is the caller graph for this function:

Number SharpTools.Formula.evaluate ( SharpTableModel  table) throws ParserException [inline, private]

This is a private function only used internally.

Evaluates the current formula of table.

Parameters:
tablethe TableModel object
Returns:
the result as a Float object
Exceptions:
ParserException

Definition at line 883 of file Formula.java.

                                                                          {
       
       // if the formula is bad, directly returns the error
       if (isBad()) {
           throw error;
       }
       return evaluate(table, postfix, row, col);
    }

Here is the call graph for this function:

static Number SharpTools.Formula.evaluate ( SharpTableModel  table,
LinkedList  postfix,
int  row,
int  col 
) throws ParserException [inline, static]

It evaluates the postfix expression by a stack.

Parameters:
tablethe TableModel object
postfixthe formula in postfix form
rowthe row of the cell to be evaluated
colthe column of the cell to be evaluated
Returns:
the result as a Float object
Exceptions:
ParserException

Definition at line 902 of file Formula.java.

                              {

       try {
           Stack stack = new Stack();
           Iterator it = postfix.iterator();
           while (it.hasNext()) {
              Node node = (Node)it.next();
              //Number result;
              Number result;
                     
              switch (node.getType()) {
              case Node.OPERATOR:
                  // pop the 2 operands from stack top and save the result
                  // back to stack
                  Number n2 = (Number)stack.pop();
                  Number n1 = (Number)stack.pop();
                  result = calc(node.getData().charAt(0), n1, n2);
                  break;
              case Node.FUNCTION:
//                // evaluate the function
                  result = evalFunction(table, node, row, col);
                  break;
              case Node.NUMBER:
                  // directly return the number
                  result = new Float(node.getNumber());
                  break;
              case Node.ABS_ADDR:
                  // get the numeric value of that cell
                  result = //getNumericValueAt(table, node.getRow(),
                            //            node.getCol());
                     table.getNumericValueAt(node.getRow(), node.getCol());
                  break;
              case Node.REL_ADDR:
                  // get the numeric value of that cell            
                  result = //getNumericValueAt(table, node.getRow()+row,
                            //            node.getCol()+col);
                     table.getNumericValueAt(node.getRow()+row,
                                          node.getCol()+col);
                  break;
              default:
                  // evaluation error
                  throw new ParserException("#EVAL?");
              }

              // push to the stack
              stack.push(result);
           }
           
           Number result = (Number)stack.pop();
           return result;
       }catch (EmptyStackException e) {
           // imbalance between operands and operators
           throw new ParserException("#OP?");
       
           // ("Wrong format of formula: too many operators");
       }catch (ParserException e) {
           throw e;
       }catch (Exception e) {
           Debug.println(e);

       }

       return new Integer(0);
    }

Here is the call graph for this function:

static String SharpTools.Formula.fixRelAddr ( String  oldFormula,
int  rowOffset,
int  colOffset 
) [inline, static]

This takes the old formula string and the change of column/row return the fixed formula string.

For instance, a string 'A1*B2' should be changed to 'B2*C3' if it's moved from A3 to B4.

Parameters:
oldFormulathe original formula string
colthe current(old) column
rowthe current(new) row
Returns:
the converted String; null if there is error

Definition at line 198 of file Formula.java.

                                               {
       if (colOffset ==0 && rowOffset == 0)
           return oldFormula;

       StringBuffer newFormulaBuf = new StringBuffer();
       int lastPos = 0;

       
       for (int i = 0; i < oldFormula.length(); i++) {
           char c = oldFormula.charAt(i);
           int letterStart = i;
           // search for uppercase letters
           if (Character.isUpperCase(c)) {
              boolean isAbsAddr = (i>0 && oldFormula.charAt(i-1)=='$');
              StringBuffer colNameBuf = new StringBuffer();
              // collect all the letters
              while (i < oldFormula.length() &&
                     Character.isUpperCase(oldFormula.charAt(i))) {
                  colNameBuf.append(oldFormula.charAt(i++));
              }

              String colName = colNameBuf.toString();
              
              if (i == oldFormula.length())
                  break;
              
              // is it followed by digits?
              if (!isAbsAddr &&
                  Character.isDigit(oldFormula.charAt(i))) {
                  // hey, we get a REL_ADDR here
                  StringBuffer rowNameBuf = new StringBuffer();
                  while (i < oldFormula.length() &&
                     Character.isDigit(oldFormula.charAt(i))) {
                     rowNameBuf.append(oldFormula.charAt(i++));
                  }

                  String rowName = rowNameBuf.toString();
                  
                  // We've got colName and rowName
                  // add the string before the rel_addr first
                  newFormulaBuf.append(oldFormula.substring(lastPos,
                                                    letterStart));
                  // then add the new address (string -> number -> string)
                  if (colOffset == 0)
                     newFormulaBuf.append(colName);
                  else {
                     String col = translateColumn(translateColumn(colName)+
                                               colOffset);
                     if (col == null)
                         return null;
                     
                     newFormulaBuf.append(col);
                  }

                  if (rowOffset == 0)
                     newFormulaBuf.append(rowName);
                  else {
                     String row = translateRow(translateRow(rowName)+
                                            rowOffset);
                     if (row == null)
                         return null;
                     
                     newFormulaBuf.append(row);
                  }
                  
                  lastPos = i;
              }
           }
       }

       newFormulaBuf.append(oldFormula.substring(lastPos));
       return newFormulaBuf.toString();
    }

Here is the call graph for this function:

Here is the caller graph for this function:

String SharpTools.Formula.getCellString ( ) [inline, private]

Gets the string form of the cell address ("A1", "B2", etc).

Returns:
the string value of the Cell

Definition at line 1037 of file Formula.java.

                                   {
       return getCellString(row, col);
    }
static final String SharpTools.Formula.getCellString ( int  row,
int  col 
) [inline, static, private]

Gets the string form of the cell address ("A1", "B2", etc).

Parameters:
rowthe row coordinate
colthe column coordinate
Returns:
the string value of the Cell

Definition at line 1048 of file Formula.java.

                                                                {
       return ""+translateColumn(col)+translateRow(row);
    }

Here is the call graph for this function:

TreeSet SharpTools.Formula.getDependency ( ) [inline]

From the Node list; Creates the dependency set.

Returns:
a TreeSet of CellPoint that the current cell references

Definition at line 750 of file Formula.java.

                                   {
       if (isBad()) {
           //     Debug.println("Bad formula: "+formulaString);
           return new TreeSet();
       }

       return dependency;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

static Function SharpTools.Formula.getFuncHandler ( String  fname) [inline, static]

Definition at line 1157 of file Formula.java.

                                                        {
        //jm.evers : needed by applet
       registerFunctions();
       return (Function)funcTable.get(fname);
    }

Here is the call graph for this function:

Here is the caller graph for this function:

static int SharpTools.Formula.getPriority ( char  op) [inline, static, private]

This gets the priority of an operator.

Parameters:
opthe operator character
Returns:
1='+' '-', 2='*' '/', 3='^'

Definition at line 765 of file Formula.java.

                                            {
       switch (op) {

       case '+':
       case '-':
           return 1;
       case '*':
       case '/':
       case '%':
           return 2;
       case '^':
           return 3;
       default:
           return 0;
       }
    }

Here is the caller graph for this function:

static int SharpTools.Formula.getPriority ( Node  node) [inline, static, private]

This returns the highest-priority node.

Definition at line 785 of file Formula.java.

                                              {
       return getPriority(node.getData().charAt(0));
    }

Here is the call graph for this function:

Check for bad formula.

Returns:
boolean true if postfix

Definition at line 162 of file Formula.java.

                           {
       // postfix was set to null when there was any error
       // in processing the formula string
       return postfix == null;
    }

Here is the caller graph for this function:

static boolean SharpTools.Formula.isSafe ( Formula  formula,
int  rowOff,
int  colOff 
) [inline, static]

Check whether the paste of a formula to a new location is safe.

A paste is not safe when at the new location relative addresses become invalid (e.g., can not use uppercase letters to represent the column number, or row number is non-positive).

Parameters:
formulathe original formula
rowOffthe row offset
colOffthe column offset
Returns:
true if it's safe
See also:
fixRelAddr

Definition at line 149 of file Formula.java.

                                                                          {
       // do necessary conversion so the formula string
       // is still right after changing the position
       String newString = fixRelAddr(formula.formulaString,
                                  rowOff, colOff);
       return newString != null;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

Check whether needs a recalc.

Returns:
boolean true if needs recalculation

Definition at line 173 of file Formula.java.

                                 {
       return needsRecalc;
    }

Here is the caller graph for this function:

static final CellPoint SharpTools.Formula.parseAddress ( String  s) [inline, static]

Definition at line 1052 of file Formula.java.

                                                         {

       try {
       
           int row, col;

           s = s.toUpperCase();
           int len = 0;
           int total = s.length();
           
           StringBuffer buf = new StringBuffer();
           char c;
           
           while (len < total) {
              c = s.charAt(len);
              if (Character.isUpperCase(c)) {
                  buf.append(c);
                  len++;
              }
              else if (Character.isDigit(c))
                  break;
              else
                  return null;            
           }
           
           col = translateColumn(buf.toString());
           
           if (col == 0)
              return null;
           
           buf = new StringBuffer();
           
           while (len < total) {
              c = s.charAt(len);
              if (Character.isDigit(c)) {
                  buf.append(c);
                  len++;
              }
              else
                  return null;
           }
           
           row = translateRow(buf.toString());
           if (row == 0)
              return null;
           
           return new CellPoint(row, col);
           
       }
       catch (Exception e) {
           return null;
       }
    }

Here is the call graph for this function:

Here is the caller graph for this function:

static void SharpTools.Formula.register ( String  funcName,
Function  func 
) [inline, static, private]

Adds a function to the function table.

Parameters:
funcNamethe name of the function
functhe Function object
See also:
Function

Definition at line 1113 of file Formula.java.

                                                                 {
       funcTable.put(funcName, func);
    }
static void SharpTools.Formula.registerFunctions ( ) [inline, static]

Registers the functions on the funcTable.

Should be called only once....well not for an applet

Definition at line 1121 of file Formula.java.

                                           {
       funcTable = new HashMap();
       register("SUM", new FunctionSum());
       register("MEAN", new FunctionAverage());
       register("AVERAGE", new FunctionAverage());
       register("MEDIAN", new FunctionMedian());
       register("ABS", new FunctionAbs());
       register("INT", new FunctionInt());
       register("ROUND", new FunctionRound());
       register("SIN", new FunctionSin());
       register("COS", new FunctionCos());
       register("TAN", new FunctionTan());
       register("ASIN", new FunctionAsin());
       register("ACOS", new FunctionAcos());
       register("ATAN", new FunctionAtan());
       register("SQRT", new FunctionSqrt());
       register("LOG", new FunctionLog());
       register("MIN", new FunctionMin());
       register("MAX", new FunctionMax());
       register("RANGE", new FunctionRange());
       register("STDDEV", new FunctionStddev());
       register("MEANDEV", new FunctionMeandev());
       register("COUNT", new FunctionCount());   
       register("PI", new FunctionPI());
       register("E", new FunctionE());
    }

Here is the caller graph for this function:

void SharpTools.Formula.setNeedsRecalc ( boolean  needs) [inline]

Mark it as needsRecalc.

boolean true if needs recalculation

Definition at line 182 of file Formula.java.

                                              {
       needsRecalc = needs;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

void SharpTools.Formula.throwError ( Object  s) throws ParserException [inline, private]

Label the bad cells and throw ParserException.

error is saved so next time it won't re-evaluate again: it directly throws the same exception.

Parameters:
sthe thing that's bad
Exceptions:
ParserException

Definition at line 1020 of file Formula.java.

                                                             {
       // test code
       //     System.err.println("Marking formula "+formulaString+" as bad");
       postfix = null;
       if (error instanceof ParserException)
           throw (ParserException)s;
       else {
           error = new ParserException(s);
           throw error;
       }
    }

Here is the caller graph for this function:

LinkedList SharpTools.Formula.tokenize ( String  input) throws ParserException [inline, private]

Tokenize the formula string into a list of Nodes.

Parameters:
inputthe input string to tokenize
Exceptions:
ParserException
See also:
Node

Definition at line 283 of file Formula.java.

                                                                     {
       LinkedList tokens = new LinkedList();
       Stack stack = new Stack();
       final Node zero = new Node();
       zero.setType(Node.NUMBER);
       zero.setNumber(0);
       //     input.toUpperCase();

       int cur = 0;
       int lastType = Node.DEFAULT;
       Node lastToken = null;
       //     boolean hasRange = false; // has a pending address range
       int nParen = 0; // balance of parens
              
       while (cur < input.length()) {
           Node node = new Node();
           try {
              char c = input.charAt(cur++);
              node.setData(String.valueOf(c));
              if (Character.isLetter(c)) {
                  // Function or Relative Address
                  node.setType(Node.FUNCTION);
                  node.setParams(new LinkedList());

                  // get all preceding letters
                  while (cur < input.length() &&
                        Character.isLetter(input.charAt(cur)))
                     node.appendData(input.charAt(cur++)); 
              
                  if (Character.isDigit(input.charAt(cur))) {
                     node.setType(Node.REL_ADDR);
                     // {letters}{numbers} is a relative address
                     node.setCol(translateColumn(node.getData()) - col);
                     
                     node.setData("");
                         
                     while (cur < input.length() &&
                            Character.isDigit(input.charAt(cur)))
                         node.appendData(input.charAt(cur++));    
                     // relative row
                     node.setRow(translateRow(node.getData()) - row);
                     node.setData(null);
                  }
              }else if (Character.isDigit(c) || c == '.')
                  /*||
                      (lastType == Node.DEFAULT ||
                       lastType == Node.LPAREN || lastType == Node.COMMA) &&
                       (c == '+' || c == '-')) */{
                  // Numbers
                  while (cur < input.length() &&
                        (Character.isDigit(input.charAt(cur)) ||
                         input.charAt(cur) == '.'))
                     // OK, we don't check for input like "3.56.4"
                     // this will be checked below by parseNumber
                     node.appendData(input.charAt(cur++));

                  try {
                     try {
                         node.setNumber(Integer.parseInt(node.getData()));
                     }
                     catch (NumberFormatException e) {
                         node.setNumber(Float.parseFloat(node.getData()));
                     }
                     node.setType(Node.NUMBER);
                  }catch (NumberFormatException e) {
                     // invalid number format
                     throwError("#NUM?");
                  }
              }else if (c == '(') {
                  nParen++;
                  node.setType(Node.LPAREN);
              }else if (c == ')') {
                  nParen--;
                  node.setType(Node.RPAREN);
              }else if (c == ',') {
                  node.setType(Node.COMMA);
              }else if (c == ':') {

                  node.setPending(true);
                  node.setType(Node.COLON);
                  
                  Node prev = null;

                  try {
                     prev = (Node)tokens.removeLast();
                  }
                  catch (Exception e) {
                     throwError("#ADDR?");
                  };

                  if (prev.isType(Node.REL_ADDR) ||
                     prev.isType(Node.ABS_ADDR)) {
                     node.setNextRange(prev);
                  }
                  else
                     // invalid address format
                     throwError("#ADDR?");
                  
              }else if (c == '+' || c == '-' || c == '*' || c == '/' ||
                      c == '^' || c == '%') {
                  node.setType(Node.OPERATOR);
              }else if (c == '$') {
                  // Absolute Address starts with $
                  node.setType(Node.ABS_ADDR);
                  node.setData("");
                  // a letter must follow the $
                  if (! Character.isLetter(input.charAt(cur))) {
                     // invalid address format
                     throwError("#ADDR?");
                  }
                  // look for column
                  while (Character.isLetter(input.charAt(cur)))
                     node.appendData(input.charAt(cur++));

                  // absolute address has to be the form of
                  // ${letters}${numbers}
                  if (input.charAt(cur++) != '$' ||
                     ! Character.isDigit(input.charAt(cur))) {
                     // invalid address format
                     throwError("#ADDR?");
                  }

                  node.setCol(translateColumn(node.getData()));
                  node.setData("");

                  while (cur < input.length() &&
                        Character.isDigit(input.charAt(cur)))
                     node.appendData(input.charAt(cur++));

                  node.setRow(translateRow(node.getData()));
                  node.setData(null);
              }else if (c == ' ')
                  continue;
              else
                  // invalid char
                  throwError("#NAME?");

              // after a ADDR or NUMBER token the following char
              // should not be a letter or digit
              if (cur < input.length() && (node.isType(Node.REL_ADDR) ||
                                        node.isType(Node.ABS_ADDR) ||
                                        node.isType(Node.NUMBER)) &&
                  Character.isLetterOrDigit(input.charAt(cur))) {
                  throwError
                     // invalid char
                     ("#NAME?");
              }

              // process the second address of a cell range
              if (lastToken != null &&
                  lastToken.isType(Node.COLON) &&
                  lastToken.isPending()) {
                  if (node.isType(Node.REL_ADDR) ||
                     node.isType(Node.ABS_ADDR)) {

                     Node range = (Node)tokens.removeLast();

                     try {
                         ((Node)range.getNextRange()).setNextRange(node);
                         range.setPending(false);
                     }
                     catch (NullPointerException e) {
                         // invalid address format
                         throwError("#ADDR?");
                     }

                     node = range;
                  }
                  else
                     throwError("#ADDR?");
              }


              if (node.isType(Node.OPERATOR) &&
                  (node.getData().equals("+") ||
                   node.getData().equals("-")) &&
                  (lastToken == null || lastToken.isType(Node.LPAREN) ||
                   lastToken.isType(Node.COMMA))) {
                  tokens.add(zero);
              }
              
              tokens.add(node);
              lastType = node.getType();
              lastToken = node;           
                  
           }catch (IndexOutOfBoundsException e) {
              // error
              throwError("#NAME?");
           }catch (ParserException e) {
              throwError(e);
           }
           catch (Exception e) {
              Debug.println(e.toString());
           }
       }

       if (nParen != 0) // imbalanced parenthesis
           throwError("#PAREN?");
       return tokens;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

LinkedList SharpTools.Formula.toPostfix ( LinkedList  tokens) throws ParserException [inline, private]

This converts tokens to postfix format using stack.

The basic rules are:

  1. Pass values to the output (a linked list)
  2. Push '(' to the stack
  3. For an operator, pop all the previous operators that have a lower priority to the output and push this one to the stack
  4. For ')', pop all the previous operators until a (
  5. If we reach the end, pop up everything
Parameters:
tokensa linked list to convert
Exceptions:
ParserException
See also:
Node
tokenize
#convertParam

Definition at line 632 of file Formula.java.

                                                                           {
       if (tokens == null) {
           throw error;
       }

       // stack is used for the conversion
       Stack stack = new Stack();
       LinkedList postfix = new LinkedList();
       Iterator it = tokens.iterator();
       while (it.hasNext()) {
           Node node = (Node)it.next();
           switch (node.getType()) {

           case Node.NUMBER:
           case Node.REL_ADDR:
           case Node.ABS_ADDR:
           case Node.COLON:
              // just add normal values to the list
              postfix.add(node);
              break;
              
           case Node.LPAREN:
              // push to stack; pop out when a RPAREN is encountered
              stack.push(node);
              break;
           case Node.OPERATOR:
              // get the precedence priority of the operator
              int priority = getPriority(node);

              // pop up operators with the same or higher priority from
              // the stack
              while (! stack.empty() &&
                     ! ((Node)stack.peek()).isType(Node.LPAREN) &&
                     getPriority((Node)stack.peek()) >= priority) {
                  postfix.add((Node)stack.pop());
              }
              stack.push(node);
              break;        
           case Node.RPAREN:
              try {
                  Node op = (Node)stack.pop();
                  // pop out until the last LPAREN
                  while (! op.isType(Node.LPAREN)) {
                     postfix.add(op);
                     op = (Node)stack.pop();
                  }
              }
              catch (EmptyStackException e) {
                  // should not happen - imbalance in parenthesis
                  throwError("#PAREN?");
              }
              break;
           case Node.FUNCTION:

              // get the param list
              LinkedList params = node.getParams();

              Iterator paramIter = params.iterator();

              while (paramIter.hasNext()) {
                  Node exp = (Node)paramIter.next();
                  exp.setExp(toPostfix(exp.getExp()));
              }

              postfix.add(node);
              
              break;
              
           default:
              // unknown error - should not happen
              throwError("#ERROR?");
           }  
       }

       // pop up the rest nodes
       while (!stack.empty())
           postfix.add((Node)stack.pop());

       return postfix;
    }

Here is the call graph for this function:

Here is the caller graph for this function:

String SharpTools.Formula.toString ( ) [inline]

This returns the string value of the formula.

Returns:
the string value

Definition at line 794 of file Formula.java.

                             {
       return formulaString;
    }

Here is the caller graph for this function:

final static int SharpTools.Formula.translateColumn ( String  column) [inline, static, private]

This translates the int form of column into column string (1 -> 'A')

Parameters:
columnthe int representation of the column
Returns:
the string represetnation of the column

Definition at line 998 of file Formula.java.

                                                            {
       return Node.translateColumn(column);
    }

Here is the call graph for this function:

Here is the caller graph for this function:

final static String SharpTools.Formula.translateColumn ( int  column) [inline, static, private]

This translates the string form of column into column number ('A' -> 1)

Parameters:
columnthe string representation of the column
Returns:
the int represetnation of the column

Definition at line 1008 of file Formula.java.

                                                            {
       return Node.translateColumn(column);
    }

Here is the call graph for this function:

final static int SharpTools.Formula.translateRow ( String  row) [inline, static, private]

This translates the string form of row into row number ('12' -> 12), and vice versa.

Parameters:
rowthe string representation of the row
Returns:
the int representation of the row

Definition at line 978 of file Formula.java.

                                                      {
       return Node.translateRow(row);
    }

Here is the call graph for this function:

Here is the caller graph for this function:

final static String SharpTools.Formula.translateRow ( int  row) [inline, static, private]

This translates the int form of row into row string (12 -> '12').

Parameters:
rowthe int representation of the row
Returns:
the string representation of the row

Definition at line 988 of file Formula.java.

                                                      {
       return Node.translateRow(row);
    }

Here is the call graph for this function:


Member Data Documentation

int SharpTools.Formula.col [package]

Definition at line 39 of file Formula.java.

TreeSet SharpTools.Formula.dependency [private]

Definition at line 36 of file Formula.java.

Definition at line 43 of file Formula.java.

Definition at line 41 of file Formula.java.

HashMap SharpTools.Formula.funcTable [static, private]

Definition at line 34 of file Formula.java.

Definition at line 46 of file Formula.java.

LinkedList SharpTools.Formula.postfix [private]

Definition at line 37 of file Formula.java.

int SharpTools.Formula.row [private]

Definition at line 39 of file Formula.java.


The documentation for this class was generated from the following file: