KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > rolap > SmartMemberReader


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/rolap/SmartMemberReader.java#39 $
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) 2001-2002 Kana Software, Inc.
7 // Copyright (C) 2001-2007 Julian Hyde and others
8 // Copyright (C) 2004-2005 TONBELLER AG
9 // All Rights Reserved.
10 // You must accept the terms of that agreement to use this software.
11 //
12 // jhyde, 21 December, 2001
13 */

14
15 package mondrian.rolap;
16 import mondrian.olap.Util;
17 import mondrian.rolap.TupleReader.MemberBuilder;
18 import mondrian.rolap.cache.SmartCache;
19 import mondrian.rolap.cache.SoftSmartCache;
20 import mondrian.rolap.sql.MemberChildrenConstraint;
21 import mondrian.rolap.sql.TupleConstraint;
22
23 import mondrian.spi.DataSourceChangeListener;
24
25 import java.util.*;
26
27 /**
28  * <code>SmartMemberReader</code> implements {@link MemberReader} by keeping a
29  * cache of members and their children. If a member is 'in cache', there is a
30  * list of its children. It also caches the members of levels.
31  *
32  * <p>Synchronization: the MemberReader <code>source</code> must be called
33  * from synchronized(this) context - it does not synchronize itself (probably
34  * it should).</p>
35  *
36  * <p>Constraints: Member.Children and Level.Members may be constrained by a
37  * SqlConstraint object. In this case a subset of all members is returned.
38  * These subsets are cached too and the SqlConstraint is part of the cache key.
39  * This is used in NON EMPTY context.</p>
40  *
41  * <p>Uniqueness. We need to ensure that there is never more than one {@link
42  * RolapMember} object representing the same member.</p>
43  *
44  * @author jhyde
45  * @since 21 December, 2001
46  * @version $Id: //open/mondrian/src/main/mondrian/rolap/SmartMemberReader.java#39 $
47  */

48 public class SmartMemberReader implements MemberReader, MemberCache {
49     private final SqlConstraintFactory sqlConstraintFactory =
50             SqlConstraintFactory.instance();
51
52     /** access to <code>source</code> must be synchronized(this) */
53     private final MemberReader source;
54
55     /** maps a parent member to a list of its children */
56     final SmartMemberListCache<RolapMember, List<RolapMember>> mapMemberToChildren;
57
58     /** a cache for alle members to ensure uniqueness */
59     SmartCache<Object JavaDoc, RolapMember> mapKeyToMember;
60
61     /** maps a level to its members */
62     final SmartMemberListCache<RolapLevel, List<RolapMember>> mapLevelToMembers;
63
64     DataSourceChangeListener changeListener;
65
66     private List<RolapMember> rootMembers;
67
68     SmartMemberReader(MemberReader source) {
69         this.source = source;
70         if (!source.setCache(this)) {
71             throw Util.newInternal(
72                     "MemberSource (" + source + ", " + source.getClass() +
73                     ") does not support cache-writeback");
74         }
75         this.mapLevelToMembers =
76             new SmartMemberListCache<RolapLevel, List<RolapMember>>();
77         this.mapKeyToMember = new SoftSmartCache<Object JavaDoc, RolapMember>();
78         this.mapMemberToChildren =
79             new SmartMemberListCache<RolapMember, List<RolapMember>>();
80
81         if (source.getHierarchy() != null) {
82             changeListener = source.getHierarchy().getRolapSchema().getDataSourceChangeListener();
83         }
84         else {
85             changeListener = null;
86         }
87     }
88
89     // implement MemberReader
90
public RolapHierarchy getHierarchy() {
91         return source.getHierarchy();
92     }
93
94     // implement MemberSource
95
public boolean setCache(MemberCache cache) {
96         // we do not support cache writeback -- we must be masters of our
97
// own cache
98
return false;
99     }
100
101     private synchronized void checkCacheStatus() {
102
103         if (changeListener != null) {
104             if (changeListener.isHierarchyChanged(getHierarchy())) {
105                 /* Flush the cache */
106                 mapMemberToChildren.clear();
107                 mapKeyToMember.clear();
108                 mapLevelToMembers.clear();
109             }
110         }
111     }
112
113     // implement MemberCache
114
public Object JavaDoc makeKey(RolapMember parent, Object JavaDoc key) {
115         return new MemberKey(parent, key);
116     }
117
118     // implement MemberCache
119
// synchronization: Must synchronize, because uses mapKeyToMember
120
public synchronized RolapMember getMember(Object JavaDoc key) {
121         return getMember(key, true);
122     }
123
124     // implement MemberCache
125
// synchronization: Must synchronize, because uses mapKeyToMember
126
public synchronized RolapMember getMember(Object JavaDoc key, boolean mustCheckCacheStatus) {
127
128         if (mustCheckCacheStatus) {
129             checkCacheStatus();
130         }
131
132         return mapKeyToMember.get(key);
133     }
134
135
136     // implement MemberCache
137
// synchronization: Must synchronize, because modifies mapKeyToMember
138
public synchronized Object JavaDoc putMember(Object JavaDoc key, RolapMember value) {
139         return mapKeyToMember.put(key, value);
140     }
141
142     // implement MemberReader
143
public RolapMember[] getMembers() {
144         List<RolapMember> v = new ArrayList<RolapMember>();
145         RolapLevel[] levels = (RolapLevel[]) getHierarchy().getLevels();
146         // todo: optimize by walking to children for members we know about
147
for (RolapLevel level : levels) {
148             List<RolapMember> membersInLevel = getMembersInLevel(
149                 level,
150                 0,
151                 Integer.MAX_VALUE);
152             v.addAll(membersInLevel);
153         }
154         return v.toArray(new RolapMember[v.size()]);
155     }
156
157     public List<RolapMember> getRootMembers() {
158         if (rootMembers == null) {
159             rootMembers = source.getRootMembers();
160         }
161         return rootMembers;
162     }
163
164
165     // Synchronization: modifies mapLevelToMembers
166
public synchronized List<RolapMember> getMembersInLevel(
167             RolapLevel level,
168             int startOrdinal,
169             int endOrdinal) {
170         TupleConstraint constraint =
171                 sqlConstraintFactory.getLevelMembersConstraint(null);
172         return getMembersInLevel(level, startOrdinal, endOrdinal, constraint);
173     }
174
175     public synchronized List<RolapMember> getMembersInLevel(
176         RolapLevel level,
177         int startOrdinal,
178         int endOrdinal,
179         TupleConstraint constraint)
180     {
181         checkCacheStatus();
182
183         List<RolapMember> members = mapLevelToMembers.get(level, constraint);
184         if (members != null) {
185             return members;
186         }
187
188         members =
189             source.getMembersInLevel(
190                 level, startOrdinal, endOrdinal, constraint);
191         mapLevelToMembers.put(level, constraint, members);
192         return members;
193     }
194
195     public void getMemberChildren(
196         RolapMember parentMember,
197         List<RolapMember> children)
198     {
199         MemberChildrenConstraint constraint =
200                 sqlConstraintFactory.getMemberChildrenConstraint(null);
201         getMemberChildren(parentMember, children, constraint);
202     }
203
204     public void getMemberChildren(
205         RolapMember parentMember,
206         List<RolapMember> children,
207         MemberChildrenConstraint constraint)
208     {
209         List<RolapMember> parentMembers = new ArrayList<RolapMember>();
210         parentMembers.add(parentMember);
211         getMemberChildren(parentMembers, children, constraint);
212     }
213
214     public synchronized void getMemberChildren(
215             List<RolapMember> parentMembers,
216             List<RolapMember> children) {
217         MemberChildrenConstraint constraint =
218                 sqlConstraintFactory.getMemberChildrenConstraint(null);
219         getMemberChildren(parentMembers, children, constraint);
220     }
221
222     public synchronized void getMemberChildren(
223             List<RolapMember> parentMembers,
224             List<RolapMember> children,
225             MemberChildrenConstraint constraint) {
226
227         checkCacheStatus();
228
229         List<RolapMember> missed = new ArrayList<RolapMember>();
230         for (RolapMember parentMember : parentMembers) {
231             List<RolapMember> list =
232                 mapMemberToChildren.get(parentMember, constraint);
233             if (list == null) {
234                 if (parentMember.isNull()) {
235                     // the null member has no children
236
} else {
237                     missed.add(parentMember);
238                 }
239             } else {
240                 children.addAll(list);
241             }
242         }
243         if (missed.size() > 0) {
244             readMemberChildren(missed, children, constraint);
245         }
246     }
247
248     public RolapMember lookupMember(
249             String JavaDoc[] uniqueNameParts,
250             boolean failIfNotFound) {
251         return RolapUtil.lookupMember(this, uniqueNameParts, failIfNotFound);
252     }
253
254     /**
255      * Reads the children of <code>member</code> into cache, and also into
256      * <code>result</code>.
257      *
258      * @param result Children are written here, in order
259      * @param members Members whose children to read
260      * @param constraint restricts the returned members if possible (optional
261      * optimization)
262      */

263     private void readMemberChildren(
264         List<RolapMember> members,
265         List<RolapMember> result,
266         MemberChildrenConstraint constraint)
267     {
268         if (false) {
269             // Pre-condition disabled. It makes sense to have the pre-
270
// condition, because lists of parent members are typically
271
// sorted by construction, and we should be able to exploit this
272
// when constructing the (significantly larger) set of children.
273
// But currently BasicQueryTest.testBasketAnalysis() fails this
274
// assert, and I haven't had time to figure out why.
275
// -- jhyde, 2004/6/10.
276
Util.assertPrecondition(isSorted(members), "isSorted(members)");
277         }
278         List<RolapMember> children = new ArrayList<RolapMember>();
279         source.getMemberChildren(members, children, constraint);
280         // Put them in a temporary hash table first. Register them later, when
281
// we know their size (hence their 'cost' to the cache pool).
282
Map<RolapMember, List<RolapMember>> tempMap =
283             new HashMap<RolapMember, List<RolapMember>>();
284         for (RolapMember member1 : members) {
285             tempMap.put(member1, Collections.EMPTY_LIST);
286         }
287         for (int i = 0, childrenCount = children.size(); i < childrenCount; i++) {
288             // todo: We could optimize here. If members.length is small, it's
289
// more efficient to drive from members, rather than hashing
290
// children.length times. We could also exploit the fact that the
291
// result is sorted by ordinal and therefore, unless the "members"
292
// contains members from different levels, children of the same
293
// member will be contiguous.
294
RolapMember child = children.get(i);
295             assert child != null : "child";
296             assert tempMap != null : "tempMap";
297             final RolapMember parentMember = child.getParentMember();
298             List<RolapMember> list = tempMap.get(parentMember);
299             if (list == null) {
300                 // The list is null if, due to dropped constraints, we now
301
// have a children list of a member we didn't explicitly
302
// ask for it. Adding it to the cache would be viable, but
303
// let's ignore it.
304
continue;
305             } else if (list == Collections.EMPTY_LIST) {
306                 list = new ArrayList<RolapMember>();
307                 tempMap.put(parentMember, list);
308             }
309             list.add(child);
310             result.add(child);
311         }
312         synchronized (this) {
313             for (RolapMember member : tempMap.keySet()) {
314                 if (getChildrenFromCache(member, constraint) == null) {
315                     List<RolapMember> list = tempMap.get(member);
316                     putChildren(member, constraint, list);
317                 }
318             }
319         }
320     }
321
322     /**
323      * Returns true if every element of <code>members</code> is not null and is
324      * strictly less than the following element; false otherwise.
325      */

326     public boolean isSorted(List<RolapMember> members) {
327         final int count = members.size();
328         if (count == 0) {
329             return true;
330         }
331         RolapMember m1 = members.get(0);
332         if (m1 == null) {
333             // Special case check for 0th element, just in case length == 1.
334
return false;
335         }
336         for (int i = 1; i < count; i++) {
337             RolapMember m0 = m1;
338             m1 = members.get(i);
339             if (m1 == null || compare(m0, m1, false) >= 0) {
340                 return false;
341             }
342         }
343         return true;
344     }
345
346     public synchronized List<RolapMember> getChildrenFromCache(
347             RolapMember member,
348             MemberChildrenConstraint constraint) {
349         if (constraint == null) {
350             constraint = sqlConstraintFactory.getMemberChildrenConstraint(null);
351         }
352         return mapMemberToChildren.get(member, constraint);
353     }
354
355     public synchronized List getLevelMembersFromCache(
356             RolapLevel level,
357             TupleConstraint constraint) {
358         if (constraint == null) {
359             constraint = sqlConstraintFactory.getLevelMembersConstraint(null);
360         }
361         return mapLevelToMembers.get(level, constraint);
362     }
363
364     public synchronized void putChildren(
365         RolapMember member,
366         MemberChildrenConstraint constraint,
367         List<RolapMember> children)
368     {
369         if (constraint == null) {
370             constraint = sqlConstraintFactory.getMemberChildrenConstraint(null);
371         }
372         mapMemberToChildren.put(member, constraint, children);
373     }
374
375     // synchronization: Must synchronize, because uses mapMemberToChildren
376
public synchronized RolapMember getLeadMember(RolapMember member, int n) {
377         if (n == 0 || member.isNull()) {
378             return member;
379         } else {
380             SiblingIterator iter = new SiblingIterator(this, member);
381             if (n > 0) {
382                 RolapMember sibling = null;
383                 while (n-- > 0) {
384                     if (!iter.hasNext()) {
385                         return (RolapMember) member.getHierarchy().getNullMember();
386                     }
387                     sibling = iter.nextMember();
388                 }
389                 return sibling;
390             } else {
391                 n = -n;
392                 RolapMember sibling = null;
393                 while (n-- > 0) {
394                     if (!iter.hasPrevious()) {
395                         return (RolapMember) member.getHierarchy().getNullMember();
396                     }
397                     sibling = iter.previousMember();
398                 }
399                 return sibling;
400             }
401         }
402     }
403
404     public void getMemberRange(RolapLevel level,
405                                RolapMember startMember,
406                                RolapMember endMember,
407                                List<RolapMember> list) {
408         Util.assertPrecondition(startMember != null, "startMember != null");
409         Util.assertPrecondition(endMember != null, "endMember != null");
410         Util.assertPrecondition(startMember.getLevel() == endMember.getLevel(),
411                 "startMember.getLevel() == endMember.getLevel()");
412
413         if (compare(startMember, endMember, false) > 0) {
414             return;
415         }
416         list.add(startMember);
417         if (startMember == endMember) {
418             return;
419         }
420         SiblingIterator siblings = new SiblingIterator(this, startMember);
421         while (siblings.hasNext()) {
422             final RolapMember member = siblings.nextMember();
423             list.add(member);
424             if (member == endMember) {
425                 return;
426             }
427         }
428         throw Util.newInternal("sibling iterator did not hit end point, start="
429                 + startMember
430                 + ", end="
431                 + endMember);
432     }
433
434     public int getMemberCount() {
435         return source.getMemberCount();
436     }
437
438     public int compare(RolapMember m1,
439                        RolapMember m2,
440                        boolean siblingsAreEqual) {
441         if (m1 == m2) {
442             return 0;
443         }
444         if (m1.getParentMember() == m2.getParentMember()) {
445             // including case where both parents are null
446
if (siblingsAreEqual) {
447                 return 0;
448             } else if (m1.getParentMember() == null) {
449                 // at this point we know that both parent members are null.
450
int pos1 = -1, pos2 = -1;
451                 List siblingList = getRootMembers();
452                 for (int i = 0, n = siblingList.size(); i < n; i++) {
453                     RolapMember child = (RolapMember) siblingList.get(i);
454                     if (child == m1) {
455                         pos1 = i;
456                     }
457                     if (child == m2) {
458                         pos2 = i;
459                     }
460                 }
461                 if (pos1 == -1) {
462                     throw Util.newInternal(m1 + " not found among siblings");
463                 }
464                 if (pos2 == -1) {
465                     throw Util.newInternal(m2 + " not found among siblings");
466                 }
467                 Util.assertTrue(pos1 != pos2);
468                 return pos1 < pos2 ? -1 : 1;
469             } else {
470                 List<RolapMember> children = new ArrayList<RolapMember>();
471                 getMemberChildren(m1.getParentMember(), children);
472                 int pos1 = -1, pos2 = -1;
473                 for (int i = 0, n = children.size(); i < n; i++) {
474                     RolapMember child = children.get(i);
475                     if (child == m1) {
476                         pos1 = i;
477                     }
478                     if (child == m2) {
479                         pos2 = i;
480                     }
481                 }
482                 if (pos1 == -1) {
483                     throw Util.newInternal(m1 + " not found among siblings");
484                 }
485                 if (pos2 == -1) {
486                     throw Util.newInternal(m2 + " not found among siblings");
487                 }
488                 Util.assertTrue(pos1 != pos2);
489                 return pos1 < pos2 ? -1 : 1;
490             }
491         }
492         int levelDepth1 = m1.getLevel().getDepth();
493         int levelDepth2 = m2.getLevel().getDepth();
494         if (levelDepth1 < levelDepth2) {
495             final int c = compare(m1, m2.getParentMember(), false);
496             return (c == 0) ? -1 : c;
497
498         } else if (levelDepth1 > levelDepth2) {
499             final int c = compare(m1.getParentMember(), m2, false);
500             return (c == 0) ? 1 : c;
501
502         } else {
503             return compare(m1.getParentMember(), m2.getParentMember(), false);
504         }
505     }
506
507     /**
508      * <code>SiblingIterator</code> helps traverse a hierarchy of members, by
509      * remembering the position at each level. Each SiblingIterator has a
510      * parent, to which it defers when the last child of the current member is
511      * reached.
512      */

513     class SiblingIterator {
514         private final MemberReader reader;
515         private final SiblingIterator parentIterator;
516         private RolapMember[] siblings;
517         private int position;
518
519         SiblingIterator(MemberReader reader, RolapMember member) {
520             this.reader = reader;
521             RolapMember parent = member.getParentMember();
522             List<RolapMember> siblingList;
523             if (parent == null) {
524                 siblingList = reader.getRootMembers();
525                 this.parentIterator = null;
526             } else {
527                 siblingList = new ArrayList<RolapMember>();
528                 reader.getMemberChildren(parent, siblingList);
529                 this.parentIterator = new SiblingIterator(reader, parent);
530             }
531             this.siblings = RolapUtil.toArray(siblingList);
532             this.position = -1;
533             for (int i = 0; i < this.siblings.length; i++) {
534                 if (siblings[i] == member) {
535                     this.position = i;
536                     break;
537                 }
538             }
539             if (this.position == -1) {
540                 throw Util.newInternal(
541                     "member " + member + " not found among its siblings");
542             }
543         }
544         boolean hasNext() {
545             return (this.position < this.siblings.length - 1) ||
546                 (parentIterator != null) &&
547                 parentIterator.hasNext();
548         }
549         Object JavaDoc next() {
550             return nextMember();
551         }
552         RolapMember nextMember() {
553             if (++this.position >= this.siblings.length) {
554                 if (parentIterator == null) {
555                     throw Util.newInternal("there is no next member");
556                 }
557                 RolapMember parent = parentIterator.nextMember();
558                 List<RolapMember> siblingList = new ArrayList<RolapMember>();
559                 reader.getMemberChildren(parent, siblingList);
560                 this.siblings = RolapUtil.toArray(siblingList);
561                 this.position = 0;
562             }
563             return this.siblings[this.position];
564         }
565         boolean hasPrevious() {
566             return (this.position > 0) ||
567                 (parentIterator != null) &&
568                 parentIterator.hasPrevious();
569         }
570         Object JavaDoc previous() {
571             return previousMember();
572         }
573         RolapMember previousMember() {
574             if (--this.position < 0) {
575                 if (parentIterator == null) {
576                     throw Util.newInternal("there is no next member");
577                 }
578                 RolapMember parent = parentIterator.previousMember();
579                 List<RolapMember> siblingList = new ArrayList<RolapMember>();
580                 reader.getMemberChildren(parent, siblingList);
581                 this.siblings = RolapUtil.toArray(siblingList);
582                 this.position = this.siblings.length - 1;
583             }
584             return this.siblings[this.position];
585         }
586     }
587
588     public MemberBuilder getMemberBuilder() {
589         return source.getMemberBuilder();
590     }
591 }
592
593 // End SmartMemberReader.java
594
Popular Tags