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;
022    
023    import java.io.IOException;
024    import java.io.ObjectInputStream;
025    import java.io.ObjectOutputStream;
026    
027    import csheets.core.IllegalValueTypeException;
028    import csheets.core.Value;
029    import csheets.core.formula.compiler.IllegalFunctionCallException;
030    import csheets.core.formula.lang.Language;
031    import csheets.core.formula.lang.UnknownElementException;
032    import csheets.core.formula.util.ExpressionVisitor;
033    
034    /**
035     * A call to a function in a formula.
036     * @author Einar Pehrson
037     */
038    public 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    }