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) 2007-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.olap.type.ScalarType;
017
018import java.util.ArrayList;
019import java.util.List;
020
021/**
022 * Definition of the <code>DrilldownLevelTop</code> and
023 * <code>DrilldownLevelBottom</code> MDX builtin functions.
024 *
025 * <p>Syntax:
026 *
027 * <blockquote>
028 * DrilldownLevelTop(Set_Expression, Count [, [Level_Expression][,
029 * Numeric_Expression]])<br/>
030 * DrilldownLevelBottom(Set_Expression, Count [, [Level_Expression][,
031 * Numeric_Expression]])
032 * </blockquote>
033 *
034 * @author jhyde
035 * @since Oct 18, 2007
036 */
037class DrilldownLevelTopBottomFunDef extends FunDefBase {
038    final boolean top;
039
040    static final MultiResolver DrilldownLevelTopResolver =
041        new MultiResolver(
042            "DrilldownLevelTop",
043            "DrilldownLevelTop(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])",
044            "Drills down the topmost members of a set, at a specified level, to one level below.",
045            new String[] {"fxxn", "fxxnl", "fxxnln", "fxxnen"})
046        {
047            protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) {
048                return new DrilldownLevelTopBottomFunDef(dummyFunDef, true);
049            }
050        };
051
052    static final MultiResolver DrilldownLevelBottomResolver =
053        new MultiResolver(
054            "DrilldownLevelBottom",
055            "DrilldownLevelBottom(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])",
056            "Drills down the bottommost members of a set, at a specified level, to one level below.",
057            new String[] {"fxxn", "fxxnl", "fxxnln", "fxxnen"})
058        {
059            protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) {
060                return new DrilldownLevelTopBottomFunDef(dummyFunDef, false);
061            }
062        };
063
064    public DrilldownLevelTopBottomFunDef(
065        FunDef dummyFunDef,
066        final boolean top)
067    {
068        super(dummyFunDef);
069        this.top = top;
070    }
071
072    public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) {
073        // Compile the member list expression. Ask for a mutable list, because
074        // we're going to insert members into it later.
075        final ListCalc listCalc =
076            compiler.compileList(call.getArg(0), true);
077        final IntegerCalc integerCalc =
078            compiler.compileInteger(call.getArg(1));
079        final LevelCalc levelCalc =
080            call.getArgCount() > 2
081                && call.getArg(2).getCategory() != Category.Empty
082                ? compiler.compileLevel(call.getArg(2))
083                : null;
084        final Calc orderCalc =
085            call.getArgCount() > 3
086            ? compiler.compileScalar(call.getArg(3), true)
087            : new ValueCalc(
088                new DummyExp(
089                    new ScalarType()));
090        return new AbstractListCalc(
091            call,
092            new Calc[] {listCalc, integerCalc, orderCalc})
093        {
094            public TupleList evaluateList(Evaluator evaluator) {
095                // Use a native evaluator, if more efficient.
096                // TODO: Figure this out at compile time.
097                SchemaReader schemaReader = evaluator.getSchemaReader();
098                NativeEvaluator nativeEvaluator =
099                    schemaReader.getNativeSetEvaluator(
100                        call.getFunDef(), call.getArgs(), evaluator, this);
101                if (nativeEvaluator != null) {
102                    return
103                        (TupleList) nativeEvaluator.execute(ResultStyle.LIST);
104                }
105
106                TupleList list = listCalc.evaluateList(evaluator);
107                int n = integerCalc.evaluateInteger(evaluator);
108                if (n == FunUtil.IntegerNull || n <= 0) {
109                    return list;
110                }
111                Level level;
112                if (levelCalc == null) {
113                    level = null;
114                } else {
115                    level = levelCalc.evaluateLevel(evaluator);
116                }
117                List<Member> result = new ArrayList<Member>();
118                assert list.getArity() == 1;
119                for (Member member : list.slice(0)) {
120                    result.add(member);
121                    if (level != null && member.getLevel() != level) {
122                        if (level.getDimension() != member.getDimension()) {
123                            throw newEvalException(
124                                DrilldownLevelTopBottomFunDef.this,
125                                "Level '"
126                                    + level.getUniqueName()
127                                    + "' not compatible with member '"
128                                    + member.getUniqueName()
129                                    + "'");
130                        }
131                        continue;
132                    }
133                    List<Member> children =
134                        schemaReader.getMemberChildren(member);
135                    final int savepoint = evaluator.savepoint();
136                    List<Member> sortedChildren;
137                    try {
138                        evaluator.setNonEmpty(false);
139                        sortedChildren =
140                            sortMembers(
141                                evaluator,
142                                children,
143                                children,
144                                orderCalc,
145                                top,
146                                true);
147                    } finally {
148                        evaluator.restore(savepoint);
149                    }
150                    int x = Math.min(n, sortedChildren.size());
151                    for (int i = 0; i < x; i++) {
152                        result.add(sortedChildren.get(i));
153                    }
154                }
155                return new UnaryTupleList(result);
156            }
157
158            public boolean dependsOn(Hierarchy hierarchy) {
159                return anyDependsButFirst(getCalcs(), hierarchy);
160            }
161        };
162    }
163}
164
165// End DrilldownLevelTopBottomFunDef.java