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;
022
023import java.io.IOException;
024import java.io.ObjectInputStream;
025import java.io.ObjectOutputStream;
026
027import csheets.core.IllegalValueTypeException;
028import csheets.core.Value;
029import csheets.core.formula.compiler.IllegalFunctionCallException;
030import csheets.core.formula.lang.Language;
031import csheets.core.formula.lang.UnknownElementException;
032import csheets.core.formula.util.ExpressionVisitor;
033
034/**
035 * A call to a function in a formula.
036 * @author Einar Pehrson
037 */
038public class FunctionCall implements Expression {
039
040        /** The unique version identifier used for serialization */
041        private static final long serialVersionUID = 1666675675246822233L;
042
043        /** The function that is called */
044        private transient Function function;
045
046        /** The arguments passed to the function */
047        private Expression[] args;
048
049        /**
050         * Creates a new function call.
051         * @param function the function that is called
052         * @param args the arguments passed to the function
053         * @throws IllegalFunctionCallException if the arguments passed to the function did not match its parameters
054         */
055        public FunctionCall(Function function, Expression... args)
056                        throws IllegalFunctionCallException {
057                // Stores members
058                this.function = function;
059                this.args = args;
060
061                // Checks arguments against parameters
062                FunctionParameter[] params = function.getParameters();
063                for (int i = 0; i < args.length; i++)
064                        if (params.length <= i && !function.isVarArg())
065                                // Too many arguments
066                                throw new IllegalFunctionCallException(function, null, args[i]);
067                for (int i = params.length - 1; i >= 0; i--)
068                        if (i >= args.length && !params[i].isOptional())
069                                // Too few arguments
070                                throw new IllegalFunctionCallException(function, params[i], null);
071        }
072
073        public Value evaluate() throws IllegalValueTypeException {
074                return function.applyTo(args);
075        }
076
077        /**
078         * Returns the function that is called.
079         * @return the function that is called
080         */
081        public Function getFunction() {
082                return function;
083        }
084
085        /**
086         * Returns the arguments passed to the function.
087         * @return the arguments passed to the function
088         */
089        public Expression[] getArguments() {
090                return args;
091        }
092
093        public Object accept(ExpressionVisitor visitor) {
094                return visitor.visitFunctionCall(this);
095        }
096
097        public String toString() {
098                String string = function.getIdentifier().toUpperCase() + "(";
099                for (int i = 0; i < args.length; i++) {
100                        string += args[i];
101                        if (i + 1 < args.length)
102                                string += "; ";
103                }
104                string += ")";
105                return string;
106        }
107
108        /**
109         * Customizes deserialization by fetching the function from the language
110         * using the stored identifier.
111         * @param stream the object input stream from which the object is to be read
112         * @throws IOException If any of the usual Input/Output related exceptions occur
113         * @throws ClassNotFoundException If the class of a serialized object cannot be found.
114         */
115        private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
116                stream.defaultReadObject();
117                String identifier = (String)stream.readObject();
118                try {
119                        function = Language.getInstance().getFunction(identifier);
120                } catch (UnknownElementException e) {
121                        throw new IOException(e.toString());
122                }
123        }
124
125        /**
126         * Customizes serialization by only writing the identifer of the function.
127         * @param stream the object output stream to which the object is to be written
128         * @throws IOException If any of the usual Input/Output related exceptions occur
129         */
130        private void writeObject(ObjectOutputStream stream) throws IOException {
131                stream.defaultWriteObject();
132                stream.writeObject(function.getIdentifier());
133        }
134}