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) 2002-2005 Julian Hyde 008// Copyright (C) 2005-2012 Pentaho and others 009// All Rights Reserved. 010*/ 011package mondrian.olap.fun; 012 013import mondrian.calc.*; 014import mondrian.calc.impl.*; 015import mondrian.mdx.ResolvedFunCall; 016import mondrian.olap.*; 017 018import java.util.AbstractList; 019import java.util.List; 020 021/** 022 * Definition of the <code>TopCount</code> and <code>BottomCount</code> 023 * MDX builtin functions. 024 * 025 * @author jhyde 026 * @since Mar 23, 2006 027 */ 028class TopBottomCountFunDef extends FunDefBase { 029 boolean top; 030 031 static final MultiResolver TopCountResolver = 032 new MultiResolver( 033 "TopCount", 034 "TopCount(<Set>, <Count>[, <Numeric Expression>])", 035 "Returns a specified number of items from the top of a set, optionally ordering the set first.", 036 new String[]{"fxxnn", "fxxn"}) 037 { 038 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 039 return new TopBottomCountFunDef(dummyFunDef, true); 040 } 041 }; 042 043 static final MultiResolver BottomCountResolver = 044 new MultiResolver( 045 "BottomCount", 046 "BottomCount(<Set>, <Count>[, <Numeric Expression>])", 047 "Returns a specified number of items from the bottom of a set, optionally ordering the set first.", 048 new String[]{"fxxnn", "fxxn"}) 049 { 050 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 051 return new TopBottomCountFunDef(dummyFunDef, false); 052 } 053 }; 054 055 public TopBottomCountFunDef(FunDef dummyFunDef, final boolean top) { 056 super(dummyFunDef); 057 this.top = top; 058 } 059 060 public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) { 061 // Compile the member list expression. Ask for a mutable list, because 062 // we're going to sort it later. 063 final ListCalc listCalc = 064 compiler.compileList(call.getArg(0), true); 065 final IntegerCalc integerCalc = 066 compiler.compileInteger(call.getArg(1)); 067 final Calc orderCalc = 068 call.getArgCount() > 2 069 ? compiler.compileScalar(call.getArg(2), true) 070 : null; 071 final int arity = call.getType().getArity(); 072 return new AbstractListCalc( 073 call, 074 new Calc[]{listCalc, integerCalc, orderCalc}) 075 { 076 public TupleList evaluateList(Evaluator evaluator) { 077 // Use a native evaluator, if more efficient. 078 // TODO: Figure this out at compile time. 079 SchemaReader schemaReader = evaluator.getSchemaReader(); 080 NativeEvaluator nativeEvaluator = 081 schemaReader.getNativeSetEvaluator( 082 call.getFunDef(), call.getArgs(), evaluator, this); 083 if (nativeEvaluator != null) { 084 return 085 (TupleList) nativeEvaluator.execute(ResultStyle.LIST); 086 } 087 088 int n = integerCalc.evaluateInteger(evaluator); 089 if (n == 0 || n == mondrian.olap.fun.FunUtil.IntegerNull) { 090 return TupleCollections.emptyList(arity); 091 } 092 093 TupleList list = listCalc.evaluateList(evaluator); 094 assert list.getArity() == arity; 095 if (list.isEmpty()) { 096 return list; 097 } 098 099 if (orderCalc == null) { 100 // REVIEW: Why require "instanceof AbstractList"? 101 if (list instanceof AbstractList && list.size() <= n) { 102 return list; 103 } else { 104 return list.subList(0, n); 105 } 106 } 107 108 return partiallySortList( 109 evaluator, list, hasHighCardDimension(list), 110 Math.min(n, list.size())); 111 } 112 113 private TupleList partiallySortList( 114 Evaluator evaluator, 115 TupleList list, 116 boolean highCard, 117 int n) 118 { 119 assert list.size() > 0; 120 assert n <= list.size(); 121 if (highCard) { 122 // sort list in chunks, collect the results 123 final int chunkSize = 6400; // what is this really? 124 TupleList allChunkResults = TupleCollections.createList( 125 arity); 126 for (int i = 0, next; i < list.size(); i = next) { 127 next = Math.min(i + chunkSize, list.size()); 128 final TupleList chunk = list.subList(i, next); 129 TupleList chunkResult = 130 partiallySortList( 131 evaluator, chunk, false, n); 132 allChunkResults.addAll(chunkResult); 133 } 134 // one last sort, to merge and cull 135 return partiallySortList( 136 evaluator, allChunkResults, false, n); 137 } 138 139 // normal case: no need for chunks 140 final int savepoint = evaluator.savepoint(); 141 try { 142 switch (list.getArity()) { 143 case 1: 144 final List<Member> members = 145 partiallySortMembers( 146 evaluator.push(), 147 list.slice(0), 148 orderCalc, n, top); 149 return new UnaryTupleList(members); 150 default: 151 final List<List<Member>> tuples = 152 partiallySortTuples( 153 evaluator.push(), 154 list, 155 orderCalc, n, top); 156 return new DelegatingTupleList( 157 list.getArity(), 158 tuples); 159 } 160 } finally { 161 evaluator.restore(savepoint); 162 } 163 } 164 165 public boolean dependsOn(Hierarchy hierarchy) { 166 return anyDependsButFirst(getCalcs(), hierarchy); 167 } 168 169 private boolean hasHighCardDimension(TupleList l) { 170 final List<Member> trial = l.get(0); 171 for (Member m : trial) { 172 if (m.getHierarchy().getDimension().isHighCardinality()) { 173 return true; 174 } 175 } 176 return false; 177 } 178 }; 179 } 180} 181 182// End TopBottomCountFunDef.java