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) 2002-2005 Julian Hyde 008// Copyright (C) 2005-2011 Pentaho and others 009// All Rights Reserved. 010*/ 011package mondrian.olap.fun; 012 013import mondrian.calc.*; 014import mondrian.calc.impl.*; 015import mondrian.mdx.ResolvedFunCall; 016import mondrian.olap.*; 017import mondrian.olap.type.*; 018import mondrian.resource.MondrianResource; 019 020import java.io.PrintWriter; 021import java.util.*; 022 023/** 024 * <code>SetFunDef</code> implements the 'set' function (whose syntax is the 025 * brace operator, <code>{ ... }</code>). 026 * 027 * @author jhyde 028 * @since 3 March, 2002 029 */ 030public class SetFunDef extends FunDefBase { 031 static final ResolverImpl Resolver = new ResolverImpl(); 032 033 SetFunDef(Resolver resolver, int[] argTypes) { 034 super(resolver, Category.Set, argTypes); 035 } 036 037 public void unparse(Exp[] args, PrintWriter pw) { 038 ExpBase.unparseList(pw, args, "{", ", ", "}"); 039 } 040 041 public Type getResultType(Validator validator, Exp[] args) { 042 // All of the members in {<Member1>[,<MemberI>]...} must have the same 043 // Hierarchy. But if there are no members, we can't derive a 044 // hierarchy. 045 Type type0 = null; 046 if (args.length == 0) { 047 // No members to go on, so we can't guess the hierarchy. 048 type0 = MemberType.Unknown; 049 } else { 050 for (int i = 0; i < args.length; i++) { 051 Exp arg = args[i]; 052 Type type = arg.getType(); 053 type = TypeUtil.toMemberOrTupleType(type); 054 if (i == 0) { 055 type0 = type; 056 } else { 057 if (!TypeUtil.isUnionCompatible(type0, type)) { 058 throw MondrianResource.instance() 059 .ArgsMustHaveSameHierarchy.ex(getName()); 060 } 061 } 062 } 063 } 064 return new SetType(type0); 065 } 066 067 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) { 068 final Exp[] args = call.getArgs(); 069 if (args.length == 0) { 070 // Special treatment for empty set, because we don't know whether it 071 // is a set of members or tuples, and so we need it to implement 072 // both MemberListCalc and ListCalc. 073 return new EmptyListCalc(call); 074 } 075 if (args.length == 1 076 && args[0].getType() instanceof SetType) 077 { 078 // Optimized case when there is only one argument. This occurs quite 079 // often, because people write '{Foo.Children} on 1' when they could 080 // write 'Foo.Children on 1'. 081 return args[0].accept(compiler); 082 } 083 return new SetListCalc( 084 call, args, compiler, ResultStyle.LIST_MUTABLELIST); 085 } 086 087 /** 088 * Compiled expression to implement the MDX set function, <code>{ ... 089 * }</code>, applied to a set of tuples, as a list. 090 * 091 * <p>The set function can contain expressions which yield sets together 092 * with expressions which yield individual tuples, provided that 093 * they all have the same type. It automatically removes null 094 * or partially-null tuples from the list. 095 * 096 * <p>Also, does not process high-cardinality dimensions specially. 097 */ 098 public static class SetListCalc extends AbstractListCalc { 099 private TupleList result; 100 private final VoidCalc[] voidCalcs; 101 102 public SetListCalc( 103 Exp exp, 104 Exp[] args, 105 ExpCompiler compiler, 106 List<ResultStyle> resultStyles) 107 { 108 super(exp, null); 109 voidCalcs = compileSelf(args, compiler, resultStyles); 110 result = TupleCollections.createList(getType().getArity()); 111 } 112 113 public Calc[] getCalcs() { 114 return voidCalcs; 115 } 116 117 private VoidCalc[] compileSelf( 118 Exp[] args, 119 ExpCompiler compiler, 120 List<ResultStyle> resultStyles) 121 { 122 VoidCalc[] voidCalcs = new VoidCalc[args.length]; 123 for (int i = 0; i < args.length; i++) { 124 voidCalcs[i] = createCalc(args[i], compiler, resultStyles); 125 } 126 return voidCalcs; 127 } 128 129 private VoidCalc createCalc( 130 Exp arg, 131 ExpCompiler compiler, 132 List<ResultStyle> resultStyles) 133 { 134 final Type type = arg.getType(); 135 if (type instanceof SetType) { 136 // TODO use resultStyles 137 final ListCalc listCalc = compiler.compileList(arg); 138 return new AbstractVoidCalc(arg, new Calc[] {listCalc}) { 139 public void evaluateVoid(Evaluator evaluator) { 140 TupleList list = 141 listCalc.evaluateList(evaluator); 142 // Add only tuples which are not null. Tuples with 143 // any null members are considered null. 144 outer: 145 for (List<Member> members : list) { 146 for (Member member : members) { 147 if (member == null || member.isNull()) { 148 continue outer; 149 } 150 } 151 result.add(members); 152 } 153 } 154 155 protected String getName() { 156 return "Sublist"; 157 } 158 }; 159 } else if (type.getArity() == 1) { 160 final MemberCalc memberCalc = compiler.compileMember(arg); 161 return new AbstractVoidCalc(arg, new Calc[]{memberCalc}) { 162 final Member[] members = {null}; 163 public void evaluateVoid(Evaluator evaluator) { 164 // Don't add null or partially null tuple to result. 165 Member member = memberCalc.evaluateMember(evaluator); 166 if (member == null || member.isNull()) { 167 return; 168 } 169 members[0] = member; 170 result.addTuple(members); 171 } 172 }; 173 } else { 174 final TupleCalc tupleCalc = compiler.compileTuple(arg); 175 return new AbstractVoidCalc(arg, new Calc[]{tupleCalc}) { 176 public void evaluateVoid(Evaluator evaluator) { 177 // Don't add null or partially null tuple to result. 178 Member[] members = tupleCalc.evaluateTuple(evaluator); 179 if (members == null 180 || tupleContainsNullMember(members)) 181 { 182 return; 183 } 184 result.addTuple(members); 185 } 186 }; 187 } 188 } 189 190 public TupleList evaluateList(final Evaluator evaluator) { 191 result.clear(); 192 for (VoidCalc voidCalc : voidCalcs) { 193 voidCalc.evaluateVoid(evaluator); 194 } 195 return result.cloneList(-1); 196 } 197 } 198 199 private static List<Calc> compileSelf( 200 Exp[] args, 201 ExpCompiler compiler, 202 List<ResultStyle> resultStyles) 203 { 204 List<Calc> calcs = new ArrayList<Calc>(args.length); 205 for (Exp arg : args) { 206 calcs.add(createCalc(arg, compiler, resultStyles)); 207 } 208 return calcs; 209 } 210 211 private static IterCalc createCalc( 212 Exp arg, 213 ExpCompiler compiler, 214 List<ResultStyle> resultStyles) 215 { 216 final Type type = arg.getType(); 217 if (type instanceof SetType) { 218 final Calc calc = compiler.compileAs(arg, null, resultStyles); 219 switch (calc.getResultStyle()) { 220 case ITERABLE: 221 final IterCalc iterCalc = (IterCalc) calc; 222 return new AbstractIterCalc(arg, new Calc[]{calc}) { 223 public TupleIterable evaluateIterable( 224 Evaluator evaluator) 225 { 226 return iterCalc.evaluateIterable(evaluator); 227 } 228 229 protected String getName() { 230 return "Sublist"; 231 } 232 }; 233 case LIST: 234 case MUTABLE_LIST: 235 final ListCalc listCalc = (ListCalc) calc; 236 return new AbstractIterCalc(arg, new Calc[]{calc}) { 237 public TupleIterable evaluateIterable( 238 Evaluator evaluator) 239 { 240 TupleList list = listCalc.evaluateList( 241 evaluator); 242 TupleList result = list.cloneList(list.size()); 243 // Add only tuples which are not null. Tuples with 244 // any null members are considered null. 245 list: 246 for (List<Member> members : list) { 247 for (Member member : members) { 248 if (member == null || member.isNull()) { 249 continue list; 250 } 251 } 252 result.add(members); 253 } 254 return result; 255 } 256 257 protected String getName() { 258 return "Sublist"; 259 } 260 }; 261 } 262 throw ResultStyleException.generateBadType( 263 ResultStyle.ITERABLE_LIST_MUTABLELIST, 264 calc.getResultStyle()); 265 } else if (TypeUtil.couldBeMember(type)) { 266 final MemberCalc memberCalc = compiler.compileMember(arg); 267 final ResolvedFunCall call = wrapAsSet(arg); 268 return new AbstractIterCalc(call, new Calc[] {memberCalc}) { 269 public TupleIterable evaluateIterable( 270 Evaluator evaluator) 271 { 272 final Member member = 273 memberCalc.evaluateMember(evaluator); 274 return member == null 275 ? TupleCollections.createList(1) 276 : new UnaryTupleList(Collections.singletonList(member)); 277 } 278 279 protected String getName() { 280 return "Sublist"; 281 } 282 }; 283 } else { 284 final TupleCalc tupleCalc = compiler.compileTuple(arg); 285 final ResolvedFunCall call = wrapAsSet(arg); 286 return new AbstractIterCalc(call, new Calc[] {tupleCalc}) { 287 public TupleIterable evaluateIterable( 288 Evaluator evaluator) 289 { 290 final Member[] members = tupleCalc.evaluateTuple(evaluator); 291 return new ListTupleList( 292 tupleCalc.getType().getArity(), 293 Arrays.asList(members)); 294 } 295 296 protected String getName() { 297 return "Sublist"; 298 } 299 }; 300 } 301 } 302 303 /** 304 * Creates a call to the set operator with a given collection of 305 * expressions. 306 * 307 * <p>There must be at least one expression. Each expression may be a set of 308 * members/tuples, or may be a member/tuple, but method assumes that 309 * expressions have compatible types. 310 * 311 * @param args Expressions 312 * @return Call to set operator 313 */ 314 public static ResolvedFunCall wrapAsSet(Exp... args) { 315 assert args.length > 0; 316 final int[] categories = new int[args.length]; 317 Type type = null; 318 for (int i = 0; i < args.length; i++) { 319 final Exp arg = args[i]; 320 categories[i] = arg.getCategory(); 321 final Type argType = arg.getType(); 322 if (argType instanceof SetType) { 323 type = ((SetType) argType).getElementType(); 324 } else { 325 type = argType; 326 } 327 } 328 return new ResolvedFunCall( 329 new SetFunDef(Resolver, categories), 330 args, 331 new SetType(type)); 332 } 333 334 /** 335 * Compiled expression that evaluates one or more expressions, each of which 336 * yields a tuple or a set of tuples, and returns the result as a tuple 337 * iterator. 338 */ 339 public static class ExprIterCalc extends AbstractIterCalc { 340 private final IterCalc[] iterCalcs; 341 342 public ExprIterCalc( 343 Exp exp, 344 Exp[] args, 345 ExpCompiler compiler, 346 List<ResultStyle> resultStyles) 347 { 348 super(exp, null); 349 final List<Calc> calcList = 350 compileSelf(args, compiler, resultStyles); 351 iterCalcs = calcList.toArray(new IterCalc[calcList.size()]); 352 } 353 354 // override return type 355 public IterCalc[] getCalcs() { 356 return iterCalcs; 357 } 358 359 public TupleIterable evaluateIterable( 360 final Evaluator evaluator) 361 { 362 return new AbstractTupleIterable(getType().getArity()) { 363 public TupleCursor tupleCursor() { 364 return new AbstractTupleCursor(arity) { 365 Iterator<IterCalc> calcIterator = 366 Arrays.asList(iterCalcs).iterator(); 367 TupleCursor currentCursor = 368 TupleCollections.emptyList(1).tupleCursor(); 369 370 public boolean forward() { 371 while (true) { 372 if (currentCursor.forward()) { 373 return true; 374 } 375 if (!calcIterator.hasNext()) { 376 return false; 377 } 378 currentCursor = 379 calcIterator.next() 380 .evaluateIterable(evaluator) 381 .tupleCursor(); 382 } 383 } 384 385 public List<Member> current() { 386 return currentCursor.current(); 387 } 388 389 @Override 390 public void setContext(Evaluator evaluator) { 391 currentCursor.setContext(evaluator); 392 } 393 394 @Override 395 public void currentToArray( 396 Member[] members, int offset) 397 { 398 currentCursor.currentToArray(members, offset); 399 } 400 401 @Override 402 public Member member(int column) { 403 return currentCursor.member(column); 404 } 405 }; 406 } 407 }; 408 } 409 } 410 411 private static class ResolverImpl extends ResolverBase { 412 public ResolverImpl() { 413 super( 414 "{}", 415 "{<Member> [, <Member>...]}", 416 "Brace operator constructs a set.", 417 Syntax.Braces); 418 } 419 420 public FunDef resolve( 421 Exp[] args, 422 Validator validator, 423 List<Conversion> conversions) 424 { 425 int[] parameterTypes = new int[args.length]; 426 for (int i = 0; i < args.length; i++) { 427 if (validator.canConvert( 428 i, args[i], Category.Member, conversions)) 429 { 430 parameterTypes[i] = Category.Member; 431 continue; 432 } 433 if (validator.canConvert( 434 i, args[i], Category.Tuple, conversions)) 435 { 436 parameterTypes[i] = Category.Tuple; 437 continue; 438 } 439 if (validator.canConvert( 440 i, args[i], Category.Set, conversions)) 441 { 442 parameterTypes[i] = Category.Set; 443 continue; 444 } 445 return null; 446 } 447 return new SetFunDef(this, parameterTypes); 448 } 449 } 450 451 /** 452 * Compiled expression that returns an empty list of members or tuples. 453 */ 454 private static class EmptyListCalc extends AbstractListCalc { 455 private final TupleList list; 456 457 /** 458 * Creates an EmptyListCalc. 459 * 460 * @param call Expression which was compiled 461 */ 462 EmptyListCalc(ResolvedFunCall call) { 463 super(call, new Calc[0]); 464 465 list = TupleCollections.emptyList(call.getType().getArity()); 466 } 467 468 public TupleList evaluateList(Evaluator evaluator) { 469 return list; 470 } 471 } 472} 473 474// End SetFunDef.java