001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/FilterFunDef.java#12 $
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) 2006-2007 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.olap.fun;
011    
012    import mondrian.calc.*;
013    import mondrian.calc.impl.AbstractListCalc;
014    import mondrian.calc.impl.AbstractIterCalc;
015    import mondrian.mdx.ResolvedFunCall;
016    import mondrian.olap.type.SetType;
017    import mondrian.olap.type.MemberType;
018    import mondrian.olap.*;
019    
020    import java.util.List;
021    import java.util.ArrayList;
022    import java.util.Iterator;
023    
024    /**
025     * Definition of the <code>Filter</code> MDX function.
026     *
027     * <p>Syntax:
028     * <blockquote><code>Filter(&lt;Set&gt;, &lt;Search Condition&gt;)</code></blockquote>
029     *
030     *
031     * @author jhyde
032     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/FilterFunDef.java#12 $
033     * @since Mar 23, 2006
034     */
035    class FilterFunDef extends FunDefBase {
036        static final FilterFunDef instance = new FilterFunDef();
037    
038        private FilterFunDef() {
039            super(
040                "Filter",
041                "Returns the set resulting from filtering a set based on a search condition.",
042                "fxxb");
043        }
044    
045        public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) {
046            // What is the desired return type?
047            for (ResultStyle r : compiler.getAcceptableResultStyles()) {
048                switch (r) {
049                case ITERABLE:
050                case ANY:
051                    // Consumer wants ITERABLE or ANY
052                    return compileCallIterable(call, compiler);
053                case MUTABLE_LIST:
054                case LIST:
055                    // Consumer wants MUTABLE_LIST or LIST
056                    return compileCallList(call, compiler);
057                }
058            }
059            throw ResultStyleException.generate(
060                ResultStyle.ITERABLE_LIST_MUTABLELIST_ANY,
061                compiler.getAcceptableResultStyles());
062        }
063    
064    
065        /**
066         * Returns an IterCalc.
067         *
068         * <p>Here we would like to get either a IterCalc or ListCalc (mutable)
069         * from the inner expression. For the IterCalc, its Iterator
070         * can be wrapped with another Iterator that filters each element.
071         * For the mutable list, remove all members that are filtered.
072         *
073         * @param call Call
074         * @param compiler Compiler
075         * @return Implementation of this function call in the Iterable result style
076         */
077        protected IterCalc compileCallIterable(
078            final ResolvedFunCall call,
079            ExpCompiler compiler)
080        {
081            // want iterable, mutable list or immutable list in that order
082            Calc imlcalc = compiler.compileAs(call.getArg(0),
083                null, ResultStyle.ITERABLE_LIST_MUTABLELIST);
084            BooleanCalc bcalc = compiler.compileBoolean(call.getArg(1));
085            Calc[] calcs = new Calc[] {imlcalc, bcalc};
086    
087            // check returned calc ResultStyles
088            checkIterListResultStyles(imlcalc);
089    
090            if (((SetType) imlcalc.getType()).getElementType() instanceof MemberType) {
091                if (imlcalc.getResultStyle() == ResultStyle.ITERABLE) {
092                    return new IterMemberIterCalc(call, calcs);
093                } else if (imlcalc.getResultStyle() == ResultStyle.LIST) {
094                    return new ImMutableMemberIterCalc(call, calcs);
095                } else {
096                    return new MutableMemberIterCalc(call, calcs);
097                }
098    
099            } else {
100    
101                if (imlcalc.getResultStyle() == ResultStyle.ITERABLE) {
102                    return new IterMemberArrayIterCalc(call, calcs);
103                } else if (imlcalc.getResultStyle() == ResultStyle.LIST) {
104                    return new ImMutableMemberArrayIterCalc(call, calcs);
105                } else {
106                    return new MutableMemberArrayIterCalc(call, calcs);
107                }
108            }
109        }
110    
111        private static abstract class BaseIterCalc extends AbstractIterCalc {
112            protected BaseIterCalc(ResolvedFunCall call, Calc[] calcs) {
113                super(call, calcs);
114            }
115            public Iterable evaluateIterable(Evaluator evaluator) {
116                ResolvedFunCall call = (ResolvedFunCall) exp;
117                // Use a native evaluator, if more efficient.
118                // TODO: Figure this out at compile time.
119                SchemaReader schemaReader = evaluator.getSchemaReader();
120                NativeEvaluator nativeEvaluator =
121                        schemaReader.getNativeSetEvaluator(
122                                call.getFunDef(), call.getArgs(), evaluator, this);
123                if (nativeEvaluator != null) {
124                    return (Iterable) nativeEvaluator.execute(
125                            ResultStyle.ITERABLE);
126                } else {
127                    return makeIterable(evaluator);
128                }
129            }
130            protected abstract Iterable makeIterable(Evaluator evaluator);
131    
132            public boolean dependsOn(Dimension dimension) {
133                return anyDependsButFirst(getCalcs(), dimension);
134            }
135        }
136        //
137        // Member Iter Calcs
138        //
139        private static class MutableMemberIterCalc extends BaseIterCalc {
140            MutableMemberIterCalc(ResolvedFunCall call, Calc[] calcs) {
141                super(call, calcs);
142            }
143            protected Iterable makeIterable(Evaluator evaluator) {
144                Calc[] calcs = getCalcs();
145                ListCalc lcalc = (ListCalc) calcs[0];
146                BooleanCalc bcalc = (BooleanCalc) calcs[1];
147    
148                final Evaluator evaluator2 = evaluator.push();
149                List members = lcalc.evaluateList(evaluator);
150    
151                Iterator it = members.iterator();
152                while (it.hasNext()) {
153                    Member member = (Member) it.next();
154                    evaluator2.setContext(member);
155                    if (! bcalc.evaluateBoolean(evaluator2)) {
156                        it.remove();
157                    }
158                }
159                return members;
160            }
161        }
162        private static class ImMutableMemberIterCalc extends BaseIterCalc {
163            ImMutableMemberIterCalc(ResolvedFunCall call, Calc[] calcs) {
164                super(call, calcs);
165            }
166            protected Iterable makeIterable(Evaluator evaluator) {
167                Calc[] calcs = getCalcs();
168                ListCalc lcalc = (ListCalc) calcs[0];
169                BooleanCalc bcalc = (BooleanCalc) calcs[1];
170    
171                final Evaluator evaluator2 = evaluator.push();
172                List<Member> members = lcalc.evaluateList(evaluator);
173    
174                // Not mutable, must create new list
175                List<Member> result = new ArrayList<Member>();
176                for (int i = 0, count = members.size(); i < count; i++) {
177                    Member member = members.get(i);
178                    evaluator2.setContext(member);
179                    if (bcalc.evaluateBoolean(evaluator2)) {
180                        result.add(member);
181                    }
182                }
183                return result;
184            }
185        }
186        private static class IterMemberIterCalc extends BaseIterCalc {
187            IterMemberIterCalc(ResolvedFunCall call, Calc[] calcs) {
188                super(call, calcs);
189            }
190            protected Iterable makeIterable(Evaluator evaluator) {
191                Calc[] calcs = getCalcs();
192                IterCalc icalc = (IterCalc) calcs[0];
193                final BooleanCalc bcalc = (BooleanCalc) calcs[1];
194    
195                final Evaluator evaluator2 = evaluator.push();
196                // This does dynamics, just in time,
197                // as needed filtering
198                final Iterable<Member> iter = icalc.evaluateIterable(evaluator);
199    
200                return new Iterable<Member>() {
201                    public Iterator<Member> iterator() {
202                        return new Iterator<Member>() {
203                            Iterator<Member> it = iter.iterator();
204                            Member m = null;
205                            public boolean hasNext() {
206                                if (m != null) {
207                                    return true;
208                                }
209                                if (! it.hasNext()) {
210                                    return false;
211                                }
212                                this.m = it.next();
213                                evaluator2.setContext(this.m);
214                                while (! bcalc.evaluateBoolean(evaluator2)) {
215                                    if (! it.hasNext()) {
216                                        return false;
217                                    }
218                                    this.m = it.next();
219                                    evaluator2.setContext(this.m);
220                                }
221                                return true;
222                            }
223                            public Member next() {
224                                try {
225                                    return this.m;
226                                } finally {
227                                    this.m = null;
228                                }
229                            }
230                            public void remove() {
231                                throw new UnsupportedOperationException("remove");
232                            }
233                        };
234                    }
235                };
236            }
237        }
238    
239        //
240        // Member[] Iter Calcs
241        //
242        private static class MutableMemberArrayIterCalc extends BaseIterCalc {
243            MutableMemberArrayIterCalc(ResolvedFunCall call, Calc[] calcs) {
244                super(call, calcs);
245            }
246            protected Iterable makeIterable(Evaluator evaluator) {
247                Calc[] calcs = getCalcs();
248                ListCalc lcalc = (ListCalc) calcs[0];
249                BooleanCalc bcalc = (BooleanCalc) calcs[1];
250    
251                final Evaluator evaluator2 = evaluator.push();
252                List members = lcalc.evaluateList(evaluator);
253    
254                Iterator it = members.iterator();
255                while (it.hasNext()) {
256                    Member[] member = (Member[]) it.next();
257                    evaluator2.setContext(member);
258                    if (! bcalc.evaluateBoolean(evaluator2)) {
259                        it.remove();
260                    }
261                }
262                return members;
263            }
264        }
265        private static class ImMutableMemberArrayIterCalc extends BaseIterCalc {
266            ImMutableMemberArrayIterCalc(ResolvedFunCall call, Calc[] calcs) {
267                super(call, calcs);
268            }
269            protected Iterable makeIterable(Evaluator evaluator) {
270                Calc[] calcs = getCalcs();
271                ListCalc lcalc = (ListCalc) calcs[0];
272                BooleanCalc bcalc = (BooleanCalc) calcs[1];
273    
274                final Evaluator evaluator2 = evaluator.push();
275                List<Member[]> members = lcalc.evaluateList(evaluator);
276    
277                // Not mutable, must create new list
278                List<Member[]> result = new ArrayList<Member[]>();
279                for (int i = 0, count = members.size(); i < count; i++) {
280                    Member[] member = members.get(i);
281                    evaluator2.setContext(member);
282                    if (bcalc.evaluateBoolean(evaluator2)) {
283                        result.add(member);
284                    }
285                }
286                return result;
287            }
288        }
289        private static class IterMemberArrayIterCalc extends BaseIterCalc {
290            IterMemberArrayIterCalc(ResolvedFunCall call, Calc[] calcs) {
291                super(call, calcs);
292            }
293            protected Iterable makeIterable(Evaluator evaluator) {
294                Calc[] calcs = getCalcs();
295                IterCalc icalc = (IterCalc) calcs[0];
296                final BooleanCalc bcalc = (BooleanCalc) calcs[1];
297    
298                final Evaluator evaluator2 = evaluator.push();
299    
300                // This does dynamics, just in time,
301                // as needed filtering
302                final Iterable<Member[]> iter = icalc.evaluateIterable(evaluator);
303                return new Iterable<Member[]>() {
304                    public Iterator<Member[]> iterator() {
305                        return new Iterator<Member[]>() {
306                            Iterator<Member[]> it = iter.iterator();
307                            Member[] m = null;
308                            public boolean hasNext() {
309                                if (m != null) {
310                                    return true;
311                                }
312                                if (! it.hasNext()) {
313                                    return false;
314                                }
315                                this.m = it.next();
316                                evaluator2.setContext(this.m);
317                                while (! bcalc.evaluateBoolean(evaluator2)) {
318                                    if (! it.hasNext()) {
319                                        return false;
320                                    }
321                                    this.m = it.next();
322                                    evaluator2.setContext(this.m);
323                                }
324                                return true;
325                            }
326                            public Member[] next() {
327                                try {
328                                    return this.m;
329                                } finally {
330                                    this.m = null;
331                                }
332                            }
333                            public void remove() {
334                                throw new UnsupportedOperationException("remove");
335                            }
336                        };
337                    }
338                };
339            }
340        }
341    
342    
343        /**
344         * Returns a ListCalc.
345         *
346         * @param call Call
347         * @param compiler Compiler
348         * @return Implementation of this function call in the List result style
349         */
350        protected ListCalc compileCallList(
351            final ResolvedFunCall call,
352                ExpCompiler compiler)
353        {
354            Calc ilcalc = compiler.compileAs(
355                call.getArg(0),
356                null, ResultStyle.MUTABLELIST_LIST);
357            BooleanCalc bcalc = compiler.compileBoolean(call.getArg(1));
358            Calc[] calcs = new Calc[] {ilcalc, bcalc};
359    
360            // Note that all of the ListCalc's return will be mutable
361            if (((SetType) ilcalc.getType()).getElementType() instanceof MemberType) {
362                switch (ilcalc.getResultStyle()) {
363                case LIST:
364                    return new ImMutableMemberListCalc(call, calcs);
365                case MUTABLE_LIST:
366                    return new MutableMemberListCalc(call, calcs);
367                }
368                throw ResultStyleException.generateBadType(
369                    ResultStyle.MUTABLELIST_LIST,
370                    ilcalc.getResultStyle());
371    
372            } else {
373    
374                switch (ilcalc.getResultStyle()) {
375                case LIST:
376                    return new ImMutableMemberArrayListCalc(call, calcs);
377                case MUTABLE_LIST:
378                    return new MutableMemberArrayListCalc(call, calcs);
379                }
380                throw ResultStyleException.generateBadType(
381                    ResultStyle.MUTABLELIST_LIST,
382                    ilcalc.getResultStyle());
383            }
384        }
385    
386        private static abstract class BaseListCalc extends AbstractListCalc {
387            protected BaseListCalc(ResolvedFunCall call, Calc[] calcs) {
388                super(call, calcs);
389            }
390            public List evaluateList(Evaluator evaluator) {
391                ResolvedFunCall call = (ResolvedFunCall) exp;
392                // Use a native evaluator, if more efficient.
393                // TODO: Figure this out at compile time.
394                SchemaReader schemaReader = evaluator.getSchemaReader();
395                NativeEvaluator nativeEvaluator =
396                        schemaReader.getNativeSetEvaluator(
397                                call.getFunDef(), call.getArgs(), evaluator, this);
398                if (nativeEvaluator != null) {
399                    return (List) nativeEvaluator.execute(
400                            ResultStyle.ITERABLE);
401    //                return (List) nativeEvaluator.execute(
402    //                        ResultStyle.MUTABLE_LIST);
403                } else {
404                    return makeList(evaluator);
405                }
406            }
407            protected abstract List makeList(Evaluator evaluator);
408    
409            public boolean dependsOn(Dimension dimension) {
410                return anyDependsButFirst(getCalcs(), dimension);
411            }
412        }
413        //
414        // Member List Calcs
415        //
416        private static class MutableMemberListCalc extends BaseListCalc {
417            MutableMemberListCalc(ResolvedFunCall call, Calc[] calcs) {
418                super(call, calcs);
419            }
420            protected List makeList(Evaluator evaluator) {
421                Calc[] calcs = getCalcs();
422                ListCalc lcalc = (ListCalc) calcs[0];
423                BooleanCalc bcalc = (BooleanCalc) calcs[1];
424    
425                final Evaluator evaluator2 = evaluator.push();
426                List members = lcalc.evaluateList(evaluator);
427    
428                Iterator it = members.iterator();
429                while (it.hasNext()) {
430                    Member member = (Member) it.next();
431                    evaluator2.setContext(member);
432                    if (! bcalc.evaluateBoolean(evaluator2)) {
433                        it.remove();
434                    }
435                }
436                return members;
437            }
438        }
439        private static class ImMutableMemberListCalc extends BaseListCalc {
440            ImMutableMemberListCalc(ResolvedFunCall call, Calc[] calcs) {
441                super(call, calcs);
442            }
443            protected List makeList(Evaluator evaluator) {
444                Calc[] calcs = getCalcs();
445                ListCalc lcalc = (ListCalc) calcs[0];
446                BooleanCalc bcalc = (BooleanCalc) calcs[1];
447    
448                final Evaluator evaluator2 = evaluator.push();
449                List<Member> members = lcalc.evaluateList(evaluator);
450    
451                // Not mutable, must create new list
452                List<Member> result = new ArrayList<Member>();
453                for (int i = 0, count = members.size(); i < count; i++) {
454                    Member member = members.get(i);
455                    evaluator2.setContext(member);
456                    if (bcalc.evaluateBoolean(evaluator2)) {
457                        result.add(member);
458                    }
459                }
460                return result;
461            }
462        }
463        //
464        // Member[] List Calcs
465        //
466        private static class MutableMemberArrayListCalc extends BaseListCalc {
467            MutableMemberArrayListCalc(ResolvedFunCall call, Calc[] calcs) {
468                super(call, calcs);
469            }
470            protected List makeList(Evaluator evaluator) {
471                Calc[] calcs = getCalcs();
472                ListCalc lcalc = (ListCalc) calcs[0];
473                BooleanCalc bcalc = (BooleanCalc) calcs[1];
474    
475                final Evaluator evaluator2 = evaluator.push();
476                List members = lcalc.evaluateList(evaluator);
477    
478                Iterator it = members.iterator();
479                while (it.hasNext()) {
480                    Member[] member = (Member[]) it.next();
481                    evaluator2.setContext(member);
482                    if (! bcalc.evaluateBoolean(evaluator2)) {
483                        it.remove();
484                    }
485                }
486                return members;
487            }
488        }
489        private static class ImMutableMemberArrayListCalc extends BaseListCalc {
490            ImMutableMemberArrayListCalc(ResolvedFunCall call, Calc[] calcs) {
491                super(call, calcs);
492            }
493            protected List makeList(Evaluator evaluator) {
494                Calc[] calcs = getCalcs();
495                ListCalc lcalc = (ListCalc) calcs[0];
496                BooleanCalc bcalc = (BooleanCalc) calcs[1];
497    
498                final Evaluator evaluator2 = evaluator.push();
499                List<Member[]> members = lcalc.evaluateList(evaluator);
500    
501                // Not mutable, must create new list
502                List<Member[]> result = new ArrayList<Member[]>();
503                for (int i = 0, count = members.size(); i < count; i++) {
504                    Member[] member = members.get(i);
505                    evaluator2.setContext(member);
506                    if (bcalc.evaluateBoolean(evaluator2)) {
507                        result.add(member);
508                    }
509                }
510                return result;
511            }
512        }
513    }
514    
515    // End FilterFunDef.java