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