formulaparser.java
来自「EXCEL read and write」· Java 代码 · 共 1,059 行 · 第 1/3 页
JAVA
1,059 行
Ref3DPtg refA = (Ref3DPtg) ptgA; return new Area3DPtg(refA.getRow(), refB.getRow(), refA.getColumn(), refB.getColumn(), refA.isRowRelative(), refB.isRowRelative(), refA.isColRelative(), refB.isColRelative(), refA.getExternSheetIndex()); } // Note - other operand types (like AreaPtg) which probably can't evaluate // do not cause validation errors at parse time return null; } private Ptg parseNameOrCellRef(Identifier iden) { if (look == '!') { GetChar(); // 3-D ref // this code assumes iden is a sheetName // TODO - handle <book name> ! <named range name> int externIdx = getExternalSheetIndex(iden.getName()); String secondIden = parseUnquotedIdentifier(); AreaReference areaRef = parseArea(secondIden); if (areaRef == null) { return new Ref3DPtg(secondIden, externIdx); } // will happen if dots are used instead of colon return new Area3DPtg(areaRef.formatAsString(), externIdx); } String name = iden.getName(); AreaReference areaRef = parseArea(name); if (areaRef != null) { // will happen if dots are used instead of colon return new AreaPtg(areaRef.formatAsString()); } // This can be either a cell ref or a named range int nameType = CellReference.classifyCellReference(name); if (nameType == NameType.CELL) { return new RefPtg(name); } if (look == ':') { if (nameType == NameType.COLUMN) { GetChar(); String secondIden = parseUnquotedIdentifier(); if (CellReference.classifyCellReference(secondIden) != NameType.COLUMN) { throw new FormulaParseException("Expected full column after '" + name + ":' but got '" + secondIden + "'"); } return new AreaPtg(name + ":" + secondIden); } } if (nameType != NameType.NAMED_RANGE) { new FormulaParseException("Name '" + name + "' does not look like a cell reference or named range"); } EvaluationName evalName = book.getName(name); if (evalName == null) { throw new FormulaParseException("Specified named range '" + name + "' does not exist in the current workbook."); } if (evalName.isRange()) { return evalName.createPtg(); } throw new FormulaParseException("Specified name '" + name + "' is not a range as expected"); } private int getExternalSheetIndex(String name) { if (name.charAt(0) == '[') { // we have a sheet name qualified with workbook name e.g. '[MyData.xls]Sheet1' int pos = name.lastIndexOf(']'); // safe because sheet names never have ']' String wbName = name.substring(1, pos); String sheetName = name.substring(pos+1); return book.getExternalSheetIndex(wbName, sheetName); } return book.getExternalSheetIndex(name); } /** * @param name an 'identifier' like string (i.e. contains alphanums, and dots) * @return <code>null</code> if name cannot be split at a dot */ private AreaReference parseArea(String name) { int dotPos = name.indexOf('.'); if (dotPos < 0) { return null; } int dotCount = 1; while (dotCount<name.length() && name.charAt(dotPos+dotCount) == '.') { dotCount++; if (dotCount>3) { // four or more consecutive dots does not convert to ':' return null; } } // This expression is only valid as an area ref, if the LHS and RHS of the dot(s) are both // cell refs. Otherwise, this expression must be a named range name String partA = name.substring(0, dotPos); if (!isValidCellReference(partA)) { return null; } String partB = name.substring(dotPos+dotCount); if (!isValidCellReference(partB)) { return null; } CellReference topLeft = new CellReference(partA); CellReference bottomRight = new CellReference(partB); return new AreaReference(topLeft, bottomRight); } /** * @return <code>true</code> if the specified name is a valid cell reference */ private static boolean isValidCellReference(String str) { return CellReference.classifyCellReference(str) == NameType.CELL; } /** * Note - Excel function names are 'case aware but not case sensitive'. This method may end * up creating a defined name record in the workbook if the specified name is not an internal * Excel function, and has not been encountered before. * * @param name case preserved function name (as it was entered/appeared in the formula). */ private ParseNode function(String name) { Ptg nameToken = null; if(!AbstractFunctionPtg.isBuiltInFunctionName(name)) { // user defined function // in the token tree, the name is more or less the first argument EvaluationName hName = book.getName(name); if (hName == null) { nameToken = book.getNameXPtg(name); if (nameToken == null) { throw new FormulaParseException("Name '" + name + "' is completely unknown in the current workbook"); } } else { if (!hName.isFunctionName()) { throw new FormulaParseException("Attempt to use name '" + name + "' as a function, but defined name in workbook does not refer to a function"); } // calls to user-defined functions within the workbook // get a Name token which points to a defined name record nameToken = hName.createPtg(); } } Match('('); ParseNode[] args = Arguments(); Match(')'); return getFunction(name, nameToken, args); } /** * Generates the variable function ptg for the formula. * <p> * For IF Formulas, additional PTGs are added to the tokens * @param name a {@link NamePtg} or {@link NameXPtg} or <code>null</code> * @param numArgs * @return Ptg a null is returned if we're in an IF formula, it needs extreme manipulation and is handled in this function */ private ParseNode getFunction(String name, Ptg namePtg, ParseNode[] args) { FunctionMetadata fm = FunctionMetadataRegistry.getFunctionByName(name.toUpperCase()); int numArgs = args.length; if(fm == null) { if (namePtg == null) { throw new IllegalStateException("NamePtg must be supplied for external functions"); } // must be external function ParseNode[] allArgs = new ParseNode[numArgs+1]; allArgs[0] = new ParseNode(namePtg); System.arraycopy(args, 0, allArgs, 1, numArgs); return new ParseNode(new FuncVarPtg(name, (byte)(numArgs+1)), allArgs); } if (namePtg != null) { throw new IllegalStateException("NamePtg no applicable to internal functions"); } boolean isVarArgs = !fm.hasFixedArgsLength(); int funcIx = fm.getIndex(); validateNumArgs(args.length, fm); AbstractFunctionPtg retval; if(isVarArgs) { retval = new FuncVarPtg(name, (byte)numArgs); } else { retval = new FuncPtg(funcIx); } return new ParseNode(retval, args); } private void validateNumArgs(int numArgs, FunctionMetadata fm) { if(numArgs < fm.getMinParams()) { String msg = "Too few arguments to function '" + fm.getName() + "'. "; if(fm.hasFixedArgsLength()) { msg += "Expected " + fm.getMinParams(); } else { msg += "At least " + fm.getMinParams() + " were expected"; } msg += " but got " + numArgs + "."; throw new FormulaParseException(msg); } if(numArgs > fm.getMaxParams()) { String msg = "Too many arguments to function '" + fm.getName() + "'. "; if(fm.hasFixedArgsLength()) { msg += "Expected " + fm.getMaxParams(); } else { msg += "At most " + fm.getMaxParams() + " were expected"; } msg += " but got " + numArgs + "."; throw new FormulaParseException(msg); } } private static boolean isArgumentDelimiter(char ch) { return ch == ',' || ch == ')'; } /** get arguments to a function */ private ParseNode[] Arguments() { //average 2 args per function List temp = new ArrayList(2); SkipWhite(); if(look == ')') { return ParseNode.EMPTY_ARRAY; } boolean missedPrevArg = true; int numArgs = 0; while (true) { SkipWhite(); if (isArgumentDelimiter(look)) { if (missedPrevArg) { temp.add(new ParseNode(MissingArgPtg.instance)); numArgs++; } if (look == ')') { break; } Match(','); missedPrevArg = true; continue; } temp.add(comparisonExpression()); numArgs++; missedPrevArg = false; SkipWhite(); if (!isArgumentDelimiter(look)) { throw expected("',' or ')'"); } } ParseNode[] result = new ParseNode[temp.size()]; temp.toArray(result); return result; } /** Parse and Translate a Math Factor */ private ParseNode powerFactor() { ParseNode result = percentFactor(); while(true) { SkipWhite(); if(look != '^') { return result; } Match('^'); ParseNode other = percentFactor(); result = new ParseNode(PowerPtg.instance, result, other); } } private ParseNode percentFactor() { ParseNode result = parseSimpleFactor(); while(true) { SkipWhite(); if(look != '%') { return result; } Match('%'); result = new ParseNode(PercentPtg.instance, result); } } /** * factors (without ^ or % ) */ private ParseNode parseSimpleFactor() { SkipWhite(); switch(look) { case '#': return new ParseNode(ErrPtg.valueOf(parseErrorLiteral())); case '-': Match('-'); return new ParseNode(UnaryMinusPtg.instance, powerFactor()); case '+': Match('+'); return new ParseNode(UnaryPlusPtg.instance, powerFactor()); case '(': Match('('); ParseNode inside = comparisonExpression(); Match(')'); return new ParseNode(ParenthesisPtg.instance, inside); case '"': return new ParseNode(new StringPtg(parseStringLiteral())); case '{': Match('{'); ParseNode arrayNode = parseArray(); Match('}'); return arrayNode; } if (IsAlpha(look) || look == '\'' || look == '['){ return parseFunctionReferenceOrName(); } // else - assume number return new ParseNode(parseNumber()); } private ParseNode parseArray() { List rowsData = new ArrayList(); while(true) { Object[] singleRowData = parseArrayRow(); rowsData.add(singleRowData); if (look == '}') { break; } if (look != ';') { throw expected("'}' or ';'"); } Match(';'); } int nRows = rowsData.size(); Object[][] values2d = new Object[nRows][]; rowsData.toArray(values2d); int nColumns = values2d[0].length; checkRowLengths(values2d, nColumns); return new ParseNode(new ArrayPtg(values2d)); } private void checkRowLengths(Object[][] values2d, int nColumns) { for (int i = 0; i < values2d.length; i++) { int rowLen = values2d[i].length; if (rowLen != nColumns) { throw new FormulaParseException("Array row " + i + " has length " + rowLen + " but row 0 has length " + nColumns); } }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?