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 */
021package csheets.core.formula.compiler;
022
023import java.io.StringReader;
024import java.text.ParseException;
025import java.util.*;
026
027import antlr.ANTLRException;
028import antlr.collections.AST;
029import csheets.core.*;
030import csheets.core.formula.*;
031import csheets.core.formula.lang.*;
032
033/**
034 * A compiler that generates Excel-style formulas from strings.
035 * 
036 * @author Einar Pehrson
037 */
038public class ExcelExpressionCompiler implements ExpressionCompiler {
039
040        /** The character that signals that a cell's content is a formula ('=') */
041        public static final char FORMULA_STARTER = '=';
042
043        /**
044         * Creates the Excel expression compiler.
045         */
046        public ExcelExpressionCompiler() {
047        }
048
049        @Override
050        public char getStarter() {
051                return FORMULA_STARTER;
052        }
053
054        @Override
055        public Expression compile(Cell cell, String source)
056                        throws FormulaCompilationException {
057                // Creates the lexer and parser
058                FormulaParser parser = new FormulaParser(new FormulaLexer(
059                                new StringReader(source)));
060
061                try {
062                        // Attempts to match an expression
063                        parser.expression();
064                } catch (ANTLRException e) {
065                        throw new FormulaCompilationException(e);
066                }
067
068                // Converts the expression and returns it
069                return convert(cell, parser.getAST());
070        }
071
072        /**
073         * Converts the given ANTLR AST to an expression.
074         * 
075         * @param node
076         *            the abstract syntax tree node to convert
077         * @return the result of the conversion
078         */
079        protected Expression convert(Cell cell, AST node)
080                        throws FormulaCompilationException {
081                // System.out.println("Converting node '" + node.getText() +
082                // "' of tree '" + node.toStringTree() + "' with " +
083                // node.getNumberOfChildren() + " children.");
084                if (node.getNumberOfChildren() == 0) {
085                        try {
086                                switch (node.getType()) {
087                                case FormulaParserTokenTypes.NUMBER:
088                                        return new Literal(Value.parseNumericValue(node.getText()));
089                                case FormulaParserTokenTypes.STRING:
090                                        return new Literal(Value.parseValue(node.getText(),
091                                                        Value.Type.BOOLEAN, Value.Type.DATE));
092                                case FormulaParserTokenTypes.CELL_REF:
093                                        return new CellReference(cell.getSpreadsheet(),
094                                                        node.getText());
095                                case FormulaParserTokenTypes.NAME:
096                                        /*
097                                         * return cell.getSpreadsheet().getWorkbook().
098                                         * getRange(node.getText()) (Reference)
099                                         */
100                                }
101                        } catch (ParseException e) {
102                                throw new FormulaCompilationException(e);
103                        }
104                }
105
106                // Convert function call
107                Function function = null;
108                try {
109                        function = Language.getInstance().getFunction(node.getText());
110                } catch (UnknownElementException e) {
111                }
112
113                if (function != null) {
114                        List<Expression> args = new ArrayList<Expression>();
115                        AST child = node.getFirstChild();
116                        if (function instanceof Eval) {
117
118                                try {
119
120                                        Expression expression = convert(cell, node.getFirstChild());
121                                        cell.setContent(expression.evaluate().toText());
122                                } catch (IllegalValueTypeException e) {
123                                }
124
125                        }
126                        if (child != null) {
127                                args.add(convert(cell, child));
128                                while ((child = child.getNextSibling()) != null)
129                                        args.add(convert(cell, child));
130                        }
131                        Expression[] argArray = args.toArray(new Expression[args.size()]);
132                        return new FunctionCall(function, argArray);
133                }
134
135                if (node.getNumberOfChildren() == 1)
136                        // Convert unary operation
137                        return new UnaryOperation(Language.getInstance().getUnaryOperator(
138                                        node.getText()), convert(cell, node.getFirstChild()));
139                else if (node.getNumberOfChildren() == 2) {
140                        // Convert binary operation
141                        BinaryOperator operator = Language.getInstance().getBinaryOperator(
142                                        node.getText());
143                        if (operator instanceof RangeReference)
144                                return new ReferenceOperation((Reference) convert(cell,
145                                                node.getFirstChild()), (RangeReference) operator,
146                                                (Reference) convert(cell, node.getFirstChild()
147                                                                .getNextSibling()));
148                        else
149                                return new BinaryOperation(convert(cell, node.getFirstChild()),
150                                                operator, convert(cell, node.getFirstChild()
151                                                                .getNextSibling()));
152                } else
153                        // Shouldn't happen
154                        throw new FormulaCompilationException();
155        }
156}