001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/DrilldownLevelFunDef.java#5 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2006-2008 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.olap.fun; 011 012 import mondrian.olap.*; 013 import mondrian.olap.type.SetType; 014 import mondrian.calc.*; 015 import mondrian.calc.impl.AbstractListCalc; 016 import mondrian.mdx.ResolvedFunCall; 017 018 import java.util.*; 019 020 /** 021 * Definition of the <code>DrilldownLevel</code> MDX function. 022 * 023 * <p>Syntax: 024 * 025 * <blockquote><pre> 026 * DrilldownLevel(Set_Expression[, Level_Expression]) 027 * DrilldownLevel(Set_Expression, , Numeric_Expression) 028 * </pre></blockquote> 029 * 030 * @author jhyde 031 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/DrilldownLevelFunDef.java#5 $ 032 * @since Mar 23, 2006 033 */ 034 class DrilldownLevelFunDef extends FunDefBase { 035 static final ReflectiveMultiResolver Resolver = new ReflectiveMultiResolver( 036 "DrilldownLevel", 037 "DrilldownLevel(<Set>[, <Level>]) or DrilldownLevel(<Set>, , <Index>)", 038 "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.", 039 new String[]{"fxx", "fxxl", "fxxen"}, 040 DrilldownLevelFunDef.class); 041 042 public DrilldownLevelFunDef(FunDef dummyFunDef) { 043 super(dummyFunDef); 044 } 045 046 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 047 final ListCalc listCalc = 048 compiler.compileList(call.getArg(0)); 049 final LevelCalc levelCalc = 050 call.getArgCount() > 1 051 && call.getArg(1).getType() 052 instanceof mondrian.olap.type.LevelType 053 ? compiler.compileLevel(call.getArg(1)) 054 : null; 055 final IntegerCalc indexCalc = 056 call.getArgCount() > 2 057 ? compiler.compileInteger(call.getArg(2)) 058 : null; 059 final int arity = ((SetType) listCalc.getType()).getArity(); 060 if (indexCalc == null) { 061 return new AbstractListCalc(call, new Calc[] {listCalc, levelCalc}) { 062 public List evaluateList(Evaluator evaluator) { 063 List<Member> list = listCalc.evaluateList(evaluator); 064 if (list.size() == 0) { 065 return list; 066 } 067 int searchDepth = -1; 068 if (levelCalc != null) { 069 Level level = levelCalc.evaluateLevel(evaluator); 070 searchDepth = level.getDepth(); 071 } 072 return drill(searchDepth, list, evaluator); 073 } 074 }; 075 } else if (arity == 1) { 076 return new AbstractListCalc(call, new Calc[] {listCalc, indexCalc}) { 077 public List evaluateList(Evaluator evaluator) { 078 List<Member> list = listCalc.evaluateList(evaluator); 079 if (list.size() == 0) { 080 return list; 081 } 082 final int index = indexCalc.evaluateInteger(evaluator); 083 List<Member> result = new ArrayList<Member>(); 084 final SchemaReader schemaReader = 085 evaluator.getSchemaReader(); 086 for (Member member : list) { 087 result.add(member); 088 if (index == 0) { 089 final List<Member> children = 090 schemaReader.getMemberChildren(member); 091 result.addAll(children); 092 } 093 } 094 return result; 095 } 096 }; 097 } else { 098 return new AbstractListCalc(call, new Calc[] {listCalc, indexCalc}) { 099 public List evaluateList(Evaluator evaluator) { 100 List<Member[]> list = listCalc.evaluateList(evaluator); 101 if (list.size() == 0) { 102 return list; 103 } 104 final int index = indexCalc.evaluateInteger(evaluator); 105 List<Member[]> result = new ArrayList<Member[]>(); 106 final SchemaReader schemaReader = 107 evaluator.getSchemaReader(); 108 for (Member[] tuple : list) { 109 result.add(tuple); 110 if (index >= 0 && index < tuple.length) { 111 final List<Member> children = 112 schemaReader.getMemberChildren(tuple[index]); 113 for (Member child : children) { 114 final Member[] tupleClone = tuple.clone(); 115 tupleClone[index] = child; 116 result.add(tupleClone); 117 } 118 } 119 } 120 return result; 121 } 122 }; 123 } 124 } 125 126 List<Member> drill(int searchDepth, List<Member> list, Evaluator evaluator) { 127 if (searchDepth == -1) { 128 searchDepth = list.get(0).getLevel().getDepth(); 129 130 for (int i = 1, m = list.size(); i < m; i++) { 131 Member member = list.get(i); 132 int memberDepth = member.getLevel().getDepth(); 133 134 if (memberDepth > searchDepth) { 135 searchDepth = memberDepth; 136 } 137 } 138 } 139 140 List<Member> drilledSet = new ArrayList<Member>(); 141 142 for (int i = 0, m = list.size(); i < m; i++) { 143 Member member = list.get(i); 144 drilledSet.add(member); 145 146 Member nextMember = i == (m - 1) ? 147 null : 148 list.get(i + 1); 149 150 // 151 // This member is drilled if it's at the correct depth 152 // and if it isn't drilled yet. A member is considered 153 // to be "drilled" if it is immediately followed by 154 // at least one descendant 155 // 156 if (member.getLevel().getDepth() == searchDepth 157 && !FunUtil.isAncestorOf(member, nextMember, true)) { 158 final List<Member> childMembers = 159 evaluator.getSchemaReader().getMemberChildren(member); 160 for (Member childMember : childMembers) { 161 drilledSet.add(childMember); 162 } 163 } 164 } 165 return drilledSet; 166 } 167 } 168 169 // End DrilldownLevelFunDef.java