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) 2004-2005 Julian Hyde
008// Copyright (C) 2005-2011 Pentaho
009// All Rights Reserved.
010*/
011package mondrian.olap.fun;
012
013import mondrian.calc.*;
014import mondrian.calc.impl.AbstractListCalc;
015import mondrian.mdx.ResolvedFunCall;
016import mondrian.olap.*;
017
018import java.util.*;
019
020/**
021 * Definition of the <code>DrilldownMember</code> MDX function.
022 *
023 * @author Grzegorz Lojek
024 * @since 6 December, 2004
025 */
026class DrilldownMemberFunDef extends FunDefBase {
027    static final String[] reservedWords = new String[] {"RECURSIVE"};
028    static final ReflectiveMultiResolver Resolver =
029        new ReflectiveMultiResolver(
030            "DrilldownMember",
031            "DrilldownMember(<Set1>, <Set2>[, RECURSIVE])",
032            "Drills down the members in a set that are present in a second specified set.",
033            new String[]{"fxxx", "fxxxy"},
034            DrilldownMemberFunDef.class,
035            reservedWords);
036
037    public DrilldownMemberFunDef(FunDef funDef) {
038        super(funDef);
039    }
040
041    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
042        final ListCalc listCalc1 = compiler.compileList(call.getArg(0));
043        final ListCalc listCalc2 = compiler.compileList(call.getArg(1));
044        final String literalArg = getLiteralArg(call, 2, "", reservedWords);
045        final boolean recursive = literalArg.equals("RECURSIVE");
046
047        return new AbstractListCalc(
048            call,
049            new Calc[] {listCalc1, listCalc2})
050        {
051            public TupleList evaluateList(Evaluator evaluator) {
052                final TupleList list1 = listCalc1.evaluateList(evaluator);
053                final TupleList list2 = listCalc2.evaluateList(evaluator);
054                return drilldownMember(list1, list2, evaluator);
055            }
056
057            /**
058             * Drills down an element.
059             *
060             * <p>Algorithm: If object is present in {@code memberSet} adds to
061             * result children of the object. If flag {@code recursive} is set
062             * then this method is called recursively for the children.
063             *
064             * @param evaluator Evaluator
065             * @param tuple Tuple (may have arity 1)
066             * @param memberSet Set of members
067             * @param resultList Result
068             */
069            protected void drillDownObj(
070                Evaluator evaluator,
071                Member[] tuple,
072                Set<Member> memberSet,
073                TupleList resultList)
074            {
075                for (int k = 0; k < tuple.length; k++) {
076                    Member member = tuple[k];
077                    if (memberSet.contains(member)) {
078                        List<Member> children =
079                            evaluator.getSchemaReader().getMemberChildren(
080                                member);
081                        final Member[] tuple2 = tuple.clone();
082                        for (Member childMember : children) {
083                            tuple2[k] = childMember;
084                            resultList.addTuple(tuple2);
085                            if (recursive) {
086                                drillDownObj(
087                                    evaluator, tuple2, memberSet, resultList);
088                            }
089                        }
090                        break;
091                    }
092                }
093            }
094
095            private TupleList drilldownMember(
096                TupleList v0,
097                TupleList v1,
098                Evaluator evaluator)
099            {
100                assert v1.getArity() == 1;
101                if (v0.isEmpty() || v1.isEmpty()) {
102                    return v0;
103                }
104
105                Set<Member> set1 = new HashSet<Member>(v1.slice(0));
106
107                TupleList result = TupleCollections.createList(v0.getArity());
108                int i = 0, n = v0.size();
109                final Member[] members = new Member[v0.getArity()];
110                while (i < n) {
111                    List<Member> o = v0.get(i++);
112                    o.toArray(members);
113                    result.add(o);
114                    drillDownObj(evaluator, members, set1, result);
115                }
116                return result;
117            }
118        };
119    }
120}
121
122// End DrilldownMemberFunDef.java