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.lang;
022
023import java.util.LinkedList;
024import java.util.List;
025
026import csheets.core.IllegalValueTypeException;
027import csheets.core.Value;
028import csheets.core.formula.BinaryOperator;
029import csheets.core.formula.Expression;
030import csheets.core.formula.Function;
031import csheets.core.formula.FunctionParameter;
032import csheets.core.formula.Literal;
033
034/**
035 * A function that emulates a looping statement, where each cell in a given
036 * range that satisfy a given condition, or each corresponding cell in
037 * another range, are passed to a function.
038 * @author Einar Pehrson
039 */
040public class Do implements Function {
041
042        /** Parameters: function, function range, condition and condition range */
043        public static final FunctionParameter[] parameters = new FunctionParameter[] {
044                new FunctionParameter(Value.Type.TEXT, "Function", false,
045                        "The name of the function to which arguments are to be passed."),
046                new FunctionParameter(Value.Type.MATRIX, "Function Range", false,
047                        "The range from which to select arguments"),
048                new FunctionParameter(Value.Type.TEXT, "Conditional Operator", false,
049                        "The binary operator to use in the condition when selecting arguments."),
050                new FunctionParameter(Value.Type.UNDEFINED, "Conditional Argument", false,
051                        "The right operand to use in the condition when selecting arguments."),
052                new FunctionParameter(Value.Type.MATRIX, "Condition Range", true,
053                        "The range to use when checking conditions.")
054        };
055
056        /**
057         * Creates a new instance of the DO function.
058         */
059        public Do() {}
060
061        public String getIdentifier() {
062                return "DO";
063        }
064
065        public Value applyTo(Expression[] arguments) throws IllegalValueTypeException {
066                // Check that the function and conditional operator exist
067                Function function = null;
068                BinaryOperator condOp = null;
069                try {
070                        function = Language.getInstance().getFunction(arguments[0].evaluate().toText());
071                        condOp = Language.getInstance().getBinaryOperator(arguments[2].evaluate().toText());
072                } catch (UnknownElementException e) {
073                        return new Value(e);
074                }
075                
076                // Check that the range dimensions agree
077                Value[][] opRange = arguments[1].evaluate().toMatrix();
078                Value[][] condRange = opRange;
079                if (arguments.length == 5) {
080                        condRange = arguments[4].evaluate().toMatrix();
081                        if (opRange.length != condRange.length || opRange[0].length != condRange[0].length)
082                                return new Value(new IllegalArgumentException("Range dimensions must be equal"));
083                }
084
085                // Collects arguments
086                Literal condArg = new Literal(arguments[3].evaluate());
087                List<Literal> accepted = new LinkedList<Literal>();
088                for (int row = 0; row < condRange.length; row++)
089                        for (int column = 0; column < condRange[row].length; column++)
090                                if (condOp.applyTo(new Literal(condRange[row][column]), condArg)
091                                                .toBoolean())
092                                        accepted.add(new Literal(opRange[row][column]));
093
094                // Evaluates function call and returns
095                if (accepted.size() > 0)
096                        return function.applyTo(accepted.toArray(new Expression[accepted.size()]));
097                else
098                        return new Value();
099        }
100
101        public FunctionParameter[] getParameters() {
102                return parameters;
103        }
104
105        public boolean isVarArg() {
106                return false;
107        }
108}