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) 2007-2011 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.olap.fun; 011 012import mondrian.calc.*; 013import mondrian.calc.impl.*; 014import mondrian.mdx.ResolvedFunCall; 015import mondrian.olap.*; 016import mondrian.olap.type.ScalarType; 017 018import java.util.ArrayList; 019import java.util.List; 020 021/** 022 * Definition of the <code>DrilldownLevelTop</code> and 023 * <code>DrilldownLevelBottom</code> MDX builtin functions. 024 * 025 * <p>Syntax: 026 * 027 * <blockquote> 028 * DrilldownLevelTop(Set_Expression, Count [, [Level_Expression][, 029 * Numeric_Expression]])<br/> 030 * DrilldownLevelBottom(Set_Expression, Count [, [Level_Expression][, 031 * Numeric_Expression]]) 032 * </blockquote> 033 * 034 * @author jhyde 035 * @since Oct 18, 2007 036 */ 037class DrilldownLevelTopBottomFunDef extends FunDefBase { 038 final boolean top; 039 040 static final MultiResolver DrilldownLevelTopResolver = 041 new MultiResolver( 042 "DrilldownLevelTop", 043 "DrilldownLevelTop(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])", 044 "Drills down the topmost members of a set, at a specified level, to one level below.", 045 new String[] {"fxxn", "fxxnl", "fxxnln", "fxxnen"}) 046 { 047 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 048 return new DrilldownLevelTopBottomFunDef(dummyFunDef, true); 049 } 050 }; 051 052 static final MultiResolver DrilldownLevelBottomResolver = 053 new MultiResolver( 054 "DrilldownLevelBottom", 055 "DrilldownLevelBottom(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])", 056 "Drills down the bottommost members of a set, at a specified level, to one level below.", 057 new String[] {"fxxn", "fxxnl", "fxxnln", "fxxnen"}) 058 { 059 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 060 return new DrilldownLevelTopBottomFunDef(dummyFunDef, false); 061 } 062 }; 063 064 public DrilldownLevelTopBottomFunDef( 065 FunDef dummyFunDef, 066 final boolean top) 067 { 068 super(dummyFunDef); 069 this.top = top; 070 } 071 072 public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) { 073 // Compile the member list expression. Ask for a mutable list, because 074 // we're going to insert members into it later. 075 final ListCalc listCalc = 076 compiler.compileList(call.getArg(0), true); 077 final IntegerCalc integerCalc = 078 compiler.compileInteger(call.getArg(1)); 079 final LevelCalc levelCalc = 080 call.getArgCount() > 2 081 && call.getArg(2).getCategory() != Category.Empty 082 ? compiler.compileLevel(call.getArg(2)) 083 : null; 084 final Calc orderCalc = 085 call.getArgCount() > 3 086 ? compiler.compileScalar(call.getArg(3), true) 087 : new ValueCalc( 088 new DummyExp( 089 new ScalarType())); 090 return new AbstractListCalc( 091 call, 092 new Calc[] {listCalc, integerCalc, orderCalc}) 093 { 094 public TupleList evaluateList(Evaluator evaluator) { 095 // Use a native evaluator, if more efficient. 096 // TODO: Figure this out at compile time. 097 SchemaReader schemaReader = evaluator.getSchemaReader(); 098 NativeEvaluator nativeEvaluator = 099 schemaReader.getNativeSetEvaluator( 100 call.getFunDef(), call.getArgs(), evaluator, this); 101 if (nativeEvaluator != null) { 102 return 103 (TupleList) nativeEvaluator.execute(ResultStyle.LIST); 104 } 105 106 TupleList list = listCalc.evaluateList(evaluator); 107 int n = integerCalc.evaluateInteger(evaluator); 108 if (n == FunUtil.IntegerNull || n <= 0) { 109 return list; 110 } 111 Level level; 112 if (levelCalc == null) { 113 level = null; 114 } else { 115 level = levelCalc.evaluateLevel(evaluator); 116 } 117 List<Member> result = new ArrayList<Member>(); 118 assert list.getArity() == 1; 119 for (Member member : list.slice(0)) { 120 result.add(member); 121 if (level != null && member.getLevel() != level) { 122 if (level.getDimension() != member.getDimension()) { 123 throw newEvalException( 124 DrilldownLevelTopBottomFunDef.this, 125 "Level '" 126 + level.getUniqueName() 127 + "' not compatible with member '" 128 + member.getUniqueName() 129 + "'"); 130 } 131 continue; 132 } 133 List<Member> children = 134 schemaReader.getMemberChildren(member); 135 final int savepoint = evaluator.savepoint(); 136 List<Member> sortedChildren; 137 try { 138 evaluator.setNonEmpty(false); 139 sortedChildren = 140 sortMembers( 141 evaluator, 142 children, 143 children, 144 orderCalc, 145 top, 146 true); 147 } finally { 148 evaluator.restore(savepoint); 149 } 150 int x = Math.min(n, sortedChildren.size()); 151 for (int i = 0; i < x; i++) { 152 result.add(sortedChildren.get(i)); 153 } 154 } 155 return new UnaryTupleList(result); 156 } 157 158 public boolean dependsOn(Hierarchy hierarchy) { 159 return anyDependsButFirst(getCalcs(), hierarchy); 160 } 161 }; 162 } 163} 164 165// End DrilldownLevelTopBottomFunDef.java