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 + -
显示快捷键?