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