001    /*
002    // $Id: //open/mondrian/src/main/mondrian/olap/fun/ToggleDrillStateFunDef.java#3 $
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.TupleType;
014    import mondrian.olap.type.SetType;
015    import mondrian.calc.Calc;
016    import mondrian.calc.ExpCompiler;
017    import mondrian.calc.ListCalc;
018    import mondrian.calc.impl.AbstractListCalc;
019    import mondrian.mdx.ResolvedFunCall;
020    import mondrian.resource.MondrianResource;
021    
022    import java.util.List;
023    import java.util.HashSet;
024    import java.util.ArrayList;
025    import java.util.Set;
026    
027    /**
028     * Definition of the <code>ToggleDrillState</code> MDX function.
029     *
030     * @author jhyde
031     * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/ToggleDrillStateFunDef.java#3 $
032     * @since Mar 23, 2006
033     */
034    class ToggleDrillStateFunDef extends FunDefBase {
035        static final String[] ReservedWords = new String[] {"RECURSIVE"};
036        static final ReflectiveMultiResolver Resolver = new ReflectiveMultiResolver(
037                "ToggleDrillState",
038                "ToggleDrillState(<Set1>, <Set2>[, RECURSIVE])",
039                "Toggles the drill state of members. This function is a combination of DrillupMember and DrilldownMember.",
040                new String[]{"fxxx", "fxxxy"},
041                ToggleDrillStateFunDef.class,
042                ReservedWords);
043    
044        public ToggleDrillStateFunDef(FunDef dummyFunDef) {
045            super(dummyFunDef);
046        }
047    
048        public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
049            if (call.getArgCount() > 2) {
050                throw MondrianResource.instance().ToggleDrillStateRecursiveNotSupported.ex();
051            }
052            final ListCalc listCalc0 =
053                    compiler.compileList(call.getArg(0));
054            final ListCalc listCalc1 =
055                    compiler.compileList(call.getArg(1));
056            if (((SetType) call.getType()).getArity() == 1) {
057                return new AbstractListCalc(call, new Calc[] {listCalc0, listCalc1}) {
058                    public List evaluateList(Evaluator evaluator) {
059                        final List<Member> list0 = listCalc0.evaluateList(evaluator);
060                        final List<Member> list1 = listCalc1.evaluateList(evaluator);
061                        return toggleDrillStateMembers(evaluator, list0, list1);
062                    }
063                };
064            } else {
065                return new AbstractListCalc(call, new Calc[] {listCalc0, listCalc1}) {
066                    public List evaluateList(Evaluator evaluator) {
067                        final List<Member[]> list0 = listCalc0.evaluateList(evaluator);
068                        final List<Member> list1 = listCalc1.evaluateList(evaluator);
069                        return toggleDrillStateTuples(evaluator, list0, list1);
070                    }
071                };
072            }
073        }
074    
075        List<Member> toggleDrillStateMembers(
076            Evaluator evaluator, List<Member> v0, List<Member> list1)
077        {
078            if (list1.isEmpty()) {
079                return v0;
080            }
081            if (v0.isEmpty()) {
082                return v0;
083            }
084            Set<Member> set = new HashSet<Member>();
085            set.addAll(list1);
086            List<Member> result = new ArrayList<Member>();
087            int i = 0, n = v0.size();
088            while (i < n) {
089                Member m = v0.get(i++);
090                result.add(m);
091                if (!set.contains(m)) {
092                    continue;
093                }
094                boolean isDrilledDown = false;
095                if (i < n) {
096                    Member nextMember = v0.get(i);
097                    boolean strict = true;
098                    if (FunUtil.isAncestorOf(m, nextMember, strict)) {
099                        isDrilledDown = true;
100                    }
101                }
102                if (isDrilledDown) {
103                    // skip descendants of this member
104                    do {
105                        Member nextMember = v0.get(i);
106                        boolean strict = true;
107                        if (FunUtil.isAncestorOf(m, nextMember, strict)) {
108                            i++;
109                        } else {
110                            break;
111                        }
112                    } while (i < n);
113                } else {
114                    List<Member> children =
115                        evaluator.getSchemaReader().getMemberChildren(m);
116                    for (Member child : children) {
117                        result.add(child);
118                    }
119                }
120            }
121            return result;
122        }
123    
124        List<Member[]> toggleDrillStateTuples(
125            Evaluator evaluator, List<Member[]> v0, List<Member> list1)
126        {
127            if (list1.isEmpty()) {
128                return v0;
129            }
130            if (v0.isEmpty()) {
131                return v0;
132            }
133            Set<Member> set = new HashSet<Member>();
134            set.addAll(list1);
135            List<Member[]> result = new ArrayList<Member[]>();
136            int i = 0, n = v0.size();
137            while (i < n) {
138                Member[] o = v0.get(i++);
139                result.add(o);
140                Member m = null;
141                int k = -1;
142                for (int j = 0; j < o.length; j++) {
143                    Member member = o[j];
144                    if (set.contains(member)) {
145                        k = j;
146                        m = member;
147                        break;
148                    }
149                }
150                if (k == -1) {
151                    continue;
152                }
153                boolean isDrilledDown = false;
154                if (i < n) {
155                    Member[] next = v0.get(i);
156                    Member nextMember = next[k];
157                    boolean strict = true;
158                    if (FunUtil.isAncestorOf(m, nextMember, strict)) {
159                        isDrilledDown = true;
160                    }
161                }
162                if (isDrilledDown) {
163                    // skip descendants of this member
164                    do {
165                        Member[] next = v0.get(i);
166                        Member nextMember = next[k];
167                        boolean strict = true;
168                        if (FunUtil.isAncestorOf(m, nextMember, strict)) {
169                            i++;
170                        } else {
171                            break;
172                        }
173                    } while (i < n);
174                } else {
175                    List<Member> children = evaluator.getSchemaReader().getMemberChildren(m);
176                    for (Member child : children) {
177                        Member[] members = o.clone();
178                        members[k] = child;
179                        result.add(members);
180                    }
181                }
182            }
183            return result;
184        }
185    }
186    
187    // End ToggleDrillStateFunDef.java