001 /*
002 // $Id: //open/mondrian-release/3.2/src/main/mondrian/olap/fun/SetFunDef.java#1 $
003 // This software is subject to the terms of the Eclipse Public License v1.0
004 // Agreement, available at the following URL:
005 // http://www.eclipse.org/legal/epl-v10.html.
006 // Copyright (C) 2002-2002 Kana Software, Inc.
007 // Copyright (C) 2002-2009 Julian Hyde and others
008 // All Rights Reserved.
009 // You must accept the terms of that agreement to use this software.
010 //
011 // jhyde, 3 March, 2002
012 */
013 package mondrian.olap.fun;
014
015 import mondrian.calc.*;
016 import mondrian.calc.impl.*;
017 import mondrian.mdx.ResolvedFunCall;
018 import mondrian.olap.*;
019 import mondrian.olap.type.*;
020 import mondrian.resource.MondrianResource;
021 import mondrian.util.ConcatenableList;
022 import mondrian.util.FilteredIterableList;
023
024 import java.io.PrintWriter;
025 import java.util.*;
026
027 /**
028 * <code>SetFunDef</code> implements the 'set' function (whose syntax is the
029 * brace operator, <code>{ ... }</code>).
030 *
031 * @author jhyde
032 * @since 3 March, 2002
033 * @version $Id: //open/mondrian-release/3.2/src/main/mondrian/olap/fun/SetFunDef.java#1 $
034 */
035 public class SetFunDef extends FunDefBase {
036 static final ResolverImpl Resolver = new ResolverImpl();
037
038 SetFunDef(Resolver resolver, int[] argTypes) {
039 super(resolver, Category.Set, argTypes);
040 }
041
042 public void unparse(Exp[] args, PrintWriter pw) {
043 ExpBase.unparseList(pw, args, "{", ", ", "}");
044 }
045
046 public Type getResultType(Validator validator, Exp[] args) {
047 // All of the members in {<Member1>[,<MemberI>]...} must have the same
048 // Hierarchy. But if there are no members, we can't derive a
049 // hierarchy.
050 Type type0 = null;
051 if (args.length == 0) {
052 // No members to go on, so we can't guess the hierarchy.
053 type0 = MemberType.Unknown;
054 } else {
055 for (int i = 0; i < args.length; i++) {
056 Exp arg = args[i];
057 Type type = arg.getType();
058 type = TypeUtil.toMemberOrTupleType(type);
059 if (i == 0) {
060 type0 = type;
061 } else {
062 if (!TypeUtil.isUnionCompatible(type0, type)) {
063 throw MondrianResource.instance()
064 .ArgsMustHaveSameHierarchy.ex(getName());
065 }
066 }
067 }
068 }
069 return new SetType(type0);
070 }
071
072 public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
073 final Exp[] args = call.getArgs();
074 if (args.length == 0) {
075 // Special treatment for empty set, because we don't know whether it
076 // is a set of members or tuples, and so we need it to implement
077 // both MemberListCalc and TupleListCalc.
078 return new EmptyListCalc(call);
079 }
080 if (args.length == 1
081 && args[0].getType() instanceof SetType)
082 {
083 // Optimized case when there is only one argument. This occurs quite
084 // often, because people write '{Foo.Children} on 1' when they could
085 // write 'Foo.Children on 1'.
086 return args[0].accept(compiler);
087 }
088 if (((SetType) call.getType()).getArity() == 1) {
089 return new MemberSetListCalc(
090 call, args, compiler,
091 ResultStyle.LIST_MUTABLELIST);
092 } else {
093 return new TupleSetListCalc(
094 call, args, compiler,
095 ResultStyle.LIST_MUTABLELIST);
096 }
097 }
098
099 /**
100 * Compiled expression to implement the MDX set function, <code>{ ...
101 * }</code>, applied to a set of members, as a list.
102 *
103 * <p>The set function can contain expressions which yield sets together
104 * with expressions which yield individual members, provided that
105 * they all have the same type. It automatically removes null members
106 * from the list.
107 *
108 * <p>The implementation uses {@link VoidCalc} objects with side-effects
109 * to avoid generating lots of intermediate lists.
110 */
111 public static class MemberSetListCalc extends AbstractMemberListCalc {
112 private List<Member> result = new ConcatenableList<Member>();
113 private final VoidCalc[] voidCalcs;
114
115 public MemberSetListCalc(
116 Exp exp, Exp[] args, ExpCompiler compiler,
117 List<ResultStyle> resultStyles)
118 {
119 super(exp, null);
120 voidCalcs = compileSelf(args, compiler, resultStyles);
121 }
122
123 public Calc[] getCalcs() {
124 return voidCalcs;
125 }
126
127 private VoidCalc[] compileSelf(
128 Exp[] args,
129 ExpCompiler compiler,
130 List<ResultStyle> resultStyles)
131 {
132 VoidCalc[] voidCalcs = new VoidCalc[args.length];
133 for (int i = 0; i < args.length; i++) {
134 voidCalcs[i] = createCalc(args[i], compiler, resultStyles);
135 }
136 return voidCalcs;
137 }
138
139 private VoidCalc createCalc(
140 Exp arg,
141 ExpCompiler compiler,
142 List<ResultStyle> resultStyles)
143 {
144 final Type type = arg.getType();
145 if (type instanceof SetType) {
146 // TODO use resultStyles
147 final ListCalc listCalc = compiler.compileList(arg);
148 final MemberListCalc memberListCalc = (MemberListCalc) listCalc;
149 return new AbstractVoidCalc(arg, new Calc[] {listCalc}) {
150 public void evaluateVoid(Evaluator evaluator) {
151 final List<Member> memberList =
152 memberListCalc.evaluateMemberList(evaluator);
153 final List<Member> list =
154 new FilteredIterableList<Member>(
155 memberList,
156 new FilteredIterableList.Filter<Member>() {
157 public boolean accept(Member m) {
158 return m != null && !m.isNull();
159 }
160 }
161 );
162 result.addAll(list);
163 }
164
165 protected String getName() {
166 return "Sublist";
167 }
168 };
169 } else {
170 final MemberCalc listCalc = compiler.compileMember(arg);
171 return new AbstractVoidCalc(arg, new Calc[]{listCalc}) {
172 public void evaluateVoid(Evaluator evaluator) {
173 Member member = listCalc.evaluateMember(evaluator);
174 if (member == null || member.isNull()) {
175 return;
176 }
177 result.add(member);
178 }
179
180 protected String getName() {
181 return "Sublist";
182 }
183 };
184 }
185 }
186
187 public List<Member> evaluateMemberList(final Evaluator evaluator) {
188 this.result = new ConcatenableList<Member>();
189 for (VoidCalc voidCalc : voidCalcs) {
190 voidCalc.evaluateVoid(evaluator);
191 }
192
193 // For non-high cardinality dimensions, consolidate the lists
194 // inside the ConcatenableList. High-cardinality dimensions should
195 // be kept intact, because enumerating the sublists is expensive.
196 if (!result.isEmpty()
197 && !result.get(0).getDimension().isHighCardinality())
198 {
199 result.toArray();
200 }
201 return result;
202 }
203 }
204
205 /**
206 * Compiled expression to implement the MDX set function, <code>{ ...
207 * }</code>, applied to a set of tuples, as a list.
208 *
209 * <p>The set function can contain expressions which yield sets together
210 * with expressions which yield individual tuples, provided that
211 * they all have the same type. It automatically removes null
212 * or partially-null tuples from the list.
213 *
214 * <p>Analogous to {@link mondrian.olap.fun.SetFunDef.MemberSetListCalc},
215 * except processes tuples instead of members.
216 *
217 * <p>Also, does not process high-cardinality dimensions specially.
218 */
219 public static class TupleSetListCalc extends AbstractTupleListCalc {
220 private List<Member[]> result = new ConcatenableList<Member[]>();
221 private final VoidCalc[] voidCalcs;
222
223 public TupleSetListCalc(
224 Exp exp, Exp[] args, ExpCompiler compiler,
225 List<ResultStyle> resultStyles)
226 {
227 super(exp, null);
228 voidCalcs = compileSelf(args, compiler, resultStyles);
229 }
230
231 public Calc[] getCalcs() {
232 return voidCalcs;
233 }
234
235 private VoidCalc[] compileSelf(
236 Exp[] args,
237 ExpCompiler compiler,
238 List<ResultStyle> resultStyles)
239 {
240 VoidCalc[] voidCalcs = new VoidCalc[args.length];
241 for (int i = 0; i < args.length; i++) {
242 voidCalcs[i] = createCalc(args[i], compiler, resultStyles);
243 }
244 return voidCalcs;
245 }
246
247 private VoidCalc createCalc(
248 Exp arg,
249 ExpCompiler compiler,
250 List<ResultStyle> resultStyles)
251 {
252 final Type type = arg.getType();
253 if (type instanceof SetType) {
254 // TODO use resultStyles
255 final ListCalc listCalc = compiler.compileList(arg);
256 final TupleListCalc tupleListCalc = (TupleListCalc) listCalc;
257 return new AbstractVoidCalc(arg, new Calc[] {listCalc}) {
258 public void evaluateVoid(Evaluator evaluator) {
259 List<Member[]> list =
260 tupleListCalc.evaluateTupleList(evaluator);
261 // Add only tuples which are not null. Tuples with
262 // any null members are considered null.
263 outer:
264 for (Member[] members : list) {
265 for (Member member : members) {
266 if (member == null || member.isNull()) {
267 continue outer;
268 }
269 }
270 result.add(members);
271 }
272 }
273
274 protected String getName() {
275 return "Sublist";
276 }
277 };
278 } else {
279 final TupleCalc tupleCalc = compiler.compileTuple(arg);
280 return new AbstractVoidCalc(arg, new Calc[]{tupleCalc}) {
281 public void evaluateVoid(Evaluator evaluator) {
282 // Don't add null or partially null tuple to result.
283 Member[] members = tupleCalc.evaluateTuple(evaluator);
284 if (members == null) {
285 return;
286 }
287 assert !tupleContainsNullMember(members);
288
289 result.add(members);
290 }
291 };
292 }
293 }
294
295 public List<Member[]> evaluateTupleList(final Evaluator evaluator) {
296 this.result = new ConcatenableList<Member[]>();
297 for (VoidCalc voidCalc : voidCalcs) {
298 voidCalc.evaluateVoid(evaluator);
299 }
300 return result;
301 }
302 }
303
304 private static List<Calc> compileSelf(
305 Exp[] args,
306 ExpCompiler compiler,
307 List<ResultStyle> resultStyles)
308 {
309 List<Calc> calcs = new ArrayList<Calc>(args.length);
310 for (Exp arg : args) {
311 calcs.add(createCalc(arg, compiler, resultStyles));
312 }
313 return calcs;
314 }
315
316 private static IterCalc createCalc(
317 Exp arg,
318 ExpCompiler compiler,
319 List<ResultStyle> resultStyles)
320 {
321 final Type type = arg.getType();
322 if (type instanceof SetType) {
323 final Calc calc = compiler.compileAs(arg, null, resultStyles);
324 if (((SetType) type).getArity() == 1) {
325 switch (calc.getResultStyle()) {
326 case ITERABLE:
327 final MemberIterCalc iterCalc = (MemberIterCalc) calc;
328 return new AbstractMemberIterCalc(arg, new Calc[] {calc}) {
329 public Iterable<Member> evaluateMemberIterable(
330 Evaluator evaluator)
331 {
332 return iterCalc.evaluateMemberIterable(evaluator);
333 }
334
335 protected String getName() {
336 return "Sublist";
337 }
338 };
339 case LIST:
340 case MUTABLE_LIST:
341 final MemberListCalc memberListCalc =
342 (MemberListCalc) calc;
343 return new AbstractMemberIterCalc(arg, new Calc[] {calc}) {
344 public Iterable<Member> evaluateMemberIterable(
345 Evaluator evaluator)
346 {
347 List<Member> result = new ArrayList<Member>();
348 List<Member> list =
349 memberListCalc.evaluateMemberList(evaluator);
350 // Add only members which are not null.
351 for (Member member : list) {
352 if (member == null || member.isNull()) {
353 continue;
354 }
355 result.add(member);
356 }
357 return result;
358 }
359
360 protected String getName() {
361 return "Sublist";
362 }
363 };
364 }
365 throw ResultStyleException.generateBadType(
366 ResultStyle.ITERABLE_LIST_MUTABLELIST,
367 calc.getResultStyle());
368 } else {
369 switch (calc.getResultStyle()) {
370 case ITERABLE:
371 final TupleIterCalc iterCalc = (TupleIterCalc) calc;
372 return new AbstractTupleIterCalc(arg, new Calc[] {calc}) {
373 public Iterable<Member[]> evaluateTupleIterable(
374 Evaluator evaluator)
375 {
376 return iterCalc.evaluateTupleIterable(evaluator);
377 }
378
379 protected String getName() {
380 return "Sublist";
381 }
382 };
383 case LIST:
384 case MUTABLE_LIST:
385 final TupleListCalc tupleListCalc = (TupleListCalc) calc;
386 return new AbstractTupleIterCalc(arg, new Calc[] {calc}) {
387 public Iterable<Member[]> evaluateTupleIterable(
388 Evaluator evaluator)
389 {
390 List<Member[]> result =
391 new ArrayList<Member[]>();
392 List<Member[]> list =
393 tupleListCalc.evaluateTupleList(evaluator);
394 // Add only tuples which are not null. Tuples with
395 // any null members are considered null.
396 list:
397 for (Member[] members : list) {
398 for (Member member : members) {
399 if (member == null || member.isNull()) {
400 continue list;
401 }
402 }
403 result.add(members);
404 }
405 return result;
406 }
407
408 protected String getName() {
409 return "Sublist";
410 }
411 };
412 }
413 throw ResultStyleException.generateBadType(
414 ResultStyle.ITERABLE_LIST_MUTABLELIST,
415 calc.getResultStyle());
416 }
417 } else if (TypeUtil.couldBeMember(type)) {
418 final MemberCalc memberCalc = compiler.compileMember(arg);
419 final ResolvedFunCall call = wrapAsSet(arg);
420 return new AbstractMemberIterCalc(call, new Calc[] {memberCalc}) {
421 public Iterable<Member> evaluateMemberIterable(
422 Evaluator evaluator)
423 {
424 final Member member =
425 memberCalc.evaluateMember(evaluator);
426 return new Iterable<Member>() {
427 public Iterator<Member> iterator() {
428 return new Iterator<Member>() {
429 private Member m = member;
430 public boolean hasNext() {
431 return (m != null);
432 }
433 public Member next() {
434 try {
435 return m;
436 } finally {
437 m = null;
438 }
439 }
440 public void remove() {
441 throw new UnsupportedOperationException(
442 "remove");
443 }
444 };
445 }
446 };
447 }
448 protected String getName() {
449 return "Sublist";
450 }
451 };
452 } else {
453 final TupleCalc tupleCalc = compiler.compileTuple(arg);
454 final ResolvedFunCall call = wrapAsSet(arg);
455 return new AbstractTupleIterCalc(call, new Calc[] {tupleCalc}) {
456 public Iterable<Member[]> evaluateTupleIterable(
457 Evaluator evaluator)
458 {
459 final Member[] members = tupleCalc.evaluateTuple(evaluator);
460 return new Iterable<Member[]>() {
461 public Iterator<Member[]> iterator() {
462 return new Iterator<Member[]>() {
463 private Member[] m = members;
464 public boolean hasNext() {
465 return (m != null);
466 }
467 public Member[] next() {
468 try {
469 return m;
470 } finally {
471 m = null;
472 }
473 }
474 public void remove() {
475 throw new UnsupportedOperationException(
476 "remove");
477 }
478 };
479 }
480 };
481 }
482 protected String getName() {
483 return "Sublist";
484 }
485 };
486 }
487 }
488
489 /**
490 * Creates a call to the set operator with a given collection of
491 * expressions.
492 *
493 * <p>There must be at least one expression. Each expression may be a set of
494 * members/tuples, or may be a member/tuple, but method assumes that
495 * expressions have compatible types.
496 *
497 * @param args Expressions
498 * @return Call to set operator
499 */
500 public static ResolvedFunCall wrapAsSet(Exp... args) {
501 assert args.length > 0;
502 final int[] categories = new int[args.length];
503 Type type = null;
504 for (int i = 0; i < args.length; i++) {
505 final Exp arg = args[i];
506 categories[i] = arg.getCategory();
507 final Type argType = arg.getType();
508 if (argType instanceof SetType) {
509 type = ((SetType) argType).getElementType();
510 } else {
511 type = argType;
512 }
513 }
514 return new ResolvedFunCall(
515 new SetFunDef(Resolver, categories),
516 args,
517 new SetType(type));
518 }
519
520 /**
521 * Compiled expression that evaluates one or more expressions, each of which
522 * yields a member or a set of members, and returns the result as an member
523 * iterator.
524 */
525 public static class ExprMemberIterCalc extends AbstractMemberIterCalc {
526 private final MemberIterCalc[] iterCalcs;
527
528 public ExprMemberIterCalc(
529 Exp exp,
530 Exp[] args,
531 ExpCompiler compiler,
532 List<ResultStyle> resultStyles)
533 {
534 super(exp, null);
535 final List<Calc> calcList =
536 compileSelf(args, compiler, resultStyles);
537 iterCalcs = calcList.toArray(new MemberIterCalc[calcList.size()]);
538 }
539
540 // override return type
541 public MemberIterCalc[] getCalcs() {
542 return iterCalcs;
543 }
544
545 public Iterable<Member> evaluateMemberIterable(
546 final Evaluator evaluator)
547 {
548 return new Iterable<Member>() {
549 public Iterator<Member> iterator() {
550 return new Iterator<Member>() {
551 int index = 0;
552 Iterator<Member> currentIterator = null;
553 Member member = null;
554
555 public boolean hasNext() {
556 if (member != null) {
557 return true;
558 }
559 if (currentIterator == null) {
560 if (index >= iterCalcs.length) {
561 return false;
562 }
563 MemberIterCalc iterCalc = iterCalcs[index++];
564 Iterable<Member> iter =
565 iterCalc.evaluateMemberIterable(evaluator);
566 currentIterator = iter.iterator();
567 }
568 while (true) {
569 boolean b = currentIterator.hasNext();
570 while (! b) {
571 if (index >= iterCalcs.length) {
572 return false;
573 }
574 MemberIterCalc iterCalc =
575 iterCalcs[index++];
576 Iterable<Member> iter =
577 iterCalc.evaluateMemberIterable(
578 evaluator);
579 currentIterator = iter.iterator();
580 b = currentIterator.hasNext();
581 }
582 member = currentIterator.next();
583 if (member != null) {
584 break;
585 }
586 }
587 return true;
588 }
589
590 public Member next() {
591 try {
592 return member;
593 } finally {
594 member = null;
595 }
596 }
597 public void remove() {
598 throw new UnsupportedOperationException("remove");
599 }
600 };
601 }
602 };
603 }
604 }
605
606 /**
607 * Compiled expression that evaluates one or more expressions, each of which
608 * yields a tuple or a set of tuples, and returns the result as an tuple
609 * iterator.
610 */
611 public static class ExprTupleIterCalc extends AbstractTupleIterCalc {
612 private final TupleIterCalc[] iterCalcs;
613
614 public ExprTupleIterCalc(
615 Exp exp,
616 Exp[] args,
617 ExpCompiler compiler,
618 List<ResultStyle> resultStyles)
619 {
620 super(exp, null);
621 final List<Calc> calcList =
622 compileSelf(args, compiler, resultStyles);
623 iterCalcs = calcList.toArray(new TupleIterCalc[calcList.size()]);
624 }
625
626 // override return type
627 public TupleIterCalc[] getCalcs() {
628 return iterCalcs;
629 }
630
631 public Iterable<Member[]> evaluateTupleIterable(
632 final Evaluator evaluator)
633 {
634 return new Iterable<Member[]>() {
635 public Iterator<Member[]> iterator() {
636 return new Iterator<Member[]>() {
637 int index = 0;
638 Iterator<Member[]> currentIterator = null;
639 Member[] tuple = null;
640
641 public boolean hasNext() {
642 if (tuple != null) {
643 return true;
644 }
645 if (currentIterator == null) {
646 if (index >= iterCalcs.length) {
647 return false;
648 }
649 TupleIterCalc iterCalc = iterCalcs[index++];
650 Iterable<Member[]> iter =
651 iterCalc.evaluateTupleIterable(evaluator);
652 currentIterator = iter.iterator();
653 }
654 while (true) {
655 boolean b = currentIterator.hasNext();
656 while (! b) {
657 if (index >= iterCalcs.length) {
658 return false;
659 }
660 TupleIterCalc iterCalc =
661 iterCalcs[index++];
662 Iterable<Member[]> iter =
663 iterCalc.evaluateTupleIterable(
664 evaluator);
665 currentIterator = iter.iterator();
666 b = currentIterator.hasNext();
667 }
668 tuple = currentIterator.next();
669 if (tuple != null) {
670 break;
671 }
672 }
673 return true;
674 }
675
676 public Member[] next() {
677 try {
678 return tuple;
679 } finally {
680 tuple = null;
681 }
682 }
683 public void remove() {
684 throw new UnsupportedOperationException("remove");
685 }
686 };
687 }
688 };
689 }
690 }
691
692 private static class ResolverImpl extends ResolverBase {
693 public ResolverImpl() {
694 super(
695 "{}",
696 "{<Member> [, <Member>...]}",
697 "Brace operator constructs a set.",
698 Syntax.Braces);
699 }
700
701 public FunDef resolve(
702 Exp[] args,
703 Validator validator,
704 List<Conversion> conversions)
705 {
706 int[] parameterTypes = new int[args.length];
707 for (int i = 0; i < args.length; i++) {
708 if (validator.canConvert(
709 i, args[i], Category.Member, conversions))
710 {
711 parameterTypes[i] = Category.Member;
712 continue;
713 }
714 if (validator.canConvert(
715 i, args[i], Category.Tuple, conversions))
716 {
717 parameterTypes[i] = Category.Tuple;
718 continue;
719 }
720 if (validator.canConvert(
721 i, args[i], Category.Set, conversions))
722 {
723 parameterTypes[i] = Category.Set;
724 continue;
725 }
726 return null;
727 }
728 return new SetFunDef(this, parameterTypes);
729 }
730 }
731
732 /**
733 * Compiled expression that returns an empty list of members or tuples.
734 */
735 private static class EmptyListCalc extends AbstractListCalc {
736 /**
737 * Creates an EmptyListCalc.
738 *
739 * @param call Expression which was compiled
740 */
741 EmptyListCalc(ResolvedFunCall call) {
742 super(call, new Calc[0]);
743 }
744
745 public List evaluateList(Evaluator evaluator) {
746 return Collections.EMPTY_LIST;
747 }
748 }
749 }
750
751 // End SetFunDef.java