Back to index

wims  3.65+svn20090927
Formula.java
Go to the documentation of this file.
00001 /*
00002  * @(#)Formula.java
00003  * 
00004  * $Id: Formula.java,v 1.74 2002/08/08 05:15:04 huaz Exp $
00005  * 
00006  * Created October 18, 2000, 3:27 PM
00007  */
00008 package SharpTools;
00009 import javax.swing.*;
00010 import java.util.*;
00011 import java.io.*;
00012 
00032 public class Formula {
00033     // a static hash table for function handlers
00034     static private HashMap funcTable;
00035     // tokens in order of postfix - used in calculation
00036     private TreeSet dependency;
00037     private LinkedList postfix;
00038     // where the formula is - important to calculate rel_addr
00039     private int row, col;
00040     // the raw formula string
00041     private String formulaString;
00042     // error message
00043     private ParserException error;
00044 
00045     // whether this formula needs recalculation
00046     private boolean needsRecalc;
00047     
00059     Formula(String input, int row, int col, ParserException e) {
00060        formulaString = input.toUpperCase();
00061        this.col = col;
00062        this.row = row;
00063        error = e;
00064     }
00065     
00077     Formula(String input, int row, int col) throws ParserException {
00078 
00079        this.col = col;
00080        this.row = row;
00081        formulaString = input.toUpperCase();
00082        try {
00083            // tokenize and convert the formula to postfix form
00084            LinkedList tokens = tokenize(formulaString);
00085            //     Debug.println("Tokens: "+tokens);
00086            dependency = createDependency(tokens);
00087            //     Debug.println("Dependency: "+dependency);
00088            
00089            postfix = toPostfix(convertParams(tokens));
00090            Debug.println("Postfix: "+postfix);
00091        }catch (ParserException e) {
00092            Debug.println("Formula constructor: "+e);
00093            throwError(e);
00094        }
00095     }
00096 
00110     Formula(Formula formula, int row, int col) throws ParserException {
00111 
00112        this.col = col;
00113        this.row = row;
00114        try {
00115            // do necessary conversion so the formula string
00116            // is still right after changing the position
00117            formulaString = fixRelAddr(formula.formulaString,
00118                                    row-formula.row,
00119                                    col-formula.col);
00120            if (formulaString == null) {
00121               formulaString = "$REFS$0";
00122               error = new ParserException("REFS");
00123               return;
00124            }
00125            // tokenize and convert the formula to postfix form
00126            LinkedList tokens = tokenize(formulaString);
00127            dependency = createDependency(tokens);
00128            postfix = toPostfix(convertParams(tokens));
00129        } catch (ParserException e) {
00130            System.err.println("Shouldn't happen!");
00131            throwError(e);
00132        }
00133     }
00134 
00135 
00136 
00149     public static boolean isSafe(Formula formula, int rowOff, int colOff) {
00150        // do necessary conversion so the formula string
00151        // is still right after changing the position
00152        String newString = fixRelAddr(formula.formulaString,
00153                                   rowOff, colOff);
00154        return newString != null;
00155     }
00156 
00162     public boolean isBad() {
00163        // postfix was set to null when there was any error
00164        // in processing the formula string
00165        return postfix == null;
00166     }
00167 
00173     public boolean needsRecalc() {
00174        return needsRecalc;
00175     }
00176     
00182     public void setNeedsRecalc(boolean needs) {
00183        needsRecalc = needs;
00184     }
00185 
00198     public static String fixRelAddr(String oldFormula,
00199                                 int rowOffset,
00200                                 int colOffset) {
00201        if (colOffset ==0 && rowOffset == 0)
00202            return oldFormula;
00203 
00204        StringBuffer newFormulaBuf = new StringBuffer();
00205        int lastPos = 0;
00206 
00207        
00208        for (int i = 0; i < oldFormula.length(); i++) {
00209            char c = oldFormula.charAt(i);
00210            int letterStart = i;
00211            // search for uppercase letters
00212            if (Character.isUpperCase(c)) {
00213               boolean isAbsAddr = (i>0 && oldFormula.charAt(i-1)=='$');
00214               StringBuffer colNameBuf = new StringBuffer();
00215               // collect all the letters
00216               while (i < oldFormula.length() &&
00217                      Character.isUpperCase(oldFormula.charAt(i))) {
00218                   colNameBuf.append(oldFormula.charAt(i++));
00219               }
00220 
00221               String colName = colNameBuf.toString();
00222               
00223               if (i == oldFormula.length())
00224                   break;
00225               
00226               // is it followed by digits?
00227               if (!isAbsAddr &&
00228                   Character.isDigit(oldFormula.charAt(i))) {
00229                   // hey, we get a REL_ADDR here
00230                   StringBuffer rowNameBuf = new StringBuffer();
00231                   while (i < oldFormula.length() &&
00232                      Character.isDigit(oldFormula.charAt(i))) {
00233                      rowNameBuf.append(oldFormula.charAt(i++));
00234                   }
00235 
00236                   String rowName = rowNameBuf.toString();
00237                   
00238                   // We've got colName and rowName
00239                   // add the string before the rel_addr first
00240                   newFormulaBuf.append(oldFormula.substring(lastPos,
00241                                                     letterStart));
00242                   // then add the new address (string -> number -> string)
00243                   if (colOffset == 0)
00244                      newFormulaBuf.append(colName);
00245                   else {
00246                      String col = translateColumn(translateColumn(colName)+
00247                                                colOffset);
00248                      if (col == null)
00249                          return null;
00250                      
00251                      newFormulaBuf.append(col);
00252                   }
00253 
00254                   if (rowOffset == 0)
00255                      newFormulaBuf.append(rowName);
00256                   else {
00257                      String row = translateRow(translateRow(rowName)+
00258                                             rowOffset);
00259                      if (row == null)
00260                          return null;
00261                      
00262                      newFormulaBuf.append(row);
00263                   }
00264                   
00265                   lastPos = i;
00266               }
00267            }
00268        }
00269 
00270        newFormulaBuf.append(oldFormula.substring(lastPos));
00271        return newFormulaBuf.toString();
00272     }
00273        
00274     
00283     private LinkedList tokenize(String input) throws ParserException {
00284        LinkedList tokens = new LinkedList();
00285        Stack stack = new Stack();
00286        final Node zero = new Node();
00287        zero.setType(Node.NUMBER);
00288        zero.setNumber(0);
00289        //     input.toUpperCase();
00290 
00291        int cur = 0;
00292        int lastType = Node.DEFAULT;
00293        Node lastToken = null;
00294        //     boolean hasRange = false; // has a pending address range
00295        int nParen = 0; // balance of parens
00296               
00297        while (cur < input.length()) {
00298            Node node = new Node();
00299            try {
00300               char c = input.charAt(cur++);
00301               node.setData(String.valueOf(c));
00302               if (Character.isLetter(c)) {
00303                   // Function or Relative Address
00304                   node.setType(Node.FUNCTION);
00305                   node.setParams(new LinkedList());
00306 
00307                   // get all preceding letters
00308                   while (cur < input.length() &&
00309                         Character.isLetter(input.charAt(cur)))
00310                      node.appendData(input.charAt(cur++)); 
00311               
00312                   if (Character.isDigit(input.charAt(cur))) {
00313                      node.setType(Node.REL_ADDR);
00314                      // {letters}{numbers} is a relative address
00315                      node.setCol(translateColumn(node.getData()) - col);
00316                      
00317                      node.setData("");
00318                          
00319                      while (cur < input.length() &&
00320                             Character.isDigit(input.charAt(cur)))
00321                          node.appendData(input.charAt(cur++));    
00322                      // relative row
00323                      node.setRow(translateRow(node.getData()) - row);
00324                      node.setData(null);
00325                   }
00326               }else if (Character.isDigit(c) || c == '.')
00327                   /*||
00328                       (lastType == Node.DEFAULT ||
00329                        lastType == Node.LPAREN || lastType == Node.COMMA) &&
00330                        (c == '+' || c == '-')) */{
00331                   // Numbers
00332                   while (cur < input.length() &&
00333                         (Character.isDigit(input.charAt(cur)) ||
00334                          input.charAt(cur) == '.'))
00335                      // OK, we don't check for input like "3.56.4"
00336                      // this will be checked below by parseNumber
00337                      node.appendData(input.charAt(cur++));
00338 
00339                   try {
00340                      try {
00341                          node.setNumber(Integer.parseInt(node.getData()));
00342                      }
00343                      catch (NumberFormatException e) {
00344                          node.setNumber(Float.parseFloat(node.getData()));
00345                      }
00346                      node.setType(Node.NUMBER);
00347                   }catch (NumberFormatException e) {
00348                      // invalid number format
00349                      throwError("#NUM?");
00350                   }
00351               }else if (c == '(') {
00352                   nParen++;
00353                   node.setType(Node.LPAREN);
00354               }else if (c == ')') {
00355                   nParen--;
00356                   node.setType(Node.RPAREN);
00357               }else if (c == ',') {
00358                   node.setType(Node.COMMA);
00359               }else if (c == ':') {
00360 
00361                   node.setPending(true);
00362                   node.setType(Node.COLON);
00363                   
00364                   Node prev = null;
00365 
00366                   try {
00367                      prev = (Node)tokens.removeLast();
00368                   }
00369                   catch (Exception e) {
00370                      throwError("#ADDR?");
00371                   };
00372 
00373                   if (prev.isType(Node.REL_ADDR) ||
00374                      prev.isType(Node.ABS_ADDR)) {
00375                      node.setNextRange(prev);
00376                   }
00377                   else
00378                      // invalid address format
00379                      throwError("#ADDR?");
00380                   
00381               }else if (c == '+' || c == '-' || c == '*' || c == '/' ||
00382                       c == '^' || c == '%') {
00383                   node.setType(Node.OPERATOR);
00384               }else if (c == '$') {
00385                   // Absolute Address starts with $
00386                   node.setType(Node.ABS_ADDR);
00387                   node.setData("");
00388                   // a letter must follow the $
00389                   if (! Character.isLetter(input.charAt(cur))) {
00390                      // invalid address format
00391                      throwError("#ADDR?");
00392                   }
00393                   // look for column
00394                   while (Character.isLetter(input.charAt(cur)))
00395                      node.appendData(input.charAt(cur++));
00396 
00397                   // absolute address has to be the form of
00398                   // ${letters}${numbers}
00399                   if (input.charAt(cur++) != '$' ||
00400                      ! Character.isDigit(input.charAt(cur))) {
00401                      // invalid address format
00402                      throwError("#ADDR?");
00403                   }
00404 
00405                   node.setCol(translateColumn(node.getData()));
00406                   node.setData("");
00407 
00408                   while (cur < input.length() &&
00409                         Character.isDigit(input.charAt(cur)))
00410                      node.appendData(input.charAt(cur++));
00411 
00412                   node.setRow(translateRow(node.getData()));
00413                   node.setData(null);
00414               }else if (c == ' ')
00415                   continue;
00416               else
00417                   // invalid char
00418                   throwError("#NAME?");
00419 
00420               // after a ADDR or NUMBER token the following char
00421               // should not be a letter or digit
00422               if (cur < input.length() && (node.isType(Node.REL_ADDR) ||
00423                                         node.isType(Node.ABS_ADDR) ||
00424                                         node.isType(Node.NUMBER)) &&
00425                   Character.isLetterOrDigit(input.charAt(cur))) {
00426                   throwError
00427                      // invalid char
00428                      ("#NAME?");
00429               }
00430 
00431               // process the second address of a cell range
00432               if (lastToken != null &&
00433                   lastToken.isType(Node.COLON) &&
00434                   lastToken.isPending()) {
00435                   if (node.isType(Node.REL_ADDR) ||
00436                      node.isType(Node.ABS_ADDR)) {
00437 
00438                      Node range = (Node)tokens.removeLast();
00439 
00440                      try {
00441                          ((Node)range.getNextRange()).setNextRange(node);
00442                          range.setPending(false);
00443                      }
00444                      catch (NullPointerException e) {
00445                          // invalid address format
00446                          throwError("#ADDR?");
00447                      }
00448 
00449                      node = range;
00450                   }
00451                   else
00452                      throwError("#ADDR?");
00453               }
00454 
00455 
00456               if (node.isType(Node.OPERATOR) &&
00457                   (node.getData().equals("+") ||
00458                    node.getData().equals("-")) &&
00459                   (lastToken == null || lastToken.isType(Node.LPAREN) ||
00460                    lastToken.isType(Node.COMMA))) {
00461                   tokens.add(zero);
00462               }
00463               
00464               tokens.add(node);
00465               lastType = node.getType();
00466               lastToken = node;           
00467                   
00468            }catch (IndexOutOfBoundsException e) {
00469               // error
00470               throwError("#NAME?");
00471            }catch (ParserException e) {
00472               throwError(e);
00473            }
00474            catch (Exception e) {
00475               Debug.println(e.toString());
00476            }
00477        }
00478 
00479        if (nParen != 0) // imbalanced parenthesis
00480            throwError("#PAREN?");
00481        return tokens;
00482     }
00483 
00512     private LinkedList convertParams(final LinkedList tokens)
00513        throws ParserException {
00514        
00515        if (tokens == null) {
00516            throw error;
00517        }
00518 
00519        LinkedList stack = new LinkedList();
00520 
00521        Iterator it = tokens.iterator();
00522 
00523        try {
00524            while (it.hasNext()) {
00525               Node node = (Node)it.next();
00526 
00527               if (node.isType(Node.FUNCTION)) {
00528                   node.setPending(true);
00529                   stack.add(node);
00530                   node = (Node)it.next();
00531                   // should be LParen
00532                   if (!node.isType(Node.LPAREN)) // ( expected
00533                      throwError("#NO(?");
00534               }             
00535               else if (node.isType(Node.LPAREN)) {
00536                   node.setPending(true);
00537                   stack.add(node);
00538               }             
00539               else if (node.isType(Node.COMMA)) {
00540                   Node exp = new Node();
00541                   LinkedList list = new LinkedList();
00542                   Node param = (Node)stack.removeLast();//pop();
00543                   // pop out until the unpending FUNCTION
00544                   while (!param.isType(Node.FUNCTION) ||
00545                         !param.isPending()) {
00546                      list.addFirst(param);
00547                      param = (Node)stack.removeLast();//pop();
00548                   }
00549 
00550                   exp.setType(Node.EXP);
00551                   exp.setExp(list);
00552 
00553                   param.addParam(exp);
00554 
00555                   // still pending
00556                   //            stack.push(param);
00557                   stack.add(param);
00558               }
00559               else if (node.isType(Node.RPAREN)) {
00560                   // we don't know whether this is for a function.
00561                   Node exp = new Node();
00562                   LinkedList list = new LinkedList();
00563                   Node param = (Node)stack.removeLast(); //stack.pop();
00564 
00565                   // process the last parameter
00566                   while (!param.isPending() ||
00567                         !param.isType(Node.FUNCTION) &&
00568                         !param.isType(Node.LPAREN)) {
00569                      list.addFirst(param);
00570                      param = (Node)stack.removeLast();//pop();
00571                   }
00572 
00573                   // set to unpending
00574                   if (param.isType(Node.LPAREN)) {
00575                      // this is a normal left paren
00576                      param.setPending(false);
00577                      // push back
00578                      stack.add(param);                  
00579                      stack.addAll(list);
00580                      stack.add(node);
00581                   }
00582                   else {
00583                      // this is a function left paren
00584                      //                   Debug.println("exp is "+list);
00585                      // set the expression of that parameter
00586                      exp.setType(Node.EXP);
00587                      exp.setExp(list);
00588                      // add a parameter for the function
00589                      param.addParam(exp);
00590                      param.setPending(false);
00591                      stack.add(param);
00592                   }
00593               }
00594               else
00595                   stack.add(node); //push(node);
00596                   
00597            }
00598               
00599        }
00600        catch (ParserException e) {
00601            throw e;
00602        }
00603        catch (Exception e) {
00604            Debug.println(e);
00605            // general param error
00606            throwError("#PARAM?");
00607        }
00608 
00609        return stack;
00610     }
00611     
00632     private LinkedList toPostfix(LinkedList tokens) throws ParserException {
00633        if (tokens == null) {
00634            throw error;
00635        }
00636 
00637        // stack is used for the conversion
00638        Stack stack = new Stack();
00639        LinkedList postfix = new LinkedList();
00640        Iterator it = tokens.iterator();
00641        while (it.hasNext()) {
00642            Node node = (Node)it.next();
00643            switch (node.getType()) {
00644 
00645            case Node.NUMBER:
00646            case Node.REL_ADDR:
00647            case Node.ABS_ADDR:
00648            case Node.COLON:
00649               // just add normal values to the list
00650               postfix.add(node);
00651               break;
00652               
00653            case Node.LPAREN:
00654               // push to stack; pop out when a RPAREN is encountered
00655               stack.push(node);
00656               break;
00657            case Node.OPERATOR:
00658               // get the precedence priority of the operator
00659               int priority = getPriority(node);
00660 
00661               // pop up operators with the same or higher priority from
00662               // the stack
00663               while (! stack.empty() &&
00664                      ! ((Node)stack.peek()).isType(Node.LPAREN) &&
00665                      getPriority((Node)stack.peek()) >= priority) {
00666                   postfix.add((Node)stack.pop());
00667               }
00668               stack.push(node);
00669               break;        
00670            case Node.RPAREN:
00671               try {
00672                   Node op = (Node)stack.pop();
00673                   // pop out until the last LPAREN
00674                   while (! op.isType(Node.LPAREN)) {
00675                      postfix.add(op);
00676                      op = (Node)stack.pop();
00677                   }
00678               }
00679               catch (EmptyStackException e) {
00680                   // should not happen - imbalance in parenthesis
00681                   throwError("#PAREN?");
00682               }
00683               break;
00684            case Node.FUNCTION:
00685 
00686               // get the param list
00687               LinkedList params = node.getParams();
00688 
00689               Iterator paramIter = params.iterator();
00690 
00691               while (paramIter.hasNext()) {
00692                   Node exp = (Node)paramIter.next();
00693                   exp.setExp(toPostfix(exp.getExp()));
00694               }
00695 
00696               postfix.add(node);
00697               
00698               break;
00699               
00700            default:
00701               // unknown error - should not happen
00702               throwError("#ERROR?");
00703            }  
00704        }
00705 
00706        // pop up the rest nodes
00707        while (!stack.empty())
00708            postfix.add((Node)stack.pop());
00709 
00710        return postfix;
00711     }
00712     
00722     private TreeSet createDependency(LinkedList tokens) {
00723        TreeSet dependency = new TreeSet();
00724        
00725        Iterator it = tokens.iterator();
00726        while (it.hasNext()) {
00727            Node node = (Node)it.next();
00728            if (node.isType(Node.REL_ADDR) || node.isType(Node.ABS_ADDR)) {
00729               // for addresses, translate into CellPoint (absolute point)
00730               CellPoint newCell = node.toCellPoint(row, col);
00731               dependency.add(newCell);
00732            } else if (node.isType(Node.COLON)) {
00733               // all the cells in this range are referenced
00734               CellPoint[] addr = node.getAddressRange(row, col);
00735               for (int i = addr[0].getRow(); i <= addr[1].getRow(); i++)
00736                   for (int j = addr[0].getCol(); j <= addr[1].getCol();
00737                       j++)
00738                      dependency.add(new CellPoint(i, j));
00739            }
00740        }
00741        return dependency;
00742     }
00743     
00750     public TreeSet getDependency() {
00751        if (isBad()) {
00752            //     Debug.println("Bad formula: "+formulaString);
00753            return new TreeSet();
00754        }
00755 
00756        return dependency;
00757     }
00758 
00765     private static int getPriority(char op) {
00766        switch (op) {
00767 
00768        case '+':
00769        case '-':
00770            return 1;
00771        case '*':
00772        case '/':
00773        case '%':
00774            return 2;
00775        case '^':
00776            return 3;
00777        default:
00778            return 0;
00779        }
00780     }
00781 
00785     private static int getPriority(Node node) {
00786        return getPriority(node.getData().charAt(0));
00787     }
00788 
00794     public String toString() {
00795        return formulaString;
00796     }
00797 
00806     private static Number calc(char op, Number op1, Number op2) {
00807        float n1 = op1.floatValue();
00808        float n2 = op2.floatValue();
00809        float result;
00810        switch (op) {
00811            case '+': result = n1+n2; break;
00812            case '-': result = n1-n2; break;
00813            case '*': result = n1*n2; break;
00814            case '/': result = n1/n2; break;
00815            case '^': result = (float)Math.pow(n1, n2); break;
00816            case '%': result = (float)((int)n1%(int)n2); break;
00817            default: result = 0; break;
00818        }
00819 
00820        return new Float(result);
00821     }
00822 
00831     static private Number evalFunction(SharpTableModel table, Node node, int row, int col) throws ParserException {
00832        String funcName = node.getData();
00833        // get function handler from the funcTable
00834        Function func=getFuncHandler(funcName);
00835 
00836        if (func == null) {
00837            // not registered function
00838            throw new ParserException("#FUNC?");
00839            
00840        }
00841        else
00842        {
00843            return func.evaluate(table, node, row, col);
00844        }    
00845     }
00846     
00856     public static Number evaluate(SharpTableModel table, int row, int col)
00857        throws ParserException {
00858 
00859        if (Debug.isDebug())
00860            Debug.println("recalculating "+new CellPoint(row, col));
00861 
00862        
00863        // get the formula object
00864        
00865        Formula formula = table.getCellAt(row, col).getFormula();
00866        formula.setNeedsRecalc(false);
00867        
00868        if (formula == null)     
00869            return new Integer(0);      
00870        else
00871            return formula.evaluate(table);
00872        
00873     }
00874 
00883     private Number evaluate(SharpTableModel table) throws ParserException {
00884        
00885        // if the formula is bad, directly returns the error
00886        if (isBad()) {
00887            throw error;
00888        }
00889        return evaluate(table, postfix, row, col);
00890     }
00891     
00902     static public Number evaluate(SharpTableModel table, LinkedList postfix,
00903                              int row, int col)
00904        throws ParserException {
00905 
00906        try {
00907            Stack stack = new Stack();
00908            Iterator it = postfix.iterator();
00909            while (it.hasNext()) {
00910               Node node = (Node)it.next();
00911               //Number result;
00912               Number result;
00913                      
00914               switch (node.getType()) {
00915               case Node.OPERATOR:
00916                   // pop the 2 operands from stack top and save the result
00917                   // back to stack
00918                   Number n2 = (Number)stack.pop();
00919                   Number n1 = (Number)stack.pop();
00920                   result = calc(node.getData().charAt(0), n1, n2);
00921                   break;
00922               case Node.FUNCTION:
00923 //                // evaluate the function
00924                   result = evalFunction(table, node, row, col);
00925                   break;
00926               case Node.NUMBER:
00927                   // directly return the number
00928                   result = new Float(node.getNumber());
00929                   break;
00930               case Node.ABS_ADDR:
00931                   // get the numeric value of that cell
00932                   result = //getNumericValueAt(table, node.getRow(),
00933                             //            node.getCol());
00934                      table.getNumericValueAt(node.getRow(), node.getCol());
00935                   break;
00936               case Node.REL_ADDR:
00937                   // get the numeric value of that cell            
00938                   result = //getNumericValueAt(table, node.getRow()+row,
00939                             //            node.getCol()+col);
00940                      table.getNumericValueAt(node.getRow()+row,
00941                                           node.getCol()+col);
00942                   break;
00943               default:
00944                   // evaluation error
00945                   throw new ParserException("#EVAL?");
00946               }
00947 
00948               // push to the stack
00949               stack.push(result);
00950            }
00951            
00952            Number result = (Number)stack.pop();
00953            return result;
00954        }catch (EmptyStackException e) {
00955            // imbalance between operands and operators
00956            throw new ParserException("#OP?");
00957        
00958            // ("Wrong format of formula: too many operators");
00959        }catch (ParserException e) {
00960            throw e;
00961        }catch (Exception e) {
00962            Debug.println(e);
00963 
00964        }
00965 
00966        return new Integer(0);
00967     }
00968     
00969     // The following are just simple functions
00970     
00978     final private static int translateRow(String row) {
00979        return Node.translateRow(row);
00980     }
00981 
00988     final private static String translateRow(int row) {
00989        return Node.translateRow(row);
00990     }
00991 
00998     final private static int translateColumn(String column) {
00999        return Node.translateColumn(column);
01000     }
01001 
01008     final private static String translateColumn(int column) {
01009        return Node.translateColumn(column);
01010     }
01011 
01020     private void throwError(Object s) throws ParserException {
01021        // test code
01022        //     System.err.println("Marking formula "+formulaString+" as bad");
01023        postfix = null;
01024        if (error instanceof ParserException)
01025            throw (ParserException)s;
01026        else {
01027            error = new ParserException(s);
01028            throw error;
01029        }
01030     }
01031 
01037     private String getCellString() {
01038        return getCellString(row, col);
01039     }
01040     
01048     final static private String getCellString(int row, int col) {
01049        return ""+translateColumn(col)+translateRow(row);
01050     }
01051 
01052     final static public CellPoint parseAddress(String s) {
01053 
01054        try {
01055        
01056            int row, col;
01057 
01058            s = s.toUpperCase();
01059            int len = 0;
01060            int total = s.length();
01061            
01062            StringBuffer buf = new StringBuffer();
01063            char c;
01064            
01065            while (len < total) {
01066               c = s.charAt(len);
01067               if (Character.isUpperCase(c)) {
01068                   buf.append(c);
01069                   len++;
01070               }
01071               else if (Character.isDigit(c))
01072                   break;
01073               else
01074                   return null;            
01075            }
01076            
01077            col = translateColumn(buf.toString());
01078            
01079            if (col == 0)
01080               return null;
01081            
01082            buf = new StringBuffer();
01083            
01084            while (len < total) {
01085               c = s.charAt(len);
01086               if (Character.isDigit(c)) {
01087                   buf.append(c);
01088                   len++;
01089               }
01090               else
01091                   return null;
01092            }
01093            
01094            row = translateRow(buf.toString());
01095            if (row == 0)
01096               return null;
01097            
01098            return new CellPoint(row, col);
01099            
01100        }
01101        catch (Exception e) {
01102            return null;
01103        }
01104     }
01105 
01113     static private void register(String funcName, Function func) {
01114        funcTable.put(funcName, func);
01115     }
01116     
01121     static public void registerFunctions() {
01122        funcTable = new HashMap();
01123        register("SUM", new FunctionSum());
01124        register("MEAN", new FunctionAverage());
01125        register("AVERAGE", new FunctionAverage());
01126        register("MEDIAN", new FunctionMedian());
01127        register("ABS", new FunctionAbs());
01128        register("INT", new FunctionInt());
01129        register("ROUND", new FunctionRound());
01130        register("SIN", new FunctionSin());
01131        register("COS", new FunctionCos());
01132        register("TAN", new FunctionTan());
01133        register("ASIN", new FunctionAsin());
01134        register("ACOS", new FunctionAcos());
01135        register("ATAN", new FunctionAtan());
01136        register("SQRT", new FunctionSqrt());
01137        register("LOG", new FunctionLog());
01138        register("MIN", new FunctionMin());
01139        register("MAX", new FunctionMax());
01140        register("RANGE", new FunctionRange());
01141        register("STDDEV", new FunctionStddev());
01142        register("MEANDEV", new FunctionMeandev());
01143        register("COUNT", new FunctionCount());   
01144        register("PI", new FunctionPI());
01145        register("E", new FunctionE());
01146     }
01147 
01148     /*
01149      * provide a way to access these function handlers
01150      *
01151      * @param fname the function name
01152      * @return the function object that can evaluate the specified function.
01153      *
01154      * @see Funciton
01155      * @see SharpTools
01156      */
01157     static public Function getFuncHandler(String fname) {
01158         //jm.evers : needed by applet
01159        registerFunctions();
01160        return (Function)funcTable.get(fname);
01161     }
01162 }