001 /* 002 // This software is subject to the terms of the Common Public License 003 // Agreement, available at the following URL: 004 // http://www.opensource.org/licenses/cpl.html. 005 // Copyright (C) 2007-2008 Julian Hyde 006 // All Rights Reserved. 007 // You must accept the terms of that agreement to use this software. 008 */ 009 package mondrian.olap.fun; 010 011 import mondrian.olap.*; 012 import mondrian.olap.type.ScalarType; 013 import mondrian.calc.*; 014 import mondrian.calc.impl.AbstractListCalc; 015 import mondrian.calc.impl.ValueCalc; 016 import mondrian.mdx.ResolvedFunCall; 017 018 import java.util.*; 019 020 /** 021 * Definition of the <code>DrilldownLevelTop</code> and 022 * <code>DrilldownLevelBottom</code> MDX builtin functions. 023 * 024 * <p>Syntax: 025 * 026 * <blockquote><pre> 027 * DrilldownLevelTop(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]]) 028 * DrilldownLevelBottom(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]]) 029 * </pre></blockquote> 030 * 031 * @author jhyde 032 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/DrilldownLevelTopBottomFunDef.java#2 $ 033 * @since Oct 18, 2007 034 */ 035 class DrilldownLevelTopBottomFunDef extends FunDefBase { 036 final boolean top; 037 038 static final MultiResolver DrilldownLevelTopResolver = new MultiResolver( 039 "DrilldownLevelTop", 040 "DrilldownLevelTop(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])", 041 "Drills down the topmost members of a set, at a specified level, to one level below.", 042 new String[] {"fxxn", "fxxnl", "fxxnln", "fxxnen"}) { 043 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 044 return new DrilldownLevelTopBottomFunDef(dummyFunDef, true); 045 } 046 }; 047 048 static final MultiResolver DrilldownLevelBottomResolver = new MultiResolver( 049 "DrilldownLevelBottom", 050 "DrilldownLevelBottom(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])", 051 "Drills down the bottommost members of a set, at a specified level, to one level below.", 052 new String[] {"fxxn", "fxxnl", "fxxnln", "fxxnen"}) { 053 protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) { 054 return new DrilldownLevelTopBottomFunDef(dummyFunDef, false); 055 } 056 }; 057 058 public DrilldownLevelTopBottomFunDef(FunDef dummyFunDef, final boolean top) { 059 super(dummyFunDef); 060 this.top = top; 061 } 062 063 public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) { 064 // Compile the member list expression. Ask for a mutable list, because 065 // we're going to insert members into it later. 066 final ListCalc listCalc = 067 compiler.compileList(call.getArg(0), true); 068 final IntegerCalc integerCalc = 069 compiler.compileInteger(call.getArg(1)); 070 final LevelCalc levelCalc = 071 call.getArgCount() > 2 072 && call.getArg(2).getCategory() != Category.Empty 073 ? compiler.compileLevel(call.getArg(2)) 074 : null; 075 final Calc orderCalc = 076 call.getArgCount() > 3 ? 077 compiler.compileScalar(call.getArg(3), true) : 078 new ValueCalc( 079 new DummyExp( 080 new ScalarType())); 081 return new AbstractListCalc(call, new Calc[] {listCalc, integerCalc, orderCalc}) { 082 public List evaluateList(Evaluator evaluator) { 083 // Use a native evaluator, if more efficient. 084 // TODO: Figure this out at compile time. 085 SchemaReader schemaReader = evaluator.getSchemaReader(); 086 NativeEvaluator nativeEvaluator = 087 schemaReader.getNativeSetEvaluator( 088 call.getFunDef(), call.getArgs(), evaluator, this); 089 if (nativeEvaluator != null) { 090 return (List) nativeEvaluator.execute(ResultStyle.LIST); 091 } 092 093 List<Member> list = listCalc.evaluateList(evaluator); 094 int n = integerCalc.evaluateInteger(evaluator); 095 if (n == FunUtil.IntegerNull || n <= 0) { 096 return list; 097 } 098 Level level; 099 if (levelCalc == null) { 100 level = null; 101 } else { 102 level = levelCalc.evaluateLevel(evaluator); 103 } 104 List<Member> result = new ArrayList<Member>(); 105 for (Member member : list) { 106 result.add(member); 107 if (level != null && member.getLevel() != level) { 108 if (level.getDimension() != member.getDimension()) { 109 throw newEvalException( 110 DrilldownLevelTopBottomFunDef.this, 111 "Level '" 112 + level.getUniqueName() 113 + "' not compatible with member '" 114 + member.getUniqueName() 115 + "'"); 116 } 117 continue; 118 } 119 List<Member> children = schemaReader.getMemberChildren(member); 120 sortMembers( 121 evaluator.push(), 122 children, 123 orderCalc, 124 top, 125 true); 126 int x = Math.min(n, children.size()); 127 for (int i = 0; i < x; i++) { 128 result.add(children.get(i)); 129 } 130 } 131 return result; 132 } 133 134 public boolean dependsOn(Dimension dimension) { 135 return anyDependsButFirst(getCalcs(), dimension); 136 } 137 }; 138 } 139 } 140 141 // End DrilldownLevelTopBottomFunDef.java