001    /*
002     * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>.
003     *
004     * This file is part of
005     * CleanSheets - a spreadsheet application for the Java platform.
006     *
007     * CleanSheets is free software; you can redistribute it and/or modify
008     * it under the terms of the GNU General Public License as published by
009     * the Free Software Foundation; either version 2 of the License, or
010     * (at your option) any later version.
011     *
012     * CleanSheets is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015     * GNU General Public License for more details.
016     *
017     * You should have received a copy of the GNU General Public License
018     * along with CleanSheets; if not, write to the Free Software
019     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
020     */
021    package csheets.core.formula.compiler;
022    
023    import java.io.StringReader;
024    import java.text.ParseException;
025    import java.util.ArrayList;
026    import java.util.List;
027    
028    import antlr.ANTLRException;
029    import antlr.collections.AST;
030    import csheets.core.Cell;
031    import csheets.core.Value;
032    import csheets.core.formula.BinaryOperation;
033    import csheets.core.formula.BinaryOperator;
034    import csheets.core.formula.Expression;
035    import csheets.core.formula.Function;
036    import csheets.core.formula.FunctionCall;
037    import csheets.core.formula.Literal;
038    import csheets.core.formula.Reference;
039    import csheets.core.formula.UnaryOperation;
040    import csheets.core.formula.lang.CellReference;
041    import csheets.core.formula.lang.Language;
042    import csheets.core.formula.lang.RangeReference;
043    import csheets.core.formula.lang.ReferenceOperation;
044    import csheets.core.formula.lang.UnknownElementException;
045    
046    /**
047     * A compiler that generates Excel-style formulas from strings.
048     * @author Einar Pehrson
049     */
050    public class ExcelExpressionCompiler implements ExpressionCompiler {
051    
052            /** The character that signals that a cell's content is a formula ('=') */
053            public static final char FORMULA_STARTER = '=';
054    
055            /**
056             * Creates the Excel expression compiler.
057             */
058            public ExcelExpressionCompiler() {}
059    
060            public char getStarter() {
061                    return FORMULA_STARTER;
062            }
063    
064            public Expression compile(Cell cell, String source) throws FormulaCompilationException {
065                    // Creates the lexer and parser
066                    FormulaParser parser = new FormulaParser(
067                            new FormulaLexer(new StringReader(source)));
068    
069                    try {
070                            // Attempts to match an expression
071                            parser.expression();
072                    } catch (ANTLRException e) {
073                            throw new FormulaCompilationException(e);
074                    }
075    
076                    // Converts the expression and returns it
077                    return convert(cell, parser.getAST());
078            }
079    
080            /**
081             * Converts the given ANTLR AST to an expression.
082             * @param node the abstract syntax tree node to convert
083             * @return the result of the conversion
084             */
085            protected Expression convert(Cell cell, AST node) throws FormulaCompilationException {
086                    // System.out.println("Converting node '" + node.getText() + "' of tree '" + node.toStringTree() + "' with " + node.getNumberOfChildren() + " children.");
087                    if (node.getNumberOfChildren() == 0) {
088                            try {
089                                    switch (node.getType()) {
090                                            case FormulaParserTokenTypes.NUMBER:
091                                                    return new Literal(Value.parseNumericValue(node.getText()));
092                                            case FormulaParserTokenTypes.STRING:
093                                                    return new Literal(Value.parseValue(node.getText(), Value.Type.BOOLEAN, Value.Type.DATE));
094                                            case FormulaParserTokenTypes.CELL_REF:
095                                                    return new CellReference(cell.getSpreadsheet(), node.getText());
096                                            case FormulaParserTokenTypes.NAME:
097                                                    /* return cell.getSpreadsheet().getWorkbook().
098                                                            getRange(node.getText()) (Reference)*/
099                                    }
100                            } catch (ParseException e) {
101                                    throw new FormulaCompilationException(e);
102                            }
103                    }
104    
105                    // Convert function call
106                    Function function = null;
107                    try {
108                            function = Language.getInstance().getFunction(node.getText());
109                    } catch (UnknownElementException e) {}
110    
111                    if (function != null) {
112                            List<Expression> args = new ArrayList<Expression>();
113                            AST child = node.getFirstChild();
114                            if (child != null) {
115                                    args.add(convert(cell, child));
116                                    while ((child = child.getNextSibling()) != null)
117                                            args.add(convert(cell, child));
118                            }
119                            Expression[] argArray = args.toArray(new Expression[args.size()]);
120                            return new FunctionCall(function, argArray);
121                    }
122    
123                    if (node.getNumberOfChildren() == 1)
124                            // Convert unary operation
125                            return new UnaryOperation(
126                                    Language.getInstance().getUnaryOperator(node.getText()),
127                                    convert(cell, node.getFirstChild())
128                            );
129                    else if (node.getNumberOfChildren() == 2) {
130                            // Convert binary operation
131                            BinaryOperator operator = Language.getInstance().getBinaryOperator(node.getText());
132                            if (operator instanceof RangeReference)
133                                    return new ReferenceOperation(
134                                            (Reference)convert(cell, node.getFirstChild()),
135                                            (RangeReference)operator,
136                                            (Reference)convert(cell, node.getFirstChild().getNextSibling())
137                                    );
138                            else 
139                                    return new BinaryOperation(
140                                            convert(cell, node.getFirstChild()),
141                                            operator,
142                                            convert(cell, node.getFirstChild().getNextSibling())
143                                    );
144                    } else
145                            // Shouldn't happen
146                            throw new FormulaCompilationException();
147            }
148    }