/* // This software is subject to the terms of the Eclipse Public License v1.0 // Agreement, available at the following URL: // http://www.eclipse.org/legal/epl-v10.html. // You must accept the terms of that agreement to use this software. // // Copyright (C) 2002-2005 Julian Hyde // Copyright (C) 2005-2012 Pentaho and others // All Rights Reserved. */ package mondrian.olap.fun; import mondrian.calc.*; import mondrian.calc.impl.*; import mondrian.mdx.ResolvedFunCall; import mondrian.olap.*; import java.util.AbstractList; import java.util.List; /** * Definition of the <code>TopCount</code> and <code>BottomCount</code> * MDX builtin functions. * * @author jhyde * @since Mar 23, 2006 */ class TopBottomCountFunDef extends FunDefBase { boolean top; static final MultiResolver TopCountResolver = new MultiResolver( "TopCount", "TopCount(<Set>, <Count>[, <Numeric Expression>])", "Returns a specified number of items from the top of a set, optionally ordering the set first.", new String[]{"fxxnn", "fxxn"}) { protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { return new TopBottomCountFunDef(dummyFunDef, true); } }; static final MultiResolver BottomCountResolver = new MultiResolver( "BottomCount", "BottomCount(<Set>, <Count>[, <Numeric Expression>])", "Returns a specified number of items from the bottom of a set, optionally ordering the set first.", new String[]{"fxxnn", "fxxn"}) { protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { return new TopBottomCountFunDef(dummyFunDef, false); } }; public TopBottomCountFunDef(FunDef dummyFunDef, final boolean top) { super(dummyFunDef); this.top = top; } public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) { // Compile the member list expression. Ask for a mutable list, because // we're going to sort it later. final ListCalc listCalc = compiler.compileList(call.getArg(0), true); final IntegerCalc integerCalc = compiler.compileInteger(call.getArg(1)); final Calc orderCalc = call.getArgCount() > 2 ? compiler.compileScalar(call.getArg(2), true) : null; final int arity = call.getType().getArity(); return new AbstractListCalc( call, new Calc[]{listCalc, integerCalc, orderCalc}) { public TupleList evaluateList(Evaluator evaluator) { // Use a native evaluator, if more efficient. // TODO: Figure this out at compile time. SchemaReader schemaReader = evaluator.getSchemaReader(); NativeEvaluator nativeEvaluator = schemaReader.getNativeSetEvaluator( call.getFunDef(), call.getArgs(), evaluator, this); if (nativeEvaluator != null) { return (TupleList) nativeEvaluator.execute(ResultStyle.LIST); } int n = integerCalc.evaluateInteger(evaluator); if (n == 0 || n == mondrian.olap.fun.FunUtil.IntegerNull) { return TupleCollections.emptyList(arity); } TupleList list = listCalc.evaluateList(evaluator); assert list.getArity() == arity; if (list.isEmpty()) { return list; } if (orderCalc == null) { // REVIEW: Why require "instanceof AbstractList"? if (list instanceof AbstractList && list.size() <= n) { return list; } else { return list.subList(0, n); } } return partiallySortList( evaluator, list, hasHighCardDimension(list), Math.min(n, list.size())); } private TupleList partiallySortList( Evaluator evaluator, TupleList list, boolean highCard, int n) { assert list.size() > 0; assert n <= list.size(); if (highCard) { // sort list in chunks, collect the results final int chunkSize = 6400; // what is this really? TupleList allChunkResults = TupleCollections.createList( arity); for (int i = 0, next; i < list.size(); i = next) { next = Math.min(i + chunkSize, list.size()); final TupleList chunk = list.subList(i, next); TupleList chunkResult = partiallySortList( evaluator, chunk, false, n); allChunkResults.addAll(chunkResult); } // one last sort, to merge and cull return partiallySortList( evaluator, allChunkResults, false, n); } // normal case: no need for chunks final int savepoint = evaluator.savepoint(); try { switch (list.getArity()) { case 1: final List<Member> members = partiallySortMembers( evaluator.push(), list.slice(0), orderCalc, n, top); return new UnaryTupleList(members); default: final List<List<Member>> tuples = partiallySortTuples( evaluator.push(), list, orderCalc, n, top); return new DelegatingTupleList( list.getArity(), tuples); } } finally { evaluator.restore(savepoint); } } public boolean dependsOn(Hierarchy hierarchy) { return anyDependsButFirst(getCalcs(), hierarchy); } private boolean hasHighCardDimension(TupleList l) { final List<Member> trial = l.get(0); for (Member m : trial) { if (m.getHierarchy().getDimension().isHighCardinality()) { return true; } } return false; } }; } } // End TopBottomCountFunDef.java