001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/OrderFunDef.java#18 $
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) 2004-2002 Kana Software, Inc.
007    // Copyright (C) 2004-2008 Julian Hyde and others
008    // All Rights Reserved.
009    // You must accept the terms of that agreement to use this software.
010    */
011    package mondrian.olap.fun;
012    
013    import mondrian.calc.*;
014    import mondrian.calc.impl.*;
015    import mondrian.olap.*;
016    import mondrian.mdx.ResolvedFunCall;
017    
018    import java.util.*;
019    
020    /**
021     * Definition of the <code>Order</code> MDX function.
022     *
023     * @author jhyde
024     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/OrderFunDef.java#18 $
025     * @since Mar 23, 2006
026     */
027    class OrderFunDef extends FunDefBase {
028    
029        static final ReflectiveMultiResolver Resolver = new ReflectiveMultiResolver(
030                "Order",
031                "Order(<Set>, <Value Expression>[, ASC | DESC | BASC | BDESC])",
032                "Arranges members of a set, optionally preserving or breaking the hierarchy.",
033                new String[]{"fxxvy", "fxxv"},
034                OrderFunDef.class,
035                Flag.getNames());
036    
037        public OrderFunDef(FunDef dummyFunDef) {
038            super(dummyFunDef);
039        }
040    
041        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
042            final ListCalc listCalc = compiler.compileList(call.getArg(0), true);
043            final Calc expCalc = compiler.compileScalar(call.getArg(1), true);
044            final Flag order = getLiteralArg(call, 2, Flag.ASC, Flag.class);
045    
046            if (expCalc instanceof MemberValueCalc) {
047                MemberValueCalc memberValueCalc = (MemberValueCalc) expCalc;
048                List<Calc> constantList = new ArrayList<Calc>();
049                List<Calc> variableList = new ArrayList<Calc>();
050                final MemberCalc[] calcs = (MemberCalc[]) memberValueCalc.getCalcs();
051                for (MemberCalc memberCalc : calcs) {
052                    if (memberCalc instanceof ConstantCalc &&
053                        !listCalc.dependsOn(
054                            memberCalc.getType().getHierarchy().getDimension())) {
055                        constantList.add(memberCalc);
056                    } else {
057                        variableList.add(memberCalc);
058                    }
059                }
060                if (constantList.isEmpty()) {
061                    // All members are non-constant -- cannot optimize
062                } else if (variableList.isEmpty()) {
063                    // All members are constant. Optimize by setting entire context
064                    // first.
065                    return new ContextCalc(
066                            calcs,
067                            new CalcImpl(
068                                    call,
069                                    listCalc,
070                                    new ValueCalc(
071                                            new DummyExp(expCalc.getType())),
072                                order.descending,
073                                order.brk));
074                } else {
075                    // Some members are constant. Evaluate these before evaluating
076                    // the list expression.
077                    return new ContextCalc(
078                        constantList.toArray(
079                            new MemberCalc[constantList.size()]),
080                            new CalcImpl(
081                                    call,
082                                    listCalc,
083                                    new MemberValueCalc(
084                                        new DummyExp(expCalc.getType()),
085                                        variableList.toArray(
086                                            new MemberCalc[variableList.size()])),
087                                order.descending,
088                                order.brk));
089                }
090            }
091            return new CalcImpl(call, listCalc, expCalc, order.descending, order.brk);
092        }
093    
094        /**
095         * Enumeration of the flags allowed to the <code>ORDER</code> MDX function.
096         */
097        enum Flag {
098            ASC(false, false),
099            DESC(true, false),
100            BASC(false, true),
101            BDESC(true, true);
102    
103            private final boolean descending;
104            private final boolean brk;
105    
106            Flag(boolean descending, boolean brk) {
107                this.descending = descending;
108                this.brk = brk;
109            }
110    
111            public static String[] getNames() {
112                List<String> names = new ArrayList<String>();
113                for (Flag flags : Flag.class.getEnumConstants()) {
114                    names.add(flags.name());
115                }
116                return names.toArray(new String[names.size()]);
117            }
118        }
119    
120        private static class CalcImpl extends AbstractListCalc {
121            private final ListCalc listCalc;
122            private final Calc expCalc;
123            private final boolean desc;
124            private final boolean brk;
125    
126            public CalcImpl(
127                ResolvedFunCall call,
128                ListCalc listCalc,
129                Calc expCalc,
130                boolean desc,
131                boolean brk)
132            {
133                super(call, new Calc[]{listCalc, expCalc});
134                assert listCalc.getResultStyle() == ResultStyle.MUTABLE_LIST;
135                this.listCalc = listCalc;
136                this.expCalc = expCalc;
137                this.desc = desc;
138                this.brk = brk;
139            }
140    
141            public List evaluateDual(Evaluator rootEvaluator, Evaluator subEvaluator) {
142                List list = listCalc.evaluateList(rootEvaluator);
143                sortMembers(subEvaluator.push(), list, expCalc, desc, brk);
144                return list;
145            }
146    
147            public List evaluateList(Evaluator evaluator) {
148                List list = listCalc.evaluateList(evaluator);
149                sortMembers(evaluator.push(), list, expCalc, desc, brk);
150                return list;
151            }
152    
153            public Calc[] getCalcs() {
154                return new Calc[] {listCalc, expCalc};
155            }
156    
157            public List<Object> getArguments() {
158                return Collections.singletonList(
159                    (Object) (desc ?
160                        (brk ? Flag.BDESC : Flag.DESC) :
161                        (brk ? Flag.BASC : Flag.ASC)));
162            }
163    
164            public boolean dependsOn(Dimension dimension) {
165                return anyDependsButFirst(getCalcs(), dimension);
166            }
167        }
168    
169        private static class ContextCalc extends GenericCalc {
170            private final MemberCalc[] memberCalcs;
171            private final CalcImpl calc;
172            private final Calc[] calcs;
173            private final Member[] members; // workspace
174    
175            protected ContextCalc(MemberCalc[] memberCalcs, CalcImpl calc) {
176                super(new DummyExp(calc.getType()));
177                this.memberCalcs = memberCalcs;
178                this.calc = calc;
179                this.calcs = new Calc[memberCalcs.length + 1];
180                System.arraycopy(memberCalcs, 0, this.calcs, 0, memberCalcs.length);
181                this.calcs[this.calcs.length - 1] = calc;
182                this.members = new Member[memberCalcs.length];
183            }
184    
185            public Calc[] getCalcs() {
186                return calcs;
187            }
188    
189            public Object evaluate(Evaluator evaluator) {
190                // Evaluate each of the members, and set as context in the
191                // sub-evaluator.
192                for (int i = 0; i < memberCalcs.length; i++) {
193                    members[i] = memberCalcs[i].evaluateMember(evaluator);
194                }
195                final Evaluator subEval = evaluator.push(members);
196                // Evaluate the expression in the new context.
197                return calc.evaluateDual(evaluator, subEval);
198            }
199    
200            public boolean dependsOn(Dimension dimension) {
201                if (anyDepends(memberCalcs, dimension)) {
202                    return true;
203                }
204                // Member calculations generate members, which mask the actual
205                // expression from the inherited context.
206                for (MemberCalc memberCalc : memberCalcs) {
207                    if (memberCalc.getType().usesDimension(dimension, true)) {
208                        return false;
209                    }
210                }
211                return calc.dependsOn(dimension);
212            }
213            public ResultStyle getResultStyle() {
214                return calc.getResultStyle();
215            }
216        }
217    }
218    
219    // End OrderFunDef.java