001 /* 002 // $Id: //open/mondrian/src/main/mondrian/olap/fun/GenerateFunDef.java#7 $ 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.*; 014 import mondrian.calc.*; 015 import mondrian.calc.impl.AbstractListCalc; 016 import mondrian.calc.impl.ConstantCalc; 017 import mondrian.calc.impl.AbstractStringCalc; 018 import mondrian.mdx.ResolvedFunCall; 019 020 import java.util.List; 021 import java.util.Arrays; 022 import java.util.ArrayList; 023 import java.util.HashSet; 024 import java.util.Set; 025 026 /** 027 * Definition of the <code>Generate</code> MDX function. 028 * 029 * @author jhyde 030 * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/GenerateFunDef.java#7 $ 031 * @since Mar 23, 2006 032 */ 033 class GenerateFunDef extends FunDefBase { 034 static final ReflectiveMultiResolver ListResolver = 035 new ReflectiveMultiResolver( 036 "Generate", 037 "Generate(<Set1>, <Set2>[, ALL])", 038 "Applies a set to each member of another set and joins the resulting sets by union.", 039 new String[] {"fxxx", "fxxxy"}, 040 GenerateFunDef.class); 041 042 static final ReflectiveMultiResolver StringResolver = 043 new ReflectiveMultiResolver( 044 "Generate", 045 "Generate(<Set>, <String>[, <String>])", 046 "Applies a set to a string expression and joins resulting sets by string concatenation.", 047 new String[] {"fSxS", "fSxSS"}, 048 GenerateFunDef.class); 049 050 private static final String[] ReservedWords = new String[] {"ALL"}; 051 052 public GenerateFunDef(FunDef dummyFunDef) { 053 super(dummyFunDef); 054 } 055 056 public Type getResultType(Validator validator, Exp[] args) { 057 final Type type = args[1].getType(); 058 if (type instanceof StringType) { 059 // Generate(<Set>, <String>[, <String>]) 060 return type; 061 } else { 062 final Type memberType = TypeUtil.toMemberOrTupleType(type); 063 return new SetType(memberType); 064 } 065 } 066 067 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 068 final ListCalc listCalc = compiler.compileList(call.getArg(0)); 069 final boolean tupleIn = ((SetType) listCalc.getType()).getElementType() 070 instanceof TupleType; 071 if (call.getArg(1).getType() instanceof StringType) { 072 final StringCalc stringCalc = compiler.compileString(call.getArg(1)); 073 final StringCalc delimCalc; 074 if (call.getArgCount() == 3) { 075 delimCalc = compiler.compileString(call.getArg(2)); 076 } else { 077 delimCalc = ConstantCalc.constantString(""); 078 } 079 080 return new GenerateStringCalcImpl( 081 call, listCalc, stringCalc, tupleIn, delimCalc); 082 } else { 083 final ListCalc listCalc2 = compiler.compileList(call.getArg(1)); 084 final String literalArg = getLiteralArg(call, 2, "", ReservedWords); 085 final boolean all = literalArg.equalsIgnoreCase("ALL"); 086 final boolean tupleOut = 087 ((SetType) call.getType()).getElementType() 088 instanceof TupleType; 089 return new GenerateListCalcImpl( 090 call, listCalc, listCalc2, tupleIn, tupleOut, all); 091 } 092 } 093 094 private static class GenerateListCalcImpl extends AbstractListCalc { 095 private final MemberListCalc memberListCalc1; 096 private final TupleListCalc tupleListCalc1; 097 private final MemberListCalc memberListCalc2; 098 private final TupleListCalc tupleListCalc2; 099 private final boolean tupleIn; 100 private final boolean tupleOut; 101 private final boolean all; 102 103 public GenerateListCalcImpl( 104 ResolvedFunCall call, 105 ListCalc listCalc1, 106 ListCalc listCalc2, 107 boolean tupleIn, 108 boolean tupleOut, 109 boolean all) 110 { 111 super(call, new Calc[]{listCalc1, listCalc2}); 112 if (tupleIn) { 113 this.memberListCalc1 = null; 114 this.tupleListCalc1 = (TupleListCalc) listCalc1; 115 } else { 116 this.memberListCalc1 = (MemberListCalc) listCalc1; 117 this.tupleListCalc1 = null; 118 } 119 if (tupleOut) { 120 this.memberListCalc2 = null; 121 this.tupleListCalc2 = (TupleListCalc) listCalc2; 122 } else { 123 this.memberListCalc2 = (MemberListCalc) listCalc2; 124 this.tupleListCalc2 = null; 125 } 126 this.tupleIn = tupleIn; 127 this.tupleOut = tupleOut; 128 this.all = all; 129 } 130 131 public List evaluateList(Evaluator evaluator) { 132 // 8 cases - all of the combinations of tupleIn x tupleOut x all 133 final Evaluator evaluator2 = evaluator.push(); 134 if (tupleIn) { 135 final List<Member[]> list1 = 136 tupleListCalc1.evaluateTupleList(evaluator); 137 if (tupleOut) { 138 List<Member[]> result = new ArrayList<Member[]>(); 139 if (all) { 140 for (Member[] members : list1) { 141 evaluator2.setContext(members); 142 final List<Member[]> result2 = 143 tupleListCalc2.evaluateTupleList(evaluator2); 144 result.addAll(result2); 145 } 146 } else { 147 final Set<List<Member>> emitted = 148 new HashSet<List<Member>>(); 149 for (Member[] members : list1) { 150 evaluator2.setContext(members); 151 final List<Member[]> result2 = 152 tupleListCalc2.evaluateTupleList(evaluator2); 153 addDistinctTuples(result, result2, emitted); 154 } 155 } 156 return result; 157 } else { 158 List<Member> result = new ArrayList<Member>(); 159 final Set<Member> emitted = 160 all ? null : new HashSet<Member>(); 161 for (Member[] members : list1) { 162 evaluator2.setContext(members); 163 final List<Member> result2 = 164 memberListCalc2.evaluateMemberList(evaluator2); 165 if (emitted != null) { 166 addDistinctMembers(result, result2, emitted); 167 } else { 168 result.addAll(result2); 169 } 170 } 171 return result; 172 } 173 } else { 174 final List<Member> list1 = 175 memberListCalc1.evaluateMemberList(evaluator); 176 if (tupleOut) { 177 List<Member[]> result = new ArrayList<Member[]>(); 178 if (all) { 179 for (Member member : list1) { 180 evaluator2.setContext(member); 181 final List<Member[]> result2 = 182 tupleListCalc2.evaluateTupleList(evaluator2); 183 result.addAll(result2); 184 } 185 } else { 186 final Set<List<Member>> emitted = 187 new HashSet<List<Member>>(); 188 for (Member member : list1) { 189 evaluator2.setContext(member); 190 final List<Member[]> result2 = 191 tupleListCalc2.evaluateTupleList(evaluator2); 192 addDistinctTuples(result, result2, emitted); 193 } 194 } 195 return result; 196 } else { 197 List<Member> result = new ArrayList<Member>(); 198 if (all) { 199 for (Member member : list1) { 200 evaluator2.setContext(member); 201 final List<Member> result2 = 202 memberListCalc2.evaluateMemberList(evaluator2); 203 result.addAll(result2); 204 } 205 } else { 206 final Set<Member> emitted = new HashSet<Member>(); 207 for (Member member : list1) { 208 evaluator2.setContext(member); 209 final List<Member> result2 = 210 memberListCalc2.evaluateMemberList(evaluator2); 211 addDistinctMembers(result, result2, emitted); 212 } 213 } 214 return result; 215 } 216 } 217 } 218 219 private static void addDistinctMembers( 220 List<Member> result, 221 List<Member> result2, 222 Set<Member> emitted) 223 { 224 for (Member row : result2) { 225 if (emitted.add(row)) { 226 result.add(row); 227 } 228 } 229 } 230 231 private static void addDistinctTuples( 232 List<Member[]> result, 233 List<Member[]> result2, 234 Set<List<Member>> emitted) 235 { 236 for (Member[] row : result2) { 237 // wrap array for correct distinctness test 238 if (emitted.add(Arrays.asList(row))) { 239 result.add(row); 240 } 241 } 242 } 243 244 public boolean dependsOn(Dimension dimension) { 245 return anyDependsButFirst(getCalcs(), dimension); 246 } 247 } 248 249 private static class GenerateStringCalcImpl extends AbstractStringCalc { 250 private final MemberListCalc memberListCalc; 251 private final TupleListCalc tupleListCalc; 252 private final StringCalc stringCalc; 253 private final boolean tuple; 254 private final StringCalc sepCalc; 255 256 public GenerateStringCalcImpl( 257 ResolvedFunCall call, 258 ListCalc listCalc, 259 StringCalc stringCalc, 260 boolean tuple, 261 StringCalc sepCalc) 262 { 263 super(call, new Calc[]{listCalc, stringCalc}); 264 if (listCalc instanceof MemberListCalc) { 265 this.memberListCalc = (MemberListCalc) listCalc; 266 this.tupleListCalc = null; 267 } else { 268 this.memberListCalc = null; 269 this.tupleListCalc = (TupleListCalc) listCalc; 270 } 271 this.stringCalc = stringCalc; 272 this.tuple = tuple; 273 this.sepCalc = sepCalc; 274 } 275 276 public String evaluateString(Evaluator evaluator) { 277 StringBuilder buf = new StringBuilder(); 278 int k = 0; 279 if (tuple) { 280 final List<Member[]> list1 = 281 tupleListCalc.evaluateTupleList(evaluator); 282 final Evaluator evaluator2 = evaluator.push(); 283 for (Member[] members : list1) { 284 evaluator2.setContext(members); 285 if (k++ > 0) { 286 String sep = sepCalc.evaluateString(evaluator2); 287 buf.append(sep); 288 } 289 final String result2 = 290 stringCalc.evaluateString(evaluator2); 291 buf.append(result2); 292 } 293 } else { 294 final List<Member> list1 = 295 memberListCalc.evaluateMemberList(evaluator); 296 final Evaluator evaluator2 = evaluator.push(); 297 for (Member member : list1) { 298 evaluator2.setContext(member); 299 if (k++ > 0) { 300 String sep = sepCalc.evaluateString(evaluator2); 301 buf.append(sep); 302 } 303 final String result2 = 304 stringCalc.evaluateString(evaluator2); 305 buf.append(result2); 306 } 307 } 308 return buf.toString(); 309 } 310 311 public boolean dependsOn(Dimension dimension) { 312 return anyDependsButFirst(getCalcs(), dimension); 313 } 314 } 315 } 316 317 // End GenerateFunDef.java