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