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