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) 2006-2011 Pentaho 008// All Rights Reserved. 009*/ 010package mondrian.olap.fun; 011 012import mondrian.calc.*; 013import mondrian.calc.impl.AbstractListCalc; 014import mondrian.calc.impl.UnaryTupleList; 015import mondrian.mdx.ResolvedFunCall; 016import mondrian.olap.*; 017 018import java.util.ArrayList; 019import java.util.List; 020 021/** 022 * Definition of the <code>DrilldownLevel</code> MDX function. 023 * 024 * <p>Syntax: 025 * 026 * <blockquote><pre> 027 * DrilldownLevel(Set_Expression[, Level_Expression]) 028 * DrilldownLevel(Set_Expression, , Numeric_Expression) 029 * </pre></blockquote> 030 * 031 * @author jhyde 032 * @since Mar 23, 2006 033 */ 034class DrilldownLevelFunDef extends FunDefBase { 035 static final ReflectiveMultiResolver Resolver = 036 new ReflectiveMultiResolver( 037 "DrilldownLevel", 038 "DrilldownLevel(<Set>[, <Level>]) or DrilldownLevel(<Set>, , <Index>)", 039 "Drills down the members of a set, at a specified level, to one level below. Alternatively, drills down on a specified dimension in the set.", 040 new String[]{"fxx", "fxxl", "fxxen"}, 041 DrilldownLevelFunDef.class); 042 043 public DrilldownLevelFunDef(FunDef dummyFunDef) { 044 super(dummyFunDef); 045 } 046 047 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 048 final ListCalc listCalc = 049 compiler.compileList(call.getArg(0)); 050 final LevelCalc levelCalc = 051 call.getArgCount() > 1 052 && call.getArg(1).getType() 053 instanceof mondrian.olap.type.LevelType 054 ? compiler.compileLevel(call.getArg(1)) 055 : null; 056 final IntegerCalc indexCalc = 057 call.getArgCount() > 2 058 ? compiler.compileInteger(call.getArg(2)) 059 : null; 060 final int arity = listCalc.getType().getArity(); 061 if (indexCalc == null) { 062 return new AbstractListCalc(call, new Calc[] {listCalc, levelCalc}) 063 { 064 public TupleList evaluateList(Evaluator evaluator) { 065 TupleList list = listCalc.evaluateList(evaluator); 066 if (list.size() == 0) { 067 return list; 068 } 069 int searchDepth = -1; 070 if (levelCalc != null) { 071 Level level = levelCalc.evaluateLevel(evaluator); 072 searchDepth = level.getDepth(); 073 } 074 return new UnaryTupleList( 075 drill(searchDepth, list.slice(0), evaluator)); 076 } 077 }; 078 } else { 079 return new AbstractListCalc(call, new Calc[] {listCalc, indexCalc}) 080 { 081 public TupleList evaluateList(Evaluator evaluator) { 082 TupleList list = listCalc.evaluateList(evaluator); 083 if (list.isEmpty()) { 084 return list; 085 } 086 final int index = indexCalc.evaluateInteger(evaluator); 087 if (index < 0 || index >= arity) { 088 return list; 089 } 090 TupleList result = TupleCollections.createList(arity); 091 final SchemaReader schemaReader = 092 evaluator.getSchemaReader(); 093 final Member[] tupleClone = new Member[arity]; 094 for (List<Member> tuple : list) { 095 result.add(tuple); 096 final List<Member> children = 097 schemaReader.getMemberChildren(tuple.get(index)); 098 for (Member child : children) { 099 tuple.toArray(tupleClone); 100 tupleClone[index] = child; 101 result.addTuple(tupleClone); 102 } 103 } 104 return result; 105 } 106 }; 107 } 108 } 109 110 List<Member> drill(int searchDepth, List<Member> list, Evaluator evaluator) 111 { 112 if (searchDepth == -1) { 113 searchDepth = list.get(0).getLevel().getDepth(); 114 115 for (int i = 1, m = list.size(); i < m; i++) { 116 Member member = list.get(i); 117 int memberDepth = member.getLevel().getDepth(); 118 119 if (memberDepth > searchDepth) { 120 searchDepth = memberDepth; 121 } 122 } 123 } 124 125 List<Member> drilledSet = new ArrayList<Member>(); 126 127 for (int i = 0, m = list.size(); i < m; i++) { 128 Member member = list.get(i); 129 drilledSet.add(member); 130 131 Member nextMember = 132 i == (m - 1) 133 ? null 134 : list.get(i + 1); 135 136 // 137 // This member is drilled if it's at the correct depth 138 // and if it isn't drilled yet. A member is considered 139 // to be "drilled" if it is immediately followed by 140 // at least one descendant 141 // 142 if (member.getLevel().getDepth() == searchDepth 143 && !FunUtil.isAncestorOf(member, nextMember, true)) 144 { 145 final List<Member> childMembers = 146 evaluator.getSchemaReader().getMemberChildren(member); 147 for (Member childMember : childMembers) { 148 drilledSet.add(childMember); 149 } 150 } 151 } 152 return drilledSet; 153 } 154} 155 156// End DrilldownLevelFunDef.java