001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2006-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.olap.fun;
011
012import mondrian.calc.Calc;
013import mondrian.calc.ExpCompiler;
014import mondrian.calc.impl.ConstantCalc;
015import mondrian.calc.impl.GenericCalc;
016import mondrian.mdx.ResolvedFunCall;
017import mondrian.olap.*;
018
019import java.util.ArrayList;
020import java.util.List;
021
022/**
023 * Definition of the matched <code>CASE</code> MDX operator.
024 *
025 * Syntax is:
026 * <blockquote><pre><code>Case &lt;Expression&gt;
027 * When &lt;Expression&gt; Then &lt;Expression&gt;
028 * [...]
029 * [Else &lt;Expression&gt;]
030 * End</code></blockquote>.
031 *
032 * @see CaseTestFunDef
033 * @author jhyde
034 * @since Mar 23, 2006
035 */
036class CaseMatchFunDef extends FunDefBase {
037    static final ResolverImpl Resolver = new ResolverImpl();
038
039    private CaseMatchFunDef(FunDef dummyFunDef) {
040        super(dummyFunDef);
041    }
042
043    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
044        final Exp[] args = call.getArgs();
045        final List<Calc> calcList = new ArrayList<Calc>();
046        final Calc valueCalc =
047                compiler.compileScalar(args[0], true);
048        calcList.add(valueCalc);
049        final int matchCount = (args.length - 1) / 2;
050        final Calc[] matchCalcs = new Calc[matchCount];
051        final Calc[] exprCalcs = new Calc[matchCount];
052        for (int i = 0, j = 1; i < exprCalcs.length; i++) {
053            matchCalcs[i] = compiler.compileScalar(args[j++], true);
054            calcList.add(matchCalcs[i]);
055            exprCalcs[i] = compiler.compile(args[j++]);
056            calcList.add(exprCalcs[i]);
057        }
058        final Calc defaultCalc =
059            args.length % 2 == 0
060            ? compiler.compile(args[args.length - 1])
061            : ConstantCalc.constantNull(call.getType());
062        calcList.add(defaultCalc);
063        final Calc[] calcs = calcList.toArray(new Calc[calcList.size()]);
064
065        return new GenericCalc(call) {
066            public Object evaluate(Evaluator evaluator) {
067                Object value = valueCalc.evaluate(evaluator);
068                for (int i = 0; i < matchCalcs.length; i++) {
069                    Object match = matchCalcs[i].evaluate(evaluator);
070                    if (match.equals(value)) {
071                        return exprCalcs[i].evaluate(evaluator);
072                    }
073                }
074                return defaultCalc.evaluate(evaluator);
075            }
076
077            public Calc[] getCalcs() {
078                return calcs;
079            }
080        };
081    }
082
083    private static class ResolverImpl extends ResolverBase {
084        private ResolverImpl() {
085            super(
086                "_CaseMatch",
087                "Case <Expression> When <Expression> Then <Expression> [...] [Else <Expression>] End",
088                "Evaluates various expressions, and returns the corresponding expression for the first which matches a particular value.",
089                Syntax.Case);
090        }
091
092        public FunDef resolve(
093            Exp[] args,
094            Validator validator,
095            List<Conversion> conversions)
096        {
097            if (args.length < 3) {
098                return null;
099            }
100            int valueType = args[0].getCategory();
101            int returnType = args[2].getCategory();
102            int j = 0;
103            int clauseCount = (args.length - 1) / 2;
104            int mismatchingArgs = 0;
105            if (!validator.canConvert(j, args[j++], valueType, conversions)) {
106                mismatchingArgs++;
107            }
108            for (int i = 0; i < clauseCount; i++) {
109                if (!validator.canConvert(j, args[j++], valueType, conversions))
110                {
111                    mismatchingArgs++;
112                }
113                if (!validator.canConvert(
114                        j, args[j++], returnType, conversions))
115                {
116                    mismatchingArgs++;
117                }
118            }
119            if (j < args.length) {
120                if (!validator.canConvert(
121                        j, args[j++], returnType, conversions))
122                {
123                    mismatchingArgs++;
124                }
125            }
126            Util.assertTrue(j == args.length);
127            if (mismatchingArgs != 0) {
128                return null;
129            }
130
131            FunDef dummy = createDummyFunDef(this, returnType, args);
132            return new CaseMatchFunDef(dummy);
133        }
134
135        public boolean requiresExpression(int k) {
136            return true;
137        }
138    }
139}
140
141// End CaseMatchFunDef.java