Search in sources :

Example 1 with AbstractFunctionPtg

use of org.apache.poi.ss.formula.ptg.AbstractFunctionPtg in project poi by apache.

the class FormulaParser method getFunction.

/**
     * 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>
     * @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(Locale.ROOT));
    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(FuncVarPtg.create(name, numArgs + 1), allArgs);
    }
    if (namePtg != null) {
        throw new IllegalStateException("NamePtg no applicable to internal functions");
    }
    boolean isVarArgs = !fm.hasFixedArgsLength();
    int funcIx = fm.getIndex();
    if (funcIx == FunctionMetadataRegistry.FUNCTION_INDEX_SUM && args.length == 1) {
        // POI does the same for consistency, but this is not critical
        return new ParseNode(AttrPtg.getSumSingle(), args);
    // The code below would encode tFuncVar(SUM) which seems to do no harm
    }
    validateNumArgs(args.length, fm);
    AbstractFunctionPtg retval;
    if (isVarArgs) {
        retval = FuncVarPtg.create(name, numArgs);
    } else {
        retval = FuncPtg.create(funcIx);
    }
    return new ParseNode(retval, args);
}
Also used : FunctionMetadata(org.apache.poi.ss.formula.function.FunctionMetadata) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg)

Example 2 with AbstractFunctionPtg

use of org.apache.poi.ss.formula.ptg.AbstractFunctionPtg in project poi by apache.

the class TestFormulaParser method testMacroFunction.

// copied from org.apache.poi.hssf.model.TestFormulaParser
@Test
public void testMacroFunction() throws Exception {
    // testNames.xlsm contains a VB function called 'myFunc'
    final String testFile = "testNames.xlsm";
    XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook(testFile);
    try {
        XSSFEvaluationWorkbook workbook = XSSFEvaluationWorkbook.create(wb);
        //Expected ptg stack: [NamePtg(myFunc), StringPtg(arg), (additional operands would go here...), FunctionPtg(myFunc)]
        Ptg[] ptg = FormulaParser.parse("myFunc(\"arg\")", workbook, FormulaType.CELL, -1);
        assertEquals(3, ptg.length);
        // the name gets encoded as the first operand on the stack
        NameXPxg tname = (NameXPxg) ptg[0];
        assertEquals("myFunc", tname.toFormulaString());
        // the function's arguments are pushed onto the stack from left-to-right as OperandPtgs
        StringPtg arg = (StringPtg) ptg[1];
        assertEquals("arg", arg.getValue());
        // The external FunctionPtg is the last Ptg added to the stack
        // During formula evaluation, this Ptg pops off the the appropriate number of
        // arguments (getNumberOfOperands()) and pushes the result on the stack 
        AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[2];
        assertTrue(tfunc.isExternalFunction());
        // confirm formula parsing is case-insensitive
        FormulaParser.parse("mYfUnC(\"arg\")", workbook, FormulaType.CELL, -1);
        // confirm formula parsing doesn't care about argument count or type
        // this should only throw an error when evaluating the formula.
        FormulaParser.parse("myFunc()", workbook, FormulaType.CELL, -1);
        FormulaParser.parse("myFunc(\"arg\", 0, TRUE)", workbook, FormulaType.CELL, -1);
        // A completely unknown formula name (not saved in workbook) should still be parseable and renderable
        // but will throw an NotImplementedFunctionException or return a #NAME? error value if evaluated.
        FormulaParser.parse("yourFunc(\"arg\")", workbook, FormulaType.CELL, -1);
        // Make sure workbook can be written and read
        XSSFTestDataSamples.writeOutAndReadBack(wb).close();
    // Manually check to make sure file isn't corrupted
    // TODO: develop a process for occasionally manually reviewing workbooks
    // to verify workbooks are not corrupted
    /*
            final File fileIn = XSSFTestDataSamples.getSampleFile(testFile);
            final File reSavedFile = new File(fileIn.getParentFile(), fileIn.getName().replace(".xlsm", "-saved.xlsm"));
            final FileOutputStream fos = new FileOutputStream(reSavedFile);
            wb.write(fos);
            fos.close();
            */
    } finally {
        wb.close();
    }
}
Also used : XSSFEvaluationWorkbook(org.apache.poi.xssf.usermodel.XSSFEvaluationWorkbook) Ptg(org.apache.poi.ss.formula.ptg.Ptg) StringPtg(org.apache.poi.ss.formula.ptg.StringPtg) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg) NameXPxg(org.apache.poi.ss.formula.ptg.NameXPxg) XSSFWorkbook(org.apache.poi.xssf.usermodel.XSSFWorkbook) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg) StringPtg(org.apache.poi.ss.formula.ptg.StringPtg) Test(org.junit.Test)

Example 3 with AbstractFunctionPtg

use of org.apache.poi.ss.formula.ptg.AbstractFunctionPtg in project poi by apache.

the class TestFormulaParser method testMacroFunction.

@Test
public void testMacroFunction() throws IOException {
    // testNames.xls contains a VB function called 'myFunc'
    final String testFile = "testNames.xls";
    HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(testFile);
    try {
        HSSFEvaluationWorkbook book = HSSFEvaluationWorkbook.create(wb);
        //Expected ptg stack: [NamePtg(myFunc), StringPtg(arg), (additional operands go here...), FunctionPtg(myFunc)]
        Ptg[] ptg = FormulaParser.parse("myFunc(\"arg\")", book, FormulaType.CELL, -1);
        assertEquals(3, ptg.length);
        // the name gets encoded as the first operand on the stack
        NamePtg tname = (NamePtg) ptg[0];
        assertEquals("myFunc", tname.toFormulaString(book));
        // the function's arguments are pushed onto the stack from left-to-right as OperandPtgs
        StringPtg arg = (StringPtg) ptg[1];
        assertEquals("arg", arg.getValue());
        // The external FunctionPtg is the last Ptg added to the stack
        // During formula evaluation, this Ptg pops off the the appropriate number of
        // arguments (getNumberOfOperands()) and pushes the result on the stack
        //FuncVarPtg
        AbstractFunctionPtg tfunc = (AbstractFunctionPtg) ptg[2];
        assertTrue(tfunc.isExternalFunction());
        // confirm formula parsing is case-insensitive
        FormulaParser.parse("mYfUnC(\"arg\")", book, FormulaType.CELL, -1);
        // confirm formula parsing doesn't care about argument count or type
        // this should only throw an error when evaluating the formula.
        FormulaParser.parse("myFunc()", book, FormulaType.CELL, -1);
        FormulaParser.parse("myFunc(\"arg\", 0, TRUE)", book, FormulaType.CELL, -1);
        // A completely unknown formula name (not saved in workbook) should still be parseable and renderable
        // but will throw an NotImplementedFunctionException or return a #NAME? error value if evaluated.
        FormulaParser.parse("yourFunc(\"arg\")", book, FormulaType.CELL, -1);
        // Verify that myFunc and yourFunc were successfully added to Workbook names
        HSSFWorkbook wb2 = HSSFTestDataSamples.writeOutAndReadBack(wb);
        try {
            // HSSFWorkbook/EXCEL97-specific side-effects user-defined function names must be added to Workbook's defined names in order to be saved.
            assertNotNull(wb2.getName("myFunc"));
            assertEqualsIgnoreCase("myFunc", wb2.getName("myFunc").getNameName());
            assertNotNull(wb2.getName("yourFunc"));
            assertEqualsIgnoreCase("yourFunc", wb2.getName("yourFunc").getNameName());
        // Manually check to make sure file isn't corrupted
        // TODO: develop a process for occasionally manually reviewing workbooks
        // to verify workbooks are not corrupted
        /*
                final File fileIn = HSSFTestDataSamples.getSampleFile(testFile);
                final File reSavedFile = new File(fileIn.getParentFile(), fileIn.getName().replace(".xls", "-saved.xls"));
                FileOutputStream fos = new FileOutputStream(reSavedFile);
                wb2.write(fos);
                fos.close();
                */
        } finally {
            wb2.close();
        }
    } finally {
        wb.close();
    }
}
Also used : NumberPtg(org.apache.poi.ss.formula.ptg.NumberPtg) ArrayPtg(org.apache.poi.ss.formula.ptg.ArrayPtg) AttrPtg(org.apache.poi.ss.formula.ptg.AttrPtg) PercentPtg(org.apache.poi.ss.formula.ptg.PercentPtg) RangePtg(org.apache.poi.ss.formula.ptg.RangePtg) AddPtg(org.apache.poi.ss.formula.ptg.AddPtg) EqualPtg(org.apache.poi.ss.formula.ptg.EqualPtg) UnaryMinusPtg(org.apache.poi.ss.formula.ptg.UnaryMinusPtg) NameXPtg(org.apache.poi.ss.formula.ptg.NameXPtg) RefPtg(org.apache.poi.ss.formula.ptg.RefPtg) DividePtg(org.apache.poi.ss.formula.ptg.DividePtg) GreaterThanPtg(org.apache.poi.ss.formula.ptg.GreaterThanPtg) MultiplyPtg(org.apache.poi.ss.formula.ptg.MultiplyPtg) Ref3DPtg(org.apache.poi.ss.formula.ptg.Ref3DPtg) StringPtg(org.apache.poi.ss.formula.ptg.StringPtg) ErrPtg(org.apache.poi.ss.formula.ptg.ErrPtg) Ptg(org.apache.poi.ss.formula.ptg.Ptg) Area3DPtg(org.apache.poi.ss.formula.ptg.Area3DPtg) NamePtg(org.apache.poi.ss.formula.ptg.NamePtg) MemAreaPtg(org.apache.poi.ss.formula.ptg.MemAreaPtg) ConcatPtg(org.apache.poi.ss.formula.ptg.ConcatPtg) UnaryPlusPtg(org.apache.poi.ss.formula.ptg.UnaryPlusPtg) BoolPtg(org.apache.poi.ss.formula.ptg.BoolPtg) IntersectionPtg(org.apache.poi.ss.formula.ptg.IntersectionPtg) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg) IntPtg(org.apache.poi.ss.formula.ptg.IntPtg) UnionPtg(org.apache.poi.ss.formula.ptg.UnionPtg) FuncVarPtg(org.apache.poi.ss.formula.ptg.FuncVarPtg) SubtractPtg(org.apache.poi.ss.formula.ptg.SubtractPtg) FuncPtg(org.apache.poi.ss.formula.ptg.FuncPtg) MissingArgPtg(org.apache.poi.ss.formula.ptg.MissingArgPtg) MemFuncPtg(org.apache.poi.ss.formula.ptg.MemFuncPtg) PowerPtg(org.apache.poi.ss.formula.ptg.PowerPtg) AreaPtg(org.apache.poi.ss.formula.ptg.AreaPtg) ParenthesisPtg(org.apache.poi.ss.formula.ptg.ParenthesisPtg) NamePtg(org.apache.poi.ss.formula.ptg.NamePtg) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg) UnicodeString(org.apache.poi.hssf.record.common.UnicodeString) StringPtg(org.apache.poi.ss.formula.ptg.StringPtg) HSSFWorkbook(org.apache.poi.hssf.usermodel.HSSFWorkbook) HSSFEvaluationWorkbook(org.apache.poi.hssf.usermodel.HSSFEvaluationWorkbook) Test(org.junit.Test)

Example 4 with AbstractFunctionPtg

use of org.apache.poi.ss.formula.ptg.AbstractFunctionPtg in project poi by apache.

the class TestParseMissingBuiltInFuncs method confirmFunc.

private static void confirmFunc(String formula, int expPtgArraySize, boolean isVarArgFunc, int funcIx) throws IOException {
    Ptg[] ptgs = parse(formula);
    // func is last RPN token in all these formulas
    Ptg ptgF = ptgs[ptgs.length - 1];
    // Check critical things in the Ptg array encoding.
    if (!(ptgF instanceof AbstractFunctionPtg)) {
        throw new RuntimeException("function token missing");
    }
    AbstractFunctionPtg func = (AbstractFunctionPtg) ptgF;
    if (func.getFunctionIndex() == 255) {
        throw new AssertionFailedError("Failed to recognise built-in function in formula '" + formula + "'");
    }
    assertEquals(expPtgArraySize, ptgs.length);
    assertEquals(funcIx, func.getFunctionIndex());
    Class<? extends AbstractFunctionPtg> expCls = isVarArgFunc ? FuncVarPtg.class : FuncPtg.class;
    assertEquals(expCls, ptgF.getClass());
    // check that parsed Ptg array converts back to formula text OK
    HSSFWorkbook book = new HSSFWorkbook();
    String reRenderedFormula = HSSFFormulaParser.toFormulaString(book, ptgs);
    assertEquals(formula, reRenderedFormula);
    book.close();
}
Also used : Ptg(org.apache.poi.ss.formula.ptg.Ptg) FuncVarPtg(org.apache.poi.ss.formula.ptg.FuncVarPtg) FuncPtg(org.apache.poi.ss.formula.ptg.FuncPtg) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg) AssertionFailedError(junit.framework.AssertionFailedError) HSSFWorkbook(org.apache.poi.hssf.usermodel.HSSFWorkbook)

Example 5 with AbstractFunctionPtg

use of org.apache.poi.ss.formula.ptg.AbstractFunctionPtg in project poi by apache.

the class FormulaParser method isValidRangeOperand.

/**
     * @return <code>false</code> if sub-expression represented the specified ParseNode definitely
     * cannot appear on either side of the range (':') operator
     */
private static boolean isValidRangeOperand(ParseNode a) {
    Ptg tkn = a.getToken();
    // Note - order is important for these instance-of checks
    if (tkn instanceof OperandPtg) {
        // notably cell refs and area refs
        return true;
    }
    // next 2 are special cases of OperationPtg
    if (tkn instanceof AbstractFunctionPtg) {
        AbstractFunctionPtg afp = (AbstractFunctionPtg) tkn;
        byte returnClass = afp.getDefaultOperandClass();
        return Ptg.CLASS_REF == returnClass;
    }
    if (tkn instanceof ValueOperatorPtg) {
        return false;
    }
    if (tkn instanceof OperationPtg) {
        return true;
    }
    // one special case of ControlPtg
    if (tkn instanceof ParenthesisPtg) {
        // parenthesis Ptg should have only one child
        return isValidRangeOperand(a.getChildren()[0]);
    }
    // one special case of ScalarConstantPtg
    if (tkn == ErrPtg.REF_INVALID) {
        return true;
    }
    // All other ControlPtgs and ScalarConstantPtgs cannot be used with ':'
    return false;
}
Also used : OperandPtg(org.apache.poi.ss.formula.ptg.OperandPtg) NumberPtg(org.apache.poi.ss.formula.ptg.NumberPtg) ArrayPtg(org.apache.poi.ss.formula.ptg.ArrayPtg) AttrPtg(org.apache.poi.ss.formula.ptg.AttrPtg) PercentPtg(org.apache.poi.ss.formula.ptg.PercentPtg) RangePtg(org.apache.poi.ss.formula.ptg.RangePtg) AddPtg(org.apache.poi.ss.formula.ptg.AddPtg) EqualPtg(org.apache.poi.ss.formula.ptg.EqualPtg) UnaryMinusPtg(org.apache.poi.ss.formula.ptg.UnaryMinusPtg) NameXPtg(org.apache.poi.ss.formula.ptg.NameXPtg) RefPtg(org.apache.poi.ss.formula.ptg.RefPtg) DividePtg(org.apache.poi.ss.formula.ptg.DividePtg) GreaterThanPtg(org.apache.poi.ss.formula.ptg.GreaterThanPtg) MultiplyPtg(org.apache.poi.ss.formula.ptg.MultiplyPtg) StringPtg(org.apache.poi.ss.formula.ptg.StringPtg) ErrPtg(org.apache.poi.ss.formula.ptg.ErrPtg) Ptg(org.apache.poi.ss.formula.ptg.Ptg) NamePtg(org.apache.poi.ss.formula.ptg.NamePtg) MemAreaPtg(org.apache.poi.ss.formula.ptg.MemAreaPtg) NotEqualPtg(org.apache.poi.ss.formula.ptg.NotEqualPtg) ValueOperatorPtg(org.apache.poi.ss.formula.ptg.ValueOperatorPtg) ConcatPtg(org.apache.poi.ss.formula.ptg.ConcatPtg) UnaryPlusPtg(org.apache.poi.ss.formula.ptg.UnaryPlusPtg) GreaterEqualPtg(org.apache.poi.ss.formula.ptg.GreaterEqualPtg) LessThanPtg(org.apache.poi.ss.formula.ptg.LessThanPtg) BoolPtg(org.apache.poi.ss.formula.ptg.BoolPtg) IntersectionPtg(org.apache.poi.ss.formula.ptg.IntersectionPtg) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg) IntPtg(org.apache.poi.ss.formula.ptg.IntPtg) LessEqualPtg(org.apache.poi.ss.formula.ptg.LessEqualPtg) UnionPtg(org.apache.poi.ss.formula.ptg.UnionPtg) FuncVarPtg(org.apache.poi.ss.formula.ptg.FuncVarPtg) SubtractPtg(org.apache.poi.ss.formula.ptg.SubtractPtg) FuncPtg(org.apache.poi.ss.formula.ptg.FuncPtg) OperandPtg(org.apache.poi.ss.formula.ptg.OperandPtg) MissingArgPtg(org.apache.poi.ss.formula.ptg.MissingArgPtg) MemFuncPtg(org.apache.poi.ss.formula.ptg.MemFuncPtg) OperationPtg(org.apache.poi.ss.formula.ptg.OperationPtg) PowerPtg(org.apache.poi.ss.formula.ptg.PowerPtg) AreaPtg(org.apache.poi.ss.formula.ptg.AreaPtg) ParenthesisPtg(org.apache.poi.ss.formula.ptg.ParenthesisPtg) OperationPtg(org.apache.poi.ss.formula.ptg.OperationPtg) ParenthesisPtg(org.apache.poi.ss.formula.ptg.ParenthesisPtg) AbstractFunctionPtg(org.apache.poi.ss.formula.ptg.AbstractFunctionPtg) ValueOperatorPtg(org.apache.poi.ss.formula.ptg.ValueOperatorPtg)

Aggregations

AbstractFunctionPtg (org.apache.poi.ss.formula.ptg.AbstractFunctionPtg)9 Ptg (org.apache.poi.ss.formula.ptg.Ptg)5 FuncVarPtg (org.apache.poi.ss.formula.ptg.FuncVarPtg)4 AttrPtg (org.apache.poi.ss.formula.ptg.AttrPtg)3 FuncPtg (org.apache.poi.ss.formula.ptg.FuncPtg)3 IntersectionPtg (org.apache.poi.ss.formula.ptg.IntersectionPtg)3 MemAreaPtg (org.apache.poi.ss.formula.ptg.MemAreaPtg)3 MemFuncPtg (org.apache.poi.ss.formula.ptg.MemFuncPtg)3 RangePtg (org.apache.poi.ss.formula.ptg.RangePtg)3 StringPtg (org.apache.poi.ss.formula.ptg.StringPtg)3 HSSFWorkbook (org.apache.poi.hssf.usermodel.HSSFWorkbook)2 AddPtg (org.apache.poi.ss.formula.ptg.AddPtg)2 AreaPtg (org.apache.poi.ss.formula.ptg.AreaPtg)2 ArrayPtg (org.apache.poi.ss.formula.ptg.ArrayPtg)2 BoolPtg (org.apache.poi.ss.formula.ptg.BoolPtg)2 ConcatPtg (org.apache.poi.ss.formula.ptg.ConcatPtg)2 DividePtg (org.apache.poi.ss.formula.ptg.DividePtg)2 EqualPtg (org.apache.poi.ss.formula.ptg.EqualPtg)2 ErrPtg (org.apache.poi.ss.formula.ptg.ErrPtg)2 GreaterThanPtg (org.apache.poi.ss.formula.ptg.GreaterThanPtg)2