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.AbstractListCalc;
014import mondrian.calc.impl.UnaryTupleList;
015import mondrian.mdx.ResolvedFunCall;
016import mondrian.olap.*;
017
018import java.util.ArrayList;
019import java.util.List;
020
021/**
022 * Definition of the <code>DrilldownLevel</code> MDX function.
023 *
024 * <p>Syntax:
025 *
026 * <blockquote><pre>
027 * DrilldownLevel(Set_Expression[, Level_Expression])
028 * DrilldownLevel(Set_Expression, , Numeric_Expression)
029 * </pre></blockquote>
030 *
031 * @author jhyde
032 * @since Mar 23, 2006
033 */
034class DrilldownLevelFunDef extends FunDefBase {
035    static final ReflectiveMultiResolver Resolver =
036        new ReflectiveMultiResolver(
037            "DrilldownLevel",
038            "DrilldownLevel(<Set>[, <Level>]) or DrilldownLevel(<Set>, , <Index>)",
039            "Drills down the members of a set, at a specified level, to one level below. Alternatively, drills down on a specified dimension in the set.",
040            new String[]{"fxx", "fxxl", "fxxen"},
041            DrilldownLevelFunDef.class);
042
043    public DrilldownLevelFunDef(FunDef dummyFunDef) {
044        super(dummyFunDef);
045    }
046
047    public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
048        final ListCalc listCalc =
049            compiler.compileList(call.getArg(0));
050        final LevelCalc levelCalc =
051            call.getArgCount() > 1
052                && call.getArg(1).getType()
053                instanceof mondrian.olap.type.LevelType
054                ? compiler.compileLevel(call.getArg(1))
055                : null;
056        final IntegerCalc indexCalc =
057            call.getArgCount() > 2
058                ? compiler.compileInteger(call.getArg(2))
059                : null;
060        final int arity = listCalc.getType().getArity();
061        if (indexCalc == null) {
062            return new AbstractListCalc(call, new Calc[] {listCalc, levelCalc})
063            {
064                public TupleList evaluateList(Evaluator evaluator) {
065                    TupleList list = listCalc.evaluateList(evaluator);
066                    if (list.size() == 0) {
067                        return list;
068                    }
069                    int searchDepth = -1;
070                    if (levelCalc != null) {
071                        Level level = levelCalc.evaluateLevel(evaluator);
072                        searchDepth = level.getDepth();
073                    }
074                    return new UnaryTupleList(
075                        drill(searchDepth, list.slice(0), evaluator));
076                }
077            };
078        } else {
079            return new AbstractListCalc(call, new Calc[] {listCalc, indexCalc})
080            {
081                public TupleList evaluateList(Evaluator evaluator) {
082                    TupleList list = listCalc.evaluateList(evaluator);
083                    if (list.isEmpty()) {
084                        return list;
085                    }
086                    final int index = indexCalc.evaluateInteger(evaluator);
087                    if (index < 0 || index >= arity) {
088                        return list;
089                    }
090                    TupleList result = TupleCollections.createList(arity);
091                    final SchemaReader schemaReader =
092                        evaluator.getSchemaReader();
093                    final Member[] tupleClone = new Member[arity];
094                    for (List<Member> tuple : list) {
095                        result.add(tuple);
096                        final List<Member> children =
097                            schemaReader.getMemberChildren(tuple.get(index));
098                        for (Member child : children) {
099                            tuple.toArray(tupleClone);
100                            tupleClone[index] = child;
101                            result.addTuple(tupleClone);
102                        }
103                    }
104                    return result;
105                }
106            };
107        }
108    }
109
110    List<Member> drill(int searchDepth, List<Member> list, Evaluator evaluator)
111    {
112        if (searchDepth == -1) {
113            searchDepth = list.get(0).getLevel().getDepth();
114
115            for (int i = 1, m = list.size(); i < m; i++) {
116                Member member = list.get(i);
117                int memberDepth = member.getLevel().getDepth();
118
119                if (memberDepth > searchDepth) {
120                    searchDepth = memberDepth;
121                }
122            }
123        }
124
125        List<Member> drilledSet = new ArrayList<Member>();
126
127        for (int i = 0, m = list.size(); i < m; i++) {
128            Member member = list.get(i);
129            drilledSet.add(member);
130
131            Member nextMember =
132                i == (m - 1)
133                ? null
134                : list.get(i + 1);
135
136            //
137            // This member is drilled if it's at the correct depth
138            // and if it isn't drilled yet. A member is considered
139            // to be "drilled" if it is immediately followed by
140            // at least one descendant
141            //
142            if (member.getLevel().getDepth() == searchDepth
143                && !FunUtil.isAncestorOf(member, nextMember, true))
144            {
145                final List<Member> childMembers =
146                    evaluator.getSchemaReader().getMemberChildren(member);
147                for (Member childMember : childMembers) {
148                    drilledSet.add(childMember);
149                }
150            }
151        }
152        return drilledSet;
153    }
154}
155
156// End DrilldownLevelFunDef.java