001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/TopBottomCountFunDef.java#10 $ 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) 2002-2002 Kana Software, Inc. 007 // Copyright (C) 2002-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.AbstractListCalc; 015 import mondrian.calc.ResultStyle; 016 import mondrian.mdx.ResolvedFunCall; 017 import mondrian.olap.*; 018 import mondrian.olap.type.TupleType; 019 020 import java.util.*; 021 022 /** 023 * Definition of the <code>TopCount</code> and <code>BottomCount</code> 024 * MDX builtin functions. 025 * 026 * @author jhyde 027 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/TopBottomCountFunDef.java#10 $ 028 * @since Mar 23, 2006 029 */ 030 class TopBottomCountFunDef extends FunDefBase { 031 boolean top; 032 033 static final MultiResolver TopCountResolver = 034 new MultiResolver( 035 "TopCount", 036 "TopCount(<Set>, <Count>[, <Numeric Expression>])", 037 "Returns a specified number of items from the top of a set, optionally ordering the set first.", 038 new String[]{"fxxnn", "fxxn"}) 039 { 040 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 041 return new TopBottomCountFunDef(dummyFunDef, true); 042 } 043 }; 044 045 static final MultiResolver BottomCountResolver = 046 new MultiResolver( 047 "BottomCount", 048 "BottomCount(<Set>, <Count>[, <Numeric Expression>])", 049 "Returns a specified number of items from the bottom of a set, optionally ordering the set first.", 050 new String[]{"fxxnn", "fxxn"}) 051 { 052 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 053 return new TopBottomCountFunDef(dummyFunDef, false); 054 } 055 }; 056 057 public TopBottomCountFunDef(FunDef dummyFunDef, final boolean top) { 058 super(dummyFunDef); 059 this.top = top; 060 } 061 062 public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) { 063 // Compile the member list expression. Ask for a mutable list, because 064 // we're going to sortMembers it later. 065 final ListCalc listCalc = 066 compiler.compileList(call.getArg(0), true); 067 final IntegerCalc integerCalc = 068 compiler.compileInteger(call.getArg(1)); 069 final Calc orderCalc = 070 call.getArgCount() > 2 ? 071 compiler.compileScalar(call.getArg(2), true) : 072 null; 073 final int arity = call.getType() instanceof TupleType ? 074 ((TupleType) call.getType()).elementTypes.length : 075 1; 076 return new AbstractListCalc(call, new Calc[] {listCalc, integerCalc, orderCalc}) { 077 public List evaluateList(Evaluator evaluator) { 078 // Use a native evaluator, if more efficient. 079 // TODO: Figure this out at compile time. 080 SchemaReader schemaReader = evaluator.getSchemaReader(); 081 NativeEvaluator nativeEvaluator = 082 schemaReader.getNativeSetEvaluator( 083 call.getFunDef(), call.getArgs(), evaluator, this); 084 if (nativeEvaluator != null) { 085 return (List) nativeEvaluator.execute(ResultStyle.LIST); 086 } 087 088 List list = listCalc.evaluateList(evaluator); 089 int n = integerCalc.evaluateInteger(evaluator); 090 091 if (n == mondrian.olap.fun.FunUtil.IntegerNull) { 092 return new java.util.ArrayList(); 093 } 094 095 if (orderCalc != null) { 096 if (list.isEmpty()) { 097 return list; 098 } 099 final Object trial = list.get(0); 100 boolean highCard = false; 101 if (trial instanceof Member) { 102 if (((Member) trial).getHierarchy().getDimension() 103 .isHighCardinality()) { 104 highCard = true; 105 } 106 } else { 107 final Member[] trials = (Member[]) trial; 108 for (int i = 0; i < trials.length; i++) { 109 if (trials[i].getHierarchy().getDimension() 110 .isHighCardinality()) { 111 break; 112 } 113 } 114 } 115 116 if (highCard) { 117 final List l2 = new ArrayList(); 118 final Iterator iterator = list.iterator(); 119 for (int i = 0; i < n + 1 && iterator.hasNext(); i++) { 120 final Object o = iterator.next(); 121 l2.add(o); 122 } 123 final Evaluator eval = evaluator.push(); 124 if (arity == 1) { 125 for (;;) { 126 sortMembers( 127 eval, 128 (List<Member>) l2, 129 orderCalc, top, true); 130 l2.remove(l2.size() - 1); 131 if (!iterator.hasNext()) { 132 break; 133 } 134 l2.add(iterator.next()); 135 } 136 return l2; 137 } else { 138 for (;;) { 139 sortTuples( 140 eval, 141 (List<mondrian.olap.Member[]>) l2, 142 orderCalc, top, true, arity); 143 l2.remove(l2.size() - 1); 144 if (!iterator.hasNext()) { 145 break; 146 } 147 l2.add(iterator.next()); 148 } 149 return l2; 150 } 151 } else { 152 if (arity == 1) { 153 sortMembers( 154 evaluator.push(), 155 (List<Member>) list, 156 orderCalc, top, true); 157 } else { 158 sortTuples( 159 evaluator.push(), 160 (List<mondrian.olap.Member[]>) list, 161 orderCalc, top, true, arity); 162 } 163 if (n < list.size()) { 164 list = list.subList(0, n); 165 } 166 return list; 167 } 168 } else { 169 if (list instanceof AbstractList && list.size() < n) { 170 return list; 171 } else { 172 return list.subList(0, n); 173 } 174 } 175 } 176 177 public boolean dependsOn(Dimension dimension) { 178 return anyDependsButFirst(getCalcs(), dimension); 179 } 180 }; 181 } 182 } 183 184 // End TopBottomCountFunDef.java