001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/TopBottomCountFunDef.java#10 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2002-2002 Kana Software, Inc.
007    // Copyright (C) 2002-2008 Julian Hyde and others
008    // All Rights Reserved.
009    // You must accept the terms of that agreement to use this software.
010    */
011    package mondrian.olap.fun;
012    
013    import mondrian.calc.*;
014    import mondrian.calc.impl.AbstractListCalc;
015    import mondrian.calc.ResultStyle;
016    import mondrian.mdx.ResolvedFunCall;
017    import mondrian.olap.*;
018    import mondrian.olap.type.TupleType;
019    
020    import java.util.*;
021    
022    /**
023     * Definition of the <code>TopCount</code> and <code>BottomCount</code>
024     * MDX builtin functions.
025     *
026     * @author jhyde
027     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/TopBottomCountFunDef.java#10 $
028     * @since Mar 23, 2006
029     */
030    class TopBottomCountFunDef extends FunDefBase {
031        boolean top;
032    
033        static final MultiResolver TopCountResolver =
034            new MultiResolver(
035                "TopCount",
036                "TopCount(<Set>, <Count>[, <Numeric Expression>])",
037                "Returns a specified number of items from the top of a set, optionally ordering the set first.",
038                new String[]{"fxxnn", "fxxn"})
039            {
040                protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) {
041                    return new TopBottomCountFunDef(dummyFunDef, true);
042                }
043            };
044    
045        static final MultiResolver BottomCountResolver =
046            new MultiResolver(
047                "BottomCount",
048                "BottomCount(<Set>, <Count>[, <Numeric Expression>])",
049                "Returns a specified number of items from the bottom of a set, optionally ordering the set first.",
050                new String[]{"fxxnn", "fxxn"})
051            {
052                protected FunDef createFunDef(Exp[] args, FunDef dummyFunDef) {
053                    return new TopBottomCountFunDef(dummyFunDef, false);
054                }
055            };
056    
057        public TopBottomCountFunDef(FunDef dummyFunDef, final boolean top) {
058            super(dummyFunDef);
059            this.top = top;
060        }
061    
062        public Calc compileCall(final ResolvedFunCall call, ExpCompiler compiler) {
063            // Compile the member list expression. Ask for a mutable list, because
064            // we're going to sortMembers it later.
065            final ListCalc listCalc =
066                    compiler.compileList(call.getArg(0), true);
067            final IntegerCalc integerCalc =
068                    compiler.compileInteger(call.getArg(1));
069            final Calc orderCalc =
070                    call.getArgCount() > 2 ?
071                    compiler.compileScalar(call.getArg(2), true) :
072                    null;
073            final int arity = call.getType() instanceof TupleType ?
074                ((TupleType) call.getType()).elementTypes.length :
075                1;
076            return new AbstractListCalc(call, new Calc[] {listCalc, integerCalc, orderCalc}) {
077                public List evaluateList(Evaluator evaluator) {
078                    // Use a native evaluator, if more efficient.
079                    // TODO: Figure this out at compile time.
080                    SchemaReader schemaReader = evaluator.getSchemaReader();
081                    NativeEvaluator nativeEvaluator =
082                            schemaReader.getNativeSetEvaluator(
083                                    call.getFunDef(), call.getArgs(), evaluator, this);
084                    if (nativeEvaluator != null) {
085                        return (List) nativeEvaluator.execute(ResultStyle.LIST);
086                    }
087    
088                    List list = listCalc.evaluateList(evaluator);
089                    int n = integerCalc.evaluateInteger(evaluator);
090    
091                    if (n == mondrian.olap.fun.FunUtil.IntegerNull) {
092                        return new java.util.ArrayList();
093                    }
094    
095                    if (orderCalc != null) {
096                        if (list.isEmpty()) {
097                            return list;
098                        }
099                        final Object trial = list.get(0);
100                        boolean highCard = false;
101                        if (trial instanceof Member) {
102                            if (((Member) trial).getHierarchy().getDimension()
103                                    .isHighCardinality()) {
104                                highCard = true;
105                            }
106                        } else {
107                            final Member[] trials = (Member[]) trial;
108                            for (int i = 0; i < trials.length; i++) {
109                                if (trials[i].getHierarchy().getDimension()
110                                        .isHighCardinality()) {
111                                    break;
112                                }
113                            }
114                        }
115    
116                        if (highCard) {
117                            final List l2 = new ArrayList();
118                            final Iterator iterator = list.iterator();
119                            for (int i = 0; i < n + 1 && iterator.hasNext(); i++) {
120                                final Object o = iterator.next();
121                                l2.add(o);
122                            }
123                            final Evaluator eval = evaluator.push();
124                            if (arity == 1) {
125                                for (;;) {
126                                    sortMembers(
127                                        eval,
128                                        (List<Member>) l2,
129                                        orderCalc, top, true);
130                                    l2.remove(l2.size() - 1);
131                                    if (!iterator.hasNext()) {
132                                        break;
133                                    }
134                                    l2.add(iterator.next());
135                                }
136                                return l2;
137                            } else {
138                                for (;;) {
139                                    sortTuples(
140                                        eval,
141                                        (List<mondrian.olap.Member[]>) l2,
142                                        orderCalc, top, true, arity);
143                                    l2.remove(l2.size() - 1);
144                                    if (!iterator.hasNext()) {
145                                        break;
146                                    }
147                                    l2.add(iterator.next());
148                                }
149                                return l2;
150                            }
151                        } else {
152                            if (arity == 1) {
153                                sortMembers(
154                                    evaluator.push(),
155                                    (List<Member>) list,
156                                    orderCalc, top, true);
157                            } else {
158                                sortTuples(
159                                    evaluator.push(),
160                                    (List<mondrian.olap.Member[]>) list,
161                                    orderCalc, top, true, arity);
162                            }
163                            if (n < list.size()) {
164                                list = list.subList(0, n);
165                            }
166                            return list;
167                        }
168                    } else {
169                        if (list instanceof AbstractList && list.size() < n) {
170                            return list;
171                        } else {
172                            return list.subList(0, n);
173                        }
174                    }
175                }
176    
177                public boolean dependsOn(Dimension dimension) {
178                    return anyDependsButFirst(getCalcs(), dimension);
179                }
180            };
181        }
182    }
183    
184    // End TopBottomCountFunDef.java