001    /*
002    // $Id: //open/mondrian/src/main/mondrian/rolap/RolapCubeHierarchy.java#13 $
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) 2001-2002 Kana Software, Inc.
007    // Copyright (C) 2001-2008 Julian Hyde and others
008    // All Rights Reserved.
009    // You must accept the terms of that agreement to use this software.
010    //
011    // wgorman, 19 October 2007
012    */
013    package mondrian.rolap;
014    
015    import java.sql.ResultSet;
016    import java.sql.SQLException;
017    import java.util.ArrayList;
018    import java.util.Collections;
019    import java.util.HashMap;
020    import java.util.Iterator;
021    import java.util.List;
022    import java.util.Map;
023    
024    import mondrian.olap.Formula;
025    import mondrian.olap.Level;
026    import mondrian.olap.Member;
027    import mondrian.olap.MondrianDef;
028    import mondrian.olap.MondrianProperties;
029    import mondrian.olap.Property;
030    import mondrian.olap.Util;
031    import mondrian.rolap.TupleReader.MemberBuilder;
032    import mondrian.rolap.sql.MemberChildrenConstraint;
033    import mondrian.rolap.sql.TupleConstraint;
034    import mondrian.util.UnsupportedList;
035    
036    /**
037     * Hierarchy that is associated with a specific Cube.
038     *
039     * @author Will Gorman (wgorman@pentaho.org)
040     * @version $Id: //open/mondrian/src/main/mondrian/rolap/RolapCubeHierarchy.java#13 $
041     */
042    public class RolapCubeHierarchy extends RolapHierarchy {
043    
044        private final RolapCubeDimension parentDimension;
045        private final RolapHierarchy rolapHierarchy;
046        private final RolapCubeLevel currentNullLevel;
047        private RolapNullMember currentNullMember;
048        private RolapCubeMember currentAllMember;
049        private final MondrianDef.RelationOrJoin currentRelation;
050        private final RolapCubeHierarchyMemberReader reader;
051        private HierarchyUsage usage;
052        private final Map<String, String> aliases = new HashMap<String, String>();
053        private RolapCubeMember currentDefaultMember;
054    
055        /**
056         * True if the hierarchy is degenerate - has no dimension table of its own,
057         * just drives from the cube's fact table.
058         */
059        protected final boolean usingCubeFact;
060    
061        public RolapCubeHierarchy(
062            RolapCubeDimension dimension,
063            MondrianDef.CubeDimension cubeDim,
064            RolapHierarchy rolapHierarchy,
065            String subName)
066        {
067            super(dimension, subName, rolapHierarchy.hasAll());
068    
069            if (!dimension.getCube().isVirtual()) {
070                this.usage =
071                    new HierarchyUsage(dimension.getCube(), rolapHierarchy, cubeDim);
072            }
073    
074            this.rolapHierarchy = rolapHierarchy;
075            this.parentDimension = dimension;
076            this.xmlHierarchy = rolapHierarchy.getXmlHierarchy();
077            // this relation should equal the name of the new dimension table
078            // The null member belongs to a level with very similar properties to
079            // the 'all' level.
080            this.currentNullLevel = new RolapCubeLevel(nullLevel, this);
081    
082            usingCubeFact =
083                (dimension.getCube().getFact() == null
084                  || dimension.getCube().getFact().equals(
085                        rolapHierarchy.getRelation()));
086    
087            // re-alias names if necessary
088            if (!usingCubeFact) {
089                // join expressions are columns only
090                assert usage.getJoinExp() instanceof MondrianDef.Column;
091                currentRelation =
092                    parentDimension.getCube().getStar().getUniqueRelation(
093                        rolapHierarchy.getRelation(),
094                        usage.getForeignKey(),
095                        ((MondrianDef.Column)usage.getJoinExp()).getColumnName(),
096                        usage.getJoinTable().getAlias());
097            } else {
098                currentRelation = rolapHierarchy.getRelation();
099            }
100            extractNewAliases(rolapHierarchy.getRelation(), currentRelation);
101            this.relation = currentRelation;
102            this.levels = new RolapCubeLevel[rolapHierarchy.getLevels().length];
103            for (int i = 0; i < rolapHierarchy.getLevels().length; i++) {
104                this.levels[i] =
105                    new RolapCubeLevel(
106                            (RolapLevel)rolapHierarchy.getLevels()[i], this);
107                if (i == 0) {
108                    if (rolapHierarchy.getAllMember() != null) {
109                        RolapCubeLevel allLevel;
110                        if (hasAll()) {
111                            allLevel = (RolapCubeLevel)this.levels[0];
112                        } else {
113                            // create an all level if one doesn't normally
114                            // exist in the hierarchy
115                            allLevel =
116                                new RolapCubeLevel(
117                                    rolapHierarchy.getAllMember().getLevel(),
118                                    this);
119                            allLevel.init(dimension.xmlDimension);
120                        }
121    
122                        this.currentAllMember =
123                            new RolapCubeMember(
124                                null,
125                                rolapHierarchy.getAllMember(),
126                                allLevel,
127                                dimension.getCube());
128                    }
129                }
130            }
131    
132            if (dimension.isHighCardinality()) {
133                this.reader = new NoCacheRolapCubeHierarchyMemberReader();
134            } else {
135                this.reader = new CacheRolapCubeHierarchyMemberReader();
136            }
137        }
138    
139        public String getAllMemberName() {
140            return rolapHierarchy.getAllMemberName();
141        }
142    
143        public String getSharedHierarchyName() {
144            return rolapHierarchy.getSharedHierarchyName();
145        }
146    
147        public String getAllLevelName() {
148            return rolapHierarchy.getAllLevelName();
149        }
150    
151        public boolean isUsingCubeFact() {
152            return usingCubeFact;
153        }
154    
155        public String lookupAlias(String origTable) {
156            return aliases.get(origTable);
157        }
158    
159        // override with stricter return type
160        public RolapCubeDimension getDimension() {
161            return (RolapCubeDimension) super.getDimension();
162        }
163    
164        public RolapHierarchy getRolapHierarchy() {
165            return rolapHierarchy;
166        }
167    
168        /**
169         * Populates the alias map for the old and new relations.
170         *
171         * <p>This method may be simplified when we obsolete
172         * {@link mondrian.rolap.HierarchyUsage}.
173         *
174         * @param oldrel Original relation, as defined in the schema
175         * @param newrel New star relation, generated by RolapStar, canonical, and
176         * shared between all cubes with similar structure
177         */
178        protected void extractNewAliases(
179            MondrianDef.RelationOrJoin oldrel,
180            MondrianDef.RelationOrJoin newrel)
181        {
182            if (oldrel == null && newrel == null) {
183                return;
184            } else if (oldrel instanceof MondrianDef.Relation
185                && newrel instanceof MondrianDef.Relation)
186            {
187                aliases.put(
188                    ((MondrianDef.Relation) oldrel).getAlias(),
189                    ((MondrianDef.Relation) newrel).getAlias());
190            } else if (oldrel instanceof MondrianDef.Join
191                         && newrel instanceof MondrianDef.Join) {
192                MondrianDef.Join oldjoin = (MondrianDef.Join)oldrel;
193                MondrianDef.Join newjoin = (MondrianDef.Join)newrel;
194                extractNewAliases(oldjoin.left, newjoin.left);
195                extractNewAliases(oldjoin.right, newjoin.right);
196            } else {
197                throw new UnsupportedOperationException();
198            }
199        }
200    
201        public boolean equals(Object o) {
202            if (this == o) {
203                return true;
204            }
205            if (!(o instanceof RolapCubeHierarchy)) {
206                return false;
207            }
208    
209            RolapCubeHierarchy that = (RolapCubeHierarchy)o;
210            return parentDimension.equals(that.parentDimension)
211                && getUniqueName().equals(that.getUniqueName());
212        }
213    
214        protected int computeHashCode() {
215            return Util.hash(super.computeHashCode(), this.parentDimension.parent);
216        }
217    
218        public Member createMember(
219            Member parent,
220            Level level,
221            String name,
222            Formula formula)
223        {
224            RolapLevel rolapLevel = ((RolapCubeLevel)level).getRolapLevel();
225            if (formula == null) {
226                RolapMember member = new RolapMember(
227                    (RolapMember) parent, rolapLevel, name);
228                return new RolapCubeMember((RolapCubeMember)parent, member,
229                        (RolapCubeLevel)level, parentDimension.getCube());
230            } else if (level.getDimension().isMeasures()) {
231                RolapCalculatedMeasure member = new RolapCalculatedMeasure(
232                    (RolapMember) parent, rolapLevel, name, formula);
233                return new RolapCubeMember((RolapCubeMember)parent, member,
234                        (RolapCubeLevel)level, parentDimension.getCube());
235            } else {
236                RolapCalculatedMember member = new RolapCalculatedMember(
237                    (RolapMember) parent, rolapLevel, name, formula);
238                return new RolapCubeMember((RolapCubeMember)parent, member,
239                        (RolapCubeLevel)level, parentDimension.getCube());
240            }
241        }
242    
243    
244        boolean tableExists(String tableName) {
245            return rolapHierarchy.tableExists(tableName);
246        }
247    
248        /**
249         * The currentRelation object is derived from the shared relation object
250         * it is generated via the RolapStar object, and contains unique aliases
251         * for it's particular join path
252         *
253         * @return rolap cube hierarchy relation
254         */
255        public MondrianDef.RelationOrJoin getRelation() {
256            return currentRelation;
257        }
258    
259        public Member getDefaultMember() {
260            if (currentDefaultMember == null) {
261                reader.getRootMembers();
262                RolapCubeLevel level =
263                    (RolapCubeLevel)
264                        levels[rolapHierarchy.getDefaultMember().getDepth()];
265                RolapMember rolapDefaultMember =
266                    (RolapMember) rolapHierarchy.getDefaultMember();
267    
268                currentDefaultMember = reader.lookupCubeMember(
269                        hasAll() ? currentAllMember : null,
270                        rolapDefaultMember, level);
271            }
272            return currentDefaultMember;
273        }
274    
275        public Member getNullMember() {
276            // use lazy initialization to get around bootstrap issues
277            if (currentNullMember == null) {
278                currentNullMember = new RolapNullMember(currentNullLevel);
279            }
280            return currentNullMember;
281        }
282    
283        /**
284         * Returns the 'all' member.
285         */
286        public RolapCubeMember getAllMember() {
287            return currentAllMember;
288        }
289    
290        /**
291         * Returns the display name of this catalog element.
292         * If no caption is defined, the name is returned.
293         */
294        public String getCaption() {
295            return rolapHierarchy.getCaption();
296        }
297    
298        /**
299         * Sets the display name of this catalog element.
300         */
301        public void setCaption(String caption) {
302            rolapHierarchy.setCaption(caption);
303        }
304    
305        void setMemberReader(MemberReader memberReader) {
306            rolapHierarchy.setMemberReader(memberReader);
307        }
308    
309        MemberReader getMemberReader() {
310            return reader;
311        }
312    
313        public void setDefaultMember(Member defaultMeasure) {
314            // refactor this!
315            rolapHierarchy.setDefaultMember(defaultMeasure);
316    
317            RolapCubeLevel level =
318                new RolapCubeLevel(
319                    (RolapLevel)rolapHierarchy.getDefaultMember().getLevel(),
320                    this);
321            currentDefaultMember =
322                new RolapCubeMember(
323                    null,
324                    (RolapMember)rolapHierarchy.getDefaultMember(),
325                    level,
326                    parentDimension.getCube());
327        }
328    
329        void init(MondrianDef.CubeDimension xmlDimension) {
330            // first init shared hierarchy
331            rolapHierarchy.init(xmlDimension);
332            // second init cube hierarchy
333            super.init(xmlDimension);
334        }
335    
336        /**
337         * TODO: Since this is part of a caching strategy, should be implemented
338         * as a Strategy Pattern, avoiding hirarchy.
339         */
340        public static interface RolapCubeHierarchyMemberReader
341            extends MemberReader
342        {
343            public RolapCubeMember lookupCubeMember(
344                final RolapCubeMember parent,
345                final RolapMember member,
346                final RolapCubeLevel level);
347            public MemberCacheHelper getRolapCubeMemberCacheHelper();
348        }
349    
350        /******
351    
352         RolapCubeMember Caching Approach:
353    
354         - RolapHierarchy.SmartMemberReader.SmartCacheHelper ->
355           This is the shared cache across shared hierarchies.  This member cache only
356           contains members loaded by non-cube specific member lookups.  This cache
357           should only contain RolapMembers, not RolapCubeMembers
358    
359         - RolapCubeHierarchy.RolapCubeHierarchyMemberReader.rolapCubeCacheHelper ->
360           This cache contains the RolapCubeMember objects, which are cube specific
361           wrappers of shared members.
362    
363         - RolapCubeHierarchy.RolapCubeHierarchyMemberReader.SmartCacheHelper ->
364           This is the inherited shared cache from SmartMemberReader, and is used when
365           a join with the fact table is necessary, SqlContextConstraint.isJoinRequired().
366           This cache may be redundant with rolapCubeCacheHelper.
367    
368         - A Special note regarding RolapCubeHierarchyMemberReader.cubeSource -
369           This class was required for the special situation getMemberBuilder() method
370           call from RolapNativeSet.  This class utilizes both the rolapCubeCacheHelper
371           class for storing RolapCubeMembers, and also the
372           RolapCubeHierarchyMemberReader's inherited SmartCacheHelper
373    
374    
375         ******/
376    
377    
378        /**
379         *  member reader wrapper - uses existing member reader,
380         *  but wraps and caches all intermediate members
381         *  <p>Synchronization. Most synchronization takes place within SmartMemberReader.
382         * All synchronization is done on the cacheHelper object.
383          */
384        public class CacheRolapCubeHierarchyMemberReader
385            extends SmartMemberReader
386            implements RolapCubeHierarchyMemberReader
387        {
388            /**
389             * cubeSource is passed as our member builder
390             */
391            protected final RolapCubeSqlMemberSource cubeSource;
392    
393            /**
394             * this cache caches RolapCubeMembers that are light wrappers around
395             * shared and non-shared Hierarchy RolapMembers.  The inherited
396             * cacheHelper object contains non-shared hierarchy RolapMembers.
397             * non-shared hierarchy RolapMembers are created when a member lookup
398             * involves the Cube's fact table.
399             */
400            protected MemberCacheHelper rolapCubeCacheHelper;
401            private final boolean enableCache =
402                MondrianProperties.instance().EnableRolapCubeMemberCache.get();
403    
404            public CacheRolapCubeHierarchyMemberReader() {
405                super(new SqlMemberSource(RolapCubeHierarchy.this));
406                rolapCubeCacheHelper =
407                    new MemberCacheHelper(RolapCubeHierarchy.this);
408    
409                cubeSource =
410                    new RolapCubeSqlMemberSource(
411                        this,
412                        RolapCubeHierarchy.this,
413                        rolapCubeCacheHelper,
414                        cacheHelper);
415    
416                cubeSource.setCache(getMemberCache());
417            }
418    
419            public MemberBuilder getMemberBuilder() {
420                return this.cubeSource;
421            }
422    
423            public MemberCacheHelper getRolapCubeMemberCacheHelper() {
424                return rolapCubeCacheHelper;
425            }
426    
427            public List<RolapMember> getRootMembers() {
428                if (rootMembers == null) {
429                    rootMembers =
430                        getMembersInLevel(
431                            (RolapCubeLevel) getLevels()[0],
432                            0,
433                            Integer.MAX_VALUE);
434                }
435                return rootMembers;
436            }
437    
438            private String getUniqueNameForMemberWithoutHierarchy(
439                RolapMember member)
440            {
441                String name =
442                    (String) member.getPropertyValue(
443                            Property.UNIQUE_NAME_WITHOUT_HIERARCHY.getName());
444                RolapMember parent = member;
445                if (name == null) {
446                    StringBuilder fullName = new StringBuilder();
447                    while (parent != null) {
448                        fullName.append("[").append(parent.getName()).append("]");
449                        parent = parent.getParentMember();
450                    }
451                    name = fullName.toString();
452                    member.setProperty(
453                            Property.UNIQUE_NAME_WITHOUT_HIERARCHY.getName(),
454                            name);
455                }
456                return name;
457            }
458    
459            protected void readMemberChildren(
460                List<RolapMember> parentMembers,
461                List<RolapMember> children,
462                MemberChildrenConstraint constraint)
463            {
464                List<RolapMember> rolapChildren = new ArrayList<RolapMember>();
465                List<RolapMember> rolapParents = new ArrayList<RolapMember>();
466                Map<String, RolapCubeMember> lookup =
467                    new HashMap<String, RolapCubeMember>();
468    
469                // extract RolapMembers from their RolapCubeMember objects
470                // populate lookup for reconnecting parents and children
471                final List<RolapCubeMember> parentRolapCubeMemberList =
472                    Util.cast(parentMembers);
473                for (RolapCubeMember member : parentRolapCubeMemberList) {
474                    lookup.put(
475                        getUniqueNameForMemberWithoutHierarchy(
476                            member.getRolapMember()), member);
477                    rolapParents.add(member.getRolapMember());
478                }
479    
480                // get member children from shared member reader if possible,
481                // if not get them from our own source
482                boolean joinReq =
483                    (constraint instanceof SqlContextConstraint)
484                    && (((SqlContextConstraint)constraint).isJoinRequired() ||
485                        ((SqlContextConstraint)constraint).getEvaluator().isNonEmpty());
486                if (joinReq) {
487                    super.readMemberChildren(parentMembers, rolapChildren, constraint);
488                } else {
489                    rolapHierarchy.getMemberReader().getMemberChildren(
490                        rolapParents, rolapChildren, constraint);
491                }
492    
493                // now lookup or create RolapCubeMember
494                for (RolapMember currMember : rolapChildren) {
495                    RolapCubeMember parent =
496                        lookup.get(
497                            getUniqueNameForMemberWithoutHierarchy(
498                                currMember.getParentMember()));
499                    RolapCubeLevel level =
500                        parent.getLevel().getChildLevel();
501                    if (level == null) {
502                        // most likely a parent child hierarchy
503                        level = parent.getLevel();
504                    }
505                    RolapCubeMember newmember =
506                        lookupCubeMember(
507                            parent, currMember, level);
508                    children.add(newmember);
509                }
510    
511                // Put them in a temporary hash table first. Register them later,
512                // when we know their size (hence their 'cost' to the cache pool).
513                Map<RolapMember, List<RolapMember>> tempMap =
514                    new HashMap<RolapMember, List<RolapMember>>();
515                for (RolapMember member1 : parentMembers) {
516                    tempMap.put(member1, Collections.<RolapMember>emptyList());
517                }
518    
519                // note that this stores RolapCubeMembers in our cache,
520                // which also stores RolapMembers.
521    
522                for (RolapMember child : children) {
523                // todo: We could optimize here. If members.length is small, it's
524                // more efficient to drive from members, rather than hashing
525                // children.length times. We could also exploit the fact that the
526                // result is sorted by ordinal and therefore, unless the "members"
527                // contains members from different levels, children of the same
528                // member will be contiguous.
529                    assert child != null : "child";
530                    final RolapMember parentMember = child.getParentMember();
531                    List<RolapMember> cacheList = tempMap.get(parentMember);
532                    if (cacheList == null) {
533                        // The list is null if, due to dropped constraints, we now
534                        // have a children list of a member we didn't explicitly
535                        // ask for it. Adding it to the cache would be viable, but
536                        // let's ignore it.
537                        continue;
538                    } else if (cacheList == Collections.EMPTY_LIST) {
539                        cacheList = new ArrayList<RolapMember>();
540                        tempMap.put(parentMember, cacheList);
541                    }
542                    cacheList.add(child);
543                }
544    
545                synchronized (cacheHelper) {
546                    for (Map.Entry<RolapMember, List<RolapMember>> entry :
547                        tempMap.entrySet())
548                    {
549                        final RolapMember member = entry.getKey();
550                        if (rolapCubeCacheHelper.getChildrenFromCache(
551                            member, constraint) == null)
552                        {
553                            final List<RolapMember> cacheList = entry.getValue();
554                            if (enableCache) {
555                                rolapCubeCacheHelper.putChildren(
556                                    member, constraint, cacheList);
557                            }
558                        }
559                    }
560                }
561            }
562    
563            public void getMemberChildren(
564                    List<RolapMember> parentMembers,
565                    List<RolapMember> children,
566                    MemberChildrenConstraint constraint) {
567    
568                synchronized (cacheHelper) {
569                    checkCacheStatus();
570    
571                    List<RolapMember> missed = new ArrayList<RolapMember>();
572                    for (RolapMember parentMember : parentMembers) {
573                        List<RolapMember> list =
574                            rolapCubeCacheHelper.getChildrenFromCache(parentMember, constraint);
575                        if (list == null) {
576                            // the null member has no children
577                            if (!parentMember.isNull()) {
578                                missed.add(parentMember);
579                            }
580                        } else {
581                            children.addAll(list);
582                        }
583                    }
584                    if (missed.size() > 0) {
585                        readMemberChildren(missed, children, constraint);
586                    }
587                }
588            }
589    
590    
591            public List<RolapMember> getMembersInLevel(
592                RolapLevel level,
593                int startOrdinal,
594                int endOrdinal,
595                TupleConstraint constraint)
596            {
597                synchronized (cacheHelper) {
598                    checkCacheStatus();
599    
600                    List<RolapMember> members =
601                        rolapCubeCacheHelper.getLevelMembersFromCache(
602                            level, constraint);
603                    if (members != null) {
604                        return members;
605                    }
606    
607                    // if a join is required, we need to pass in the RolapCubeLevel
608                    // vs. the regular level
609                    boolean joinReq =
610                        (constraint instanceof SqlContextConstraint)
611                            && (((SqlContextConstraint)constraint).isJoinRequired() ||
612                            constraint.getEvaluator().isNonEmpty());
613    
614                    List<RolapMember> list;
615                    if (!joinReq) {
616                        list =
617                            rolapHierarchy.getMemberReader().getMembersInLevel(
618                                ((RolapCubeLevel) level).getRolapLevel(),
619                                startOrdinal, endOrdinal, constraint);
620                    } else {
621                        list =
622                            super.getMembersInLevel(
623                                level, startOrdinal, endOrdinal, constraint);
624                    }
625                    List<RolapMember> newlist = new ArrayList<RolapMember>();
626                    for (RolapMember member : list) {
627                        // note that there is a special case for the all member
628    
629                        // REVIEW: disabled, to see what happens. if this code is for
630                        // performance, we should check level.isAll at the top of the
631                        // method; if it is for correctness, leave the code in
632                        if (false && member == rolapHierarchy.getAllMember()) {
633                            newlist.add(getAllMember());
634                        } else {
635                            // i'm not quite sure about this one yet
636                            RolapCubeMember parent = null;
637                            if (member.getParentMember() != null) {
638                                parent =
639                                    createAncestorMembers(
640                                        (RolapCubeLevel) level.getParentLevel(),
641                                        member.getParentMember());
642                            }
643                            RolapCubeMember newmember =
644                                lookupCubeMember(
645                                    parent, member, (RolapCubeLevel) level);
646                            newlist.add(newmember);
647                        }
648                    }
649                    rolapCubeCacheHelper.putLevelMembersInCache(
650                        level, constraint, newlist);
651    
652                    return newlist;
653                }
654            }
655    
656            private RolapCubeMember createAncestorMembers(
657                RolapCubeLevel level,
658                RolapMember member)
659            {
660                RolapCubeMember parent = null;
661                if (member.getParentMember() != null) {
662                    parent =
663                        createAncestorMembers(
664                            level.getParentLevel(), member.getParentMember());
665                }
666                return lookupCubeMember(parent, member, level);
667            }
668    
669            public RolapCubeMember lookupCubeMember(
670                RolapCubeMember parent,
671                RolapMember member,
672                RolapCubeLevel level)
673            {
674                synchronized (cacheHelper) {
675                    if (member.getKey() == null) {
676                        if (member.isAll()) {
677                            return getAllMember();
678                        }
679    
680                        throw new NullPointerException();
681                    }
682    
683                    RolapCubeMember cubeMember;
684                    if (enableCache) {
685                        Object key =
686                            rolapCubeCacheHelper.makeKey(parent, member.getKey());
687                        cubeMember = (RolapCubeMember)
688                            rolapCubeCacheHelper.getMember(key, false);
689                        if (cubeMember == null) {
690                            cubeMember =
691                                new RolapCubeMember(
692                                    parent, member, level, parentDimension.getCube());
693                            rolapCubeCacheHelper.putMember(key, cubeMember);
694                        }
695                    } else {
696                        cubeMember =
697                            new RolapCubeMember(
698                                parent, member, level, parentDimension.getCube());
699                    }
700                    return cubeMember;
701                }
702            }
703    
704            public int getMemberCount() {
705                return rolapHierarchy.getMemberReader().getMemberCount();
706            }
707    
708            protected void checkCacheStatus() {
709                synchronized (cacheHelper) {
710                    // if necessary, flush all caches:
711                    //   - shared SmartMemberReader RolapMember cache
712                    //   - local key to cube member RolapCubeMember cache
713                    //   - cube source RolapCubeMember cache
714                    //   - local regular RolapMember cache, used when cube
715                    //     specific joins occur
716    
717                    if (cacheHelper.getChangeListener() != null) {
718                        if (cacheHelper.getChangeListener()
719                                .isHierarchyChanged(getHierarchy())) {
720                            cacheHelper.flushCache();
721                            rolapCubeCacheHelper.flushCache();
722    
723                            if (rolapHierarchy.getMemberReader()
724                                    instanceof SmartMemberReader) {
725                                SmartMemberReader smartMemberReader =
726                                    (SmartMemberReader)
727                                        rolapHierarchy.getMemberReader();
728                                if (smartMemberReader.getMemberCache()
729                                        instanceof MemberCacheHelper) {
730                                    MemberCacheHelper helper =
731                                        (MemberCacheHelper)
732                                            smartMemberReader.getMemberCache();
733                                    helper.flushCache();
734                                }
735                            }
736                        }
737                    }
738                }
739            }
740        }
741    
742        /**
743         * Same as RolapCubeHierarchyMemberReader but without caching anything.
744         */
745        public class NoCacheRolapCubeHierarchyMemberReader
746            extends NoCacheMemberReader
747            implements RolapCubeHierarchyMemberReader
748        {
749            public NoCacheRolapCubeHierarchyMemberReader() {
750                super(new SqlMemberSource(RolapCubeHierarchy.this));
751            }
752    
753            public List<RolapMember> getMembersInLevel(
754                final RolapLevel level,
755                int startOrdinal,
756                int endOrdinal,
757                TupleConstraint constraint)
758            {
759                final RolapCubeLevel cubeLevel = (RolapCubeLevel) level;
760                // if a join is required, we need to pass in the RolapCubeLevel
761                // vs. the regular level
762                boolean joinReq = (constraint instanceof SqlContextConstraint)
763                && ((SqlContextConstraint)constraint).isJoinRequired();
764                final List<RolapMember> list;
765                if (!joinReq) {
766                    list =
767                        rolapHierarchy.getMemberReader().getMembersInLevel(
768                            cubeLevel.getRolapLevel(),
769                            startOrdinal, endOrdinal, constraint);
770                } else {
771                    list =
772                        super.getMembersInLevel(
773                            cubeLevel, startOrdinal, endOrdinal, constraint);
774                }
775                return new UnsupportedList<RolapMember>() {
776                    public RolapMember get(final int index) {
777                        return mutate(list.get(index));
778                    }
779    
780                    public int size() {
781                        return list.size();
782                    }
783    
784                    public Iterator<RolapMember> iterator() {
785                        final Iterator<RolapMember> it = list.iterator();
786                        return new Iterator<RolapMember>() {
787                            public boolean hasNext() {
788                                return it.hasNext();
789                            }
790                            public RolapMember next() {
791                                return mutate(it.next());
792                            }
793    
794                            public void remove() {
795                                throw new UnsupportedOperationException();
796                            }
797                        };
798                    }
799    
800                    private RolapMember mutate(final RolapMember member) {
801                        RolapCubeMember parent = null;
802                        if (member.getParentMember() != null) {
803                            parent =
804                                createAncestorMembers(
805                                    cubeLevel.getParentLevel(),
806                                    member.getParentMember());
807                        }
808                        return lookupCubeMember(
809                            parent,
810                            member,
811                            cubeLevel);
812                    }
813                };
814            }
815    
816            public MemberCacheHelper getRolapCubeMemberCacheHelper() {
817                return null;
818            }
819    
820            public RolapCubeMember lookupCubeMember(
821                final RolapCubeMember parent,
822                final RolapMember member,
823                final RolapCubeLevel level)
824            {
825                if (member.getKey() == null) {
826                    if (member.isAll()) {
827                        return getAllMember();
828                    }
829                    throw new NullPointerException();
830                }
831                return new RolapCubeMember(
832                    parent, member, level, parentDimension.getCube());
833            }
834    
835            private RolapCubeMember createAncestorMembers(
836                final RolapCubeLevel level,
837                final RolapMember member)
838            {
839                RolapCubeMember parent = null;
840                if (member.getParentMember() != null) {
841                    parent =
842                        createAncestorMembers(
843                            level.getParentLevel(),
844                            member.getParentMember());
845                }
846                return lookupCubeMember(parent, member, level);
847            }
848        }
849    
850    
851        public static class RolapCubeSqlMemberSource extends SqlMemberSource {
852    
853            private final RolapCubeHierarchyMemberReader memberReader;
854            private final MemberCacheHelper memberSourceCacheHelper;
855            private final Object memberCacheLock;
856    
857            public RolapCubeSqlMemberSource(
858                RolapCubeHierarchyMemberReader memberReader,
859                RolapCubeHierarchy hierarchy,
860                MemberCacheHelper memberSourceCacheHelper,
861                Object memberCacheLock)
862            {
863                super(hierarchy);
864                this.memberReader = memberReader;
865                this.memberSourceCacheHelper = memberSourceCacheHelper;
866                this.memberCacheLock = memberCacheLock;
867            }
868    
869            public RolapMember makeMember(
870                    RolapMember parentMember,
871                    RolapLevel childLevel,
872                    Object value,
873                    Object captionValue,
874                    boolean parentChild,
875                    ResultSet resultSet,
876                    Object key,
877                    int columnOffset)
878                    throws SQLException {
879    
880                RolapMember member =
881                    super.makeMember(
882                        parentMember,
883                        ((RolapCubeLevel)childLevel).getRolapLevel(),
884                        value, captionValue, parentChild, resultSet, key,
885                        columnOffset);
886                return
887                    memberReader.lookupCubeMember(
888                        (RolapCubeMember)parentMember,
889                        member,
890                        (RolapCubeLevel)childLevel);
891            }
892    
893            public MemberCache getMemberCache() {
894                // this is a special cache used solely for rolapcubemembers
895                return memberSourceCacheHelper;
896            }
897    
898            /**
899             * use the same lock in the RolapCubeMemberSource as the
900             * RolapCubeHiearchyMemberReader to avoid deadlocks
901             */
902            public Object getMemberCacheLock() {
903                return memberCacheLock;
904            }
905        }
906    }
907    
908    // End RolapCubeHierarchy.java
909