001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/SetFunDef.java#27 $
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) 2002-2002 Kana Software, Inc.
007    // Copyright (C) 2002-2008 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/src/main/mondrian/olap/fun/SetFunDef.java#27 $
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().ArgsMustHaveSameHierarchy.ex(getName());
064                        }
065                    }
066                }
067            }
068            return new SetType(type0);
069        }
070    
071        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
072            final Exp[] args = call.getArgs();
073            return new ListSetCalc(
074                call, args, compiler,
075                ResultStyle.LIST_MUTABLELIST);
076        }
077    
078        /**
079         * Compiled expression to implement the MDX set function, <code>{ ...
080         * }</code>.
081         *
082         * <p>The set function can contain expressions which yield sets together
083         * with expressions which yield individual members/tuples, provided that
084         * they all have the same type. It automatically removes null members
085         * or partially-null tuples from the list.
086         *
087         * <p>The implementation uses {@link VoidCalc} objects with side-effects
088         * to avoid generating lots of intermediate lists.
089         */
090        public static class ListSetCalc extends AbstractListCalc {
091            private List result = new ConcatenableList();
092            private final VoidCalc[] voidCalcs;
093    
094            public ListSetCalc(
095                Exp exp, Exp[] args, ExpCompiler compiler,
096                List<ResultStyle> resultStyles)
097            {
098                super(exp, null);
099                voidCalcs = compileSelf(args, compiler, resultStyles);
100            }
101    
102            public Calc[] getCalcs() {
103                return voidCalcs;
104            }
105    
106            private VoidCalc[] compileSelf(Exp[] args,
107                    ExpCompiler compiler,
108                    List<ResultStyle> resultStyles) {
109                VoidCalc[] voidCalcs = new VoidCalc[args.length];
110                for (int i = 0; i < args.length; i++) {
111                    voidCalcs[i] = createCalc(args[i], compiler, resultStyles);
112                }
113                return voidCalcs;
114            }
115    
116            private VoidCalc createCalc(
117                Exp arg,
118                ExpCompiler compiler,
119                List<ResultStyle> resultStyles)
120            {
121                final Type type = arg.getType();
122                if (type instanceof SetType) {
123                    // TODO use resultStyles
124                    final ListCalc listCalc = compiler.compileList(arg);
125                    final Type elementType = ((SetType) type).getElementType();
126                    if (elementType instanceof MemberType) {
127                        final MemberListCalc memberListCalc =
128                            (MemberListCalc) listCalc;
129                        return new AbstractVoidCalc(arg, new Calc[] {listCalc}) {
130                            public void evaluateVoid(Evaluator evaluator) {
131                                final List<Member> memberList =
132                                    memberListCalc.evaluateMemberList(evaluator);
133                                final List<Member> list =
134                                    new FilteredIterableList<Member>(
135                                        memberList,
136                                        new FilteredIterableList.Filter<Member>() {
137                                            public boolean accept(Member m) {
138                                                return m != null && !m.isNull();
139                                            }
140                                        }
141                                    );
142                                result.addAll(list);
143                            }
144    
145                            protected String getName() {
146                                return "Sublist";
147                            }
148                        };
149                    } else {
150                        final TupleListCalc tupleListCalc =
151                            (TupleListCalc) listCalc;
152                        return new AbstractVoidCalc(arg, new Calc[] {listCalc}) {
153                            public void evaluateVoid(Evaluator evaluator) {
154                                List<Member[]> list =
155                                    tupleListCalc.evaluateTupleList(evaluator);
156                                // Add only tuples which are not null. Tuples with
157                                // any null members are considered null.
158                                outer:
159                                for (Member[] members : list) {
160                                    for (Member member : members) {
161                                        if (member == null || member.isNull()) {
162                                            continue outer;
163                                        }
164                                    }
165                                    result.add(members);
166                                }
167                            }
168    
169                            protected String getName() {
170                                return "Sublist";
171                            }
172                        };
173                    }
174                } else if (TypeUtil.couldBeMember(type)) {
175                    final MemberCalc listCalc = compiler.compileMember(arg);
176                    return new AbstractVoidCalc(arg, new Calc[] {listCalc}) {
177                        public void evaluateVoid(Evaluator evaluator) {
178                            Member member = listCalc.evaluateMember(evaluator);
179                            if (member == null || member.isNull()) {
180                                return;
181                            }
182                            result.add(member);
183                        }
184    
185                        protected String getName() {
186                            return "Sublist";
187                        }
188                    };
189                } else {
190                    final TupleCalc tupleCalc = compiler.compileTuple(arg);
191                    return new AbstractVoidCalc(arg, new Calc[] {tupleCalc}) {
192                        public void evaluateVoid(Evaluator evaluator) {
193                            // Don't add null or partially null tuple to result.
194                            Member[] members = tupleCalc.evaluateTuple(evaluator);
195                            if (members == null) {
196                                return;
197                            }
198                            assert !tupleContainsNullMember(members);
199    
200                            result.add(members);
201                        }
202                    };
203                }
204            }
205    
206            public List evaluateList(final Evaluator evaluator) {
207                this.result = new ConcatenableList();
208                for (VoidCalc voidCalc : voidCalcs) {
209                    voidCalc.evaluateVoid(evaluator);
210                }
211                // REVIEW: What the heck is this code doing? Why is it OK to
212                // ignore IndexOutOfBoundsException and NullPointerException?
213                try {
214                    if (result.get(0) instanceof Member) {
215                        if (!((Member)result.get(0)).getDimension()
216                                .isHighCardinality()) {
217                            result.toArray();
218                        }
219                    }
220                } catch (IndexOutOfBoundsException iooe) {
221                } catch (NullPointerException npe) {
222                }
223                return result;
224            }
225        }
226    
227        public static class IterSetCalc extends AbstractIterCalc {
228            private final IterCalc[] iterCalcs;
229    
230            public IterSetCalc(
231                Exp exp,
232                Exp[] args,
233                ExpCompiler compiler,
234                List<ResultStyle> resultStyles)
235            {
236                super(exp, null);
237                iterCalcs = compileSelf(args, compiler, resultStyles);
238            }
239    
240            public Calc[] getCalcs() {
241                return iterCalcs;
242            }
243    
244            private IterCalc[] compileSelf(
245                Exp[] args,
246                ExpCompiler compiler,
247                List<ResultStyle> resultStyles)
248            {
249                IterCalc[] iterCalcs = new IterCalc[args.length];
250                for (int i = 0; i < args.length; i++) {
251                    iterCalcs[i] = createCalc(args[i], compiler, resultStyles);
252                }
253                return iterCalcs;
254            }
255    
256            private IterCalc createCalc(
257                Exp arg,
258                ExpCompiler compiler,
259                List<ResultStyle> resultStyles)
260            {
261                final Type type = arg.getType();
262                if (type instanceof SetType) {
263                    final Calc calc = compiler.compileAs(arg, null, resultStyles);
264                    final Type elementType = ((SetType) type).getElementType();
265                    if (elementType instanceof MemberType) {
266                        switch (calc.getResultStyle()) {
267                        case ITERABLE:
268                            return new AbstractIterCalc(arg, new Calc[] {calc}) {
269                                private final IterCalc iterCalc = (IterCalc) calc;
270                                public Iterable evaluateIterable(Evaluator evaluator) {
271                                    return iterCalc.evaluateIterable(evaluator);
272                                }
273                                protected String getName() {
274                                    return "Sublist";
275                                }
276                            };
277                        case LIST:
278                        case MUTABLE_LIST:
279                            return new AbstractIterCalc(arg, new Calc[] {calc}) {
280                                private final MemberListCalc memberListCalc =
281                                    (MemberListCalc) calc;
282                                public Iterable evaluateIterable(Evaluator evaluator) {
283                                    List<Member> result = new ArrayList<Member>();
284                                    List<Member> list =
285                                        memberListCalc.evaluateMemberList(evaluator);
286                                    // Add only members which are not null.
287                                    for (Member member : list) {
288                                        if (member == null || member.isNull()) {
289                                            continue;
290                                        }
291                                        result.add(member);
292                                    }
293                                    return result;
294                                }
295                                protected String getName() {
296                                    return "Sublist";
297                                }
298                            };
299                        }
300                        throw ResultStyleException.generateBadType(
301                            ResultStyle.ITERABLE_LIST_MUTABLELIST,
302                            calc.getResultStyle());
303                    } else {
304                        switch (calc.getResultStyle()) {
305                        case ITERABLE:
306                            return new AbstractIterCalc(arg, new Calc[] {calc}) {
307                                private final IterCalc iterCalc = (IterCalc) calc;
308                                public Iterable evaluateIterable(Evaluator evaluator) {
309                                    return iterCalc.evaluateIterable(evaluator);
310                                }
311                                protected String getName() {
312                                    return "Sublist";
313                                }
314                            };
315                        case LIST:
316                        case MUTABLE_LIST:
317                            return new AbstractIterCalc(arg, new Calc[] {calc}) {
318                                private final TupleListCalc tupleListCalc =
319                                    (TupleListCalc) calc;
320    
321                                public Iterable evaluateIterable(Evaluator evaluator) {
322                                    return evaluateTupleIterable(evaluator);
323                                }
324    
325                                public Iterable<Member[]> evaluateTupleIterable(
326                                    Evaluator evaluator)
327                                {
328                                    List<Member[]> result =
329                                        new ArrayList<Member[]>();
330                                    List<Member[]> list =
331                                        tupleListCalc.evaluateTupleList(evaluator);
332                                    // Add only tuples which are not null. Tuples with
333                                    // any null members are considered null.
334                                    list:
335                                    for (Member[] members : list) {
336                                        for (Member member : members) {
337                                            if (member == null || member.isNull()) {
338                                                continue list;
339                                            }
340                                        }
341                                        result.add(members);
342                                    }
343                                    return result;
344                                }
345    
346                                protected String getName() {
347                                    return "Sublist";
348                                }
349                            };
350                        }
351                        throw ResultStyleException.generateBadType(
352                            ResultStyle.ITERABLE_LIST_MUTABLELIST,
353                            calc.getResultStyle());
354                    }
355                } else if (TypeUtil.couldBeMember(type)) {
356                    final MemberCalc memberCalc = compiler.compileMember(arg);
357                    final ResolvedFunCall call = wrapAsSet(arg);
358                    return new AbstractIterCalc(call, new Calc[] {memberCalc}) {
359                        public Iterable evaluateIterable(Evaluator evaluator) {
360                            final Member member =
361                                memberCalc.evaluateMember(evaluator);
362                            return new Iterable<Member>() {
363                                public Iterator<Member> iterator() {
364                                    return new Iterator<Member>() {
365                                        private Member m = member;
366                                        public boolean hasNext() {
367                                            return (m != null);
368                                        }
369                                        public Member next() {
370                                            try {
371                                                return m;
372                                            } finally {
373                                                m = null;
374                                            }
375                                        }
376                                        public void remove() {
377                                            throw new UnsupportedOperationException("remove");
378                                        }
379                                    };
380                                }
381                            };
382                        }
383                        protected String getName() {
384                            return "Sublist";
385                        }
386                    };
387                } else {
388                    final TupleCalc tupleCalc = compiler.compileTuple(arg);
389                    final ResolvedFunCall call = wrapAsSet(arg);
390                    return new AbstractIterCalc(call, new Calc[] {tupleCalc}) {
391                        public Iterable evaluateIterable(Evaluator evaluator) {
392                            final Member[] members = tupleCalc.evaluateTuple(evaluator);
393                            return new Iterable<Member[]>() {
394                                public Iterator<Member[]> iterator() {
395                                    return new Iterator<Member[]>() {
396                                        private Member[] m = members;
397                                        public boolean hasNext() {
398                                            return (m != null);
399                                        }
400                                        public Member[] next() {
401                                            try {
402                                                return m;
403                                            } finally {
404                                                m = null;
405                                            }
406                                        }
407                                        public void remove() {
408                                            throw new UnsupportedOperationException("remove");
409                                        }
410                                    };
411                                }
412                            };
413                        }
414                        protected String getName() {
415                            return "Sublist";
416                        }
417                    };
418                }
419            }
420    
421            private ResolvedFunCall wrapAsSet(Exp arg) {
422                return new ResolvedFunCall(
423                        new SetFunDef(Resolver, new int[] {arg.getCategory()}),
424                        new Exp[] {arg},
425                        new SetType(arg.getType()));
426            }
427    
428            public Iterable evaluateIterable(final Evaluator evaluator) {
429                return new Iterable<Member>() {
430                    public Iterator<Member> iterator() {
431                        return new Iterator<Member>() {
432                            int index = 0;
433                            Iterator<Member> currentIterator = null;
434                            Member member = null;
435                            public boolean hasNext() {
436                                if (member != null) {
437                                    return true;
438                                }
439                                if (currentIterator == null) {
440                                    if (index >= iterCalcs.length) {
441                                        return false;
442                                    }
443                                    IterCalc iterCalc = iterCalcs[index++];
444                                    Iterable<Member> iter =
445                                        iterCalc.evaluateMemberIterable(evaluator);
446                                    currentIterator = iter.iterator();
447                                }
448                                while (true) {
449                                    boolean b = currentIterator.hasNext();
450                                    while (! b) {
451                                        if (index >= iterCalcs.length) {
452                                            return false;
453                                        }
454                                        IterCalc iterCalc = iterCalcs[index++];
455                                        Iterable<Member> iter =
456                                            iterCalc.evaluateMemberIterable(evaluator);
457                                        currentIterator = iter.iterator();
458                                        b = currentIterator.hasNext();
459                                    }
460                                    member = currentIterator.next();
461                                    if (member != null) {
462                                        break;
463                                    }
464                                }
465                                return true;
466                            }
467                            public Member next() {
468                                try {
469                                    return member;
470                                } finally {
471                                    member = null;
472                                }
473                            }
474                            public void remove() {
475                                throw new UnsupportedOperationException("remove");
476                            }
477                        };
478                    }
479                };
480            }
481        }
482    
483        private static class ResolverImpl extends ResolverBase {
484            public ResolverImpl() {
485                super(
486                        "{}",
487                        "{<Member> [, <Member>...]}",
488                        "Brace operator constructs a set.",
489                        Syntax.Braces);
490            }
491    
492            public FunDef resolve(
493                    Exp[] args, Validator validator, int[] conversionCount) {
494                int[] parameterTypes = new int[args.length];
495                for (int i = 0; i < args.length; i++) {
496                    if (validator.canConvert(
497                            args[i], Category.Member, conversionCount)) {
498                        parameterTypes[i] = Category.Member;
499                        continue;
500                    }
501                    if (validator.canConvert(
502                            args[i], Category.Set, conversionCount)) {
503                        parameterTypes[i] = Category.Set;
504                        continue;
505                    }
506                    if (validator.canConvert(
507                            args[i], Category.Tuple, conversionCount)) {
508                        parameterTypes[i] = Category.Tuple;
509                        continue;
510                    }
511                    return null;
512                }
513                return new SetFunDef(this, parameterTypes);
514            }
515        }
516    }
517    
518    // End SetFunDef.java