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