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) 2006-2011 Pentaho
008// All Rights Reserved.
009*/
010package mondrian.olap.fun;
011
012import mondrian.calc.*;
013import mondrian.calc.impl.*;
014import mondrian.mdx.ResolvedFunCall;
015import mondrian.olap.*;
016import mondrian.server.Locus;
017
018import java.util.List;
019
020/**
021 * Definition of the <code>Filter</code> MDX function.
022 *
023 * <p>Syntax:
024 * <blockquote><code>Filter(&lt;Set&gt;, &lt;Search
025 * Condition&gt;)</code></blockquote>
026 *
027 * @author jhyde
028 * @since Mar 23, 2006
029 */
030class FilterFunDef extends FunDefBase {
031
032    private static final String TIMING_NAME =
033        FilterFunDef.class.getSimpleName();
034
035    static final FilterFunDef instance = new FilterFunDef();
036
037    private FilterFunDef() {
038        super(
039            "Filter",
040            "Returns the set resulting from filtering a set based on a search condition.",
041            "fxxb");
042    }
043
044    public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) {
045        // Ignore the caller's priority. We prefer to return iterable, because
046        // it makes NamedSet.CurrentOrdinal work.
047        List<ResultStyle> styles = compiler.getAcceptableResultStyles();
048        if (call.getArg(0) instanceof ResolvedFunCall
049            && ((ResolvedFunCall) call.getArg(0)).getFunName().equals("AS"))
050        {
051            styles = ResultStyle.ITERABLE_ONLY;
052        }
053        if (styles.contains(ResultStyle.ITERABLE)
054            || styles.contains(ResultStyle.ANY))
055        {
056            return compileCallIterable(call, compiler);
057        } else if (styles.contains(ResultStyle.LIST)
058            || styles.contains(ResultStyle.MUTABLE_LIST))
059        {
060            return compileCallList(call, compiler);
061        } else {
062            throw ResultStyleException.generate(
063                ResultStyle.ITERABLE_LIST_MUTABLELIST_ANY,
064                styles);
065        }
066    }
067
068    /**
069     * Returns an IterCalc.
070     *
071     * <p>Here we would like to get either a IterCalc or ListCalc (mutable)
072     * from the inner expression. For the IterCalc, its Iterator
073     * can be wrapped with another Iterator that filters each element.
074     * For the mutable list, remove all members that are filtered.
075     *
076     * @param call Call
077     * @param compiler Compiler
078     * @return Implementation of this function call in the Iterable result style
079     */
080    protected IterCalc compileCallIterable(
081        final ResolvedFunCall call,
082        ExpCompiler compiler)
083    {
084        // want iterable, mutable list or immutable list in that order
085        Calc imlcalc = compiler.compileAs(
086            call.getArg(0), null, ResultStyle.ITERABLE_LIST_MUTABLELIST);
087        BooleanCalc bcalc = compiler.compileBoolean(call.getArg(1));
088        Calc[] calcs = new Calc[] {imlcalc, bcalc};
089
090        // check returned calc ResultStyles
091        checkIterListResultStyles(imlcalc);
092
093        if (imlcalc.getResultStyle() == ResultStyle.ITERABLE) {
094            return new IterIterCalc(call, calcs);
095        } else if (imlcalc.getResultStyle() == ResultStyle.LIST) {
096            return new ImmutableIterCalc(call, calcs);
097        } else {
098            return new MutableIterCalc(call, calcs);
099        }
100    }
101
102    private static abstract class BaseIterCalc extends AbstractIterCalc {
103        protected BaseIterCalc(ResolvedFunCall call, Calc[] calcs) {
104            super(call, calcs);
105        }
106
107        public TupleIterable evaluateIterable(Evaluator evaluator) {
108            evaluator.getTiming().markStart(TIMING_NAME);
109            try {
110                ResolvedFunCall call = (ResolvedFunCall) exp;
111                // Use a native evaluator, if more efficient.
112                // TODO: Figure this out at compile time.
113                SchemaReader schemaReader = evaluator.getSchemaReader();
114                NativeEvaluator nativeEvaluator =
115                    schemaReader.getNativeSetEvaluator(
116                        call.getFunDef(), call.getArgs(), evaluator, this);
117                if (nativeEvaluator != null) {
118                    return (TupleIterable)
119                        nativeEvaluator.execute(ResultStyle.ITERABLE);
120                } else {
121                    return makeIterable(evaluator);
122                }
123            } finally {
124                evaluator.getTiming().markEnd(TIMING_NAME);
125            }
126        }
127
128        protected abstract TupleIterable makeIterable(Evaluator evaluator);
129
130        public boolean dependsOn(Hierarchy hierarchy) {
131            return anyDependsButFirst(getCalcs(), hierarchy);
132        }
133    }
134
135    private static class MutableIterCalc extends BaseIterCalc {
136        MutableIterCalc(ResolvedFunCall call, Calc[] calcs) {
137            super(call, calcs);
138            assert calcs[0] instanceof ListCalc;
139            assert calcs[1] instanceof BooleanCalc;
140        }
141
142        protected TupleIterable makeIterable(Evaluator evaluator) {
143            evaluator.getTiming().markStart(TIMING_NAME);
144            final int savepoint = evaluator.savepoint();
145            try {
146                Calc[] calcs = getCalcs();
147                ListCalc lcalc = (ListCalc) calcs[0];
148                BooleanCalc bcalc = (BooleanCalc) calcs[1];
149
150                TupleList list = lcalc.evaluateList(evaluator);
151
152                // make list mutable; guess selectivity .5
153                TupleList result =
154                    TupleCollections.createList(
155                        list.getArity(), list.size() / 2);
156                evaluator.setNonEmpty(false);
157                TupleCursor cursor = list.tupleCursor();
158                while (cursor.forward()) {
159                    cursor.setContext(evaluator);
160                    if (bcalc.evaluateBoolean(evaluator)) {
161                        result.addCurrent(cursor);
162                    }
163                }
164                return result;
165            } finally {
166                evaluator.restore(savepoint);
167                evaluator.getTiming().markEnd(TIMING_NAME);
168            }
169        }
170    }
171
172    private static class ImmutableIterCalc extends BaseIterCalc {
173        ImmutableIterCalc(ResolvedFunCall call, Calc[] calcs) {
174            super(call, calcs);
175            assert calcs[0] instanceof ListCalc;
176            assert calcs[1] instanceof BooleanCalc;
177        }
178
179        protected TupleIterable makeIterable(Evaluator evaluator) {
180            Calc[] calcs = getCalcs();
181            ListCalc lcalc = (ListCalc) calcs[0];
182            BooleanCalc bcalc = (BooleanCalc) calcs[1];
183            TupleList members = lcalc.evaluateList(evaluator);
184
185            // Not mutable, must create new list
186            TupleList result = members.cloneList(members.size() / 2);
187            final int savepoint = evaluator.savepoint();
188            try {
189                evaluator.setNonEmpty(false);
190                TupleCursor cursor = members.tupleCursor();
191                while (cursor.forward()) {
192                    cursor.setContext(evaluator);
193                    if (bcalc.evaluateBoolean(evaluator)) {
194                        result.addCurrent(cursor);
195                    }
196                }
197                return result;
198            } finally {
199                evaluator.restore(savepoint);
200            }
201        }
202    }
203
204    private static class IterIterCalc
205        extends BaseIterCalc
206    {
207        IterIterCalc(ResolvedFunCall call, Calc[] calcs) {
208            super(call, calcs);
209            assert calcs[0] instanceof IterCalc;
210            assert calcs[1] instanceof BooleanCalc;
211        }
212
213        protected TupleIterable makeIterable(Evaluator evaluator) {
214            Calc[] calcs = getCalcs();
215            IterCalc icalc = (IterCalc) calcs[0];
216            final BooleanCalc bcalc = (BooleanCalc) calcs[1];
217
218            // This does dynamics, just in time,
219            // as needed filtering
220            final TupleIterable iterable =
221                icalc.evaluateIterable(evaluator);
222            final Evaluator evaluator2 = evaluator.push();
223            evaluator2.setNonEmpty(false);
224            return new AbstractTupleIterable(iterable.getArity()) {
225                public TupleCursor tupleCursor() {
226                    return new AbstractTupleCursor(iterable.getArity()) {
227                        final TupleCursor cursor = iterable.tupleCursor();
228
229                        public boolean forward() {
230                            while (cursor.forward()) {
231                                Locus.peek().execution.checkCancelOrTimeout();
232                                cursor.setContext(evaluator2);
233                                if (bcalc.evaluateBoolean(evaluator2)) {
234                                    return true;
235                                }
236                            }
237                            return false;
238                        }
239
240                        public List<Member> current() {
241                            return cursor.current();
242                        }
243                    };
244                }
245            };
246        }
247    }
248
249
250    /**
251     * Returns a ListCalc.
252     *
253     * @param call Call
254     * @param compiler Compiler
255     * @return Implementation of this function call in the List result style
256     */
257    protected ListCalc compileCallList(
258        final ResolvedFunCall call,
259        ExpCompiler compiler)
260    {
261        Calc ilcalc = compiler.compileList(call.getArg(0), false);
262        BooleanCalc bcalc = compiler.compileBoolean(call.getArg(1));
263        Calc[] calcs = new Calc[] {ilcalc, bcalc};
264
265        // Note that all of the ListCalc's return will be mutable
266        switch (ilcalc.getResultStyle()) {
267        case LIST:
268            return new ImmutableListCalc(call, calcs);
269        case MUTABLE_LIST:
270            return new MutableListCalc(call, calcs);
271        }
272        throw ResultStyleException.generateBadType(
273            ResultStyle.MUTABLELIST_LIST,
274            ilcalc.getResultStyle());
275    }
276
277    private static abstract class BaseListCalc extends AbstractListCalc {
278        protected BaseListCalc(ResolvedFunCall call, Calc[] calcs) {
279            super(call, calcs);
280        }
281
282        public TupleList evaluateList(Evaluator evaluator) {
283            ResolvedFunCall call = (ResolvedFunCall) exp;
284            // Use a native evaluator, if more efficient.
285            // TODO: Figure this out at compile time.
286            SchemaReader schemaReader = evaluator.getSchemaReader();
287            NativeEvaluator nativeEvaluator =
288                schemaReader.getNativeSetEvaluator(
289                    call.getFunDef(), call.getArgs(), evaluator, this);
290            if (nativeEvaluator != null) {
291                return (TupleList) nativeEvaluator.execute(
292                    ResultStyle.ITERABLE);
293            } else {
294                return makeList(evaluator);
295            }
296        }
297        protected abstract TupleList makeList(Evaluator evaluator);
298
299        public boolean dependsOn(Hierarchy hierarchy) {
300            return anyDependsButFirst(getCalcs(), hierarchy);
301        }
302    }
303
304    private static class MutableListCalc extends BaseListCalc {
305        MutableListCalc(ResolvedFunCall call, Calc[] calcs) {
306            super(call, calcs);
307            assert calcs[0] instanceof ListCalc;
308            assert calcs[1] instanceof BooleanCalc;
309        }
310
311        protected TupleList makeList(Evaluator evaluator) {
312            Calc[] calcs = getCalcs();
313            ListCalc lcalc = (ListCalc) calcs[0];
314            BooleanCalc bcalc = (BooleanCalc) calcs[1];
315            TupleList members0 = lcalc.evaluateList(evaluator);
316
317            // make list mutable;
318            // for capacity planning, guess selectivity = .5
319            TupleList result = members0.cloneList(members0.size() / 2);
320            final int savepoint = evaluator.savepoint();
321            try {
322                evaluator.setNonEmpty(false);
323                final TupleCursor cursor = members0.tupleCursor();
324                while (cursor.forward()) {
325                    cursor.setContext(evaluator);
326                    if (bcalc.evaluateBoolean(evaluator)) {
327                        result.addCurrent(cursor);
328                    }
329                }
330                return result;
331            } finally {
332                evaluator.restore(savepoint);
333            }
334        }
335    }
336
337    private static class ImmutableListCalc extends BaseListCalc {
338        ImmutableListCalc(ResolvedFunCall call, Calc[] calcs) {
339            super(call, calcs);
340            assert calcs[0] instanceof ListCalc;
341            assert calcs[1] instanceof BooleanCalc;
342        }
343
344        protected TupleList makeList(Evaluator evaluator) {
345            evaluator.getTiming().markStart(TIMING_NAME);
346            final int savepoint = evaluator.savepoint();
347            try {
348                Calc[] calcs = getCalcs();
349                ListCalc lcalc = (ListCalc) calcs[0];
350                BooleanCalc bcalc = (BooleanCalc) calcs[1];
351                TupleList members0 = lcalc.evaluateList(evaluator);
352
353                // Not mutable, must create new list;
354                // for capacity planning, guess selectivity = .5
355                TupleList result = members0.cloneList(members0.size() / 2);
356                evaluator.setNonEmpty(false);
357                final TupleCursor cursor = members0.tupleCursor();
358                while (cursor.forward()) {
359                    cursor.setContext(evaluator);
360                    if (bcalc.evaluateBoolean(evaluator)) {
361                        result.addCurrent(cursor);
362                    }
363                }
364                return result;
365            } finally {
366                evaluator.restore(savepoint);
367                evaluator.getTiming().markEnd(TIMING_NAME);
368            }
369        }
370    }
371}
372
373// End FilterFunDef.java