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.lang;
022    
023    import java.util.LinkedList;
024    import java.util.List;
025    
026    import csheets.core.IllegalValueTypeException;
027    import csheets.core.Value;
028    import csheets.core.formula.BinaryOperator;
029    import csheets.core.formula.Expression;
030    import csheets.core.formula.Function;
031    import csheets.core.formula.FunctionParameter;
032    import 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     */
040    public 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    }