1 2 17 18 19 20 package org.apache.poi.hssf.model; 21 22 import java.util.ArrayList ; 23 import java.util.Iterator ; 24 import java.util.LinkedList ; 25 import java.util.List ; 26 27 import org.apache.poi.hssf.record.formula.*; 29 30 31 32 48 public class FormulaParser { 49 50 public static int FORMULA_TYPE_CELL = 0; 51 public static int FORMULA_TYPE_SHARED = 1; 52 public static int FORMULA_TYPE_ARRAY =2; 53 public static int FORMULA_TYPE_CONDFOMRAT = 3; 54 public static int FORMULA_TYPE_NAMEDRANGE = 4; 55 56 private String formulaString; 57 private int pointer=0; 58 private int formulaLength; 59 60 private List tokens = new java.util.Stack (); 61 62 65 private List functionTokens = new LinkedList (); 66 67 private List result = new ArrayList (); 69 private int numParen; 70 71 private static char TAB = '\t'; 72 private static char CR = '\n'; 73 74 private char look; private boolean inFunction = false; 76 77 private Workbook book; 78 79 80 85 public FormulaParser(String formula, Workbook book){ 86 formulaString = formula; 87 pointer=0; 88 this.book = book; 89 formulaLength = formulaString.length(); 90 } 91 92 93 94 private void GetChar() { 95 if (pointer == formulaLength) { 99 look = (char)0; 100 return; 101 } 102 look=formulaString.charAt(pointer++); 103 } 105 106 107 108 private void Error(String s) { 109 System.out.println("Error: "+s); 110 } 111 112 113 114 115 private void Abort(String s) { 116 Error(s); 117 throw new RuntimeException ("Cannot Parse, sorry : "+s); 119 } 120 121 122 123 124 private void Expected(String s) { 125 Abort(s + " Expected"); 126 } 127 128 129 130 131 private boolean IsAlpha(char c) { 132 return Character.isLetter(c) || c == '$'; 133 } 134 135 136 137 138 private boolean IsDigit(char c) { 139 return Character.isDigit(c); 141 } 142 143 144 145 146 private boolean IsAlNum(char c) { 147 return (IsAlpha(c) || IsDigit(c)); 148 } 149 150 151 152 153 private boolean IsAddop( char c) { 154 return (c =='+' || c =='-'); 155 } 156 157 158 159 private boolean IsWhite( char c) { 160 return (c ==' ' || c== TAB); 161 } 162 163 168 private boolean IsSpecialChar(char c) { 169 return (c == '>' || c== '<' || c== '=' || c=='&' || c=='[' || c==']'); 170 } 171 172 173 174 private void SkipWhite() { 175 while (IsWhite(look)) { 176 GetChar(); 177 } 178 } 179 180 181 182 183 private void Match(char x) { 184 if (look != x) { 185 Expected("" + x + ""); 186 }else { 187 GetChar(); 188 SkipWhite(); 189 } 190 } 191 192 193 private String GetName() { 194 StringBuffer Token = new StringBuffer (); 195 if (!IsAlpha(look) && look != '\'') { 196 Expected("Name"); 197 } 198 if(look == '\'') 199 { 200 Match('\''); 201 boolean done = look == '\''; 202 while(!done) 203 { 204 Token.append(Character.toUpperCase(look)); 205 GetChar(); 206 if(look == '\'') 207 { 208 Match('\''); 209 done = look != '\''; 210 } 211 } 212 } 213 else 214 { 215 while (IsAlNum(look)) { 216 Token.append(Character.toUpperCase(look)); 217 GetChar(); 218 } 219 } 220 SkipWhite(); 221 return Token.toString(); 222 } 223 224 226 private String GetNameAsIs() { 227 StringBuffer Token = new StringBuffer (); 228 229 while (IsAlNum(look) || IsWhite(look) || IsSpecialChar(look)) { 230 Token = Token.append(look); 231 GetChar(); 232 } 233 return Token.toString(); 234 } 235 236 237 238 private String GetNum() { 239 String Value =""; 240 if (!IsDigit(look)) Expected("Integer"); 241 while (IsDigit(look)){ 242 Value = Value + look; 243 GetChar(); 244 } 245 SkipWhite(); 246 return Value; 247 } 248 249 250 private void Emit(String s){ 251 System.out.print(TAB+s); 252 } 253 254 255 private void EmitLn(String s) { 256 Emit(s); 257 System.out.println();; 258 } 259 260 261 private void Ident() { 262 String name; 263 name = GetName(); 264 if (look == '('){ 265 function(name); 267 } else if (look == ':') { String first = name; 269 Match(':'); 270 String second = GetName(); 271 tokens.add(new AreaPtg(first+":"+second)); 272 } else if (look == '!') { 273 Match('!'); 274 String sheetName = name; 275 String first = GetName(); 276 short externIdx = book.checkExternSheet(book.getSheetIndex(sheetName)); 277 if (look == ':') { 278 Match(':'); 279 String second=GetName(); 280 281 tokens.add(new Area3DPtg(first+":"+second,externIdx)); 282 } else { 283 tokens.add(new Ref3DPtg(first,externIdx)); 284 } 285 } else { 286 boolean cellRef = true ; boolean boolLit = (name.equals("TRUE") || name.equals("FALSE")); 289 if (boolLit) { 290 tokens.add(new BoolPtg(name)); 291 } else if (cellRef) { 292 tokens.add(new ReferencePtg(name)); 293 }else { 294 } 296 } 297 } 298 299 303 private void addArgumentPointer() { 304 if (this.functionTokens.size() > 0) { 305 List arguments = (List )this.functionTokens.get(0); 307 arguments.add(tokens.get(tokens.size()-1)); 308 } 309 } 310 311 private void function(String name) { 312 this.functionTokens.add(0, new ArrayList (2)); 314 315 Match('('); 316 int numArgs = Arguments(); 317 Match(')'); 318 319 AbstractFunctionPtg functionPtg = getFunction(name,(byte)numArgs); 320 321 tokens.add(functionPtg); 322 323 if (functionPtg.getName().equals("externalflag")) { 324 tokens.add(new NamePtg(name, this.book)); 325 } 326 327 this.functionTokens.remove(0); 329 } 330 331 338 private int getPtgSize(int index) { 339 int count = 0; 340 341 Iterator ptgIterator = tokens.listIterator(index); 342 while (ptgIterator.hasNext()) { 343 Ptg ptg = (Ptg)ptgIterator.next(); 344 count+=ptg.getSize(); 345 } 346 347 return count; 348 } 349 350 private int getPtgSize(int start, int end) { 351 int count = 0; 352 int index = start; 353 Iterator ptgIterator = tokens.listIterator(index); 354 while (ptgIterator.hasNext() && index <= end) { 355 Ptg ptg = (Ptg)ptgIterator.next(); 356 count+=ptg.getSize(); 357 index++; 358 } 359 360 return count; 361 } 362 370 private AbstractFunctionPtg getFunction(String name, byte numArgs) { 371 AbstractFunctionPtg retval = null; 372 373 if (name.equals("IF")) { 374 retval = new FuncVarPtg(AbstractFunctionPtg.ATTR_NAME, numArgs); 375 376 List argumentPointers = (List )this.functionTokens.get(0); 378 379 380 AttrPtg ifPtg = new AttrPtg(); 381 ifPtg.setData((short)7); ifPtg.setOptimizedIf(true); 383 384 if (argumentPointers.size() != 2 && argumentPointers.size() != 3) { 385 throw new IllegalArgumentException ("["+argumentPointers.size()+"] Arguments Found - An IF formula requires 2 or 3 arguments. IF(CONDITION, TRUE_VALUE, FALSE_VALUE [OPTIONAL]"); 386 } 387 388 int ifIndex = tokens.indexOf(argumentPointers.get(0))+1; 392 tokens.add(ifIndex, ifPtg); 393 394 397 int gotoIndex = tokens.indexOf(argumentPointers.get(1))+1; 398 399 AttrPtg goto1Ptg = new AttrPtg(); 400 goto1Ptg.setGoto(true); 401 402 403 tokens.add(gotoIndex, goto1Ptg); 404 405 406 if (numArgs > 2) { 408 AttrPtg goto2Ptg = new AttrPtg(); 410 goto2Ptg.setGoto(true); 411 goto2Ptg.setData((short)(retval.getSize()-1)); 412 415 tokens.add(goto2Ptg); } 417 418 ifPtg.setData((short)(getPtgSize(ifIndex+1, gotoIndex))); 422 423 int ptgCount = this.getPtgSize(gotoIndex)-goto1Ptg.getSize()+retval.getSize(); 425 if (ptgCount > (int)Short.MAX_VALUE) { 426 throw new RuntimeException ("Ptg Size exceeds short when being specified for a goto ptg in an if"); 427 } 428 429 goto1Ptg.setData((short)(ptgCount-1)); 430 431 } else { 432 433 retval = new FuncVarPtg(name,numArgs); 434 } 435 436 return retval; 437 } 438 439 440 private int Arguments() { 441 int numArgs = 0; 442 if (look != ')') { 443 numArgs++; 444 Expression(); 445 addArgumentPointer(); 446 } 447 while (look == ',' || look == ';') { if(look == ',') { 449 Match(','); 450 } 451 else { 452 Match(';'); 453 } 454 Expression(); 455 addArgumentPointer(); 456 numArgs++; 457 } 458 return numArgs; 459 } 460 461 462 private void Factor() { 463 if (look == '-') 464 { 465 Match('-'); 466 Factor(); 467 tokens.add(new UnaryMinusPtg()); 468 } 469 else if (look == '(' ) { 470 Match('('); 471 Expression(); 472 Match(')'); 473 tokens.add(new ParenthesisPtg()); 474 } else if (IsAlpha(look) || look == '\''){ 475 Ident(); 476 } else if(look == '"') { 477 StringLiteral(); 478 } else { 479 480 String number = GetNum(); 481 if (look=='.') { 482 Match('.'); 483 String decimalPart = null; 484 if (IsDigit(look)) number = number +"."+ GetNum(); tokens.add(new NumberPtg(number)); 486 } else { 487 tokens.add(new IntPtg(number)); } 489 } 490 } 491 492 private void StringLiteral() 493 { 494 if (look != '"') 499 Expected("\""); 500 else 501 { 502 GetChar(); 503 StringBuffer Token = new StringBuffer (); 504 for (;;) 505 { 506 if (look == '"') 507 { 508 GetChar(); 509 SkipWhite(); if (look == '"') 511 Token.append("\""); 512 else 513 break; 514 } 515 else if (look == 0) 516 { 517 break; 518 } 519 else 520 { 521 Token.append(look); 522 GetChar(); 523 } 524 } 525 tokens.add(new StringPtg(Token.toString())); 526 } 527 } 528 529 530 private void Multiply(){ 531 Match('*'); 532 Factor(); 533 tokens.add(new MultiplyPtg()); 534 535 } 536 537 538 539 private void Divide() { 540 Match('/'); 541 Factor(); 542 tokens.add(new DividePtg()); 543 544 } 545 546 547 548 private void Term(){ 549 Factor(); 550 while (look == '*' || look == '/' || look == '^' || look == '&') { 551 552 if (look == '*') Multiply(); 554 else if (look == '/') Divide(); 555 else if (look == '^') Power(); 556 else if (look == '&') Concat(); 557 } 558 } 559 560 561 private void Add() { 562 Match('+'); 563 Term(); 564 tokens.add(new AddPtg()); 565 } 566 567 568 private void Concat() { 569 Match('&'); 570 Term(); 571 tokens.add(new ConcatPtg()); 572 } 573 574 575 private void Equal() { 576 Match('='); 577 Expression(); 578 tokens.add(new EqualPtg()); 579 } 580 581 582 private void Subtract() { 583 Match('-'); 584 Term(); 585 tokens.add(new SubtractPtg()); 586 } 587 588 private void Power() { 589 Match('^'); 590 Term(); 591 tokens.add(new PowerPtg()); 592 } 593 594 595 596 private void Expression() { 597 Term(); 598 while (IsAddop(look)) { 599 if (look == '+' ) Add(); 600 else if (look == '-') Subtract(); 601 } 602 603 606 607 if(look == '=' || look == '>' || look == '<') { 608 if (look == '=') Equal(); 609 else if (look == '>') GreaterThan(); 610 else if (look == '<') LessThan(); 611 return; 612 } 613 614 615 } 616 617 618 private void GreaterThan() { 619 Match('>'); 620 if(look == '=') 621 GreaterEqual(); 622 else { 623 Expression(); 624 tokens.add(new GreaterThanPtg()); 625 } 626 } 627 628 629 private void LessThan() { 630 Match('<'); 631 if(look == '=') 632 LessEqual(); 633 else if(look == '>') 634 NotEqual(); 635 else { 636 Expression(); 637 tokens.add(new LessThanPtg()); 638 } 639 640 } 641 642 646 private void GreaterEqual() { 647 Match('='); 648 Expression(); 649 tokens.add(new GreaterEqualPtg()); 650 } 651 652 656 657 private void LessEqual() { 658 Match('='); 659 Expression(); 660 tokens.add(new LessEqualPtg()); 661 } 662 663 667 668 private void NotEqual() { 669 Match('>'); 670 Expression(); 671 tokens.add(new NotEqualPtg()); 672 } 673 674 686 687 688 689 690 private void init() { 691 GetChar(); 692 SkipWhite(); 693 } 694 695 698 public void parse() { 699 synchronized (tokens) { 700 init(); 701 Expression(); 702 } 703 } 704 705 706 710 711 714 public Ptg[] getRPNPtg() { 715 return getRPNPtg(FORMULA_TYPE_CELL); 716 } 717 718 public Ptg[] getRPNPtg(int formulaType) { 719 Node node = createTree(); 720 setRootLevelRVA(node, formulaType); 721 setParameterRVA(node,formulaType); 722 return (Ptg[]) tokens.toArray(new Ptg[0]); 723 } 724 725 private void setRootLevelRVA(Node n, int formulaType) { 726 Ptg p = (Ptg) n.getValue(); 728 if (formulaType == FormulaParser.FORMULA_TYPE_NAMEDRANGE) { 729 if (p.getDefaultOperandClass() == Ptg.CLASS_REF) { 730 setClass(n,Ptg.CLASS_REF); 731 } else { 732 setClass(n,Ptg.CLASS_ARRAY); 733 } 734 } else { 735 setClass(n,Ptg.CLASS_VALUE); 736 } 737 738 } 739 740 private void setParameterRVA(Node n, int formulaType) { 741 Ptg p = n.getValue(); 742 int numOperands = n.getNumChildren(); 743 if (p instanceof AbstractFunctionPtg) { 744 for (int i =0;i<numOperands;i++) { 745 setParameterRVA(n.getChild(i),((AbstractFunctionPtg)p).getParameterClass(i),formulaType); 746 setParameterRVA(n.getChild(i),formulaType); 750 } 751 } else { 752 for (int i =0;i<numOperands;i++) { 753 setParameterRVA(n.getChild(i),formulaType); 754 } 755 } 756 } 757 private void setParameterRVA(Node n, int expectedClass,int formulaType) { 758 Ptg p = (Ptg) n.getValue(); 759 if (expectedClass == Ptg.CLASS_REF) { if (p.getDefaultOperandClass() == Ptg.CLASS_REF ) { 761 setClass(n, Ptg.CLASS_REF); 762 } 763 if (p.getDefaultOperandClass() == Ptg.CLASS_VALUE) { 764 if (formulaType==FORMULA_TYPE_CELL || formulaType == FORMULA_TYPE_SHARED) { 765 setClass(n,Ptg.CLASS_VALUE); 766 } else { 767 setClass(n,Ptg.CLASS_ARRAY); 768 } 769 } 770 if (p.getDefaultOperandClass() == Ptg.CLASS_ARRAY ) { 771 setClass(n, Ptg.CLASS_ARRAY); 772 } 773 } else if (expectedClass == Ptg.CLASS_VALUE) { if (formulaType == FORMULA_TYPE_NAMEDRANGE) { 775 setClass(n,Ptg.CLASS_ARRAY) ; 776 } else { 777 setClass(n,Ptg.CLASS_VALUE); 778 } 779 } else { if (p.getDefaultOperandClass() == Ptg.CLASS_VALUE && 781 (formulaType==FORMULA_TYPE_CELL || formulaType == FORMULA_TYPE_SHARED)) { 782 setClass(n,Ptg.CLASS_VALUE); 783 } else { 784 setClass(n,Ptg.CLASS_ARRAY); 785 } 786 } 787 } 788 789 private void setClass(Node n, byte theClass) { 790 Ptg p = (Ptg) n.getValue(); 791 if (p instanceof AbstractFunctionPtg || !(p instanceof OperationPtg)) { 792 p.setClass(theClass); 793 } else { 794 for (int i =0;i<n.getNumChildren();i++) { 795 setClass(n.getChild(i),theClass); 796 } 797 } 798 } 799 806 public static String toFormulaString(Workbook book, List lptgs) { 807 String retval = null; 808 if (lptgs == null || lptgs.size() == 0) return "#NAME"; 809 Ptg[] ptgs = new Ptg[lptgs.size()]; 810 ptgs = (Ptg[])lptgs.toArray(ptgs); 811 retval = toFormulaString(book, ptgs); 812 return retval; 813 } 814 815 822 public static String toFormulaString(Workbook book, Ptg[] ptgs) { 823 if (ptgs == null || ptgs.length == 0) return "#NAME"; 824 java.util.Stack stack = new java.util.Stack (); 825 AttrPtg ifptg = null; 826 827 stack.push(ptgs[0].toFormulaString(book)); 830 831 for (int i = 1; i < ptgs.length; i++) { 832 if (! (ptgs[i] instanceof OperationPtg)) { 833 stack.push(ptgs[i].toFormulaString(book)); 834 continue; 835 } 836 837 if (ptgs[i] instanceof AttrPtg && ((AttrPtg) ptgs[i]).isOptimizedIf()) { 838 ifptg = (AttrPtg) ptgs[i]; 839 continue; 840 } 841 842 final OperationPtg o = (OperationPtg) ptgs[i]; 843 final String [] operands = new String [o.getNumberOfOperands()]; 844 845 for (int j = operands.length; j > 0; j--) { 846 operands[j - 1] = (String ) stack.pop(); 848 } 849 850 stack.push(o.toFormulaString(operands)); 851 if (!(o instanceof AbstractFunctionPtg)) continue; 852 853 final AbstractFunctionPtg f = (AbstractFunctionPtg) o; 854 final String fname = f.getName(); 855 if (fname == null) continue; 856 857 if ((ifptg != null) && (fname.equals("specialflag"))) { 858 stack.push(ifptg.toFormulaString(new String []{(String ) stack.pop()})); 860 continue; 861 } 862 if (fname.equals("externalflag")) { 863 final String top = (String ) stack.pop(); 864 final int paren = top.indexOf('('); 865 final int comma = top.indexOf(','); 866 if (comma == -1) { 867 final int rparen = top.indexOf(')'); 868 stack.push(top.substring(paren + 1, rparen) + "()"); 869 } 870 else { 871 stack.push(top.substring(paren + 1, comma) + '(' + 872 top.substring(comma + 1)); 873 } 874 } 875 } 876 return (String ) stack.pop(); 878 } 879 880 881 884 private Node createTree() { 885 java.util.Stack stack = new java.util.Stack (); 886 int numPtgs = tokens.size(); 887 OperationPtg o; 888 int numOperands; 889 Node[] operands; 890 for (int i=0;i<numPtgs;i++) { 891 if (tokens.get(i) instanceof OperationPtg) { 892 893 o = (OperationPtg) tokens.get(i); 894 numOperands = o.getNumberOfOperands(); 895 operands = new Node[numOperands]; 896 for (int j=0;j<numOperands;j++) { 897 operands[numOperands-j-1] = (Node) stack.pop(); 898 } 899 Node result = new Node(o); 900 result.setChildren(operands); 901 stack.push(result); 902 } else { 903 stack.push(new Node((Ptg)tokens.get(i))); 904 } 905 } 906 return (Node) stack.pop(); 907 } 908 909 912 public String toString() { 913 StringBuffer buf = new StringBuffer (); 914 for (int i=0;i<tokens.size();i++) { 915 buf.append( ( (Ptg)tokens.get(i)).toFormulaString(book)); 916 buf.append(' '); 917 } 918 return buf.toString(); 919 } 920 921 } 922 923 class Node { 924 private Ptg value=null; 925 private Node[] children=new Node[0]; 926 private int numChild=0; 927 public Node(Ptg val) { 928 value = val; 929 } 930 public void setChildren(Node[] child) {children = child;numChild=child.length;} 931 public int getNumChildren() {return numChild;} 932 public Node getChild(int number) {return children[number];} 933 public Ptg getValue() {return value;} 934 } 935 | Popular Tags |