📄 formulaparser.java
字号:
/* ==================================================================== Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.==================================================================== */ package org.apache.poi.hssf.model;import java.util.ArrayList;import java.util.Iterator;import java.util.LinkedList;import java.util.List;import java.util.regex.Pattern;//import PTG's .. since we need everything, import *import org.apache.poi.hssf.record.formula.*;/** * This class parses a formula string into a List of tokens in RPN order. * Inspired by * Lets Build a Compiler, by Jack Crenshaw * BNF for the formula expression is : * <expression> ::= <term> [<addop> <term>]* * <term> ::= <factor> [ <mulop> <factor> ]* * <factor> ::= <number> | (<expression>) | <cellRef> | <function> * <function> ::= <functionName> ([expression [, expression]*]) * * @author Avik Sengupta <avik at apache dot org> * @author Andrew C. oliver (acoliver at apache dot org) * @author Eric Ladner (eladner at goldinc dot com) * @author Cameron Riley (criley at ekmail.com) * @author Peter M. Murray (pete at quantrix dot com) * @author Pavel Krupets (pkrupets at palmtreebusiness dot com) */public class FormulaParser { public static int FORMULA_TYPE_CELL = 0; public static int FORMULA_TYPE_SHARED = 1; public static int FORMULA_TYPE_ARRAY =2; public static int FORMULA_TYPE_CONDFOMRAT = 3; public static int FORMULA_TYPE_NAMEDRANGE = 4; private String formulaString; private int pointer=0; private int formulaLength; private List tokens = new java.util.Stack(); /** * Using an unsynchronized linkedlist to implement a stack since we're not multi-threaded. */ private List functionTokens = new LinkedList(); /** * Used for spotting if we have a cell reference, * or a named range */ private final static Pattern CELL_REFERENCE_PATTERN = Pattern.compile("(?:('?)[^:\\\\/\\?\\*\\[\\]]+\\1!)?\\$?[A-Za-z]+\\$?[\\d]+"); private static char TAB = '\t'; private static char CR = '\n'; private char look; // Lookahead Character private Workbook book; /** * Create the formula parser, with the string that is to be * parsed against the supplied workbook. * A later call the parse() method to return ptg list in * rpn order, then call the getRPNPtg() to retrive the * parse results. * This class is recommended only for single threaded use. * * If you only have a usermodel.HSSFWorkbook, and not a * model.Workbook, then use the convenience method on * usermodel.HSSFFormulaEvaluator */ public FormulaParser(String formula, Workbook book){ formulaString = formula; pointer=0; this.book = book; formulaLength = formulaString.length(); } /** Read New Character From Input Stream */ private void GetChar() { // Check to see if we've walked off the end of the string. // Just return if so and reset Look to smoething to keep // SkipWhitespace from spinning if (pointer == formulaLength) { look = (char)0; return; } look=formulaString.charAt(pointer++); //System.out.println("Got char: "+ look); } /** Report an Error */ private void Error(String s) { System.out.println("Error: "+s); } /** Report Error and Halt */ private void Abort(String s) { Error(s); //System.exit(1); //throw exception?? throw new RuntimeException("Cannot Parse, sorry : " + s + " @ " + pointer + " [Formula String was: '" + formulaString + "']"); } /** Report What Was Expected */ private void Expected(String s) { Abort(s + " Expected"); } /** Recognize an Alpha Character */ private boolean IsAlpha(char c) { return Character.isLetter(c) || c == '$' || c=='_'; } /** Recognize a Decimal Digit */ private boolean IsDigit(char c) { //System.out.println("Checking digit for"+c); return Character.isDigit(c); } /** Recognize an Alphanumeric */ private boolean IsAlNum(char c) { return (IsAlpha(c) || IsDigit(c)); } /** Recognize an Addop */ private boolean IsAddop( char c) { return (c =='+' || c =='-'); } /** Recognize White Space */ private boolean IsWhite( char c) { return (c ==' ' || c== TAB); } /** * Determines special characters;primarily in use for definition of string literals * @param c * @return boolean */ private boolean IsSpecialChar(char c) { return (c == '>' || c== '<' || c== '=' || c=='&' || c=='[' || c==']'); } /** Skip Over Leading White Space */ private void SkipWhite() { while (IsWhite(look)) { GetChar(); } } /** Match a Specific Input Character */ private void Match(char x) { if (look != x) { Expected("" + x + ""); }else { GetChar(); SkipWhite(); } } /** Get an Identifier */ private String GetName() { StringBuffer Token = new StringBuffer(); if (!IsAlpha(look) && look != '\'') { Expected("Name"); } if(look == '\'') { Match('\''); boolean done = look == '\''; while(!done) { Token.append(Character.toUpperCase(look)); GetChar(); if(look == '\'') { Match('\''); done = look != '\''; } } } else { while (IsAlNum(look)) { Token.append(Character.toUpperCase(look)); GetChar(); } } SkipWhite(); return Token.toString(); } /**Get an Identifier AS IS, without stripping white spaces or converting to uppercase; used for literals */ private String GetNameAsIs() { StringBuffer Token = new StringBuffer(); while (IsAlNum(look) || IsWhite(look) || IsSpecialChar(look)) { Token = Token.append(look); GetChar(); } return Token.toString(); } /** Get a Number */ private String GetNum() { StringBuffer value = new StringBuffer(); while (IsDigit(this.look)){ value.append(this.look); GetChar(); } SkipWhite(); return value.length() == 0 ? null : value.toString(); } /** Output a String with Tab */ private void Emit(String s){ System.out.print(TAB+s); } /** Output a String with Tab and CRLF */ private void EmitLn(String s) { Emit(s); System.out.println();; } /** Parse and Translate a String Identifier */ private void Ident() { String name; name = GetName(); if (look == '('){ //This is a function function(name); } else if (look == ':' || look == '.') { // this is a AreaReference GetChar(); while (look == '.') { // formulas can have . or .. or ... instead of : GetChar(); } String first = name; String second = GetName(); tokens.add(new AreaPtg(first+":"+second)); } else if (look == '!') { Match('!'); String sheetName = name; String first = GetName(); short externIdx = book.checkExternSheet(book.getSheetIndex(sheetName)); if (look == ':') { Match(':'); String second=GetName(); if (look == '!') { //The sheet name was included in both of the areas. Only really //need it once Match('!'); String third=GetName(); if (!sheetName.equals(second)) throw new RuntimeException("Unhandled double sheet reference."); tokens.add(new Area3DPtg(first+":"+third,externIdx)); } else { tokens.add(new Area3DPtg(first+":"+second,externIdx)); } } else { tokens.add(new Ref3DPtg(first,externIdx)); } } else { // This can be either a cell ref or a named range // Try to spot which it is boolean cellRef = CELL_REFERENCE_PATTERN.matcher(name).matches(); boolean boolLit = (name.equals("TRUE") || name.equals("FALSE")); if (boolLit) { tokens.add(new BoolPtg(name)); } else if (cellRef) { tokens.add(new ReferencePtg(name)); } else { boolean nameRecordExists = false; for(int i = 0; i < book.getNumNames(); i++) { // Our formula will by now contain an upper-cased // version of any named range names if(book.getNameRecord(i).getNameText().toUpperCase().equals(name)) { nameRecordExists = true; } } if(!nameRecordExists) Abort("Found reference to named range \"" + name + "\", but that named range wasn't defined!"); tokens.add(new NamePtg(name, book)); } } } /** * Adds a pointer to the last token to the latest function argument list. * @param obj */ private void addArgumentPointer() { if (this.functionTokens.size() > 0) { //no bounds check because this method should not be called unless a token array is setup by function() List arguments = (List)this.functionTokens.get(0); arguments.add(tokens.get(tokens.size()-1)); } } private void function(String name) { //average 2 args per function this.functionTokens.add(0, new ArrayList(2));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -