001    /*
002    // This software is subject to the terms of the Common Public License
003    // Agreement, available at the following URL:
004    // http://www.opensource.org/licenses/cpl.html.
005    // Copyright (C) 2007-2008 Julian Hyde
006    // All Rights Reserved.
007    // You must accept the terms of that agreement to use this software.
008    */
009    package mondrian.olap.fun;
010    
011    import mondrian.olap.*;
012    import mondrian.olap.type.ScalarType;
013    import mondrian.calc.*;
014    import mondrian.calc.impl.AbstractListCalc;
015    import mondrian.calc.impl.ValueCalc;
016    import mondrian.mdx.ResolvedFunCall;
017    
018    import java.util.*;
019    
020    /**
021     * Definition of the <code>DrilldownLevelTop</code> and
022     * <code>DrilldownLevelBottom</code> MDX builtin functions.
023     *
024     * <p>Syntax:
025     *
026     * <blockquote><pre>
027     * DrilldownLevelTop(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])
028     * DrilldownLevelBottom(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])
029     * </pre></blockquote>
030     *
031     * @author jhyde
032     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/DrilldownLevelTopBottomFunDef.java#2 $
033     * @since Oct 18, 2007
034     */
035    class DrilldownLevelTopBottomFunDef extends FunDefBase {
036        final boolean top;
037    
038        static final MultiResolver DrilldownLevelTopResolver = new MultiResolver(
039                "DrilldownLevelTop",
040                "DrilldownLevelTop(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])",
041                "Drills down the topmost members of a set, at a specified level, to one level below.",
042                new String[] {"fxxn", "fxxnl", "fxxnln", "fxxnen"}) {
043            protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) {
044                return new DrilldownLevelTopBottomFunDef(dummyFunDef, true);
045            }
046        };
047    
048        static final MultiResolver DrilldownLevelBottomResolver = new MultiResolver(
049                "DrilldownLevelBottom",
050                "DrilldownLevelBottom(Set_Expression, Count [, [Level_Expression][, Numeric_Expression]])",
051                "Drills down the bottommost members of a set, at a specified level, to one level below.",
052                new String[] {"fxxn", "fxxnl", "fxxnln", "fxxnen"}) {
053            protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) {
054                return new DrilldownLevelTopBottomFunDef(dummyFunDef, false);
055            }
056        };
057    
058        public DrilldownLevelTopBottomFunDef(FunDef dummyFunDef, final boolean top) {
059            super(dummyFunDef);
060            this.top = top;
061        }
062    
063        public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) {
064            // Compile the member list expression. Ask for a mutable list, because
065            // we're going to insert members into it later.
066            final ListCalc listCalc =
067                compiler.compileList(call.getArg(0), true);
068            final IntegerCalc integerCalc =
069                compiler.compileInteger(call.getArg(1));
070            final LevelCalc levelCalc =
071                call.getArgCount() > 2
072                    && call.getArg(2).getCategory() != Category.Empty
073                    ? compiler.compileLevel(call.getArg(2))
074                    : null;
075            final Calc orderCalc =
076                    call.getArgCount() > 3 ?
077                    compiler.compileScalar(call.getArg(3), true) :
078                    new ValueCalc(
079                        new DummyExp(
080                            new ScalarType()));
081            return new AbstractListCalc(call, new Calc[] {listCalc, integerCalc, orderCalc}) {
082                public List evaluateList(Evaluator evaluator) {
083                    // Use a native evaluator, if more efficient.
084                    // TODO: Figure this out at compile time.
085                    SchemaReader schemaReader = evaluator.getSchemaReader();
086                    NativeEvaluator nativeEvaluator =
087                        schemaReader.getNativeSetEvaluator(
088                            call.getFunDef(), call.getArgs(), evaluator, this);
089                    if (nativeEvaluator != null) {
090                        return (List) nativeEvaluator.execute(ResultStyle.LIST);
091                    }
092    
093                    List<Member> list = listCalc.evaluateList(evaluator);
094                    int n = integerCalc.evaluateInteger(evaluator);
095                    if (n == FunUtil.IntegerNull || n <= 0) {
096                        return list;
097                    }
098                    Level level;
099                    if (levelCalc == null) {
100                        level = null;
101                    } else {
102                        level = levelCalc.evaluateLevel(evaluator);
103                    }
104                    List<Member> result = new ArrayList<Member>();
105                    for (Member member : list) {
106                        result.add(member);
107                        if (level != null && member.getLevel() != level) {
108                            if (level.getDimension() != member.getDimension()) {
109                                throw newEvalException(
110                                    DrilldownLevelTopBottomFunDef.this,
111                                    "Level '"
112                                        + level.getUniqueName()
113                                        + "' not compatible with member '"
114                                        + member.getUniqueName()
115                                        + "'");
116                            }
117                            continue;
118                        }
119                        List<Member> children = schemaReader.getMemberChildren(member);
120                        sortMembers(
121                            evaluator.push(),
122                            children,
123                            orderCalc,
124                            top,
125                            true);
126                        int x = Math.min(n, children.size());
127                        for (int i = 0; i < x; i++) {
128                            result.add(children.get(i));
129                        }
130                    }
131                    return result;
132                }
133    
134                public boolean dependsOn(Dimension dimension) {
135                    return anyDependsButFirst(getCalcs(), dimension);
136                }
137            };
138        }
139    }
140    
141    // End DrilldownLevelTopBottomFunDef.java