/** * @param sheetIden may be <code>null</code> * @param part1 * @param part2 may be <code>null</code> */ private ParseNode createAreaRefParseNode( SheetIdentifier sheetIden, SimpleRangePart part1, SimpleRangePart part2) throws FormulaParseException { int extIx; if (sheetIden == null) { extIx = Integer.MIN_VALUE; } else { String sName = sheetIden.getSheetIdentifier().getName(); if (sheetIden.getBookName() == null) { extIx = _book.getExternalSheetIndex(sName); } else { extIx = _book.getExternalSheetIndex(sheetIden.getBookName(), sName); } } Ptg ptg; if (part2 == null) { CellReference cr = part1.getCellReference(); if (sheetIden == null) { ptg = new RefPtg(cr); } else { ptg = new Ref3DPtg(cr, extIx); } } else { AreaReference areaRef = createAreaRef(part1, part2); if (sheetIden == null) { ptg = new AreaPtg(areaRef); } else { ptg = new Area3DPtg(areaRef, extIx); } } return new ParseNode(ptg); }
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); } // the maximum number of arguments depends on the Excel version int maxArgs; if (fm.hasUnlimitedVarags()) { if (_book != null) { maxArgs = _book.getSpreadsheetVersion().getMaxFunctionArgs(); } else { // _book can be omitted by test cases maxArgs = fm.getMaxParams(); // just use BIFF8 } } else { maxArgs = fm.getMaxParams(); } if (numArgs > maxArgs) { String msg = "Too many arguments to function '" + fm.getName() + "'. "; if (fm.hasFixedArgsLength()) { msg += "Expected " + maxArgs; } else { msg += "At most " + maxArgs + " were expected"; } msg += " but got " + numArgs + "."; throw new FormulaParseException(msg); } }
/** * 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 * retrieve the parse results. This class is recommended only for single threaded use. * * <p>If you only have a usermodel.HSSFWorkbook, and not a model.Workbook, then use the * convenience method on usermodel.HSSFFormulaEvaluator */ private FormulaParser(String formula, FormulaParsingWorkbook book, int sheetIndex) { _formulaString = formula; _pointer = 0; _book = book; _ssVersion = book == null ? SpreadsheetVersion.EXCEL97 : book.getSpreadsheetVersion(); _formulaLength = _formulaString.length(); _sheetIndex = sheetIndex; }
/** * 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 if (_book == null) { // Only test cases omit the book (expecting it not to be needed) throw new IllegalStateException("Need book to evaluate name '" + name + "'"); } EvaluationName hName = _book.getName(name, _sheetIndex); 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); }
/** * Parses simple factors that are not primitive ranges or range components i.e. '!', ':'(and equiv * '...') do not appear Examples * * <pre> * my.named...range. * foo.bar(123.456, "abc") * 123.456 * "abc" * true * </pre> */ private ParseNode parseNonRange(int savePointer) { resetPointer(savePointer); if (Character.isDigit(look)) { return new ParseNode(parseNumber()); } if (look == '"') { return new ParseNode(new StringPtg(parseStringLiteral())); } // from now on we can only be dealing with non-quoted identifiers // which will either be named ranges or functions StringBuilder sb = new StringBuilder(); if (!Character.isLetter(look)) { throw expected("number, string, or defined name"); } while (isValidDefinedNameChar(look)) { sb.append(look); GetChar(); } SkipWhite(); String name = sb.toString(); if (look == '(') { return function(name); } if (name.equalsIgnoreCase("TRUE") || name.equalsIgnoreCase("FALSE")) { return new ParseNode(new BoolPtg(name.toUpperCase())); } if (_book == null) { // Only test cases omit the book (expecting it not to be needed) throw new IllegalStateException("Need book to evaluate name '" + name + "'"); } EvaluationName evalName = _book.getName(name, _sheetIndex); if (evalName == null) { throw new FormulaParseException( "Specified named range '" + name + "' does not exist in the current workbook."); } if (evalName.isRange()) { return new ParseNode(evalName.createPtg()); } // TODO - what about NameX ? throw new FormulaParseException("Specified name '" + name + "' is not a range as expected."); }