KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > olap > fun > DescendantsFunDef


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/olap/fun/DescendantsFunDef.java#17 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2004-2002 Kana Software, Inc.
7 // Copyright (C) 2004-2006 Julian Hyde and others
8 // All Rights Reserved.
9 // You must accept the terms of that agreement to use this software.
10 */

11 package mondrian.olap.fun;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.List JavaDoc;
16
17 import mondrian.olap.*;
18 import mondrian.olap.type.NumericType;
19 import mondrian.calc.*;
20 import mondrian.calc.impl.AbstractListCalc;
21 import mondrian.mdx.ResolvedFunCall;
22
23 /**
24  * Definition of the <code>Descendants</code> MDX function.
25  *
26  * @author jhyde
27  * @version $Id: //open/mondrian/src/main/mondrian/olap/fun/DescendantsFunDef.java#17 $
28  * @since Mar 23, 2006
29  */

30 class DescendantsFunDef extends FunDefBase {
31
32     static final ReflectiveMultiResolver Resolver = new ReflectiveMultiResolver(
33             "Descendants",
34             "Descendants(<Member>[, <Level>[, <Desc_flag>]])",
35             "Returns the set of descendants of a member at a specified level, optionally including or excluding descendants in other levels.",
36             new String JavaDoc[]{"fxm", "fxml", "fxmly", "fxmn", "fxmny"},
37             DescendantsFunDef.class,
38             Flag.getNames());
39
40     public DescendantsFunDef(FunDef dummyFunDef) {
41         super(dummyFunDef);
42
43     }
44
45     public Calc compileCall(ResolvedFunCall call, ExpCompiler compiler) {
46         final MemberCalc memberCalc = compiler.compileMember(call.getArg(0));
47         Flag flag = Flag.SELF;
48         if (call.getArgCount() == 1) {
49             flag = Flag.SELF_BEFORE_AFTER;
50         }
51         final boolean depthSpecified = call.getArgCount() >= 2 &&
52             call.getArg(1).getType() instanceof NumericType;
53         if (call.getArgCount() >= 3) {
54             flag = FunUtil.getLiteralArg(call, 2, Flag.SELF, Flag.class);
55         }
56
57         if (depthSpecified && flag.leaves) {
58             final IntegerCalc depthCalc = call.getArgCount() > 1 ?
59                     compiler.compileInteger(call.getArg(1)) :
60                     null;
61             return new AbstractListCalc(call, new Calc[] {memberCalc, depthCalc}) {
62                 public List JavaDoc evaluateList(Evaluator evaluator) {
63                     final Member member = memberCalc.evaluateMember(evaluator);
64                     List JavaDoc<Member> result = new ArrayList JavaDoc<Member>();
65                     int depth = depthCalc.evaluateInteger(evaluator);
66                     if (depth < 0) {
67                         depth = -1; // no limit
68
}
69                     final SchemaReader schemaReader = evaluator.getSchemaReader();
70                     descendantsLeavesByDepth(
71                             member, result, schemaReader, depth);
72                     hierarchize(result, false);
73                     return result;
74                 }
75             };
76         } else if (depthSpecified) {
77             final IntegerCalc depthCalc = call.getArgCount() > 1 ?
78                     compiler.compileInteger(call.getArg(1)) :
79                     null;
80             final Flag flag1 = flag;
81             return new AbstractListCalc(call, new Calc[] {memberCalc, depthCalc}) {
82                 public List JavaDoc evaluateList(Evaluator evaluator) {
83                     final Member member = memberCalc.evaluateMember(evaluator);
84                     List JavaDoc result = new ArrayList JavaDoc();
85                     final int depth = depthCalc.evaluateInteger(evaluator);
86                     final SchemaReader schemaReader = evaluator.getSchemaReader();
87                     descendantsByDepth(
88                             member, result, schemaReader,
89                             depth, flag1.before, flag1.self, flag1.after, evaluator);
90                     hierarchize(result, false);
91                     return result;
92                 }
93             };
94         } else {
95             final LevelCalc levelCalc = call.getArgCount() > 1 ?
96                     compiler.compileLevel(call.getArg(1)) :
97                     null;
98             final Flag flag2 = flag;
99             return new AbstractListCalc(call, new Calc[] {memberCalc, levelCalc}) {
100                 public List JavaDoc evaluateList(Evaluator evaluator) {
101                     final Evaluator context =
102                             evaluator.isNonEmpty() ? evaluator : null;
103                     final Member member = memberCalc.evaluateMember(evaluator);
104                     List JavaDoc<Member> result = new ArrayList JavaDoc<Member>();
105                     final SchemaReader schemaReader = evaluator.getSchemaReader();
106                     final Level level = levelCalc != null ?
107                             levelCalc.evaluateLevel(evaluator) :
108                             member.getLevel();
109                     descendantsByLevel(
110                             schemaReader, member, level, result,
111                         flag2.before, flag2.self,
112                         flag2.after, flag2.leaves, context);
113                     hierarchize(result, false);
114                     return result;
115                 }
116             };
117         }
118     }
119
120     private static void descendantsByDepth(
121             Member member,
122             List JavaDoc result,
123             final SchemaReader schemaReader,
124             final int depthLimitFinal,
125             final boolean before,
126             final boolean self,
127             final boolean after,
128             final Evaluator context) {
129         Member[] children = {member};
130         for (int depth = 0;; ++depth) {
131             if (depth == depthLimitFinal) {
132                 if (self) {
133                     addAll(result, children);
134                 }
135                 if (!after) {
136                     break; // no more results after this level
137
}
138             } else if (depth < depthLimitFinal) {
139                 if (before) {
140                     addAll(result, children);
141                 }
142             } else {
143                 if (after) {
144                     addAll(result, children);
145                 } else {
146                     break; // no more results after this level
147
}
148             }
149
150             children = schemaReader.getMemberChildren(children, context);
151             if (children.length == 0) {
152                 break;
153             }
154         }
155     }
156
157     /**
158      * Populates 'result' with the descendants at the leaf level at depth
159      * 'depthLimit' or less. If 'depthLimit' is -1, does not apply a depth
160      * constraint.
161      */

162     private static void descendantsLeavesByDepth(
163             final Member member,
164             final List JavaDoc<Member> result,
165             final SchemaReader schemaReader,
166             final int depthLimit) {
167         if (!schemaReader.isDrillable(member)) {
168             if (depthLimit >= 0) {
169                 result.add(member);
170             }
171             return;
172         }
173         Member[] children = {member};
174         for (int depth = 0; depthLimit == -1 || depth <= depthLimit; ++depth) {
175             children = schemaReader.getMemberChildren(children);
176             if (children.length == 0) {
177                 throw Util.newInternal("drillable member must have children");
178             }
179             List JavaDoc<Member> nextChildren = new ArrayList JavaDoc<Member>();
180             for (Member child : children) {
181                 // TODO: Implement this more efficiently. The current
182
// implementation of isDrillable for a parent-child hierarchy
183
// simply retrieves the children sees whether there are any,
184
// so we end up fetching each member's children twice.
185
if (schemaReader.isDrillable(child)) {
186                     nextChildren.add(child);
187                 } else {
188                     result.add(child);
189                 }
190             }
191             if (nextChildren.isEmpty()) {
192                 return;
193             }
194             children = nextChildren.toArray(new Member[nextChildren.size()]);
195         }
196     }
197
198     /**
199      * Finds all descendants of a member which are before/at/after a level,
200      * and/or are leaves (have no descendants) and adds them to a result list.
201      *
202      * @param schemaReader Member reader
203      * @param ancestor Member to find descendants of
204      * @param level Level relative to which to filter, must not be null
205      * @param result Result list
206      * @param before Whether to output members above <code>level</code>
207      * @param self Whether to output members at <code>level</code>
208      * @param after Whether to output members below <code>level</code>
209      * @param leaves Whether to output members which are leaves
210      * @param context Evaluation context; determines criteria by which the
211      * result set should be filtered
212      */

213     static void descendantsByLevel(
214             SchemaReader schemaReader,
215             Member ancestor,
216             Level level,
217             List JavaDoc<Member> result,
218             boolean before,
219             boolean self,
220             boolean after,
221             boolean leaves,
222             Evaluator context) {
223         // We find the descendants of a member by making breadth-first passes
224
// down the hierarchy. Initially the list just contains the ancestor.
225
// Then we find its children. We add those children to the result if
226
// they fulfill the before/self/after conditions relative to the level.
227
//
228
// We add a child to the "fertileMembers" list if some of its children
229
// might be in the result. Again, this depends upon the
230
// before/self/after conditions.
231
//
232
// Note that for some member readers -- notably the
233
// RestrictedMemberReader, when it is reading a ragged hierarchy -- the
234
// children of a member do not always belong to the same level. For
235
// example, the children of USA include WA (a state) and Washington
236
// (a city). This is why we repeat the before/self/after logic for
237
// each member.
238
final int levelDepth = level.getDepth();
239         Member[] members = {ancestor};
240         // Each pass, "fertileMembers" has the same contents as "members",
241
// except that we omit members whose children we are not interested
242
// in. We allocate it once, and clear it each pass, to save a little
243
// memory allocation.
244
if (leaves) {
245             assert !before && !self && !after;
246             do {
247                 List JavaDoc<Member> nextMembers = new ArrayList JavaDoc<Member>();
248                 for (Member member : members) {
249                     final int currentDepth = member.getLevel().getDepth();
250                     Member[] childMembers =
251                         schemaReader.getMemberChildren(member, context);
252                     if (childMembers.length == 0) {
253                         // this member is a leaf -- add it
254
if (currentDepth == levelDepth) {
255                             result.add(member);
256                         }
257                         continue;
258                     } else {
259                         // this member is not a leaf -- add its children
260
// to the list to be considered next iteration
261
if (currentDepth <= levelDepth) {
262                             nextMembers.addAll(Arrays.asList(childMembers));
263                         }
264                     }
265                 }
266                 members = nextMembers.toArray(new Member[nextMembers.size()]);
267             }
268             while (members.length > 0);
269         } else {
270             List JavaDoc<Member> fertileMembers = new ArrayList JavaDoc<Member>();
271             do {
272                 fertileMembers.clear();
273                 for (Member member : members) {
274                     final int currentDepth = member.getLevel().getDepth();
275                     if (currentDepth == levelDepth) {
276                         if (self) {
277                             result.add(member);
278                         }
279                         if (after) {
280                             // we are interested in member's children
281
fertileMembers.add(member);
282                         }
283                     } else if (currentDepth < levelDepth) {
284                         if (before) {
285                             result.add(member);
286                         }
287                         fertileMembers.add(member);
288                     } else {
289                         if (after) {
290                             result.add(member);
291                             fertileMembers.add(member);
292                         }
293                     }
294                 }
295                 members = new Member[fertileMembers.size()];
296                 members = fertileMembers.toArray(members);
297                 members = schemaReader.getMemberChildren(members, context);
298             }
299             while (members.length > 0);
300         }
301     }
302
303     /**
304      * Enumeration of the flags allowed to the <code>DESCENDANTS</code>
305      * function.
306      */

307     enum Flag {
308         SELF(true, false, false, false),
309         AFTER(false, true, false, false),
310         BEFORE(false, false, true, false),
311         BEFORE_AND_AFTER(false, true, true, false),
312         SELF_AND_AFTER(true, true, false, false),
313         SELF_AND_BEFORE(true, false, true, false),
314         SELF_BEFORE_AFTER(true, true, true, false),
315         LEAVES(false, false, false, true);
316
317         private final boolean self;
318         private final boolean after;
319         private final boolean before;
320         private final boolean leaves;
321
322         Flag(boolean self, boolean after, boolean before, boolean leaves) {
323             this.self = self;
324             this.after = after;
325             this.before = before;
326             this.leaves = leaves;
327         }
328
329         public static String JavaDoc[] getNames() {
330             List JavaDoc<String JavaDoc> names = new ArrayList JavaDoc<String JavaDoc>();
331             for (Flag flags : Flag.class.getEnumConstants()) {
332                 names.add(flags.name());
333             }
334             return names.toArray(new String JavaDoc[names.size()]);
335         }
336     }
337 }
338
339 // End DescendantsFunDef.java
340
Popular Tags